/* * Copyright 2002-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.glassfish; import java.lang.instrument.ClassFileTransformer; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * Reflective wrapper around the GlassFish class loader. Used to * encapsulate the classloader-specific methods (discovered and * called through reflection) from the load-time weaver. * * <p>Supports GlassFish V1, V2 and V3 (currently in beta). * * @author Costin Leau * @since 3.0 */ class GlassFishClassLoaderAdapter { static final String INSTRUMENTABLE_CLASSLOADER_GLASSFISH_V2 = "com.sun.enterprise.loader.InstrumentableClassLoader"; static final String INSTRUMENTABLE_CLASSLOADER_GLASSFISH_V3 = "org.glassfish.api.deployment.InstrumentableClassLoader"; private static final String CLASS_TRANSFORMER = "javax.persistence.spi.ClassTransformer"; private final ClassLoader classLoader; private final Method addTransformer; private final Method copy; private final boolean glassFishV3; public GlassFishClassLoaderAdapter(ClassLoader classLoader) { Class<?> instrumentableLoaderClass; boolean glassV3 = false; try { // try the V1/V2 API first instrumentableLoaderClass = classLoader.loadClass(INSTRUMENTABLE_CLASSLOADER_GLASSFISH_V2); } catch (ClassNotFoundException ex) { // fall back to V3 try { instrumentableLoaderClass = classLoader.loadClass(INSTRUMENTABLE_CLASSLOADER_GLASSFISH_V3); glassV3 = true; } catch (ClassNotFoundException cnfe) { throw new IllegalStateException("Could not initialize GlassFish LoadTimeWeaver because " + "GlassFish (V1, V2 or V3) API classes are not available", ex); } } try { Class<?> classTransformerClass = (glassV3 ? ClassFileTransformer.class : classLoader.loadClass(CLASS_TRANSFORMER)); this.addTransformer = instrumentableLoaderClass.getMethod("addTransformer", classTransformerClass); this.copy = instrumentableLoaderClass.getMethod("copy"); } catch (Exception ex) { throw new IllegalStateException( "Could not initialize GlassFish LoadTimeWeaver because GlassFish API classes are not available", ex); } ClassLoader clazzLoader = null; // Detect transformation-aware ClassLoader by traversing the hierarchy // (as in GlassFish, Spring can be loaded by the WebappClassLoader). for (ClassLoader cl = classLoader; cl != null && clazzLoader == null; cl = cl.getParent()) { if (instrumentableLoaderClass.isInstance(cl)) { clazzLoader = cl; } } if (clazzLoader == null) { throw new IllegalArgumentException(classLoader + " and its parents are not suitable ClassLoaders: A [" + instrumentableLoaderClass.getName() + "] implementation is required."); } this.classLoader = clazzLoader; this.glassFishV3 = glassV3; } public void addTransformer(ClassFileTransformer transformer) { try { this.addTransformer.invoke(this.classLoader, (this.glassFishV3 ? transformer : new ClassTransformerAdapter(transformer))); } catch (InvocationTargetException ex) { throw new IllegalStateException("GlassFish addTransformer method threw exception ", ex.getCause()); } catch (Exception ex) { throw new IllegalStateException("Could not invoke GlassFish addTransformer method", ex); } } public ClassLoader getClassLoader() { return this.classLoader; } public ClassLoader getThrowawayClassLoader() { try { return (ClassLoader) this.copy.invoke(this.classLoader); } catch (InvocationTargetException ex) { throw new IllegalStateException("GlassFish copy method threw exception ", ex.getCause()); } catch (Exception ex) { throw new IllegalStateException("Could not invoke GlassFish copy method", ex); } } }