001/* 002 * Copyright 2002-2017 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.InvocationTargetException; 021import java.lang.reflect.Method; 022 023import org.springframework.core.OverridingClassLoader; 024import org.springframework.instrument.classloading.LoadTimeWeaver; 025import org.springframework.lang.Nullable; 026import org.springframework.util.Assert; 027import org.springframework.util.ClassUtils; 028 029/** 030 * {@link org.springframework.instrument.classloading.LoadTimeWeaver} implementation 031 * for Tomcat's new {@code org.apache.tomcat.InstrumentableClassLoader}. 032 * Also capable of handling Spring's TomcatInstrumentableClassLoader when encountered. 033 * 034 * @author Juergen Hoeller 035 * @since 4.0 036 */ 037public class TomcatLoadTimeWeaver implements LoadTimeWeaver { 038 039 private static final String INSTRUMENTABLE_LOADER_CLASS_NAME = "org.apache.tomcat.InstrumentableClassLoader"; 040 041 042 private final ClassLoader classLoader; 043 044 private final Method addTransformerMethod; 045 046 private final Method copyMethod; 047 048 049 /** 050 * Create a new instance of the {@link TomcatLoadTimeWeaver} class using 051 * the default {@link ClassLoader class loader}. 052 * @see org.springframework.util.ClassUtils#getDefaultClassLoader() 053 */ 054 public TomcatLoadTimeWeaver() { 055 this(ClassUtils.getDefaultClassLoader()); 056 } 057 058 /** 059 * Create a new instance of the {@link TomcatLoadTimeWeaver} class using 060 * the supplied {@link ClassLoader}. 061 * @param classLoader the {@code ClassLoader} to delegate to for weaving 062 */ 063 public TomcatLoadTimeWeaver(@Nullable ClassLoader classLoader) { 064 Assert.notNull(classLoader, "ClassLoader must not be null"); 065 this.classLoader = classLoader; 066 067 Class<?> instrumentableLoaderClass; 068 try { 069 instrumentableLoaderClass = classLoader.loadClass(INSTRUMENTABLE_LOADER_CLASS_NAME); 070 if (!instrumentableLoaderClass.isInstance(classLoader)) { 071 // Could still be a custom variant of a convention-compatible ClassLoader 072 instrumentableLoaderClass = classLoader.getClass(); 073 } 074 } 075 catch (ClassNotFoundException ex) { 076 // We're on an earlier version of Tomcat, probably with Spring's TomcatInstrumentableClassLoader 077 instrumentableLoaderClass = classLoader.getClass(); 078 } 079 080 try { 081 this.addTransformerMethod = instrumentableLoaderClass.getMethod("addTransformer", ClassFileTransformer.class); 082 // Check for Tomcat's new copyWithoutTransformers on InstrumentableClassLoader first 083 Method copyMethod = ClassUtils.getMethodIfAvailable(instrumentableLoaderClass, "copyWithoutTransformers"); 084 if (copyMethod == null) { 085 // Fallback: expecting TomcatInstrumentableClassLoader's getThrowawayClassLoader 086 copyMethod = instrumentableLoaderClass.getMethod("getThrowawayClassLoader"); 087 } 088 this.copyMethod = copyMethod; 089 } 090 catch (Throwable ex) { 091 throw new IllegalStateException( 092 "Could not initialize TomcatLoadTimeWeaver because Tomcat API classes are not available", ex); 093 } 094 } 095 096 097 @Override 098 public void addTransformer(ClassFileTransformer transformer) { 099 try { 100 this.addTransformerMethod.invoke(this.classLoader, transformer); 101 } 102 catch (InvocationTargetException ex) { 103 throw new IllegalStateException("Tomcat addTransformer method threw exception", ex.getCause()); 104 } 105 catch (Throwable ex) { 106 throw new IllegalStateException("Could not invoke Tomcat addTransformer method", ex); 107 } 108 } 109 110 @Override 111 public ClassLoader getInstrumentableClassLoader() { 112 return this.classLoader; 113 } 114 115 @Override 116 public ClassLoader getThrowawayClassLoader() { 117 try { 118 return new OverridingClassLoader(this.classLoader, (ClassLoader) this.copyMethod.invoke(this.classLoader)); 119 } 120 catch (InvocationTargetException ex) { 121 throw new IllegalStateException("Tomcat copy method threw exception", ex.getCause()); 122 } 123 catch (Throwable ex) { 124 throw new IllegalStateException("Could not invoke Tomcat copy method", ex); 125 } 126 } 127 128}