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