001/* 002 * Copyright 2002-2019 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.jboss; 018 019import java.lang.instrument.ClassFileTransformer; 020import java.lang.reflect.Field; 021import java.lang.reflect.Method; 022 023import org.springframework.instrument.classloading.LoadTimeWeaver; 024import org.springframework.instrument.classloading.SimpleThrowawayClassLoader; 025import org.springframework.lang.Nullable; 026import org.springframework.util.Assert; 027import org.springframework.util.ClassUtils; 028import org.springframework.util.ReflectionUtils; 029 030/** 031 * {@link LoadTimeWeaver} implementation for JBoss's instrumentable ClassLoader. 032 * Thanks to Ales Justin and Marius Bogoevici for the initial prototype. 033 * 034 * <p>As of Spring Framework 5.0, this weaver supports WildFly 8+. 035 * As of Spring Framework 5.1.5, it also supports WildFly 13+. 036 * 037 * @author Costin Leau 038 * @author Juergen Hoeller 039 * @since 3.0 040 */ 041public class JBossLoadTimeWeaver implements LoadTimeWeaver { 042 043 private static final String DELEGATING_TRANSFORMER_CLASS_NAME = 044 "org.jboss.as.server.deployment.module.DelegatingClassFileTransformer"; 045 046 private static final String WRAPPER_TRANSFORMER_CLASS_NAME = 047 "org.jboss.modules.JLIClassTransformer"; 048 049 050 private final ClassLoader classLoader; 051 052 private final Object delegatingTransformer; 053 054 private final Method addTransformer; 055 056 057 /** 058 * Create a new instance of the {@link JBossLoadTimeWeaver} class using 059 * the default {@link ClassLoader class loader}. 060 * @see org.springframework.util.ClassUtils#getDefaultClassLoader() 061 */ 062 public JBossLoadTimeWeaver() { 063 this(ClassUtils.getDefaultClassLoader()); 064 } 065 066 /** 067 * Create a new instance of the {@link JBossLoadTimeWeaver} class using 068 * the supplied {@link ClassLoader}. 069 * @param classLoader the {@code ClassLoader} to delegate to for weaving 070 */ 071 public JBossLoadTimeWeaver(@Nullable ClassLoader classLoader) { 072 Assert.notNull(classLoader, "ClassLoader must not be null"); 073 this.classLoader = classLoader; 074 075 try { 076 Field transformer = ReflectionUtils.findField(classLoader.getClass(), "transformer"); 077 if (transformer == null) { 078 throw new IllegalArgumentException("Could not find 'transformer' field on JBoss ClassLoader: " + 079 classLoader.getClass().getName()); 080 } 081 transformer.setAccessible(true); 082 083 Object suggestedTransformer = transformer.get(classLoader); 084 if (suggestedTransformer.getClass().getName().equals(WRAPPER_TRANSFORMER_CLASS_NAME)) { 085 Field wrappedTransformer = ReflectionUtils.findField(suggestedTransformer.getClass(), "transformer"); 086 if (wrappedTransformer == null) { 087 throw new IllegalArgumentException( 088 "Could not find 'transformer' field on JBoss JLIClassTransformer: " + 089 suggestedTransformer.getClass().getName()); 090 } 091 wrappedTransformer.setAccessible(true); 092 suggestedTransformer = wrappedTransformer.get(suggestedTransformer); 093 } 094 if (!suggestedTransformer.getClass().getName().equals(DELEGATING_TRANSFORMER_CLASS_NAME)) { 095 throw new IllegalStateException( 096 "Transformer not of the expected type DelegatingClassFileTransformer: " + 097 suggestedTransformer.getClass().getName()); 098 } 099 this.delegatingTransformer = suggestedTransformer; 100 101 Method addTransformer = ReflectionUtils.findMethod(this.delegatingTransformer.getClass(), 102 "addTransformer", ClassFileTransformer.class); 103 if (addTransformer == null) { 104 throw new IllegalArgumentException( 105 "Could not find 'addTransformer' method on JBoss DelegatingClassFileTransformer: " + 106 this.delegatingTransformer.getClass().getName()); 107 } 108 addTransformer.setAccessible(true); 109 this.addTransformer = addTransformer; 110 } 111 catch (Throwable ex) { 112 throw new IllegalStateException("Could not initialize JBoss LoadTimeWeaver", ex); 113 } 114 } 115 116 117 @Override 118 public void addTransformer(ClassFileTransformer transformer) { 119 try { 120 this.addTransformer.invoke(this.delegatingTransformer, transformer); 121 } 122 catch (Throwable ex) { 123 throw new IllegalStateException("Could not add transformer on JBoss ClassLoader: " + this.classLoader, ex); 124 } 125 } 126 127 @Override 128 public ClassLoader getInstrumentableClassLoader() { 129 return this.classLoader; 130 } 131 132 @Override 133 public ClassLoader getThrowawayClassLoader() { 134 return new SimpleThrowawayClassLoader(getInstrumentableClassLoader()); 135 } 136 137}