Skip to content

Commit

Permalink
Found the root cause of the redisson shutdown exception problem
Browse files Browse the repository at this point in the history
The classCache and proxyCache is been set to static (class level) which
means previous test have registered the class is still been cached even
after the tests were finished. New tests fetched the old proxied class
from the cache with reference to a redisson instance that has been
created in the previous tests and that has already been shutdown when
the tests were finished.
  • Loading branch information
jackygurui committed Jun 5, 2016
1 parent 248f961 commit 6f028eb
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 49 deletions.
10 changes: 9 additions & 1 deletion src/main/java/org/redisson/Redisson.java
Expand Up @@ -75,6 +75,7 @@
import org.redisson.core.RTopic;

import io.netty.util.concurrent.Future;
import io.netty.util.internal.PlatformDependent;
import java.util.concurrent.TimeUnit;

/**
Expand All @@ -89,6 +90,12 @@ public class Redisson implements RedissonClient {
private final EvictionScheduler evictionScheduler;
private final CommandExecutor commandExecutor;
private final ConnectionManager connectionManager;

private final Map<Class, Class> liveObjectClassCache
= PlatformDependent.<Class, Class>newConcurrentHashMap();
private final Map<Class, Class> liveObjectProxyCache
= PlatformDependent.<Class, Class>newConcurrentHashMap();

private final Config config;

private final UUID id = UUID.randomUUID();
Expand Down Expand Up @@ -392,6 +399,7 @@ public RScript getScript() {
return new RedissonScript(commandExecutor);
}

@Override
public RRemoteService getRemoteSerivce() {
return new RedissonRemoteService(this);
}
Expand Down Expand Up @@ -533,7 +541,7 @@ public RBatch createBatch() {

@Override
public RedissonAttachedLiveObjectService getAttachedLiveObjectService() {
return new RedissonAttachedLiveObjectService(this, commandExecutor);
return new RedissonAttachedLiveObjectService(this, commandExecutor, liveObjectClassCache, liveObjectProxyCache);
}

@Override
Expand Down
36 changes: 9 additions & 27 deletions src/main/java/org/redisson/RedissonAttachedLiveObjectService.java
@@ -1,7 +1,6 @@
package org.redisson;

import io.netty.util.internal.PlatformDependent;
import java.lang.reflect.Field;
import java.util.Map;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.field.FieldDescription;
Expand All @@ -19,17 +18,17 @@

public class RedissonAttachedLiveObjectService implements RAttachedLiveObjectService {

private static final Map<Class, Class> classCache
= PlatformDependent.<Class, Class>newConcurrentHashMap();
private static final Map<Class, Class> proxyCache
= PlatformDependent.<Class, Class>newConcurrentHashMap();
private final Map<Class, Class> classCache;
private final Map<Class, Class> proxyCache;

private final RedissonClient redisson;
private final CommandAsyncExecutor commandExecutor;

public RedissonAttachedLiveObjectService(RedissonClient redisson, CommandAsyncExecutor commandExecutor) {
public RedissonAttachedLiveObjectService(RedissonClient redisson, CommandAsyncExecutor commandExecutor, Map<Class, Class> classCache, Map<Class, Class> proxyCache) {
this.redisson = redisson;
this.commandExecutor = commandExecutor;
this.classCache = classCache;
this.proxyCache = proxyCache;
}

//TODO: Support ID Generator
Expand All @@ -42,11 +41,7 @@ public <T, K> T get(Class<T> entityClass, K id, long ttl) {
public <T, K> T get(Class<T> entityClass, K id) {
try {
//TODO: support class with no arg constructor
T instance = getProxyClass(entityClass).getConstructor(id.getClass()).newInstance(id);
AccessorInterceptor interceptor = getInterceptor(instance);
interceptor.setCommandExecutor(commandExecutor);
interceptor.setRedisson(redisson);
return instance;
return getProxyClass(entityClass).getConstructor(id.getClass()).newInstance(id);
} catch (Exception ex) {
unregisterClass(entityClass);
throw new RuntimeException(ex);
Expand Down Expand Up @@ -88,38 +83,25 @@ private <T, K> void registerClass(Class<T> entityClass) throws Exception {
.and(ElementMatchers.isGetter()
.or(ElementMatchers.isSetter()))
.and(ElementMatchers.isPublic()))
.intercept(MethodDelegation.to(new AccessorInterceptor(entityClass, idFieldName)))
.intercept(MethodDelegation.to(new AccessorInterceptor(redisson, commandExecutor, entityClass, idFieldName)))
.make().load(getClass().getClassLoader(),
ClassLoadingStrategy.Default.WRAPPER)
.getLoaded());
proxyCache.putIfAbsent(classCache.get(entityClass), entityClass);
}

private AccessorInterceptor getInterceptor(Object proxy) throws Exception {
for (Field field : proxy.getClass().getFields()) {
if (field.getType().isAssignableFrom(AccessorInterceptor.class)) {
return (AccessorInterceptor) field.get(proxy);
}
}
return null;
}

public static void unregisterProxy(Class proxy) {
public void unregisterProxy(Class proxy) {
Class cls = proxyCache.remove(proxy);
if (cls != null) {
classCache.remove(cls);
}
}

public static void unregisterClass(Class cls) {
public void unregisterClass(Class cls) {
Class proxy = classCache.remove(cls);
if (proxy != null) {
proxyCache.remove(proxy);
}
}

public static Class getActualClass(Class proxyClass) {
return proxyCache.get(proxyClass);
}

}
39 changes: 18 additions & 21 deletions src/main/java/org/redisson/liveobject/core/AccessorInterceptor.java
Expand Up @@ -25,13 +25,16 @@
*/
public class AccessorInterceptor {

private RedissonClient redisson;
private CommandAsyncExecutor commandExecutor;
private final RedissonClient redisson;
private final CommandAsyncExecutor commandExecutor;
private final Class originalClass;
private final String idFieldName;
private final REntity.NamingScheme namingScheme;

public AccessorInterceptor(Class entityClass, String idFieldName) throws Exception {
private RMap liveMap;

public AccessorInterceptor(RedissonClient redisson, CommandAsyncExecutor commandExecutor, Class entityClass, String idFieldName) throws Exception {
this.redisson = redisson;
this.commandExecutor = commandExecutor;
this.originalClass = entityClass;
this.idFieldName = idFieldName;
this.namingScheme = ((REntity) entityClass.getAnnotation(REntity.class))
Expand All @@ -44,8 +47,9 @@ public Object intercept(@Origin Method method, @SuperCall Callable<?> superMetho
if (isGetter(method, idFieldName)) {
return superMethod.call();
}
RMap liveMap = redisson.getMap(getMapKey(getId(me)));
initLiveMapIfRequired(getId(me));
if (isSetter(method, idFieldName)) {
//TODO: distributed locking maybe required.
superMethod.call();
try {
liveMap.rename(getMapKey(args[0]));
Expand All @@ -54,6 +58,7 @@ public Object intercept(@Origin Method method, @SuperCall Callable<?> superMetho
throw e;
}
}
liveMap = null;
return null;
}
String fieldName = getFieldName(method);
Expand Down Expand Up @@ -85,6 +90,12 @@ public Object intercept(@Origin Method method, @SuperCall Callable<?> superMetho
return superMethod.call();
}

private void initLiveMapIfRequired(Object id) {
if (liveMap == null) {
liveMap = redisson.getMap(getMapKey(id));
}
}

private String getFieldName(Method method) {
return method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4);
}
Expand Down Expand Up @@ -112,29 +123,15 @@ private static String getFieldNameSuffix(String fieldName) {
}

private static Object getFieldValue(Object o, String fieldName) throws Exception {
return RedissonAttachedLiveObjectService.getActualClass(o.getClass()).getDeclaredMethod("get" + getFieldNameSuffix(fieldName)).invoke(o);
return o.getClass().getSuperclass().getDeclaredMethod("get" + getFieldNameSuffix(fieldName)).invoke(o);
}

private static Object getREntityId(Object o) throws Exception {
String idName = Introspectior
.getFieldsWithAnnotation(RedissonAttachedLiveObjectService.getActualClass(o.getClass()), RId.class)
.getFieldsWithAnnotation(o.getClass().getSuperclass(), RId.class)
.getOnly()
.getName();
return getFieldValue(o, idName);
}

/**
* @param redisson the redisson to set
*/
public void setRedisson(RedissonClient redisson) {
this.redisson = redisson;
}

/**
* @param commandExecutor the commandExecutor to set
*/
public void setCommandExecutor(CommandAsyncExecutor commandExecutor) {
this.commandExecutor = commandExecutor;
}

}

0 comments on commit 6f028eb

Please sign in to comment.