Initial import of instrument.classloading module
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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;
|
||||
|
||||
import java.lang.instrument.ClassFileTransformer;
|
||||
import java.lang.instrument.IllegalClassFormatException;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* ClassFileTransformer-based weaver, allowing for a list of transformers to be
|
||||
* applied on a class byte array. Normally used inside class loaders.
|
||||
*
|
||||
* <p>Note: This class is deliberately implemented for minimal external dependencies,
|
||||
* since it is included in weaver jars (to be deployed into application servers).
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Costin Leau
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public class WeavingTransformer {
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
private final List<ClassFileTransformer> transformers = new ArrayList<ClassFileTransformer>();
|
||||
|
||||
|
||||
/**
|
||||
* Create a new WeavingTransformer for the given class loader.
|
||||
* @param classLoader the ClassLoader to build a transformer for
|
||||
*/
|
||||
public WeavingTransformer(ClassLoader classLoader) {
|
||||
if (classLoader == null) {
|
||||
throw new IllegalArgumentException("ClassLoader must not be null");
|
||||
}
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a class file transformer to be applied by this weaver.
|
||||
* @param transformer the class file transformer to register
|
||||
*/
|
||||
public void addTransformer(ClassFileTransformer transformer) {
|
||||
if (transformer == null) {
|
||||
throw new IllegalArgumentException("Transformer must not be null");
|
||||
}
|
||||
this.transformers.add(transformer);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Apply transformation on a given class byte definition.
|
||||
* The method will always return a non-null byte array (if no transformation has taken place
|
||||
* the array content will be identical to the original one).
|
||||
* @param className the full qualified name of the class in dot format (i.e. some.package.SomeClass)
|
||||
* @param bytes class byte definition
|
||||
* @return (possibly transformed) class byte definition
|
||||
*/
|
||||
public byte[] transformIfNecessary(String className, byte[] bytes) {
|
||||
String internalName = className.replace(".", "/");
|
||||
return transformIfNecessary(className, internalName, bytes, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply transformation on a given class byte definition.
|
||||
* The method will always return a non-null byte array (if no transformation has taken place
|
||||
* the array content will be identical to the original one).
|
||||
* @param className the full qualified name of the class in dot format (i.e. some.package.SomeClass)
|
||||
* @param internalName class name internal name in / format (i.e. some/package/SomeClass)
|
||||
* @param bytes class byte definition
|
||||
* @param pd protection domain to be used (can be null)
|
||||
* @return (possibly transformed) class byte definition
|
||||
*/
|
||||
public byte[] transformIfNecessary(String className, String internalName, byte[] bytes, ProtectionDomain pd) {
|
||||
byte[] result = bytes;
|
||||
for (ClassFileTransformer cft : this.transformers) {
|
||||
try {
|
||||
byte[] transformed = cft.transform(this.classLoader, internalName, null, pd, result);
|
||||
if (transformed != null) {
|
||||
result = transformed;
|
||||
}
|
||||
}
|
||||
catch (IllegalClassFormatException ex) {
|
||||
throw new IllegalStateException("Class file transformation failed", ex);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
* Copyright 2002-2007 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.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import org.apache.catalina.loader.ResourceEntry;
|
||||
import org.apache.catalina.loader.WebappClassLoader;
|
||||
|
||||
import org.springframework.instrument.classloading.WeavingTransformer;
|
||||
|
||||
/**
|
||||
* Extension of Tomcat's default class loader which adds instrumentation
|
||||
* to loaded classes without the need to use a VM-wide agent.
|
||||
*
|
||||
* <p>To be registered using a <code>Loader</code> tag in Tomcat's <code>Context</code>
|
||||
* definition in the <code>server.xml</code> file, with the Spring-provided
|
||||
* "spring-tomcat-weaver.jar" file deployed into Tomcat's "server/lib" directory.
|
||||
* The required configuration tag looks as follows:
|
||||
*
|
||||
* <pre class="code"><Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/></pre>
|
||||
*
|
||||
* <p>Typically used in combination with a
|
||||
* {@link org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver}
|
||||
* defined in the Spring application context. The <code>addTransformer</code> and
|
||||
* <code>getThrowawayClassLoader</code> methods mirror the corresponding methods
|
||||
* in the LoadTimeWeaver interface, as expected by ReflectiveLoadTimeWeaver.
|
||||
*
|
||||
* <p>See the PetClinic sample application for a full example of this
|
||||
* ClassLoader in action.
|
||||
*
|
||||
* <p><b>NOTE:</b> Requires Apache Tomcat version 5.0 or higher.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see #addTransformer
|
||||
* @see #getThrowawayClassLoader
|
||||
* @see org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver
|
||||
*/
|
||||
public class TomcatInstrumentableClassLoader extends WebappClassLoader {
|
||||
|
||||
/** Use an internal WeavingTransformer */
|
||||
private final WeavingTransformer weavingTransformer;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new <code>TomcatInstrumentableClassLoader</code> using the
|
||||
* current context class loader.
|
||||
* @see #TomcatInstrumentableClassLoader(ClassLoader)
|
||||
*/
|
||||
public TomcatInstrumentableClassLoader() {
|
||||
super();
|
||||
this.weavingTransformer = new WeavingTransformer(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new <code>TomcatInstrumentableClassLoader</code> with the
|
||||
* supplied class loader as parent.
|
||||
* @param parent the parent {@link ClassLoader} to be used
|
||||
*/
|
||||
public TomcatInstrumentableClassLoader(ClassLoader parent) {
|
||||
super(parent);
|
||||
this.weavingTransformer = new WeavingTransformer(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delegate for LoadTimeWeaver's <code>addTransformer</code> method.
|
||||
* Typically called through ReflectiveLoadTimeWeaver.
|
||||
* @see org.springframework.instrument.classloading.LoadTimeWeaver#addTransformer
|
||||
* @see org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver
|
||||
*/
|
||||
public void addTransformer(ClassFileTransformer transformer) {
|
||||
this.weavingTransformer.addTransformer(transformer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate for LoadTimeWeaver's <code>getThrowawayClassLoader</code> method.
|
||||
* Typically called through ReflectiveLoadTimeWeaver.
|
||||
* @see org.springframework.instrument.classloading.LoadTimeWeaver#getThrowawayClassLoader
|
||||
* @see org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver
|
||||
*/
|
||||
public ClassLoader getThrowawayClassLoader() {
|
||||
WebappClassLoader tempLoader = new WebappClassLoader();
|
||||
// Use reflection to copy all the fields since most of them are private
|
||||
// on pre-5.5 Tomcat.
|
||||
shallowCopyFieldState(this, tempLoader);
|
||||
return tempLoader;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected ResourceEntry findResourceInternal(String name, String path) {
|
||||
ResourceEntry entry = super.findResourceInternal(name, path);
|
||||
// Postpone String parsing as much as possible (it is slow).
|
||||
if (entry != null && entry.binaryContent != null && path.endsWith(".class")) {
|
||||
byte[] transformed = this.weavingTransformer.transformIfNecessary(name, entry.binaryContent);
|
||||
entry.binaryContent = transformed;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder(getClass().getName());
|
||||
sb.append("\r\n");
|
||||
sb.append(super.toString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
// The code below is orginially taken from ReflectionUtils and optimized for
|
||||
// local usage. There is no dependency on ReflectionUtils to keep this class
|
||||
// self-contained (since it gets deployed into Tomcat's server class loader).
|
||||
|
||||
/**
|
||||
* Given the source object and the destination, which must be the same class
|
||||
* or a subclass, copy all fields, including inherited fields. Designed to
|
||||
* work on objects with public no-arg constructors.
|
||||
* @throws IllegalArgumentException if arguments are incompatible or either
|
||||
* is <code>null</code>
|
||||
*/
|
||||
private static void shallowCopyFieldState(final Object src, final Object dest) throws IllegalArgumentException {
|
||||
if (src == null) {
|
||||
throw new IllegalArgumentException("Source for field copy cannot be null");
|
||||
}
|
||||
if (dest == null) {
|
||||
throw new IllegalArgumentException("Destination for field copy cannot be null");
|
||||
}
|
||||
Class targetClass = findCommonAncestor(src.getClass(), dest.getClass());
|
||||
|
||||
// Keep backing up the inheritance hierarchy.
|
||||
do {
|
||||
// Copy each field declared on this class unless it's static or
|
||||
// file.
|
||||
Field[] fields = targetClass.getDeclaredFields();
|
||||
for (int i = 0; i < fields.length; i++) {
|
||||
Field field = fields[i];
|
||||
// Skip static and final fields (the old FieldFilter)
|
||||
// do not copy resourceEntries - it's a cache that holds class entries.
|
||||
if (!(Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers()) ||
|
||||
field.getName().equals("resourceEntries"))) {
|
||||
try {
|
||||
// copy the field (the old FieldCallback)
|
||||
field.setAccessible(true);
|
||||
Object srcValue = field.get(src);
|
||||
field.set(dest, srcValue);
|
||||
}
|
||||
catch (IllegalAccessException ex) {
|
||||
throw new IllegalStateException(
|
||||
"Shouldn't be illegal to access field '" + fields[i].getName() + "': " + ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
targetClass = targetClass.getSuperclass();
|
||||
}
|
||||
while (targetClass != null && targetClass != Object.class);
|
||||
}
|
||||
|
||||
private static Class findCommonAncestor(Class one, Class two) throws IllegalArgumentException {
|
||||
Class ancestor = one;
|
||||
while (ancestor != Object.class || ancestor != null) {
|
||||
if (ancestor.isAssignableFrom(two)) {
|
||||
return ancestor;
|
||||
}
|
||||
ancestor = ancestor.getSuperclass();
|
||||
}
|
||||
// try the other class hierarchy
|
||||
ancestor = two;
|
||||
while (ancestor != Object.class || ancestor != null) {
|
||||
if (ancestor.isAssignableFrom(one)) {
|
||||
return ancestor;
|
||||
}
|
||||
ancestor = ancestor.getSuperclass();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<html>
|
||||
<body>
|
||||
|
||||
Support for class instrumentation on Apache Tomcat.
|
||||
|
||||
</html>
|
||||
</body>
|
||||
@@ -0,0 +1,7 @@
|
||||
<html>
|
||||
<body>
|
||||
<p>
|
||||
The Spring Data Binding framework, an internal library used by Spring Web Flow.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user