diff --git a/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBossClassLoaderAdapter.java b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBossClassLoaderAdapter.java index b6f6fb4931..3fefc04ab3 100644 --- a/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBossClassLoaderAdapter.java +++ b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBossClassLoaderAdapter.java @@ -1,144 +1,32 @@ /* - * Copyright 2006-2009 the original author or authors. - * + * Copyright 2011 the original author or authors. + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.instrument.classloading.jboss; import java.lang.instrument.ClassFileTransformer; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; - -import org.springframework.util.Assert; -import org.springframework.util.ReflectionUtils; /** - * Reflective wrapper around a JBoss 5 class loader methods (discovered and called - * through reflection) for load time weaving. + * Simple interface used for handling the different JBoss class loader adapters. * * @author Costin Leau */ -class JBossClassLoaderAdapter { +interface JBossClassLoaderAdapter { - private static final String TRANSLATOR_NAME = "org.jboss.util.loading.Translator"; - private static final String POLICY_NAME = "org.jboss.classloader.spi.base.BaseClassLoaderPolicy"; - private static final String DOMAIN_NAME = "org.jboss.classloader.spi.base.BaseClassLoaderDomain"; - private static final String DEDICATED_SYSTEM = "org.jboss.classloader.spi.ClassLoaderSystem"; - private static final String LOADER_NAME = "org.jboss.classloader.spi.base.BaseClassLoader"; - private static final String GET_POLICY = "getPolicy"; - private static final String GET_DOMAIN = "getClassLoaderDomain"; - private static final String GET_SYSTEM = "getClassLoaderSystem"; + void addTransformer(ClassFileTransformer transformer); - // available since JBoss AS 5.1.0 / MC 2.0.6 (allows multiple transformers to be added) - private static final String ADD_TRANSLATOR_NAME = "addTranslator"; - // available since JBoss AS 5.0.0 / MC 2.0.1 (allows only one transformer to be added) - private static final String SET_TRANSLATOR_NAME = "setTranslator"; + ClassLoader getInstrumentableClassLoader(); - private final ClassLoader classLoader; - private final Class translatorClass; - - private final Method addTranslator; - private final Object target; - - JBossClassLoaderAdapter(ClassLoader classLoader) { - Class clazzLoaderType = null; - try { - // resolve BaseClassLoader.class - clazzLoaderType = classLoader.loadClass(LOADER_NAME); - - ClassLoader clazzLoader = null; - // walk the hierarchy to detect the instrumentation aware classloader - for (ClassLoader cl = classLoader; cl != null && clazzLoader == null; cl = cl.getParent()) { - if (clazzLoaderType.isInstance(cl)) { - clazzLoader = cl; - } - } - - if (clazzLoader == null) { - throw new IllegalArgumentException(classLoader + " and its parents are not suitable ClassLoaders: " - + "A [" + LOADER_NAME + "] implementation is required."); - } - - this.classLoader = clazzLoader; - // use the classloader that loaded the classloader to load - // the types for reflection purposes - classLoader = clazzLoader.getClass().getClassLoader(); - - // BaseClassLoader#getPolicy - Method method = clazzLoaderType.getDeclaredMethod(GET_POLICY); - ReflectionUtils.makeAccessible(method); - Object policy = method.invoke(this.classLoader); - - Object addTarget = null; - Method addMethod = null; - - // try the 5.1.x hooks - // check existence of BaseClassLoaderPolicy#addTranslator(Translator) - this.translatorClass = classLoader.loadClass(TRANSLATOR_NAME); - Class clazz = classLoader.loadClass(POLICY_NAME); - try { - addMethod = clazz.getDeclaredMethod(ADD_TRANSLATOR_NAME, translatorClass); - addTarget = policy; - } catch (NoSuchMethodException ex) { - } - - // fall back to 5.0.x method - if (addMethod == null) { - - // BaseClassLoaderPolicy#getClassLoaderDomain - method = clazz.getDeclaredMethod(GET_DOMAIN); - ReflectionUtils.makeAccessible(method); - Object domain = method.invoke(policy); - - // BaseClassLoaderDomain#getClassLoaderSystem - clazz = classLoader.loadClass(DOMAIN_NAME); - method = clazz.getDeclaredMethod(GET_SYSTEM); - ReflectionUtils.makeAccessible(method); - Object system = method.invoke(domain); - - // resolve ClassLoaderSystem - clazz = classLoader.loadClass(DEDICATED_SYSTEM); - Assert.isInstanceOf(clazz, system, "JBoss LoadTimeWeaver requires JBoss loader system of type " - + clazz.getName() + " on JBoss 5.0.x"); - - // ClassLoaderSystem#setTranslator - addMethod = clazz.getDeclaredMethod(SET_TRANSLATOR_NAME, translatorClass); - addTarget = system; - } - - this.addTranslator = addMethod; - this.target = addTarget; - - } catch (Exception ex) { - throw new IllegalStateException( - "Could not initialize JBoss LoadTimeWeaver because the JBoss 5 API classes are not available", ex); - } - } - - public void addTransformer(ClassFileTransformer transformer) { - InvocationHandler adapter = new JBossTranslatorAdapter(transformer); - Object adapterInstance = Proxy.newProxyInstance(this.translatorClass.getClassLoader(), - new Class[] { this.translatorClass }, adapter); - - try { - addTranslator.invoke(target, adapterInstance); - } catch (Exception ex) { - throw new IllegalStateException("Could not add transformer on JBoss classloader " + classLoader, ex); - } - } - - public ClassLoader getClassLoader() { - return classLoader; - } -} \ No newline at end of file +} diff --git a/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBossLoadTimeWeaver.java b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBossLoadTimeWeaver.java index dbe14654dc..008deb56c3 100644 --- a/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBossLoadTimeWeaver.java +++ b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBossLoadTimeWeaver.java @@ -25,6 +25,7 @@ import org.springframework.util.ClassUtils; /** * {@link LoadTimeWeaver} implementation for JBoss's instrumentable ClassLoader. + * Currently supports JBoss 5, 6 and 7 (since Spring 3.1). * *

NOTE: Requires JBoss AS version 5.0.0 or higher. *

NOTE: On JBoss 6.0.0, to avoid the container loading the classes before @@ -41,8 +42,7 @@ import org.springframework.util.ClassUtils; */ public class JBossLoadTimeWeaver implements LoadTimeWeaver { - private final JBossClassLoaderAdapter classLoader; - + private final JBossClassLoaderAdapter adapter; /** * Create a new instance of the {@link JBossLoadTimeWeaver} class using @@ -61,16 +61,25 @@ public class JBossLoadTimeWeaver implements LoadTimeWeaver { */ public JBossLoadTimeWeaver(ClassLoader classLoader) { Assert.notNull(classLoader, "ClassLoader must not be null"); - this.classLoader = new JBossClassLoaderAdapter(classLoader); + String loaderClassName = classLoader.getClass().getName(); + + if (loaderClassName.startsWith("org.jboss.classloader")) { + // JBoss AS 5 or JBoss AS 6 + this.adapter = new JBossMCAdapter(classLoader); + } else if (loaderClassName.startsWith("org.jboss.modules")) { + // JBoss AS 7 + this.adapter = new JBossModulesAdapter(classLoader); + } else { + throw new IllegalArgumentException("Unexpected classloader type: " + loaderClassName); + } } - public void addTransformer(ClassFileTransformer transformer) { - this.classLoader.addTransformer(transformer); + this.adapter.addTransformer(transformer); } public ClassLoader getInstrumentableClassLoader() { - return this.classLoader.getClassLoader(); + return this.adapter.getInstrumentableClassLoader(); } public ClassLoader getThrowawayClassLoader() { diff --git a/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBossMCAdapter.java b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBossMCAdapter.java new file mode 100644 index 0000000000..ec4ca6db09 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBossMCAdapter.java @@ -0,0 +1,144 @@ +/* + * Copyright 2006-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.instrument.classloading.jboss; + +import java.lang.instrument.ClassFileTransformer; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; + +/** + * Reflective wrapper around a JBoss 5 and 6 class loader methods (discovered and called + * through reflection) for load time weaving. + * + * @author Costin Leau + */ +class JBossMCAdapter implements JBossClassLoaderAdapter { + + private static final String TRANSLATOR_NAME = "org.jboss.util.loading.Translator"; + private static final String POLICY_NAME = "org.jboss.classloader.spi.base.BaseClassLoaderPolicy"; + private static final String DOMAIN_NAME = "org.jboss.classloader.spi.base.BaseClassLoaderDomain"; + private static final String DEDICATED_SYSTEM = "org.jboss.classloader.spi.ClassLoaderSystem"; + private static final String LOADER_NAME = "org.jboss.classloader.spi.base.BaseClassLoader"; + private static final String GET_POLICY = "getPolicy"; + private static final String GET_DOMAIN = "getClassLoaderDomain"; + private static final String GET_SYSTEM = "getClassLoaderSystem"; + + // available since JBoss AS 5.1.0 / MC 2.0.6 (allows multiple transformers to be added) + private static final String ADD_TRANSLATOR_NAME = "addTranslator"; + // available since JBoss AS 5.0.0 / MC 2.0.1 (allows only one transformer to be added) + private static final String SET_TRANSLATOR_NAME = "setTranslator"; + + private final ClassLoader classLoader; + private final Class translatorClass; + + private final Method addTranslator; + private final Object target; + + JBossMCAdapter(ClassLoader classLoader) { + Class clazzLoaderType = null; + try { + // resolve BaseClassLoader.class + clazzLoaderType = classLoader.loadClass(LOADER_NAME); + + ClassLoader clazzLoader = null; + // walk the hierarchy to detect the instrumentation aware classloader + for (ClassLoader cl = classLoader; cl != null && clazzLoader == null; cl = cl.getParent()) { + if (clazzLoaderType.isInstance(cl)) { + clazzLoader = cl; + } + } + + if (clazzLoader == null) { + throw new IllegalArgumentException(classLoader + " and its parents are not suitable ClassLoaders: " + + "A [" + LOADER_NAME + "] implementation is required."); + } + + this.classLoader = clazzLoader; + // use the classloader that loaded the classloader to load + // the types for reflection purposes + classLoader = clazzLoader.getClass().getClassLoader(); + + // BaseClassLoader#getPolicy + Method method = clazzLoaderType.getDeclaredMethod(GET_POLICY); + ReflectionUtils.makeAccessible(method); + Object policy = method.invoke(this.classLoader); + + Object addTarget = null; + Method addMethod = null; + + // try the 5.1.x hooks + // check existence of BaseClassLoaderPolicy#addTranslator(Translator) + this.translatorClass = classLoader.loadClass(TRANSLATOR_NAME); + Class clazz = classLoader.loadClass(POLICY_NAME); + try { + addMethod = clazz.getDeclaredMethod(ADD_TRANSLATOR_NAME, translatorClass); + addTarget = policy; + } catch (NoSuchMethodException ex) { + } + + // fall back to 5.0.x method + if (addMethod == null) { + + // BaseClassLoaderPolicy#getClassLoaderDomain + method = clazz.getDeclaredMethod(GET_DOMAIN); + ReflectionUtils.makeAccessible(method); + Object domain = method.invoke(policy); + + // BaseClassLoaderDomain#getClassLoaderSystem + clazz = classLoader.loadClass(DOMAIN_NAME); + method = clazz.getDeclaredMethod(GET_SYSTEM); + ReflectionUtils.makeAccessible(method); + Object system = method.invoke(domain); + + // resolve ClassLoaderSystem + clazz = classLoader.loadClass(DEDICATED_SYSTEM); + Assert.isInstanceOf(clazz, system, "JBoss LoadTimeWeaver requires JBoss loader system of type " + + clazz.getName() + " on JBoss 5.0.x"); + + // ClassLoaderSystem#setTranslator + addMethod = clazz.getDeclaredMethod(SET_TRANSLATOR_NAME, translatorClass); + addTarget = system; + } + + this.addTranslator = addMethod; + this.target = addTarget; + + } catch (Exception ex) { + throw new IllegalStateException( + "Could not initialize JBoss LoadTimeWeaver because the JBoss 5 API classes are not available", ex); + } + } + + public void addTransformer(ClassFileTransformer transformer) { + InvocationHandler adapter = new JBossMCTranslatorAdapter(transformer); + Object adapterInstance = Proxy.newProxyInstance(this.translatorClass.getClassLoader(), + new Class[] { this.translatorClass }, adapter); + + try { + addTranslator.invoke(target, adapterInstance); + } catch (Exception ex) { + throw new IllegalStateException("Could not add transformer on JBoss 5/6 classloader " + classLoader, ex); + } + } + + public ClassLoader getInstrumentableClassLoader() { + return classLoader; + } +} \ No newline at end of file diff --git a/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBossTranslatorAdapter.java b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBossMCTranslatorAdapter.java similarity index 90% rename from org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBossTranslatorAdapter.java rename to org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBossMCTranslatorAdapter.java index 6b134afa18..4b65878cd6 100644 --- a/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBossTranslatorAdapter.java +++ b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBossMCTranslatorAdapter.java @@ -29,16 +29,16 @@ import java.security.ProtectionDomain; * * @author Costin Leau */ -class JBossTranslatorAdapter implements InvocationHandler { +class JBossMCTranslatorAdapter implements InvocationHandler { private final ClassFileTransformer transformer; /** - * Creates a new {@link JBossTranslatorAdapter}. + * Creates a new {@link JBossMCTranslatorAdapter}. * @param transformer the {@link ClassFileTransformer} to be adapted (must * not be null) */ - public JBossTranslatorAdapter(ClassFileTransformer transformer) { + public JBossMCTranslatorAdapter(ClassFileTransformer transformer) { this.transformer = transformer; } diff --git a/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBossModulesAdapter.java b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBossModulesAdapter.java new file mode 100644 index 0000000000..a03889d104 --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/instrument/classloading/jboss/JBossModulesAdapter.java @@ -0,0 +1,70 @@ +/* + * Copyright 2011 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.instrument.classloading.jboss; + +import java.lang.instrument.ClassFileTransformer; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; + +/** + * JBoss 7 Adapter. + * + * @author Costin Leau + */ +class JBossModulesAdapter implements JBossClassLoaderAdapter { + + private static final String TRANSFORMER_FIELD_NAME = "transformer"; + private static final String TRANSFORMER_ADD_METHOD_NAME = "addTransformer"; + private static final String DELEGATING_TRANSFORMER_CLASS_NAME = "org.jboss.as.server.deployment.module.DelegatingClassFileTransformer"; + private final ClassLoader classLoader; + private final Method addTransformer; + private final Object delegatingTransformer; + + public JBossModulesAdapter(ClassLoader loader) { + this.classLoader = loader; + + try { + Field transformers = ReflectionUtils.findField(classLoader.getClass(), TRANSFORMER_FIELD_NAME); + transformers.setAccessible(true); + + delegatingTransformer = transformers.get(classLoader); + + Assert.state(delegatingTransformer.getClass().getName().equals(DELEGATING_TRANSFORMER_CLASS_NAME), + "Transformer not of the expected type: " + delegatingTransformer.getClass().getName()); + addTransformer = ReflectionUtils.findMethod(delegatingTransformer.getClass(), TRANSFORMER_ADD_METHOD_NAME, + ClassFileTransformer.class); + addTransformer.setAccessible(true); + } catch (Exception ex) { + throw new IllegalStateException("Could not initialize JBoss 7 LoadTimeWeaver", ex); + } + } + + public void addTransformer(ClassFileTransformer transformer) { + try { + addTransformer.invoke(delegatingTransformer, transformer); + } catch (Exception ex) { + throw new IllegalStateException("Could not add transformer on JBoss 7 classloader " + classLoader, ex); + } + } + + public ClassLoader getInstrumentableClassLoader() { + return classLoader; + } +} \ No newline at end of file