/*
* 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 class loader methods (discovered and called
* through reflection) for load time weaving.
*
* @author Costin Leau
*/
class 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;
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;
}
}
|