001/* 002 * Copyright 2002-2018 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.lang.Nullable; 028import org.springframework.util.Assert; 029import org.springframework.util.ClassUtils; 030import org.springframework.util.ReflectionUtils; 031 032/** 033 * {@link LoadTimeWeaver} which uses reflection to delegate to an underlying ClassLoader 034 * with well-known transformation hooks. The underlying ClassLoader is expected to 035 * support the following weaving methods (as defined in the {@link LoadTimeWeaver} 036 * interface): 037 * <ul> 038 * <li>{@code public void addTransformer(java.lang.instrument.ClassFileTransformer)}: 039 * for registering the given ClassFileTransformer on this ClassLoader 040 * <li>{@code public ClassLoader getThrowawayClassLoader()}: 041 * for obtaining a throwaway class loader for this ClassLoader (optional; 042 * ReflectiveLoadTimeWeaver will fall back to a SimpleThrowawayClassLoader if 043 * that method isn't available) 044 * </ul> 045 * 046 * <p>Please note that the above methods <i>must</i> reside in a class that is 047 * publicly accessible, although the class itself does not have to be visible 048 * to the application's class loader. 049 * 050 * <p>The reflective nature of this LoadTimeWeaver is particularly useful when the 051 * underlying ClassLoader implementation is loaded in a different class loader itself 052 * (such as the application server's class loader which is not visible to the 053 * web application). There is no direct API dependency between this LoadTimeWeaver 054 * adapter and the underlying ClassLoader, just a 'loose' method contract. 055 * 056 * <p>This is the LoadTimeWeaver to use e.g. with the Resin application server 057 * 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 */ 066public class ReflectiveLoadTimeWeaver implements LoadTimeWeaver { 067 068 private static final String ADD_TRANSFORMER_METHOD_NAME = "addTransformer"; 069 070 private static final String GET_THROWAWAY_CLASS_LOADER_METHOD_NAME = "getThrowawayClassLoader"; 071 072 private static final Log logger = LogFactory.getLog(ReflectiveLoadTimeWeaver.class); 073 074 075 private final ClassLoader classLoader; 076 077 private final Method addTransformerMethod; 078 079 @Nullable 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(@Nullable ClassLoader classLoader) { 099 Assert.notNull(classLoader, "ClassLoader must not be null"); 100 this.classLoader = classLoader; 101 102 Method addTransformerMethod = ClassUtils.getMethodIfAvailable( 103 this.classLoader.getClass(), ADD_TRANSFORMER_METHOD_NAME, ClassFileTransformer.class); 104 if (addTransformerMethod == null) { 105 throw new IllegalStateException( 106 "ClassLoader [" + classLoader.getClass().getName() + "] does NOT provide an " + 107 "'addTransformer(ClassFileTransformer)' method."); 108 } 109 this.addTransformerMethod = addTransformerMethod; 110 111 Method getThrowawayClassLoaderMethod = ClassUtils.getMethodIfAvailable( 112 this.classLoader.getClass(), GET_THROWAWAY_CLASS_LOADER_METHOD_NAME); 113 // getThrowawayClassLoader method is optional 114 if (getThrowawayClassLoaderMethod == null) { 115 if (logger.isDebugEnabled()) { 116 logger.debug("The ClassLoader [" + classLoader.getClass().getName() + "] does NOT provide a " + 117 "'getThrowawayClassLoader()' method; SimpleThrowawayClassLoader will be used instead."); 118 } 119 } 120 this.getThrowawayClassLoaderMethod = getThrowawayClassLoaderMethod; 121 } 122 123 124 @Override 125 public void addTransformer(ClassFileTransformer transformer) { 126 Assert.notNull(transformer, "Transformer must not be null"); 127 ReflectionUtils.invokeMethod(this.addTransformerMethod, this.classLoader, transformer); 128 } 129 130 @Override 131 public ClassLoader getInstrumentableClassLoader() { 132 return this.classLoader; 133 } 134 135 @Override 136 public ClassLoader getThrowawayClassLoader() { 137 if (this.getThrowawayClassLoaderMethod != null) { 138 ClassLoader target = (ClassLoader) 139 ReflectionUtils.invokeMethod(this.getThrowawayClassLoaderMethod, this.classLoader); 140 return (target instanceof DecoratingClassLoader ? target : 141 new OverridingClassLoader(this.classLoader, target)); 142 } 143 else { 144 return new SimpleThrowawayClassLoader(this.classLoader); 145 } 146 } 147 148}