diff --git a/springloaded/src/main/java/org/springsource/loaded/ReloadableType.java b/springloaded/src/main/java/org/springsource/loaded/ReloadableType.java index c2912f0..6aa8ba2 100644 --- a/springloaded/src/main/java/org/springsource/loaded/ReloadableType.java +++ b/springloaded/src/main/java/org/springsource/loaded/ReloadableType.java @@ -166,8 +166,9 @@ public class ReloadableType { this.typeRegistry = typeRegistry; this.dottedtypename = dottedtypename; this.slashedtypename = dottedtypename.replace('.', '/'); - this.typedescriptor = (typeDescriptor != null ? typeDescriptor : typeRegistry.getExtractor().extract( - initialBytes, true)); + this.typedescriptor = (typeDescriptor != null ? typeDescriptor + : typeRegistry.getExtractor().extract( + initialBytes, true)); this.interfaceBytes = InterfaceExtractor.extract(initialBytes, typeRegistry, this.typedescriptor); this.bytesInitial = initialBytes; rewriteCallSitesAndDefine(); @@ -544,12 +545,17 @@ public class ReloadableType { private void reloadProxiesIfNecessary(String versionsuffix) { ReloadableType proxy = typeRegistry.cglibProxies.get(this.slashedtypename); if (proxy != null) { + if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.INFO)) { + log.log(Level.INFO, "Attempting reload of cglib proxy for type " + this.slashedtypename); + } + 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"); + "Unable to find regeneration methods for cglib proxies - proxies will be out of date for type: " + + getClazz()); } return; } @@ -567,6 +573,9 @@ public class ReloadableType { } found.setAccessible(true); byte[] bs = (byte[]) found.invoke(a, b); + if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.INFO)) { + log.log(Level.INFO, "Proxy regenerate successful for " + this.slashedtypename); + } proxy.loadNewVersion(versionsuffix, bs); proxy.runStaticInitializer(); } @@ -596,6 +605,9 @@ public class ReloadableType { } } byte[] bs = (byte[]) found.invoke(a, b); + if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.INFO)) { + log.log(Level.INFO, "Proxy (fastclass) regenerate successful for " + this.slashedtypename); + } proxy.loadNewVersion(versionsuffix, bs); proxy.runStaticInitializer(); } 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 41611c3..a0dac3b 100644 --- a/springloaded/src/main/java/org/springsource/loaded/agent/CglibPluginCapturing.java +++ b/springloaded/src/main/java/org/springsource/loaded/agent/CglibPluginCapturing.java @@ -19,23 +19,28 @@ package org.springsource.loaded.agent; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.springsource.loaded.Constants; +import org.springsource.loaded.GlobalConfiguration; /** * 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. - * + * * @author Andy Clement * @since 0.8.3 */ public class CglibPluginCapturing extends ClassVisitor implements Constants { + private static Logger log = Logger.getLogger(CglibPluginCapturing.class.getName()); + public static Map, Object[]> clazzToGeneratorStrategyAndClassGeneratorMap = new HashMap, Object[]>(); public static Map, Object[]> clazzToGeneratorStrategyAndFastClassGeneratorMap = new HashMap, Object[]>(); @@ -68,11 +73,22 @@ public class CglibPluginCapturing extends ClassVisitor implements Constants { return ((ClassWriter) cv).toByteArray(); } + @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { if (name.equals("create")) { + if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.INFO)) { + log.info("intercepting create method"); + } MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); return new CreateMethodInterceptor(mv); } + else if (name.equals("generate")) { + if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.INFO)) { + log.info("intercepting generate method"); + } + MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); + return new GenerateMethodInterceptor(mv); + } else { return super.visitMethod(access, name, desc, signature, exceptions); } @@ -98,6 +114,9 @@ public class CglibPluginCapturing extends ClassVisitor implements Constants { final boolean itf) { super.visitMethodInsn(opcode, owner, name, desc, itf); if (name.equals("generate")) { + if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.INFO)) { + log.info("intercepting call to generate in create method"); + } // Code that calls generate: // ALOAD 0 // GETFIELD net/sf/cglib/core/AbstractClassGenerator.strategy : Lnet/sf/cglib/core/GeneratorStrategy; @@ -108,22 +127,46 @@ public class CglibPluginCapturing extends ClassVisitor implements Constants { "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", false);//Lnet/sf/cglib/core/GeneratorStrategy;Lnet/sf/cglib/core/AbstractClassGenerator);"); + "(Ljava/lang/Object;Ljava/lang/Object;)V", false);//Lnet/sf/cglib/core/GeneratorStrategy;Lnet/sf/cglib/core/AbstractClassGenerator);"); } } } + + class GenerateMethodInterceptor extends MethodVisitor implements Constants { + + public GenerateMethodInterceptor(MethodVisitor mv) { + super(ASM5, mv); + } + + @Override + public void visitCode() { + mv.visitVarInsn(ALOAD, 0); // AbstractClassGenerator instance + 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", false);//Lnet/sf/cglib/core/GeneratorStrategy;Lnet/sf/cglib/core/AbstractClassGenerator);"); + + } + + } + /** * The classloader for class artifacts is used to load the generated classes for call sites. We need to rewrite * these classes because they may be either calling something that disappears on a later reload (so need to fail * appropriately) or calling something that isnt there on the first load - in this latter case they are changed to * route the dynamic executor method. - * + * * @param a the GeneratorStrategy being used * @param b the AbstractClassGenerator */ public static void record(Object a, Object b) { + // if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.INFO)) { + // log.info("recording invocation of generate with " + (a == null ? "null" : a.getClass().getName()) + " b=" + // + (b == null ? "null" : b.getClass().getName())); + // } // a is a Lnet/sf/cglib/core/GeneratorStrategy; // b is a Lnet/sf/cglib/core/AbstractClassGenerator (or specifically net/sf/cglib/reflect/FastClass$Generator) // a is something like 'UndeclaredThrowableStrategy' @@ -134,7 +177,9 @@ public class CglibPluginCapturing extends ClassVisitor implements Constants { Field f = b.getClass().getDeclaredField("superclass"); f.setAccessible(true); Class clazz = (Class) f.get(b); - // System.out.println("Recording pair " + clazz.getName() + " > " + b); + if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.INFO)) { + log.info("recording pair " + clazz.getName() + " > " + b); + } clazzToGeneratorStrategyAndClassGeneratorMap.put(clazz, new Object[] { a, b }); } catch (Throwable re) { @@ -146,7 +191,9 @@ public class CglibPluginCapturing extends ClassVisitor implements Constants { Field f = b.getClass().getDeclaredField("type"); f.setAccessible(true); Class clazz = (Class) f.get(b); - // System.out.println("Recording pair (fastclass) " + clazz.getName() + " > " + b); + if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.INFO)) { + log.info("recording pair (fastclass) " + clazz.getName() + " > " + b); + } clazzToGeneratorStrategyAndFastClassGeneratorMap.put(clazz, new Object[] { a, b }); } catch (Throwable re) { diff --git a/springloaded/src/test/java/org/springsource/loaded/test/ConstantPoolScannerTests.java b/springloaded/src/test/java/org/springsource/loaded/test/ConstantPoolScannerTests.java index 0c55c10..1c32efe 100644 --- a/springloaded/src/test/java/org/springsource/loaded/test/ConstantPoolScannerTests.java +++ b/springloaded/src/test/java/org/springsource/loaded/test/ConstantPoolScannerTests.java @@ -23,6 +23,7 @@ import java.io.FileInputStream; import java.io.Serializable; import java.util.List; +import org.junit.Ignore; import org.junit.Test; import org.springsource.loaded.ConstantPoolScanner; import org.springsource.loaded.ConstantPoolScanner.References; @@ -55,6 +56,7 @@ public class ConstantPoolScannerTests { } + @Ignore @Test public void foo() throws Exception {