@@ -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<Object> instancesOf_AnnotationMethodHandlerAdapter = new ArrayList<Object>();
|
||||
public static List<Object> instancesOf_DefaultAnnotationHandlerMapping = new ArrayList<Object>();
|
||||
public static List<Object> instancesOf_RequestMappingHandlerMapping = new ArrayList<Object>();
|
||||
public static List<Object> instancesOf_LocalVariableTableParameterNameDiscoverer = null;
|
||||
|
||||
ClassLoader springLoader = null;
|
||||
public static List<Object> annotationMethodHandlerAdapterInstances = new ArrayList<Object>();
|
||||
public static List<Object> defaultAnnotationHandlerMappingInstances = new ArrayList<Object>();
|
||||
public static List<Object> requestMappingHandlerMappingInstances = new ArrayList<Object>();
|
||||
public static List<Object> 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<Object>();
|
||||
if (localVariableTableParameterNameDiscovererInstances == null) {
|
||||
localVariableTableParameterNameDiscovererInstances = new ArrayList<Object>();
|
||||
}
|
||||
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");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user