Skip to content

Commit

Permalink
TTL Implementation completed
Browse files Browse the repository at this point in the history
  • Loading branch information
jackygurui committed Jun 10, 2016
1 parent b24a3e9 commit dd7b93f
Show file tree
Hide file tree
Showing 10 changed files with 223 additions and 215 deletions.
30 changes: 23 additions & 7 deletions src/main/java/org/redisson/RedissonAttachedLiveObjectService.java
@@ -1,16 +1,19 @@
package org.redisson;

import java.lang.reflect.Constructor;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.field.FieldDescription;
import net.bytebuddy.description.field.FieldList;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.implementation.FieldAccessor;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.bind.annotation.FieldProxy;
import net.bytebuddy.matcher.ElementMatchers;
import org.redisson.core.RExpirable;
import org.redisson.core.RExpirableAsync;
import org.redisson.core.RMap;
import org.redisson.core.RObject;
import org.redisson.core.RObjectAsync;
import org.redisson.liveobject.CodecProvider;
Expand All @@ -21,6 +24,7 @@
import org.redisson.liveobject.annotation.RId;
import org.redisson.liveobject.core.AccessorInterceptor;
import org.redisson.liveobject.core.ExpirableInterceptor;
import org.redisson.liveobject.core.LiveObjectInterceptor;
import org.redisson.liveobject.misc.Introspectior;

public class RedissonAttachedLiveObjectService implements RAttachedLiveObjectService {
Expand All @@ -38,16 +42,26 @@ public RedissonAttachedLiveObjectService(RedissonClient redisson, Map<Class, Cla

//TODO: Support ID Generator
@Override
public <T, K> T get(Class<T> entityClass, K id, long ttl) {
public <T, K> T get(Class<T> entityClass, K id, long timeToLive, TimeUnit timeUnit) {
T instance = get(entityClass, id);
RMap map = ((RLiveObject) instance).getLiveObjectLiveMap();
map.put("RLiveObjectDefaultTimeToLiveValue", timeToLive);
map.put("RLiveObjectDefaultTimeToLiveUnit", timeUnit.toString());
map.expire(timeToLive, timeUnit);
return instance;
}

@Override
public <T, K> T get(Class<T> entityClass, K id) {
try {
//TODO: support class with no arg constructor
return getProxyClass(entityClass).getConstructor(id.getClass()).newInstance(id);
T instance;
try {
instance = getProxyClass(entityClass).getDeclaredConstructor(id.getClass()).newInstance(id);
} catch (NoSuchMethodException exception) {
instance = getProxyClass(entityClass).newInstance();
}
((RLiveObject) instance).setLiveObjectId(id);
return instance;
} catch (Exception ex) {
unregisterClass(entityClass);
throw new RuntimeException(ex);
Expand Down Expand Up @@ -94,13 +108,15 @@ private <T, K> void registerClass(Class<T> entityClass) throws Exception {
Class<? extends T> loaded = builder.method(ElementMatchers.isDeclaredBy(
Introspectior.getTypeDescription(RLiveObject.class))
.and(ElementMatchers.isGetter().or(ElementMatchers.isSetter())))
.intercept(FieldAccessor.ofBeanProperty())
.intercept(MethodDelegation.to(new LiveObjectInterceptor(redisson, codecProvider, entityClass, idFieldName))
.appendParameterBinder(FieldProxy.Binder
.install(LiveObjectInterceptor.Getter.class, LiveObjectInterceptor.Setter.class)))
.implement(RLiveObject.class)
.method(ElementMatchers.isDeclaredBy(RExpirable.class)
.or(ElementMatchers.isDeclaredBy(RExpirableAsync.class))
.or(ElementMatchers.isDeclaredBy(RObject.class))
.or(ElementMatchers.isDeclaredBy(RObjectAsync.class)))
.intercept(MethodDelegation.to(new ExpirableInterceptor()))
.intercept(MethodDelegation.to(ExpirableInterceptor.class))
.implement(RExpirable.class)
.method(ElementMatchers.not(ElementMatchers.isDeclaredBy(Object.class))
.and(ElementMatchers.not(ElementMatchers.isDeclaredBy(RLiveObject.class)))
Expand All @@ -112,7 +128,7 @@ private <T, K> void registerClass(Class<T> entityClass) throws Exception {
.or(ElementMatchers.isSetter()))
.and(ElementMatchers.isPublic()))
.intercept(MethodDelegation.to(
new AccessorInterceptor(redisson, codecProvider, entityClass, idFieldName)))
new AccessorInterceptor(redisson, codecProvider)))
.make().load(getClass().getClassLoader(),
ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
Expand Down
25 changes: 15 additions & 10 deletions src/main/java/org/redisson/RedissonReference.java
Expand Up @@ -2,6 +2,7 @@

import org.redisson.client.codec.Codec;
import org.redisson.core.RObject;
import org.redisson.liveobject.annotation.REntity;

/**
*
Expand All @@ -16,16 +17,17 @@ public class RedissonReference {
public RedissonReference() {
}

public RedissonReference(Class<? extends RObject> type, String keyName) {
this.type = type.getCanonicalName();
this.keyName = keyName;
this.codec = null;
public RedissonReference(Class type, String keyName) {
this(type, keyName, null);
}

public RedissonReference(Class<? extends RObject> type, String keyName, Codec codec) {
this.type = type.getCanonicalName();
public RedissonReference(Class type, String keyName, Codec codec) {
if (!type.isAnnotationPresent(REntity.class) && !RObject.class.isAssignableFrom(type)) {
throw new IllegalArgumentException("Class reference has to be a type of either RObject or RLiveObject");
}
this.type = type.getName();
this.keyName = keyName;
this.codec = codec.getClass().getCanonicalName();
this.codec = codec != null ? codec.getClass().getCanonicalName() : null;
}

public boolean isDefaultCodec() {
Expand All @@ -35,8 +37,8 @@ public boolean isDefaultCodec() {
/**
* @return the type
*/
public Class<? extends RObject> getType() throws Exception {
return (Class<? extends RObject>) Class.forName(type);
public Class getType() throws Exception {
return Class.forName(type);
}

/**
Expand All @@ -49,7 +51,10 @@ public String getTypeName() {
/**
* @param type the type to set
*/
public void setType(Class<? extends RObject> type) {
public void setType(Class type) {
if (!type.isAnnotationPresent(REntity.class) && !RObject.class.isAssignableFrom(type)) {
throw new IllegalArgumentException("Class reference has to be a type of either RObject or RLiveObject");
}
this.type = type.getCanonicalName();
}

Expand Down
11 changes: 2 additions & 9 deletions src/main/java/org/redisson/liveobject/LiveObjectTemplate.java
Expand Up @@ -6,16 +6,9 @@
*
* @author Rui Gu (https://github.com/jackygurui)
*/
public class LiveObjectTemplate implements RLiveObject {
public class LiveObjectTemplate {

private Object liveObjectId;
private RMap liveObjectLiveMap;

/**
* @return the liveObjectLiveMap
*/
@Override
public RMap getLiveObjectLiveMap() {
return liveObjectLiveMap;
}

}
@@ -1,5 +1,7 @@
package org.redisson.liveobject;

import java.util.concurrent.TimeUnit;

/**
*
* @author Rui Gu (https://github.com/jackygurui)
Expand All @@ -12,14 +14,15 @@ public interface RAttachedLiveObjectService extends RLiveObjectService {
*
* @param entityClass Entity class
* @param id identifier
* @param ttl sets the time to live on the object. Any calls to the accessor
* @param timeToLive sets the time to live on the object. Any calls to the accessor
* of this object will renew this. If it is not been accessed
* before the ttl reaches. This object is then expires and
* removed from redis. Think of it is been garbage collected.
* @param timeUnit sets the time unit of the time to live balue on the object.
* @param <T> Entity type
* @param <K> Key type
* @return In ATTACHED Mode, this always returns a proxy class. Even it does
* not exist in redis.
*/
public <T, K> T get(Class<T> entityClass, K id, long ttl);
public <T, K> T get(Class<T> entityClass, K id, long timeToLive, TimeUnit timeUnit);
}
13 changes: 12 additions & 1 deletion src/main/java/org/redisson/liveobject/RLiveObject.java
Expand Up @@ -7,9 +7,20 @@
* @author Rui Gu (https://github.com/jackygurui)
*/
public interface RLiveObject {

/**
* @return the liveObjectLiveMap
*/
public RMap getLiveObjectLiveMap();

/**
* @return the liveObjectId
*/
public Object getLiveObjectId();

/**
* @param liveObjectId the liveObjectId to set
*/
public void setLiveObjectId(Object liveObjectId);

}
25 changes: 23 additions & 2 deletions src/main/java/org/redisson/liveobject/annotation/REntity.java
Expand Up @@ -15,14 +15,20 @@
@Target({ElementType.TYPE})
public @interface REntity {


Class<? extends NamingScheme> namingScheme() default DefaultNamingScheme.class;

Class<? extends Codec> codec() default JsonJacksonCodec.class;

public interface NamingScheme {

public String getName(Class cls, String idFieldName, Object id);

public String resolveClassName(String name);

public String resolveIdFieldName(String name);

public Object resolveId(String name);

}

public class DefaultNamingScheme implements NamingScheme {
Expand All @@ -34,5 +40,20 @@ public String getName(Class cls, String idFieldName, Object id) {
return "redisson_live_object:{class=" + cls.getName() + ", " + idFieldName + "=" + id.toString() + "}";
}

@Override
public String resolveClassName(String name) {
return name.substring("redisson_live_object:{class=".length(), name.indexOf(","));
}

@Override
public String resolveIdFieldName(String name) {
return name.substring(name.indexOf(", ") + 2, name.indexOf("=", name.indexOf("=") + 1));
}

@Override
public Object resolveId(String name) {
return name.substring(name.indexOf("=", name.indexOf("=") + 1) + 1, name.length() - 1);
}

}
}

0 comments on commit dd7b93f

Please sign in to comment.