001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.instrument.classloading;
018:
019: import java.lang.instrument.ClassFileTransformer;
020: import java.lang.reflect.Method;
021:
022: import org.apache.commons.logging.Log;
023: import org.apache.commons.logging.LogFactory;
024:
025: import org.springframework.util.Assert;
026: import org.springframework.util.ClassUtils;
027: import org.springframework.util.ReflectionUtils;
028:
029: /**
030: * LoadTimeWeaver which uses reflection to delegate to an underlying ClassLoader
031: * with well-known transformation hooks. The underlying ClassLoader is expected to
032: * support the following weaving methods (as defined in the {@link LoadTimeWeaver}
033: * interface):
034: * <ul>
035: * <li><code>public void addTransformer(java.lang.instrument.ClassFileTransformer)</code>:
036: * for registering the given ClassFileTransformer on this ClassLoader
037: * <li><code>public ClassLoader getThrowawayClassLoader()</code>:
038: * for obtaining a throwaway class loader for this ClassLoader (optional;
039: * ReflectiveLoadTimeWeaver will fall back to a SimpleThrowawayClassLoader if
040: * that method isn't available)
041: * </ul>
042: *
043: * <p>Please note that the above methods <i>must</i> reside in a class that is
044: * publicly accessible, although the class itself does not have to be visible
045: * to the application's class loader.
046: *
047: * <p>The reflective nature of this LoadTimeWeaver is particularly useful when the
048: * underlying class loader implementation is loaded in a different class loader
049: * (such as the application server's class loader which is not visible to the
050: * web application). There is no direct API dependency between this LoadTimeWeaver
051: * adapter and the underlying ClassLoader, just a 'loose' method contract.
052: *
053: * <p>This is the LoadTimeWeaver to use in combination with Spring's
054: * {@link org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader}.
055: *
056: * @author Costin Leau
057: * @author Juergen Hoeller
058: * @since 2.0
059: * @see #addTransformer(java.lang.instrument.ClassFileTransformer)
060: * @see #getThrowawayClassLoader()
061: * @see SimpleThrowawayClassLoader
062: * @see org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader
063: */
064: public class ReflectiveLoadTimeWeaver implements LoadTimeWeaver {
065:
066: private static final String ADD_TRANSFORMER_METHOD_NAME = "addTransformer";
067:
068: private static final String GET_THROWAWAY_CLASS_LOADER_METHOD_NAME = "getThrowawayClassLoader";
069:
070: private static final Log logger = LogFactory
071: .getLog(ReflectiveLoadTimeWeaver.class);
072:
073: private final ClassLoader classLoader;
074:
075: private final Method addTransformerMethod;
076:
077: private final Method getThrowawayClassLoaderMethod;
078:
079: /**
080: * Create a new ReflectiveLoadTimeWeaver for the current context class
081: * loader, <i>which needs to support the required weaving methods</i>.
082: */
083: public ReflectiveLoadTimeWeaver() {
084: this (ClassUtils.getDefaultClassLoader());
085: }
086:
087: /**
088: * Create a new SimpleLoadTimeWeaver for the given class loader.
089: * @param classLoader the <code>ClassLoader</code> to delegate to for
090: * weaving (<i>must</i> support the required weaving methods).
091: * @throws IllegalArgumentException if the supplied <code>ClassLoader</code>
092: * is <code>null</code>
093: * @throws IllegalStateException if the supplied <code>ClassLoader</code>
094: * does not support the required weaving methods
095: */
096: public ReflectiveLoadTimeWeaver(ClassLoader classLoader) {
097: Assert.notNull(classLoader, "ClassLoader must not be null");
098: this .classLoader = classLoader;
099: this .addTransformerMethod = ClassUtils.getMethodIfAvailable(
100: this .classLoader.getClass(),
101: ADD_TRANSFORMER_METHOD_NAME,
102: new Class[] { ClassFileTransformer.class });
103: if (this .addTransformerMethod == null) {
104: throw new IllegalStateException("ClassLoader ["
105: + classLoader.getClass().getName()
106: + "] does NOT provide an "
107: + "'addTransformer(ClassFileTransformer)' method.");
108: }
109: this .getThrowawayClassLoaderMethod = ClassUtils
110: .getMethodIfAvailable(this .classLoader.getClass(),
111: GET_THROWAWAY_CLASS_LOADER_METHOD_NAME,
112: new Class[0]);
113: // getThrowawayClassLoader method is optional
114: if (this .getThrowawayClassLoaderMethod == null) {
115: if (logger.isInfoEnabled()) {
116: logger
117: .info("The ClassLoader ["
118: + classLoader.getClass().getName()
119: + "] does NOT provide a "
120: + "'getThrowawayClassLoader()' method; SimpleThrowawayClassLoader will be used instead.");
121: }
122: }
123: }
124:
125: public void addTransformer(ClassFileTransformer transformer) {
126: Assert.notNull(transformer, "Transformer must not be null");
127: ReflectionUtils.invokeMethod(this .addTransformerMethod,
128: this .classLoader, new Object[] { transformer });
129: }
130:
131: public ClassLoader getInstrumentableClassLoader() {
132: return this .classLoader;
133: }
134:
135: public ClassLoader getThrowawayClassLoader() {
136: if (this .getThrowawayClassLoaderMethod != null) {
137: return (ClassLoader) ReflectionUtils.invokeMethod(
138: this .getThrowawayClassLoaderMethod,
139: this .classLoader, new Object[0]);
140: } else {
141: return new SimpleThrowawayClassLoader(this.classLoader);
142: }
143: }
144:
145: }
|