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.glassfish; 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.util.Assert; 026import org.springframework.util.ClassUtils; 027 028/** 029 * {@link LoadTimeWeaver} implementation for GlassFish's 030 * {@code org.glassfish.api.deployment.InstrumentableClassLoader InstrumentableClassLoader}. 031 * 032 * <p>As of Spring 4.0, this weaver supports GlassFish V3 and V4. 033 * 034 * @author Costin Leau 035 * @author Juergen Hoeller 036 * @since 2.0.1 037 */ 038public class GlassFishLoadTimeWeaver implements LoadTimeWeaver { 039 040 private static final String INSTRUMENTABLE_LOADER_CLASS_NAME = 041 "org.glassfish.api.deployment.InstrumentableClassLoader"; 042 043 044 private final ClassLoader classLoader; 045 046 private final Method addTransformerMethod; 047 048 private final Method copyMethod; 049 050 051 /** 052 * Create a new instance of the {@link GlassFishLoadTimeWeaver} class using 053 * the default {@link ClassLoader class loader}. 054 * @see org.springframework.util.ClassUtils#getDefaultClassLoader() 055 */ 056 public GlassFishLoadTimeWeaver() { 057 this(ClassUtils.getDefaultClassLoader()); 058 } 059 060 /** 061 * Create a new instance of the {@link GlassFishLoadTimeWeaver} class using 062 * the supplied {@link ClassLoader}. 063 * @param classLoader the {@code ClassLoader} to delegate to for weaving 064 */ 065 public GlassFishLoadTimeWeaver(ClassLoader classLoader) { 066 Assert.notNull(classLoader, "ClassLoader must not be null"); 067 068 Class<?> instrumentableLoaderClass; 069 try { 070 instrumentableLoaderClass = classLoader.loadClass(INSTRUMENTABLE_LOADER_CLASS_NAME); 071 this.addTransformerMethod = instrumentableLoaderClass.getMethod("addTransformer", ClassFileTransformer.class); 072 this.copyMethod = instrumentableLoaderClass.getMethod("copy"); 073 } 074 catch (Throwable ex) { 075 throw new IllegalStateException( 076 "Could not initialize GlassFishLoadTimeWeaver because GlassFish API classes are not available", ex); 077 } 078 079 ClassLoader clazzLoader = null; 080 // Detect transformation-aware ClassLoader by traversing the hierarchy 081 // (as in GlassFish, Spring can be loaded by the WebappClassLoader). 082 for (ClassLoader cl = classLoader; cl != null && clazzLoader == null; cl = cl.getParent()) { 083 if (instrumentableLoaderClass.isInstance(cl)) { 084 clazzLoader = cl; 085 } 086 } 087 088 if (clazzLoader == null) { 089 throw new IllegalArgumentException(classLoader + " and its parents are not suitable ClassLoaders: A [" + 090 instrumentableLoaderClass.getName() + "] implementation is required."); 091 } 092 093 this.classLoader = clazzLoader; 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("GlassFish addTransformer method threw exception", ex.getCause()); 104 } 105 catch (Throwable ex) { 106 throw new IllegalStateException("Could not invoke GlassFish 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("GlassFish copy method threw exception", ex.getCause()); 122 } 123 catch (Throwable ex) { 124 throw new IllegalStateException("Could not invoke GlassFish copy method", ex); 125 } 126 } 127 128}