001/*
002 * Copyright 2002-2017 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.glassfish;
018
019import java.lang.instrument.ClassFileTransformer;
020import java.lang.reflect.InvocationTargetException;
021import java.lang.reflect.Method;
022
023import org.springframework.core.OverridingClassLoader;
024import org.springframework.instrument.classloading.LoadTimeWeaver;
025import org.springframework.util.Assert;
026import org.springframework.util.ClassUtils;
027
028/**
029 * {@link LoadTimeWeaver} implementation for GlassFish's
030 * {@code org.glassfish.api.deployment.InstrumentableClassLoader InstrumentableClassLoader}.
031 *
032 * <p>As of Spring 4.0, this weaver supports GlassFish V3 and V4.
033 *
034 * @author Costin Leau
035 * @author Juergen Hoeller
036 * @since 2.0.1
037 */
038public class GlassFishLoadTimeWeaver implements LoadTimeWeaver {
039
040        private static final String INSTRUMENTABLE_LOADER_CLASS_NAME =
041                        "org.glassfish.api.deployment.InstrumentableClassLoader";
042
043
044        private final ClassLoader classLoader;
045
046        private final Method addTransformerMethod;
047
048        private final Method copyMethod;
049
050
051        /**
052         * Create a new instance of the {@link GlassFishLoadTimeWeaver} class using
053         * the default {@link ClassLoader class loader}.
054         * @see org.springframework.util.ClassUtils#getDefaultClassLoader()
055         */
056        public GlassFishLoadTimeWeaver() {
057                this(ClassUtils.getDefaultClassLoader());
058        }
059
060        /**
061         * Create a new instance of the {@link GlassFishLoadTimeWeaver} class using
062         * the supplied {@link ClassLoader}.
063         * @param classLoader the {@code ClassLoader} to delegate to for weaving
064         */
065        public GlassFishLoadTimeWeaver(ClassLoader classLoader) {
066                Assert.notNull(classLoader, "ClassLoader must not be null");
067
068                Class<?> instrumentableLoaderClass;
069                try {
070                        instrumentableLoaderClass = classLoader.loadClass(INSTRUMENTABLE_LOADER_CLASS_NAME);
071                        this.addTransformerMethod = instrumentableLoaderClass.getMethod("addTransformer", ClassFileTransformer.class);
072                        this.copyMethod = instrumentableLoaderClass.getMethod("copy");
073                }
074                catch (Throwable ex) {
075                        throw new IllegalStateException(
076                                        "Could not initialize GlassFishLoadTimeWeaver because GlassFish API classes are not available", ex);
077                }
078
079                ClassLoader clazzLoader = null;
080                // Detect transformation-aware ClassLoader by traversing the hierarchy
081                // (as in GlassFish, Spring can be loaded by the WebappClassLoader).
082                for (ClassLoader cl = classLoader; cl != null && clazzLoader == null; cl = cl.getParent()) {
083                        if (instrumentableLoaderClass.isInstance(cl)) {
084                                clazzLoader = cl;
085                        }
086                }
087
088                if (clazzLoader == null) {
089                        throw new IllegalArgumentException(classLoader + " and its parents are not suitable ClassLoaders: A [" +
090                                        instrumentableLoaderClass.getName() + "] implementation is required.");
091                }
092
093                this.classLoader = clazzLoader;
094        }
095
096
097        @Override
098        public void addTransformer(ClassFileTransformer transformer) {
099                try {
100                        this.addTransformerMethod.invoke(this.classLoader, transformer);
101                }
102                catch (InvocationTargetException ex) {
103                        throw new IllegalStateException("GlassFish addTransformer method threw exception", ex.getCause());
104                }
105                catch (Throwable ex) {
106                        throw new IllegalStateException("Could not invoke GlassFish addTransformer method", ex);
107                }
108        }
109
110        @Override
111        public ClassLoader getInstrumentableClassLoader() {
112                return this.classLoader;
113        }
114
115        @Override
116        public ClassLoader getThrowawayClassLoader() {
117                try {
118                        return new OverridingClassLoader(this.classLoader, (ClassLoader) this.copyMethod.invoke(this.classLoader));
119                }
120                catch (InvocationTargetException ex) {
121                        throw new IllegalStateException("GlassFish copy method threw exception", ex.getCause());
122                }
123                catch (Throwable ex) {
124                        throw new IllegalStateException("Could not invoke GlassFish copy method", ex);
125                }
126        }
127
128}