Fix for problem with cglib proxies in spring boot apps

This commit is contained in:
Andy Clement
2014-02-04 15:09:48 -08:00
parent 59be31b9f9
commit fd4244aca1
5 changed files with 52 additions and 13 deletions

View File

@@ -273,7 +273,7 @@ public class ReloadableType {
*/
public boolean loadNewVersion(String versionsuffix, byte[] newbytedata) {
javaMethodCache = null;
if (log.isLoggable(Level.INFO)) {
if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
log.info("Loading new version of "+slashedtypename+", identifying suffix "+versionsuffix+", new data length is "+newbytedata.length+"bytes");
}
@@ -454,6 +454,12 @@ public class ReloadableType {
ReloadableType proxy = typeRegistry.cglibProxies.get(this.slashedtypename);
if (proxy != null) {
Object[] strategyAndGeneratorPair = CglibPluginCapturing.clazzToGeneratorStrategyAndClassGeneratorMap.get(getClazz());
if (strategyAndGeneratorPair == null) {
if (log.isLoggable(Level.SEVERE)) {
log.severe("Unable to find regeneration methods for cglib proxies - proxies will be out of date for this type");
}
return;
}
Object a = strategyAndGeneratorPair[0];
Object b = strategyAndGeneratorPair[1];
// want to call a.generate(b)

View File

@@ -28,18 +28,25 @@ import org.springsource.loaded.LoadtimeInstrumentationPlugin;
*/
public class CglibPlugin implements LoadtimeInstrumentationPlugin {
// private static Logger log = Logger.getLogger(CglibPlugin.class.getName());
// private static Logger log = Logger.getLogger(CglibPlugin.class.getName());
// implementing LoadtimeInstrumentationPlugin
public boolean accept(String slashedTypeName, ClassLoader classLoader, ProtectionDomain protectionDomain, byte[] bytes) {
if (slashedTypeName==null) {
return false;
}
return slashedTypeName.equals("net/sf/cglib/core/AbstractClassGenerator");
// if (slashedTypeName.contains("cglib")) {
// System.out.println(">>CglibPlugin.accept("+slashedTypeName+")");
// }
// Seen in the wild:
// net/sf/cglib/core/AbstractClassGenerator
// org/springframework/cglib/core/AbstractClassGenerator
return slashedTypeName.endsWith("/cglib/core/AbstractClassGenerator");
// || slashedTypeName.equals("net/sf/cglib/reflect/FastClass");
}
public byte[] modify(String slashedClassName, ClassLoader classLoader, byte[] bytes) {
System.out.println(">> CglibPlugin.modify("+slashedClassName+","+classLoader+","+bytes.length);
// if (slashedClassName.equals("net/sf/cglib/core/AbstractClassGenerator")) {
return CglibPluginCapturing.catchGenerate(bytes);
// } else {

View File

@@ -26,7 +26,6 @@ import org.objectweb.asm.MethodAdapter;
import org.objectweb.asm.MethodVisitor;
import org.springsource.loaded.Constants;
/**
* This bytecode rewriter intercepts calls to generate made in the CGLIB framework and allows us to record what generator is called
* to create the proxy for some type. The same generator can then be driven again if the type is reloaded.
@@ -38,7 +37,8 @@ public class CglibPluginCapturing extends ClassAdapter implements Constants {
public static Map<Class<?>, Object[]> clazzToGeneratorStrategyAndClassGeneratorMap = new HashMap<Class<?>, Object[]>();
public static Map<Class<?>, Object[]> clazzToGeneratorStrategyAndFastClassGeneratorMap = new HashMap<Class<?>, Object[]>();
public String prefix;
public static byte[] catchGenerate(byte[] bytesIn) {
ClassReader cr = new ClassReader(bytesIn);
CglibPluginCapturing ca = new CglibPluginCapturing();
@@ -50,6 +50,16 @@ public class CglibPluginCapturing extends ClassAdapter implements Constants {
private CglibPluginCapturing() {
super(new ClassWriter(0)); // TODO review 0 here
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
// The name could be a repackaged form of cglib:
// net/sf/cglib/core/AbstractClassGenerator
// org/springframework/cglib/core/AbstractClassGenerator
int index = name.indexOf("/cglib");
prefix = name.substring(0,index);
super.visit(version,access,name,signature,superName,interfaces);
}
public byte[] getBytes() {
return ((ClassWriter) cv).toByteArray();
@@ -64,7 +74,7 @@ public class CglibPluginCapturing extends ClassAdapter implements Constants {
}
}
static class CreateMethodInterceptor extends MethodAdapter implements Constants {
class CreateMethodInterceptor extends MethodAdapter implements Constants {
public CreateMethodInterceptor(MethodVisitor mv) {
super(mv);
@@ -88,8 +98,8 @@ public class CglibPluginCapturing extends ClassAdapter implements Constants {
// ALOAD 0
// INVOKEINTERFACE net/sf/cglib/core/GeneratorStrategy.generate(Lnet/sf/cglib/core/ClassGenerator;)[B
mv.visitVarInsn(ALOAD, 0); // AbstractClassGenerator instance
mv.visitFieldInsn(GETFIELD, "net/sf/cglib/core/AbstractClassGenerator", "strategy",
"Lnet/sf/cglib/core/GeneratorStrategy;");
mv.visitFieldInsn(GETFIELD, prefix+"/cglib/core/AbstractClassGenerator", "strategy",
"L"+prefix+"/cglib/core/GeneratorStrategy;");
mv.visitVarInsn(ALOAD, 0); // AbstractClassGenerator instance
mv.visitMethodInsn(INVOKESTATIC, "org/springsource/loaded/agent/CglibPluginCapturing", "record",
"(Ljava/lang/Object;Ljava/lang/Object;)V");//Lnet/sf/cglib/core/GeneratorStrategy;Lnet/sf/cglib/core/AbstractClassGenerator);");
@@ -114,7 +124,7 @@ public class CglibPluginCapturing extends ClassAdapter implements Constants {
// a is something like 'UndeclaredThrowableStrategy'
// b is an Enhancer: namePrefix="example.Simple" superclass=example.Simple
String generatorName = b.getClass().getName();
if (generatorName.equals("net.sf.cglib.proxy.Enhancer")) {
if (generatorName.endsWith(".cglib.proxy.Enhancer")) {
try {
Field f = b.getClass().getDeclaredField("superclass");
f.setAccessible(true);
@@ -124,7 +134,7 @@ public class CglibPluginCapturing extends ClassAdapter implements Constants {
} catch (Throwable re) {
re.printStackTrace();
}
} else if (generatorName.equals("net.sf.cglib.reflect.FastClass$Generator")) {
} else if (generatorName.endsWith(".cglib.reflect.FastClass$Generator")) {
try {
Field f = b.getClass().getDeclaredField("type");
f.setAccessible(true);

View File

@@ -117,7 +117,7 @@ public class SpringPlugin implements LoadtimeInstrumentationPlugin, ReloadEventP
removeClazzFromMethodResolverCache(clazz);
clearCachedIntrospectionResults(clazz);
reinvokeDetectHandlers(); // Spring 3.0
reinvokeInitHandlerMethods(); // Spring 3.1
reinvokeInitHandlerMethods(); // Spring 3.1
}
private void removeClazzFromMethodResolverCache(Class<?> clazz) {
@@ -211,6 +211,15 @@ public class SpringPlugin implements LoadtimeInstrumentationPlugin, ReloadEventP
Method method_initHandlerMethods = clazz_AbstractHandlerMethodMapping.getDeclaredMethod("initHandlerMethods");
method_initHandlerMethods.setAccessible(true);
method_initHandlerMethods.invoke(o);
} catch (NoSuchFieldException nsfe) {
if (log.isLoggable(Level.WARNING)) {
if (nsfe.getMessage().equals("handlerMethods")) {
log.warning("problem resetting request mapping handlers - unable to find field 'handlerMethods' on type 'AbstractHandlerMethodMapping' - you probably are not on Spring 3.1");
}
else {
log.warning("problem resetting request mapping handlers - NoSuchFieldException: "+nsfe.getMessage());
}
}
} catch (Exception e) {
e.printStackTrace();
}