From 3fad0fa4320b29129326e3bb50519b4360471776 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 27 Sep 2013 15:56:11 +0200 Subject: [PATCH] Introduced TomcatLoadTimeWeaver for Tomcat's new InstrumentableClassLoader interface At the same time, dropped GlassFish 1/2 support from GlassFishLoadTimeWeaver and redesigned it to be as analogous to TomcatLoadTimeWeaver as possible. Issue: SPR-10788 --- .../weaving/DefaultContextLoadTimeWeaver.java | 16 ++- .../glassfish/ClassTransformerAdapter.java | 55 -------- .../GlassFishClassLoaderAdapter.java | 128 ------------------ .../glassfish/GlassFishLoadTimeWeaver.java | 83 +++++++++--- .../classloading/glassfish/package-info.java | 2 +- .../tomcat/TomcatLoadTimeWeaver.java | 116 ++++++++++++++++ .../classloading/tomcat/package-info.java | 8 ++ 7 files changed, 198 insertions(+), 210 deletions(-) delete mode 100644 spring-context/src/main/java/org/springframework/instrument/classloading/glassfish/ClassTransformerAdapter.java delete mode 100644 spring-context/src/main/java/org/springframework/instrument/classloading/glassfish/GlassFishClassLoaderAdapter.java create mode 100644 spring-context/src/main/java/org/springframework/instrument/classloading/tomcat/TomcatLoadTimeWeaver.java create mode 100644 spring-context/src/main/java/org/springframework/instrument/classloading/tomcat/package-info.java diff --git a/spring-context/src/main/java/org/springframework/context/weaving/DefaultContextLoadTimeWeaver.java b/spring-context/src/main/java/org/springframework/context/weaving/DefaultContextLoadTimeWeaver.java index ec69b4862e0..c30f77d167d 100644 --- a/spring-context/src/main/java/org/springframework/context/weaving/DefaultContextLoadTimeWeaver.java +++ b/spring-context/src/main/java/org/springframework/context/weaving/DefaultContextLoadTimeWeaver.java @@ -29,6 +29,7 @@ import org.springframework.instrument.classloading.LoadTimeWeaver; import org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver; import org.springframework.instrument.classloading.glassfish.GlassFishLoadTimeWeaver; import org.springframework.instrument.classloading.jboss.JBossLoadTimeWeaver; +import org.springframework.instrument.classloading.tomcat.TomcatLoadTimeWeaver; import org.springframework.instrument.classloading.weblogic.WebLogicLoadTimeWeaver; import org.springframework.instrument.classloading.websphere.WebSphereLoadTimeWeaver; @@ -40,13 +41,11 @@ import org.springframework.instrument.classloading.websphere.WebSphereLoadTimeWe * "{@code loadTimeWeaver}"; the most convenient way to achieve this is * Spring's {@code <context:load-time-weaver>} XML tag. * - *

This class implements a runtime environment check for obtaining - * the appropriate weaver implementation: As of Spring 3.1, it detects - * Oracle WebLogic 10, GlassFish 3, JBoss AS 5, 6 and 7, IBM WebSphere 7 and 8, + *

This class implements a runtime environment check for obtaining the + * appropriate weaver implementation: As of Spring 4.0, it detects Oracle WebLogic 10, + * GlassFish 3, Tomcat 6, 7 and 8, JBoss AS 5, 6 and 7, IBM WebSphere 7 and 8, * {@link InstrumentationSavingAgent Spring's VM agent}, and any {@link ClassLoader} - * supported by Spring's {@link ReflectiveLoadTimeWeaver} (for example the - * {@link org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader} - * for Tomcat 6 and 7). + * supported by Spring's {@link ReflectiveLoadTimeWeaver}. * * @author Juergen Hoeller * @author Ramnivas Laddad @@ -111,9 +110,12 @@ public class DefaultContextLoadTimeWeaver implements LoadTimeWeaver, BeanClassLo if (name.startsWith("weblogic")) { return new WebLogicLoadTimeWeaver(classLoader); } - else if (name.startsWith("com.sun.enterprise") || name.startsWith("org.glassfish")) { + else if (name.startsWith("org.glassfish")) { return new GlassFishLoadTimeWeaver(classLoader); } + else if (name.startsWith("org.apache.catalina")) { + return new TomcatLoadTimeWeaver(classLoader); + } else if (name.startsWith("org.jboss")) { return new JBossLoadTimeWeaver(classLoader); } diff --git a/spring-context/src/main/java/org/springframework/instrument/classloading/glassfish/ClassTransformerAdapter.java b/spring-context/src/main/java/org/springframework/instrument/classloading/glassfish/ClassTransformerAdapter.java deleted file mode 100644 index 67e33d6902c..00000000000 --- a/spring-context/src/main/java/org/springframework/instrument/classloading/glassfish/ClassTransformerAdapter.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2002-2012 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.instrument.classloading.glassfish; - -import java.lang.instrument.ClassFileTransformer; -import java.lang.instrument.IllegalClassFormatException; -import java.security.ProtectionDomain; - -import javax.persistence.spi.ClassTransformer; - -/** - * Adapter that implements the JPA ClassTransformer interface (as required by GlassFish V1 and V2) - * based on a given JDK 1.5 ClassFileTransformer. - * - * @author Costin Leau - * @author Juergen Hoeller - * @since 2.0.1 - */ -class ClassTransformerAdapter implements ClassTransformer { - - private final ClassFileTransformer classFileTransformer; - - /** - * Build a new ClassTransformerAdapter for the given ClassFileTransformer. - * @param classFileTransformer the JDK 1.5 ClassFileTransformer to wrap - */ - public ClassTransformerAdapter(ClassFileTransformer classFileTransformer) { - this.classFileTransformer = classFileTransformer; - } - - @Override - public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, - ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { - - byte[] result = this.classFileTransformer.transform(loader, className, classBeingRedefined, protectionDomain, - classfileBuffer); - - // If no transformation was done, return null. - return (result == classfileBuffer ? null : result); - } -} diff --git a/spring-context/src/main/java/org/springframework/instrument/classloading/glassfish/GlassFishClassLoaderAdapter.java b/spring-context/src/main/java/org/springframework/instrument/classloading/glassfish/GlassFishClassLoaderAdapter.java deleted file mode 100644 index 92b997d615e..00000000000 --- a/spring-context/src/main/java/org/springframework/instrument/classloading/glassfish/GlassFishClassLoaderAdapter.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2002-2012 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. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.instrument.classloading.glassfish; - -import java.lang.instrument.ClassFileTransformer; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -/** - * Reflective wrapper around the GlassFish class loader. Used to - * encapsulate the classloader-specific methods (discovered and - * called through reflection) from the load-time weaver. - * - *

Supports GlassFish V1, V2 and V3 (currently in beta). - * - * @author Costin Leau - * @since 3.0 - */ -class GlassFishClassLoaderAdapter { - - static final String INSTRUMENTABLE_CLASSLOADER_GLASSFISH_V2 = "com.sun.enterprise.loader.InstrumentableClassLoader"; - - static final String INSTRUMENTABLE_CLASSLOADER_GLASSFISH_V3 = "org.glassfish.api.deployment.InstrumentableClassLoader"; - - private static final String CLASS_TRANSFORMER = "javax.persistence.spi.ClassTransformer"; - - - private final ClassLoader classLoader; - - private final Method addTransformer; - - private final Method copy; - - private final boolean glassFishV3; - - - public GlassFishClassLoaderAdapter(ClassLoader classLoader) { - Class instrumentableLoaderClass; - boolean glassV3 = false; - try { - // try the V1/V2 API first - instrumentableLoaderClass = classLoader.loadClass(INSTRUMENTABLE_CLASSLOADER_GLASSFISH_V2); - } - catch (ClassNotFoundException ex) { - // fall back to V3 - try { - instrumentableLoaderClass = classLoader.loadClass(INSTRUMENTABLE_CLASSLOADER_GLASSFISH_V3); - glassV3 = true; - } - catch (ClassNotFoundException cnfe) { - throw new IllegalStateException("Could not initialize GlassFish LoadTimeWeaver because " + - "GlassFish (V1, V2 or V3) API classes are not available", ex); - } - } - try { - Class classTransformerClass = - (glassV3 ? ClassFileTransformer.class : classLoader.loadClass(CLASS_TRANSFORMER)); - - this.addTransformer = instrumentableLoaderClass.getMethod("addTransformer", classTransformerClass); - this.copy = instrumentableLoaderClass.getMethod("copy"); - } - catch (Exception ex) { - throw new IllegalStateException( - "Could not initialize GlassFish LoadTimeWeaver because GlassFish API classes are not available", ex); - } - - ClassLoader clazzLoader = null; - // Detect transformation-aware ClassLoader by traversing the hierarchy - // (as in GlassFish, Spring can be loaded by the WebappClassLoader). - for (ClassLoader cl = classLoader; cl != null && clazzLoader == null; cl = cl.getParent()) { - if (instrumentableLoaderClass.isInstance(cl)) { - clazzLoader = cl; - } - } - - if (clazzLoader == null) { - throw new IllegalArgumentException(classLoader + " and its parents are not suitable ClassLoaders: A [" + - instrumentableLoaderClass.getName() + "] implementation is required."); - } - - this.classLoader = clazzLoader; - this.glassFishV3 = glassV3; - } - - public void addTransformer(ClassFileTransformer transformer) { - try { - this.addTransformer.invoke(this.classLoader, - (this.glassFishV3 ? transformer : new ClassTransformerAdapter(transformer))); - } - catch (InvocationTargetException ex) { - throw new IllegalStateException("GlassFish addTransformer method threw exception ", ex.getCause()); - } - catch (Exception ex) { - throw new IllegalStateException("Could not invoke GlassFish addTransformer method", ex); - } - } - - public ClassLoader getClassLoader() { - return this.classLoader; - } - - public ClassLoader getThrowawayClassLoader() { - try { - return (ClassLoader) this.copy.invoke(this.classLoader); - } - catch (InvocationTargetException ex) { - throw new IllegalStateException("GlassFish copy method threw exception ", ex.getCause()); - } - catch (Exception ex) { - throw new IllegalStateException("Could not invoke GlassFish copy method", ex); - } - } - -} 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 205ed59c2a2..5070049af8d 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-2012 the original author or authors. + * Copyright 2002-2013 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. @@ -17,6 +17,8 @@ package org.springframework.instrument.classloading.glassfish; import java.lang.instrument.ClassFileTransformer; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import org.springframework.instrument.classloading.LoadTimeWeaver; import org.springframework.util.Assert; @@ -26,7 +28,7 @@ import org.springframework.util.ClassUtils; * {@link LoadTimeWeaver} implementation for GlassFish's * {@link org.glassfish.api.deployment.InstrumentableClassLoader InstrumentableClassLoader}. * - *

As of Spring 3.0, GlassFish V3 is supported as well. + *

As of Spring 4.0, this weaver supports GlassFish V3 and V4. * * @author Costin Leau * @author Juergen Hoeller @@ -34,44 +36,87 @@ import org.springframework.util.ClassUtils; */ public class GlassFishLoadTimeWeaver implements LoadTimeWeaver { - private final GlassFishClassLoaderAdapter classLoader; + private static final String INSTRUMENTABLE_LOADER_CLASS_NAME = "org.glassfish.api.deployment.InstrumentableClassLoader"; + + + private final ClassLoader classLoader; + + private final Method addTransformerMethod; + + private final Method copyMethod; - /** - * Creates a new instance of the {@code GlassFishLoadTimeWeaver} class - * using the default {@link ClassLoader}. - * @see #GlassFishLoadTimeWeaver(ClassLoader) - */ public GlassFishLoadTimeWeaver() { this(ClassUtils.getDefaultClassLoader()); } - /** - * Creates a new instance of the {@code GlassFishLoadTimeWeaver} class. - * @param classLoader the specific {@link ClassLoader} to use; must not be {@code null} - * @throws IllegalArgumentException if the supplied {@code classLoader} is {@code null}; - * or if the supplied {@code classLoader} is not an - * {@link org.glassfish.api.deployment.InstrumentableClassLoader InstrumentableClassLoader} - */ public GlassFishLoadTimeWeaver(ClassLoader classLoader) { Assert.notNull(classLoader, "ClassLoader must not be null"); - this.classLoader = new GlassFishClassLoaderAdapter(classLoader); + + Class instrumentableLoaderClass; + try { + instrumentableLoaderClass = classLoader.loadClass(INSTRUMENTABLE_LOADER_CLASS_NAME); + } + catch (ClassNotFoundException ex) { + throw new IllegalStateException( + "Could not initialize GlassFishLoadTimeWeaver because GlassFish API classes are not available", ex); + } + try { + this.addTransformerMethod = instrumentableLoaderClass.getMethod("addTransformer", ClassFileTransformer.class); + this.copyMethod = instrumentableLoaderClass.getMethod("copy"); + } + catch (Exception ex) { + throw new IllegalStateException( + "Could not initialize GlassFishLoadTimeWeaver because GlassFish API classes are not available", ex); + } + + ClassLoader clazzLoader = null; + // Detect transformation-aware ClassLoader by traversing the hierarchy + // (as in GlassFish, Spring can be loaded by the WebappClassLoader). + for (ClassLoader cl = classLoader; cl != null && clazzLoader == null; cl = cl.getParent()) { + if (instrumentableLoaderClass.isInstance(cl)) { + clazzLoader = cl; + } + } + + if (clazzLoader == null) { + throw new IllegalArgumentException(classLoader + " and its parents are not suitable ClassLoaders: A [" + + instrumentableLoaderClass.getName() + "] implementation is required."); + } + + this.classLoader = clazzLoader; } @Override public void addTransformer(ClassFileTransformer transformer) { - this.classLoader.addTransformer(transformer); + try { + this.addTransformerMethod.invoke(this.classLoader, transformer); + } + catch (InvocationTargetException ex) { + throw new IllegalStateException("GlassFish addTransformer method threw exception", ex.getCause()); + } + catch (Exception ex) { + throw new IllegalStateException("Could not invoke GlassFish addTransformer method", ex); + } } @Override public ClassLoader getInstrumentableClassLoader() { - return this.classLoader.getClassLoader(); + return this.classLoader; } @Override public ClassLoader getThrowawayClassLoader() { - return this.classLoader.getThrowawayClassLoader(); + try { + return (ClassLoader) this.copyMethod.invoke(this.classLoader); + } + catch (InvocationTargetException ex) { + throw new IllegalStateException("GlassFish copy method threw exception", ex.getCause()); + } + catch (Exception ex) { + throw new IllegalStateException("Could not invoke GlassFish copy method", ex); + } } } diff --git a/spring-context/src/main/java/org/springframework/instrument/classloading/glassfish/package-info.java b/spring-context/src/main/java/org/springframework/instrument/classloading/glassfish/package-info.java index 178a1904a68..57fcb6e4adb 100644 --- a/spring-context/src/main/java/org/springframework/instrument/classloading/glassfish/package-info.java +++ b/spring-context/src/main/java/org/springframework/instrument/classloading/glassfish/package-info.java @@ -1,7 +1,7 @@ /** * - * Support for class instrumentation on GlassFish / Sun Application Server. + * Support for class instrumentation on GlassFish. * */ package org.springframework.instrument.classloading.glassfish; 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 new file mode 100644 index 00000000000..1ef36c95510 --- /dev/null +++ b/spring-context/src/main/java/org/springframework/instrument/classloading/tomcat/TomcatLoadTimeWeaver.java @@ -0,0 +1,116 @@ +/* + * Copyright 2002-2013 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.instrument.classloading.tomcat; + +import java.lang.instrument.ClassFileTransformer; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import org.springframework.instrument.classloading.LoadTimeWeaver; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; + +/** + * {@link org.springframework.instrument.classloading.LoadTimeWeaver} implementation for Tomcat's + * new {@link org.apache.tomcat.InstrumentableClassLoader InstrumentableClassLoader}. + * Also capable of handling Spring's TomcatInstrumentableClassLoader when encountered. + * + * @author Juergen Hoeller + * @since 4.0 + */ +public class TomcatLoadTimeWeaver implements LoadTimeWeaver { + + private static final String INSTRUMENTABLE_LOADER_CLASS_NAME = "org.apache.tomcat.InstrumentableClassLoader"; + + + private final ClassLoader classLoader; + + private final Method addTransformerMethod; + + private final Method copyMethod; + + + public TomcatLoadTimeWeaver() { + this(ClassUtils.getDefaultClassLoader()); + } + + public TomcatLoadTimeWeaver(ClassLoader classLoader) { + Assert.notNull(classLoader, "ClassLoader must not be null"); + this.classLoader = classLoader; + + Class instrumentableLoaderClass; + try { + instrumentableLoaderClass = classLoader.loadClass(INSTRUMENTABLE_LOADER_CLASS_NAME); + if (!instrumentableLoaderClass.isInstance(classLoader)) { + // Could still be a custom variant of a convention-compatible ClassLoader + instrumentableLoaderClass = classLoader.getClass(); + } + } + catch (ClassNotFoundException ex) { + // We're on an earlier version of Tomcat, probably with Spring's TomcatInstrumentableClassLoader + instrumentableLoaderClass = classLoader.getClass(); + } + + try { + this.addTransformerMethod = instrumentableLoaderClass.getMethod("addTransformer", ClassFileTransformer.class); + // Check for Tomcat's new copyWithoutTransformers on InstrumentableClassLoader first + Method copyMethod = ClassUtils.getMethodIfAvailable(instrumentableLoaderClass, "copyWithoutTransformers"); + if (copyMethod == null) { + // Fallback: expecting TomcatInstrumentableClassLoader's getThrowawayClassLoader + copyMethod = instrumentableLoaderClass.getMethod("getThrowawayClassLoader"); + } + this.copyMethod = copyMethod; + } + catch (Exception ex) { + throw new IllegalStateException( + "Could not initialize TomcatLoadTimeWeaver because Tomcat API classes are not available", ex); + } + } + + + @Override + public void addTransformer(ClassFileTransformer transformer) { + try { + this.addTransformerMethod.invoke(this.classLoader, transformer); + } + catch (InvocationTargetException ex) { + throw new IllegalStateException("Tomcat addTransformer method threw exception", ex.getCause()); + } + catch (Exception ex) { + throw new IllegalStateException("Could not invoke Tomcat addTransformer method", ex); + } + } + + @Override + public ClassLoader getInstrumentableClassLoader() { + return this.classLoader; + } + + @Override + public ClassLoader getThrowawayClassLoader() { + try { + return (ClassLoader) this.copyMethod.invoke(this.classLoader); + } + catch (InvocationTargetException ex) { + throw new IllegalStateException("Tomcat copy method threw exception", ex.getCause()); + } + catch (Exception ex) { + throw new IllegalStateException("Could not invoke Tomcat copy method", ex); + } + } + +} diff --git a/spring-context/src/main/java/org/springframework/instrument/classloading/tomcat/package-info.java b/spring-context/src/main/java/org/springframework/instrument/classloading/tomcat/package-info.java new file mode 100644 index 00000000000..e724bb2d428 --- /dev/null +++ b/spring-context/src/main/java/org/springframework/instrument/classloading/tomcat/package-info.java @@ -0,0 +1,8 @@ + +/** + * + * Support for class instrumentation on Tomcat. + * + */ +package org.springframework.instrument.classloading.tomcat; +