001/* 002 * Copyright 2002-2015 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.tomcat; 018 019import java.lang.instrument.ClassFileTransformer; 020import java.lang.reflect.Field; 021import java.lang.reflect.Modifier; 022 023import org.apache.catalina.loader.ResourceEntry; 024import org.apache.catalina.loader.WebappClassLoader; 025 026import org.springframework.instrument.classloading.WeavingTransformer; 027 028/** 029 * Extension of Tomcat's default class loader which adds instrumentation 030 * to loaded classes without the need to use a VM-wide agent. 031 * 032 * <p>To be registered using a 033 * <a href="https://tomcat.apache.org/tomcat-6.0-doc/config/loader.html">{@code Loader}</a> tag 034 * in Tomcat's <a href="https://tomcat.apache.org/tomcat-6.0-doc/config/context.html">{@code Context}</a> 035 * definition in the {@code server.xml} file, with the Spring-provided "spring-instrument-tomcat.jar" 036 * file deployed into Tomcat's "lib" directory. The required configuration tag looks as follows: 037 * 038 * <pre class="code"><Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/></pre> 039 * 040 * <p>Typically used in combination with a 041 * {@link org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver} 042 * defined in the Spring application context. The {@code addTransformer} and 043 * {@code getThrowawayClassLoader} methods mirror the corresponding methods 044 * in the LoadTimeWeaver interface, as expected by ReflectiveLoadTimeWeaver. 045 * 046 * <p><b>NOTE:</b> Requires Apache Tomcat version 6.0 or higher, as of Spring 4.0. 047 * This class is not intended to work on Tomcat 8.0+; please rely on Tomcat's own 048 * {@code InstrumentableClassLoader} facility instead, as autodetected by Spring's 049 * {@link org.springframework.instrument.classloading.tomcat.TomcatLoadTimeWeaver}. 050 * 051 * @author Costin Leau 052 * @author Juergen Hoeller 053 * @since 2.0 054 * @see #addTransformer 055 * @see #getThrowawayClassLoader 056 * @see org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver 057 * @see org.springframework.instrument.classloading.tomcat.TomcatLoadTimeWeaver 058 */ 059public class TomcatInstrumentableClassLoader extends WebappClassLoader { 060 061 private static final String CLASS_SUFFIX = ".class"; 062 063 /** Use an internal WeavingTransformer */ 064 private final WeavingTransformer weavingTransformer; 065 066 067 /** 068 * Create a new {@code TomcatInstrumentableClassLoader} using the 069 * current context class loader. 070 * @see #TomcatInstrumentableClassLoader(ClassLoader) 071 */ 072 public TomcatInstrumentableClassLoader() { 073 super(); 074 this.weavingTransformer = new WeavingTransformer(this); 075 } 076 077 /** 078 * Create a new {@code TomcatInstrumentableClassLoader} with the 079 * supplied class loader as parent. 080 * @param parent the parent {@link ClassLoader} to be used 081 */ 082 public TomcatInstrumentableClassLoader(ClassLoader parent) { 083 super(parent); 084 this.weavingTransformer = new WeavingTransformer(this); 085 } 086 087 088 /** 089 * Delegate for LoadTimeWeaver's {@code addTransformer} method. 090 * Typically called through ReflectiveLoadTimeWeaver. 091 * @see org.springframework.instrument.classloading.LoadTimeWeaver#addTransformer 092 * @see org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver 093 */ 094 public void addTransformer(ClassFileTransformer transformer) { 095 this.weavingTransformer.addTransformer(transformer); 096 } 097 098 /** 099 * Delegate for LoadTimeWeaver's {@code getThrowawayClassLoader} method. 100 * Typically called through ReflectiveLoadTimeWeaver. 101 * @see org.springframework.instrument.classloading.LoadTimeWeaver#getThrowawayClassLoader 102 * @see org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver 103 */ 104 public ClassLoader getThrowawayClassLoader() { 105 WebappClassLoader tempLoader = new WebappClassLoader(); 106 // Use reflection to copy all the fields since they are not exposed any other way. 107 shallowCopyFieldState(this, tempLoader); 108 return tempLoader; 109 } 110 111 112 @Override // overriding the pre-7.0.63 variant of findResourceInternal 113 protected ResourceEntry findResourceInternal(String name, String path) { 114 ResourceEntry entry = super.findResourceInternal(name, path); 115 if (entry != null && entry.binaryContent != null && path.endsWith(CLASS_SUFFIX)) { 116 String className = (name.endsWith(CLASS_SUFFIX) ? name.substring(0, name.length() - CLASS_SUFFIX.length()) : name); 117 entry.binaryContent = this.weavingTransformer.transformIfNecessary(className, entry.binaryContent); 118 } 119 return entry; 120 } 121 122 @Override // overriding the 7.0.63+ variant of findResourceInternal 123 protected ResourceEntry findResourceInternal(String name, String path, boolean manifestRequired) { 124 ResourceEntry entry = super.findResourceInternal(name, path, manifestRequired); 125 if (entry != null && entry.binaryContent != null && path.endsWith(CLASS_SUFFIX)) { 126 String className = (name.endsWith(CLASS_SUFFIX) ? name.substring(0, name.length() - CLASS_SUFFIX.length()) : name); 127 entry.binaryContent = this.weavingTransformer.transformIfNecessary(className, entry.binaryContent); 128 } 129 return entry; 130 } 131 132 @Override 133 public String toString() { 134 return getClass().getName() + "\r\n" + super.toString(); 135 } 136 137 138 // The code below is originally taken from ReflectionUtils and optimized for 139 // local usage. There is no dependency on ReflectionUtils to keep this class 140 // self-contained (since it gets deployed into Tomcat's server class loader). 141 private static void shallowCopyFieldState(final WebappClassLoader src, final WebappClassLoader dest) { 142 Class<?> targetClass = WebappClassLoader.class; 143 // Keep backing up the inheritance hierarchy. 144 do { 145 Field[] fields = targetClass.getDeclaredFields(); 146 for (Field field : fields) { 147 // Do not copy resourceEntries - it's a cache that holds class entries. 148 if (!(Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers()) || 149 field.getName().equals("resourceEntries"))) { 150 try { 151 field.setAccessible(true); 152 Object srcValue = field.get(src); 153 field.set(dest, srcValue); 154 } 155 catch (IllegalAccessException ex) { 156 throw new IllegalStateException( 157 "Shouldn't be illegal to access field '" + field.getName() + "': " + ex); 158 } 159 } 160 } 161 targetClass = targetClass.getSuperclass(); 162 } 163 while (targetClass != null && targetClass != Object.class); 164 } 165 166}