Open Source Repository

Home /spring/spring-context-3.0.5 | Repository Home



org/springframework/instrument/classloading/jboss/JBossClassLoaderAdapter.java
/*
 * 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;
  }
}