From fd4244aca10ea54adb900a65c89d2c40ec95a808 Mon Sep 17 00:00:00 2001 From: Andy Clement Date: Tue, 4 Feb 2014 15:09:48 -0800 Subject: [PATCH] Fix for problem with cglib proxies in spring boot apps --- .../springsource/loaded/ReloadableType.java | 8 ++++++- .../loaded/agent/CglibPlugin.java | 11 +++++++-- .../loaded/agent/CglibPluginCapturing.java | 24 +++++++++++++------ .../loaded/agent/SpringPlugin.java | 11 ++++++++- .../infra/TestClassloaderWithRewriting.java | 11 +++++++-- 5 files changed, 52 insertions(+), 13 deletions(-) diff --git a/springloaded/src/main/java/org/springsource/loaded/ReloadableType.java b/springloaded/src/main/java/org/springsource/loaded/ReloadableType.java index 546be6d..a04120f 100644 --- a/springloaded/src/main/java/org/springsource/loaded/ReloadableType.java +++ b/springloaded/src/main/java/org/springsource/loaded/ReloadableType.java @@ -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) diff --git a/springloaded/src/main/java/org/springsource/loaded/agent/CglibPlugin.java b/springloaded/src/main/java/org/springsource/loaded/agent/CglibPlugin.java index 215fc32..4eec126 100644 --- a/springloaded/src/main/java/org/springsource/loaded/agent/CglibPlugin.java +++ b/springloaded/src/main/java/org/springsource/loaded/agent/CglibPlugin.java @@ -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 { diff --git a/springloaded/src/main/java/org/springsource/loaded/agent/CglibPluginCapturing.java b/springloaded/src/main/java/org/springsource/loaded/agent/CglibPluginCapturing.java index 9ba2b8b..cd9de43 100644 --- a/springloaded/src/main/java/org/springsource/loaded/agent/CglibPluginCapturing.java +++ b/springloaded/src/main/java/org/springsource/loaded/agent/CglibPluginCapturing.java @@ -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, Object[]> clazzToGeneratorStrategyAndClassGeneratorMap = new HashMap, Object[]>(); public static Map, Object[]> clazzToGeneratorStrategyAndFastClassGeneratorMap = new HashMap, 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); diff --git a/springloaded/src/main/java/org/springsource/loaded/agent/SpringPlugin.java b/springloaded/src/main/java/org/springsource/loaded/agent/SpringPlugin.java index fcfad8a..a006b9f 100644 --- a/springloaded/src/main/java/org/springsource/loaded/agent/SpringPlugin.java +++ b/springloaded/src/main/java/org/springsource/loaded/agent/SpringPlugin.java @@ -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(); } diff --git a/springloaded/src/test/java/org/springsource/loaded/test/infra/TestClassloaderWithRewriting.java b/springloaded/src/test/java/org/springsource/loaded/test/infra/TestClassloaderWithRewriting.java index 6a6fb18..29a5a2b 100644 --- a/springloaded/src/test/java/org/springsource/loaded/test/infra/TestClassloaderWithRewriting.java +++ b/springloaded/src/test/java/org/springsource/loaded/test/infra/TestClassloaderWithRewriting.java @@ -124,6 +124,7 @@ public class TestClassloaderWithRewriting extends ClassLoader { } catch (Exception e) { e.printStackTrace(); } + Exception ex = null; if (c == null) { // Try the jar try { @@ -164,12 +165,18 @@ public class TestClassloaderWithRewriting extends ClassLoader { } // zipfile.close(); } catch (Exception e) { - e.printStackTrace(); + ex = e; + e.printStackTrace(System.err); } } if (c == null) { - throw new ClassNotFoundException(name); + if (ex!=null) { + throw new ClassNotFoundException(name,ex); + } + else { + throw new ClassNotFoundException(name); + } } return c; }