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 e0c5a69..cbbff6f 100644 --- a/springloaded/src/main/java/org/springsource/loaded/agent/SpringPlugin.java +++ b/springloaded/src/main/java/org/springsource/loaded/agent/SpringPlugin.java @@ -53,21 +53,24 @@ public class SpringPlugin implements LoadtimeInstrumentationPlugin, ReloadEventP private static boolean debug = true; // TODO [gc] what about GC here - how do we know when they are finished with? - public static List instancesOf_AnnotationMethodHandlerAdapter = new ArrayList(); - public static List instancesOf_DefaultAnnotationHandlerMapping = new ArrayList(); - public static List instancesOf_RequestMappingHandlerMapping = new ArrayList(); - public static List instancesOf_LocalVariableTableParameterNameDiscoverer = null; - - ClassLoader springLoader = null; + public static List annotationMethodHandlerAdapterInstances = new ArrayList(); + public static List defaultAnnotationHandlerMappingInstances = new ArrayList(); + public static List requestMappingHandlerMappingInstances = new ArrayList(); + public static List localVariableTableParameterNameDiscovererInstances = null; public static boolean support305 = true; - private Field classCacheField; - private Field field_parameterNamesCache; // From LocalVariableTableParameterNameDiscoverer + private Field classCacheField; // From CachedIntrospectionResults (Spring <= 4.0.x) + private Field strongClassCacheField; // From CachedIntrospectionResults (Spring >= 4.1.0) + private Field softClassCacheField; // From CachedIntrospectionResults (Spring >= 4.1.0) + private Field declaredMethodsCacheField; // From ReflectionUtils + private Field parameterNamesCacheField; // From LocalVariableTableParameterNameDiscoverer private boolean cachedIntrospectionResultsClassLoaded = false; - + private boolean reflectionUtilsClassLoaded = false; + private Class cachedIntrospectionResultsClass = null; + private Class reflectionUtilsClass = null; public boolean accept(String slashedTypeName, ClassLoader classLoader, ProtectionDomain protectionDomain, byte[] bytes) { // TODO take classloader into account? @@ -81,6 +84,10 @@ public class SpringPlugin implements LoadtimeInstrumentationPlugin, ReloadEventP if (slashedTypeName.equals("org/springframework/beans/CachedIntrospectionResults")) { cachedIntrospectionResultsClassLoaded = true; } + // Just interested in whether this type got loaded + if (slashedTypeName.equals("org/springframework/util/ReflectionUtils")) { + reflectionUtilsClassLoaded = true; + } return slashedTypeName.equals("org/springframework/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter") || slashedTypeName.equals("org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping") || // 3.1 (support305 && slashedTypeName @@ -113,18 +120,18 @@ public class SpringPlugin implements LoadtimeInstrumentationPlugin, ReloadEventP // called by the modified code public static void recordAnnotationMethodHandlerAdapterInstance(Object obj) { - instancesOf_AnnotationMethodHandlerAdapter.add(obj); + annotationMethodHandlerAdapterInstances.add(obj); } public static void recordRequestMappingHandlerMappingInstance(Object obj) { - instancesOf_RequestMappingHandlerMapping.add(obj); + requestMappingHandlerMappingInstances.add(obj); } public static void recordLocalVariableTableParameterNameDiscoverer(Object obj) { - if (instancesOf_LocalVariableTableParameterNameDiscoverer == null) { - instancesOf_LocalVariableTableParameterNameDiscoverer = new ArrayList(); + if (localVariableTableParameterNameDiscovererInstances == null) { + localVariableTableParameterNameDiscovererInstances = new ArrayList(); } - instancesOf_LocalVariableTableParameterNameDiscoverer.add(obj); + localVariableTableParameterNameDiscovererInstances.add(obj); } static { @@ -141,11 +148,12 @@ public class SpringPlugin implements LoadtimeInstrumentationPlugin, ReloadEventP if (debug) { System.out.println("Recording new instance of DefaultAnnotationHandlerMappingInstance"); } - instancesOf_DefaultAnnotationHandlerMapping.add(obj); + defaultAnnotationHandlerMappingInstances.add(obj); } public void reloadEvent(String typename, Class clazz, String versionsuffix) { removeClazzFromMethodResolverCache(clazz); + removeClazzFromDeclaredMethodsCache(clazz); clearCachedIntrospectionResults(clazz); reinvokeDetectHandlers(); // Spring 3.0 reinvokeInitHandlerMethods(); // Spring 3.1 @@ -158,23 +166,24 @@ public class SpringPlugin implements LoadtimeInstrumentationPlugin, ReloadEventP * @param clazz the class being reloaded, which may exist in a parameter name discoverer cache */ private void clearLocalVariableTableParameterNameDiscovererCache(Class clazz) { - if (instancesOf_LocalVariableTableParameterNameDiscoverer == null) { + if (localVariableTableParameterNameDiscovererInstances == null) { return; } if (debug) { System.out.println("ParameterNamesCache: Clearing parameter name discoverer caches"); } - if (field_parameterNamesCache == null) { + if (parameterNamesCacheField == null) { try { - field_parameterNamesCache = instancesOf_LocalVariableTableParameterNameDiscoverer.get(0).getClass().getDeclaredField("parameterNamesCache"); + parameterNamesCacheField = localVariableTableParameterNameDiscovererInstances + .get(0).getClass().getDeclaredField("parameterNamesCache"); } catch (NoSuchFieldException nsfe) { log.log(Level.SEVERE, "Unexpectedly cannot find parameterNamesCache field on LocalVariableTableParameterNameDiscoverer class"); } } - for (Object instance: instancesOf_LocalVariableTableParameterNameDiscoverer) { - field_parameterNamesCache.setAccessible(true); + for (Object instance: localVariableTableParameterNameDiscovererInstances) { + parameterNamesCacheField.setAccessible(true); try { - Map parameterNamesCache = (Map)field_parameterNamesCache.get(instance); + Map parameterNamesCache = (Map) parameterNamesCacheField.get(instance); Object o = parameterNamesCache.remove(clazz); if (debug) { System.out.println("ParameterNamesCache: Removed "+clazz.getName()+" from cache?"+(o!=null)); @@ -186,7 +195,7 @@ public class SpringPlugin implements LoadtimeInstrumentationPlugin, ReloadEventP } private void removeClazzFromMethodResolverCache(Class clazz) { - for (Object o : instancesOf_AnnotationMethodHandlerAdapter) { + for (Object o : annotationMethodHandlerAdapterInstances) { try { Field f = o.getClass().getDeclaredField("methodResolverCache"); f.setAccessible(true); @@ -205,6 +214,41 @@ public class SpringPlugin implements LoadtimeInstrumentationPlugin, ReloadEventP } } + private void removeClazzFromDeclaredMethodsCache(Class clazz) { + if (reflectionUtilsClassLoaded) { + try { + // TODO not a fan of classloading like this + if (reflectionUtilsClass == null) { + // TODO what about two apps using reloading and diff versions of spring? + reflectionUtilsClass = clazz.getClassLoader().loadClass( + "org.springframework.util.ReflectionUtils"); + } + + if (declaredMethodsCacheField == null) { + try { + declaredMethodsCacheField = reflectionUtilsClass.getDeclaredField("declaredMethodsCache"); + } catch(NoSuchFieldException e) { + + } + + } + if(declaredMethodsCacheField != null) { + declaredMethodsCacheField.setAccessible(true); + Map m = (Map) declaredMethodsCacheField.get(null); + Object o = m.remove(clazz); + if (GlobalConfiguration.debugplugins) { + System.err.println("SpringPlugin: clearing ReflectionUtils.declaredMethodsCache for " + clazz.getName() + " removed=" + o); + } + } + + } catch (Exception e) { + if (GlobalConfiguration.debugplugins) { + e.printStackTrace(); + } + } + } + } + private void clearCachedIntrospectionResults(Class clazz) { if (cachedIntrospectionResultsClassLoaded) { try { @@ -214,16 +258,41 @@ public class SpringPlugin implements LoadtimeInstrumentationPlugin, ReloadEventP cachedIntrospectionResultsClass = clazz.getClassLoader().loadClass( "org.springframework.beans.CachedIntrospectionResults"); } - if (classCacheField == null) { - classCacheField = cachedIntrospectionResultsClass.getDeclaredField("classCache"); + + if (classCacheField == null && strongClassCacheField == null) { + try { + classCacheField = cachedIntrospectionResultsClass.getDeclaredField("classCache"); + } catch(NoSuchFieldException e) { + strongClassCacheField = cachedIntrospectionResultsClass.getDeclaredField("strongClassCache"); + softClassCacheField = cachedIntrospectionResultsClass.getDeclaredField("softClassCache"); + } + } - classCacheField.setAccessible(true); - Map m = (Map) classCacheField.get(null); - Object o = m.remove(clazz); - if (GlobalConfiguration.debugplugins) { - System.err - .println("SpringPlugin: clearing CachedIntrospectionResults for " + clazz.getName() + " removed=" + o); + if(classCacheField != null) { + classCacheField.setAccessible(true); + Map m = (Map) classCacheField.get(null); + Object o = m.remove(clazz); + if (GlobalConfiguration.debugplugins) { + System.err.println("SpringPlugin: clearing CachedIntrospectionResults.classCache for " + clazz.getName() + " removed=" + o); + } } + if(strongClassCacheField != null) { + strongClassCacheField.setAccessible(true); + Map m = (Map) strongClassCacheField.get(null); + Object o = m.remove(clazz); + if (GlobalConfiguration.debugplugins) { + System.err.println("SpringPlugin: clearing CachedIntrospectionResults.strongClassCache for " + clazz.getName() + " removed=" + o); + } + } + if(softClassCacheField != null) { + softClassCacheField.setAccessible(true); + Map m = (Map) softClassCacheField.get(null); + Object o = m.remove(clazz); + if (GlobalConfiguration.debugplugins) { + System.err.println("SpringPlugin: clearing CachedIntrospectionResults.softClassCache for " + clazz.getName() + " removed=" + o); + } + } + } catch (Exception e) { if (GlobalConfiguration.debugplugins) { e.printStackTrace(); @@ -235,7 +304,7 @@ public class SpringPlugin implements LoadtimeInstrumentationPlugin, ReloadEventP private void reinvokeDetectHandlers() { // want to call detectHandlers on the DefaultAnnotationHandlerMapping type // protected void detectHandlers() throws BeansException { is defined on AbstractDetectingUrlHandlerMapping - for (Object o : instancesOf_DefaultAnnotationHandlerMapping) { + for (Object o : defaultAnnotationHandlerMappingInstances) { if (debug) { System.out.println("Invoking detectHandlers on instance of DefaultAnnotationHandlerMappingInstance"); } @@ -257,7 +326,7 @@ public class SpringPlugin implements LoadtimeInstrumentationPlugin, ReloadEventP private void reinvokeInitHandlerMethods() { // org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping (super AbstractHandlerMethodMapping) - call protected void initHandlerMethods() on it. - for (Object o : instancesOf_RequestMappingHandlerMapping) { + for (Object o : requestMappingHandlerMappingInstances) { if (debug) { System.out.println("Invoking initHandlerMethods on instance of RequestMappingHandlerMapping"); }