From b82df144e48efa115d2cffaf4daa6e707af36ecc Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 11 Apr 2016 19:19:37 +0200 Subject: [PATCH] LoadTimeWeaver.getThrowawayClassLoader() decorated for exclude support (if necessary) Issue: SPR-13886 --- .../ReflectiveLoadTimeWeaver.java | 14 +++--- .../glassfish/GlassFishLoadTimeWeaver.java | 5 ++- .../tomcat/TomcatLoadTimeWeaver.java | 5 ++- .../weblogic/WebLogicLoadTimeWeaver.java | 7 ++- .../WebSphereClassLoaderAdapter.java | 45 +++++++++++-------- .../websphere/WebSphereLoadTimeWeaver.java | 6 ++- .../core/DecoratingClassLoader.java | 33 ++++++-------- .../core/OverridingClassLoader.java | 44 +++++++++++++----- 8 files changed, 97 insertions(+), 62 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/instrument/classloading/ReflectiveLoadTimeWeaver.java b/spring-context/src/main/java/org/springframework/instrument/classloading/ReflectiveLoadTimeWeaver.java index 45b6836fd33..ac807eee996 100644 --- a/spring-context/src/main/java/org/springframework/instrument/classloading/ReflectiveLoadTimeWeaver.java +++ b/spring-context/src/main/java/org/springframework/instrument/classloading/ReflectiveLoadTimeWeaver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,8 @@ import java.lang.reflect.Method; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.core.DecoratingClassLoader; +import org.springframework.core.OverridingClassLoader; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; @@ -97,15 +99,14 @@ public class ReflectiveLoadTimeWeaver implements LoadTimeWeaver { Assert.notNull(classLoader, "ClassLoader must not be null"); this.classLoader = classLoader; this.addTransformerMethod = ClassUtils.getMethodIfAvailable( - this.classLoader.getClass(), ADD_TRANSFORMER_METHOD_NAME, - new Class[] {ClassFileTransformer.class}); + this.classLoader.getClass(), ADD_TRANSFORMER_METHOD_NAME, ClassFileTransformer.class); if (this.addTransformerMethod == null) { throw new IllegalStateException( "ClassLoader [" + classLoader.getClass().getName() + "] does NOT provide an " + "'addTransformer(ClassFileTransformer)' method."); } this.getThrowawayClassLoaderMethod = ClassUtils.getMethodIfAvailable( - this.classLoader.getClass(), GET_THROWAWAY_CLASS_LOADER_METHOD_NAME, new Class[0]); + this.classLoader.getClass(), GET_THROWAWAY_CLASS_LOADER_METHOD_NAME); // getThrowawayClassLoader method is optional if (this.getThrowawayClassLoaderMethod == null) { if (logger.isInfoEnabled()) { @@ -130,7 +131,10 @@ public class ReflectiveLoadTimeWeaver implements LoadTimeWeaver { @Override public ClassLoader getThrowawayClassLoader() { if (this.getThrowawayClassLoaderMethod != null) { - return (ClassLoader) ReflectionUtils.invokeMethod(this.getThrowawayClassLoaderMethod, this.classLoader); + ClassLoader target = (ClassLoader) + ReflectionUtils.invokeMethod(this.getThrowawayClassLoaderMethod, this.classLoader); + return (target instanceof DecoratingClassLoader ? target : + new OverridingClassLoader(this.classLoader, target)); } else { return new SimpleThrowawayClassLoader(this.classLoader); diff --git a/spring-context/src/main/java/org/springframework/instrument/classloading/glassfish/GlassFishLoadTimeWeaver.java b/spring-context/src/main/java/org/springframework/instrument/classloading/glassfish/GlassFishLoadTimeWeaver.java index 81222f3f5e2..663a7175ce8 100644 --- a/spring-context/src/main/java/org/springframework/instrument/classloading/glassfish/GlassFishLoadTimeWeaver.java +++ b/spring-context/src/main/java/org/springframework/instrument/classloading/glassfish/GlassFishLoadTimeWeaver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import java.lang.instrument.ClassFileTransformer; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import org.springframework.core.OverridingClassLoader; import org.springframework.instrument.classloading.LoadTimeWeaver; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -109,7 +110,7 @@ public class GlassFishLoadTimeWeaver implements LoadTimeWeaver { @Override public ClassLoader getThrowawayClassLoader() { try { - return (ClassLoader) this.copyMethod.invoke(this.classLoader); + return new OverridingClassLoader(this.classLoader, (ClassLoader) this.copyMethod.invoke(this.classLoader)); } catch (InvocationTargetException ex) { throw new IllegalStateException("GlassFish copy method threw exception", ex.getCause()); diff --git a/spring-context/src/main/java/org/springframework/instrument/classloading/tomcat/TomcatLoadTimeWeaver.java b/spring-context/src/main/java/org/springframework/instrument/classloading/tomcat/TomcatLoadTimeWeaver.java index 1ef36c95510..019952712b8 100644 --- a/spring-context/src/main/java/org/springframework/instrument/classloading/tomcat/TomcatLoadTimeWeaver.java +++ b/spring-context/src/main/java/org/springframework/instrument/classloading/tomcat/TomcatLoadTimeWeaver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import java.lang.instrument.ClassFileTransformer; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import org.springframework.core.OverridingClassLoader; import org.springframework.instrument.classloading.LoadTimeWeaver; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -103,7 +104,7 @@ public class TomcatLoadTimeWeaver implements LoadTimeWeaver { @Override public ClassLoader getThrowawayClassLoader() { try { - return (ClassLoader) this.copyMethod.invoke(this.classLoader); + return new OverridingClassLoader(this.classLoader, (ClassLoader) this.copyMethod.invoke(this.classLoader)); } catch (InvocationTargetException ex) { throw new IllegalStateException("Tomcat copy method threw exception", ex.getCause()); diff --git a/spring-context/src/main/java/org/springframework/instrument/classloading/weblogic/WebLogicLoadTimeWeaver.java b/spring-context/src/main/java/org/springframework/instrument/classloading/weblogic/WebLogicLoadTimeWeaver.java index 466221730a7..f4ebb47ab1f 100644 --- a/spring-context/src/main/java/org/springframework/instrument/classloading/weblogic/WebLogicLoadTimeWeaver.java +++ b/spring-context/src/main/java/org/springframework/instrument/classloading/weblogic/WebLogicLoadTimeWeaver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package org.springframework.instrument.classloading.weblogic; import java.lang.instrument.ClassFileTransformer; +import org.springframework.core.OverridingClassLoader; import org.springframework.instrument.classloading.LoadTimeWeaver; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -70,6 +71,8 @@ public class WebLogicLoadTimeWeaver implements LoadTimeWeaver { @Override public ClassLoader getThrowawayClassLoader() { - return this.classLoader.getThrowawayClassLoader(); + return new OverridingClassLoader(this.classLoader.getClassLoader(), + this.classLoader.getThrowawayClassLoader()); } + } diff --git a/spring-context/src/main/java/org/springframework/instrument/classloading/websphere/WebSphereClassLoaderAdapter.java b/spring-context/src/main/java/org/springframework/instrument/classloading/websphere/WebSphereClassLoaderAdapter.java index 3ee521c3bee..00e8bbafab5 100644 --- a/spring-context/src/main/java/org/springframework/instrument/classloading/websphere/WebSphereClassLoaderAdapter.java +++ b/spring-context/src/main/java/org/springframework/instrument/classloading/websphere/WebSphereClassLoaderAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,47 +29,57 @@ import org.springframework.util.Assert; /** * - * Reflective wrapper around a WebSphere 7 class loader. Used to + * Reflective wrapper around a WebSphere 7+ class loader. Used to * encapsulate the classloader-specific methods (discovered and * called through reflection) from the load-time weaver. * * @author Costin Leau + * @author Juergen Hoeller * @since 3.1 */ class WebSphereClassLoaderAdapter { private static final String COMPOUND_CLASS_LOADER_NAME = "com.ibm.ws.classloader.CompoundClassLoader"; + private static final String CLASS_PRE_PROCESSOR_NAME = "com.ibm.websphere.classloader.ClassLoaderInstancePreDefinePlugin"; + private static final String PLUGINS_FIELD = "preDefinePlugins"; private ClassLoader classLoader; + private Class wsPreProcessorClass; + private Method addPreDefinePlugin; + private Constructor cloneConstructor; + private Field transformerList; + public WebSphereClassLoaderAdapter(ClassLoader classLoader) { - Class wsCompoundClassLoaderClass = null; + Class wsCompoundClassLoaderClass; try { wsCompoundClassLoaderClass = classLoader.loadClass(COMPOUND_CLASS_LOADER_NAME); - cloneConstructor = classLoader.getClass().getDeclaredConstructor(wsCompoundClassLoaderClass); - cloneConstructor.setAccessible(true); + this.cloneConstructor = classLoader.getClass().getDeclaredConstructor(wsCompoundClassLoaderClass); + this.cloneConstructor.setAccessible(true); - wsPreProcessorClass = classLoader.loadClass(CLASS_PRE_PROCESSOR_NAME); - addPreDefinePlugin = classLoader.getClass().getMethod("addPreDefinePlugin", wsPreProcessorClass); - transformerList = wsCompoundClassLoaderClass.getDeclaredField(PLUGINS_FIELD); - transformerList.setAccessible(true); + this.wsPreProcessorClass = classLoader.loadClass(CLASS_PRE_PROCESSOR_NAME); + this.addPreDefinePlugin = classLoader.getClass().getMethod("addPreDefinePlugin", this.wsPreProcessorClass); + this.transformerList = wsCompoundClassLoaderClass.getDeclaredField(PLUGINS_FIELD); + this.transformerList.setAccessible(true); } catch (Exception ex) { throw new IllegalStateException( - "Could not initialize WebSphere LoadTimeWeaver because WebSphere 7 API classes are not available", - ex); + "Could not initialize WebSphere LoadTimeWeaver because WebSphere API classes are not available", ex); + } + + if (!wsCompoundClassLoaderClass.isInstance(classLoader)) { + throw new IllegalArgumentException("ClassLoader must be instance of [" + COMPOUND_CLASS_LOADER_NAME + "]"); } - Assert.isInstanceOf(wsCompoundClassLoaderClass, classLoader, - "ClassLoader must be instance of [" + COMPOUND_CLASS_LOADER_NAME + "]"); this.classLoader = classLoader; } + public ClassLoader getClassLoader() { return this.classLoader; } @@ -79,9 +89,8 @@ class WebSphereClassLoaderAdapter { try { InvocationHandler adapter = new WebSphereClassPreDefinePlugin(transformer); Object adapterInstance = Proxy.newProxyInstance(this.wsPreProcessorClass.getClassLoader(), - new Class[] { this.wsPreProcessorClass }, adapter); + new Class[] {this.wsPreProcessorClass}, adapter); this.addPreDefinePlugin.invoke(this.classLoader, adapterInstance); - } catch (InvocationTargetException ex) { throw new IllegalStateException("WebSphere addPreDefinePlugin method threw exception", ex.getCause()); @@ -93,9 +102,9 @@ class WebSphereClassLoaderAdapter { public ClassLoader getThrowawayClassLoader() { try { - ClassLoader loader = cloneConstructor.newInstance(getClassLoader()); - // clear out the transformers (copied as well) - List list = (List) transformerList.get(loader); + ClassLoader loader = this.cloneConstructor.newInstance(getClassLoader()); + // Clear out the transformers (copied as well) + List list = (List) this.transformerList.get(loader); list.clear(); return loader; } diff --git a/spring-context/src/main/java/org/springframework/instrument/classloading/websphere/WebSphereLoadTimeWeaver.java b/spring-context/src/main/java/org/springframework/instrument/classloading/websphere/WebSphereLoadTimeWeaver.java index 55c6f863ef7..aeed6929f14 100644 --- a/spring-context/src/main/java/org/springframework/instrument/classloading/websphere/WebSphereLoadTimeWeaver.java +++ b/spring-context/src/main/java/org/springframework/instrument/classloading/websphere/WebSphereLoadTimeWeaver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package org.springframework.instrument.classloading.websphere; import java.lang.instrument.ClassFileTransformer; +import org.springframework.core.OverridingClassLoader; import org.springframework.instrument.classloading.LoadTimeWeaver; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -67,7 +68,8 @@ public class WebSphereLoadTimeWeaver implements LoadTimeWeaver { @Override public ClassLoader getThrowawayClassLoader() { - return this.classLoader.getThrowawayClassLoader(); + return new OverridingClassLoader(this.classLoader.getClassLoader(), + this.classLoader.getThrowawayClassLoader()); } } diff --git a/spring-core/src/main/java/org/springframework/core/DecoratingClassLoader.java b/spring-core/src/main/java/org/springframework/core/DecoratingClassLoader.java index 350df5cfa1f..b29ac07161d 100644 --- a/spring-core/src/main/java/org/springframework/core/DecoratingClassLoader.java +++ b/spring-core/src/main/java/org/springframework/core/DecoratingClassLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,9 @@ package org.springframework.core; -import java.util.HashSet; +import java.util.Collections; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import org.springframework.lang.UsesJava7; import org.springframework.util.Assert; @@ -49,11 +50,11 @@ public abstract class DecoratingClassLoader extends ClassLoader { } - private final Set excludedPackages = new HashSet(); + private final Set excludedPackages = + Collections.newSetFromMap(new ConcurrentHashMap(8)); - private final Set excludedClasses = new HashSet(); - - private final Object exclusionMonitor = new Object(); + private final Set excludedClasses = + Collections.newSetFromMap(new ConcurrentHashMap(8)); /** @@ -79,9 +80,7 @@ public abstract class DecoratingClassLoader extends ClassLoader { */ public void excludePackage(String packageName) { Assert.notNull(packageName, "Package name must not be null"); - synchronized (this.exclusionMonitor) { - this.excludedPackages.add(packageName); - } + this.excludedPackages.add(packageName); } /** @@ -92,9 +91,7 @@ public abstract class DecoratingClassLoader extends ClassLoader { */ public void excludeClass(String className) { Assert.notNull(className, "Class name must not be null"); - synchronized (this.exclusionMonitor) { - this.excludedClasses.add(className); - } + this.excludedClasses.add(className); } /** @@ -107,15 +104,13 @@ public abstract class DecoratingClassLoader extends ClassLoader { * @see #excludeClass */ protected boolean isExcluded(String className) { - synchronized (this.exclusionMonitor) { - if (this.excludedClasses.contains(className)) { + if (this.excludedClasses.contains(className)) { + return true; + } + for (String packageName : this.excludedPackages) { + if (className.startsWith(packageName)) { return true; } - for (String packageName : this.excludedPackages) { - if (className.startsWith(packageName)) { - return true; - } - } } return false; } diff --git a/spring-core/src/main/java/org/springframework/core/OverridingClassLoader.java b/spring-core/src/main/java/org/springframework/core/OverridingClassLoader.java index c60a4c8b4b6..2e3fd3eaa09 100644 --- a/spring-core/src/main/java/org/springframework/core/OverridingClassLoader.java +++ b/spring-core/src/main/java/org/springframework/core/OverridingClassLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,7 +38,8 @@ import org.springframework.util.FileCopyUtils; public class OverridingClassLoader extends DecoratingClassLoader { /** Packages that are excluded by default */ - public static final String[] DEFAULT_EXCLUDED_PACKAGES = new String[] {"java.", "javax.", "sun.", "oracle."}; + public static final String[] DEFAULT_EXCLUDED_PACKAGES = new String[] + {"java.", "javax.", "sun.", "oracle.", "javassist.", "org.springframework.core."}; private static final String CLASS_FILE_SUFFIX = ".class"; @@ -49,33 +50,52 @@ public class OverridingClassLoader extends DecoratingClassLoader { } + private final ClassLoader overrideDelegate; + + /** * Create a new OverridingClassLoader for the given ClassLoader. * @param parent the ClassLoader to build an overriding ClassLoader for */ public OverridingClassLoader(ClassLoader parent) { + this(parent, null); + } + + /** + * Create a new OverridingClassLoader for the given ClassLoader. + * @param parent the ClassLoader to build an overriding ClassLoader for + * @param overrideDelegate the ClassLoader to delegate to for overriding + * @since 4.3 + */ + public OverridingClassLoader(ClassLoader parent, ClassLoader overrideDelegate) { super(parent); + this.overrideDelegate = overrideDelegate; for (String packageName : DEFAULT_EXCLUDED_PACKAGES) { excludePackage(packageName); } } + @Override + public Class loadClass(String name) throws ClassNotFoundException { + if (this.overrideDelegate != null && isEligibleForOverriding(name)) { + return this.overrideDelegate.loadClass(name); + } + return super.loadClass(name); + } + @Override protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { - Class result = null; if (isEligibleForOverriding(name)) { - result = loadClassForOverriding(name); - } - if (result != null) { - if (resolve) { - resolveClass(result); + Class result = loadClassForOverriding(name); + if (result != null) { + if (resolve) { + resolveClass(result); + } + return result; } - return result; - } - else { - return super.loadClass(name, resolve); } + return super.loadClass(name, resolve); } /**