001/* 002 * Copyright 2002-2016 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 * https://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 017package org.springframework.instrument.classloading; 018 019import java.lang.instrument.ClassFileTransformer; 020import java.lang.reflect.Method; 021 022import org.apache.commons.logging.Log; 023import org.apache.commons.logging.LogFactory; 024 025import org.springframework.core.DecoratingClassLoader; 026import org.springframework.core.OverridingClassLoader; 027import org.springframework.util.Assert; 028import org.springframework.util.ClassUtils; 029import org.springframework.util.ReflectionUtils; 030 031/** 032 * {@link LoadTimeWeaver} which uses reflection to delegate to an underlying ClassLoader 033 * with well-known transformation hooks. The underlying ClassLoader is expected to 034 * support the following weaving methods (as defined in the {@link LoadTimeWeaver} 035 * interface): 036 * <ul> 037 * <li>{@code public void addTransformer(java.lang.instrument.ClassFileTransformer)}: 038 * for registering the given ClassFileTransformer on this ClassLoader 039 * <li>{@code public ClassLoader getThrowawayClassLoader()}: 040 * for obtaining a throwaway class loader for this ClassLoader (optional; 041 * ReflectiveLoadTimeWeaver will fall back to a SimpleThrowawayClassLoader if 042 * that method isn't available) 043 * </ul> 044 * 045 * <p>Please note that the above methods <i>must</i> reside in a class that is 046 * publicly accessible, although the class itself does not have to be visible 047 * to the application's class loader. 048 * 049 * <p>The reflective nature of this LoadTimeWeaver is particularly useful when the 050 * underlying ClassLoader implementation is loaded in a different class loader itself 051 * (such as the application server's class loader which is not visible to the 052 * web application). There is no direct API dependency between this LoadTimeWeaver 053 * adapter and the underlying ClassLoader, just a 'loose' method contract. 054 * 055 * <p>This is the LoadTimeWeaver to use in combination with Spring's 056 * {@link org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader} 057 * for Tomcat 5.0+ as well as with the Resin application server version 3.1+. 058 * 059 * @author Costin Leau 060 * @author Juergen Hoeller 061 * @since 2.0 062 * @see #addTransformer(java.lang.instrument.ClassFileTransformer) 063 * @see #getThrowawayClassLoader() 064 * @see SimpleThrowawayClassLoader 065 * @see org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader 066 */ 067public class ReflectiveLoadTimeWeaver implements LoadTimeWeaver { 068 069 private static final String ADD_TRANSFORMER_METHOD_NAME = "addTransformer"; 070 071 private static final String GET_THROWAWAY_CLASS_LOADER_METHOD_NAME = "getThrowawayClassLoader"; 072 073 private static final Log logger = LogFactory.getLog(ReflectiveLoadTimeWeaver.class); 074 075 076 private final ClassLoader classLoader; 077 078 private final Method addTransformerMethod; 079 080 private final Method getThrowawayClassLoaderMethod; 081 082 083 /** 084 * Create a new ReflectiveLoadTimeWeaver for the current context class 085 * loader, <i>which needs to support the required weaving methods</i>. 086 */ 087 public ReflectiveLoadTimeWeaver() { 088 this(ClassUtils.getDefaultClassLoader()); 089 } 090 091 /** 092 * Create a new SimpleLoadTimeWeaver for the given class loader. 093 * @param classLoader the {@code ClassLoader} to delegate to for 094 * weaving (<i>must</i> support the required weaving methods). 095 * @throws IllegalStateException if the supplied {@code ClassLoader} 096 * does not support the required weaving methods 097 */ 098 public ReflectiveLoadTimeWeaver(ClassLoader classLoader) { 099 Assert.notNull(classLoader, "ClassLoader must not be null"); 100 this.classLoader = classLoader; 101 this.addTransformerMethod = ClassUtils.getMethodIfAvailable( 102 this.classLoader.getClass(), ADD_TRANSFORMER_METHOD_NAME, ClassFileTransformer.class); 103 if (this.addTransformerMethod == null) { 104 throw new IllegalStateException( 105 "ClassLoader [" + classLoader.getClass().getName() + "] does NOT provide an " + 106 "'addTransformer(ClassFileTransformer)' method."); 107 } 108 this.getThrowawayClassLoaderMethod = ClassUtils.getMethodIfAvailable( 109 this.classLoader.getClass(), GET_THROWAWAY_CLASS_LOADER_METHOD_NAME); 110 // getThrowawayClassLoader method is optional 111 if (this.getThrowawayClassLoaderMethod == null) { 112 if (logger.isInfoEnabled()) { 113 logger.info("The ClassLoader [" + classLoader.getClass().getName() + "] does NOT provide a " + 114 "'getThrowawayClassLoader()' method; SimpleThrowawayClassLoader will be used instead."); 115 } 116 } 117 } 118 119 120 @Override 121 public void addTransformer(ClassFileTransformer transformer) { 122 Assert.notNull(transformer, "Transformer must not be null"); 123 ReflectionUtils.invokeMethod(this.addTransformerMethod, this.classLoader, transformer); 124 } 125 126 @Override 127 public ClassLoader getInstrumentableClassLoader() { 128 return this.classLoader; 129 } 130 131 @Override 132 public ClassLoader getThrowawayClassLoader() { 133 if (this.getThrowawayClassLoaderMethod != null) { 134 ClassLoader target = (ClassLoader) 135 ReflectionUtils.invokeMethod(this.getThrowawayClassLoaderMethod, this.classLoader); 136 return (target instanceof DecoratingClassLoader ? target : 137 new OverridingClassLoader(this.classLoader, target)); 138 } 139 else { 140 return new SimpleThrowawayClassLoader(this.classLoader); 141 } 142 } 143 144}