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
This commit is contained in:
Juergen Hoeller 2013-09-27 15:56:11 +02:00 committed by unknown
parent 888e3c7e53
commit 3fad0fa432
7 changed files with 198 additions and 210 deletions

View File

@ -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.
*
* <p>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,
* <p>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);
}

View File

@ -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);
}
}

View File

@ -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.
*
* <p>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);
}
}
}

View File

@ -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}.
*
* <p>As of Spring 3.0, GlassFish V3 is supported as well.
* <p>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);
}
}
}

View File

@ -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;

View File

@ -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);
}
}
}

View File

@ -0,0 +1,8 @@
/**
*
* Support for class instrumentation on Tomcat.
*
*/
package org.springframework.instrument.classloading.tomcat;