Browse Source

Initial import of instrument.classloading module

pull/23217/head
Arjen Poutsma 17 years ago
parent
commit
bc53873707
  1. 1
      build-spring-framework/build.xml
  2. 6
      org.springframework.instrument.classloading/build.xml
  3. 25
      org.springframework.instrument.classloading/ivy.xml
  4. 12
      org.springframework.instrument.classloading/pom.xml
  5. 107
      org.springframework.instrument.classloading/src/main/java/org/springframework/instrument/classloading/WeavingTransformer.java
  6. 196
      org.springframework.instrument.classloading/src/main/java/org/springframework/instrument/classloading/tomcat/TomcatInstrumentableClassLoader.java
  7. 7
      org.springframework.instrument.classloading/src/main/java/org/springframework/instrument/classloading/tomcat/package.html
  8. 7
      org.springframework.instrument.classloading/src/main/java/overview.html
  9. 28
      org.springframework.instrument.classloading/src/test/resources/log4j.xml
  10. 6
      org.springframework.instrument.classloading/template.mf

1
build-spring-framework/build.xml

@ -3,6 +3,7 @@
<path id="bundles"> <path id="bundles">
<pathelement location="../org.springframework.instrument"/> <pathelement location="../org.springframework.instrument"/>
<pathelement location="../org.springframework.instrument.classloading"/>
<pathelement location="../org.springframework.core"/> <pathelement location="../org.springframework.core"/>
<pathelement location="../org.springframework.expression"/> <pathelement location="../org.springframework.expression"/>
<pathelement location="../org.springframework.beans"/> <pathelement location="../org.springframework.beans"/>

6
org.springframework.instrument.classloading/build.xml

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="org.springframework.instrument.classloading">
<property file="${basedir}/../build.properties"/>
<import file="${basedir}/../build-spring-framework/package-bundle.xml"/>
<import file="${basedir}/../spring-build/standard/default.xml"/>
</project>

25
org.springframework.instrument.classloading/ivy.xml

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="http://ivyrep.jayasoft.org/ivy-doc.xsl"?>
<ivy-module
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://incubator.apache.org/ivy/schemas/ivy.xsd"
version="1.3">
<info organisation="org.springframework" module="${ant.project.name}">
<license name="Apache 2.0" url="http://www.apache.org/licenses/LICENSE-2.0"/>
</info>
<configurations>
<include file="${spring.build.dir}/common/default-ivy-configurations.xml"/>
</configurations>
<publications>
<artifact name="${ant.project.name}"/>
<artifact name="${ant.project.name}-sources" type="src" ext="jar"/>
</publications>
<dependencies>
<dependency org="org.apache.catalina" name="com.springsource.org.apache.catalina" rev="6.0.16" conf="provided->compile"/>
</dependencies>
</ivy-module>

12
org.springframework.instrument.classloading/pom.xml

@ -0,0 +1,12 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.parent</artifactId>
<version>3.0-M1-SNAPSHOT</version>
</parent>
<artifactId>org.springframework.agent</artifactId>
<packaging>jar</packaging>
<name>Spring Framework: Agent</name>
</project>

107
org.springframework.instrument.classloading/src/main/java/org/springframework/instrument/classloading/WeavingTransformer.java

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

196
org.springframework.instrument.classloading/src/main/java/org/springframework/instrument/classloading/tomcat/TomcatInstrumentableClassLoader.java

@ -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">&lt;Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/&gt;</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;
}
}

7
org.springframework.instrument.classloading/src/main/java/org/springframework/instrument/classloading/tomcat/package.html

@ -0,0 +1,7 @@
<html>
<body>
Support for class instrumentation on Apache Tomcat.
</html>
</body>

7
org.springframework.instrument.classloading/src/main/java/overview.html

@ -0,0 +1,7 @@
<html>
<body>
<p>
The Spring Data Binding framework, an internal library used by Spring Web Flow.
</p>
</body>
</html>

28
org.springframework.instrument.classloading/src/test/resources/log4j.xml

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<!-- Appenders -->
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p: %c - %m%n" />
</layout>
</appender>
<logger name="org.springframework.beans">
<level value="warn" />
</logger>
<logger name="org.springframework.binding">
<level value="debug" />
</logger>
<!-- Root Logger -->
<root>
<priority value="warn" />
<appender-ref ref="console" />
</root>
</log4j:configuration>

6
org.springframework.instrument.classloading/template.mf

@ -0,0 +1,6 @@
Bundle-SymbolicName: org.springframework.instrument.classloading
Bundle-Name: Spring Instrument Classloading
Bundle-Vendor: SpringSource
Bundle-ManifestVersion: 2
Import-Template:
org.apache.catalina.*;version="[6.0.16, 7.0.0)"
Loading…
Cancel
Save