001/*
002 * Copyright 2002-2019 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.context.weaving;
018
019import java.lang.instrument.ClassFileTransformer;
020
021import org.apache.commons.logging.Log;
022import org.apache.commons.logging.LogFactory;
023
024import org.springframework.beans.factory.BeanClassLoaderAware;
025import org.springframework.beans.factory.DisposableBean;
026import org.springframework.instrument.InstrumentationSavingAgent;
027import org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver;
028import org.springframework.instrument.classloading.LoadTimeWeaver;
029import org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver;
030import org.springframework.instrument.classloading.glassfish.GlassFishLoadTimeWeaver;
031import org.springframework.instrument.classloading.jboss.JBossLoadTimeWeaver;
032import org.springframework.instrument.classloading.tomcat.TomcatLoadTimeWeaver;
033import org.springframework.instrument.classloading.weblogic.WebLogicLoadTimeWeaver;
034import org.springframework.instrument.classloading.websphere.WebSphereLoadTimeWeaver;
035import org.springframework.lang.Nullable;
036import org.springframework.util.Assert;
037
038/**
039 * Default {@link LoadTimeWeaver} bean for use in an application context,
040 * decorating an automatically detected internal {@code LoadTimeWeaver}.
041 *
042 * <p>Typically registered for the default bean name "{@code loadTimeWeaver}";
043 * the most convenient way to achieve this is Spring's
044 * {@code <context:load-time-weaver>} XML tag or {@code @EnableLoadTimeWeaving}
045 * on a {@code @Configuration} class.
046 *
047 * <p>This class implements a runtime environment check for obtaining the
048 * appropriate weaver implementation. As of Spring Framework 5.0, it detects
049 * Oracle WebLogic 10+, GlassFish 4+, Tomcat 8+, WildFly 8+, IBM WebSphere 8.5+,
050 * {@link InstrumentationSavingAgent Spring's VM agent}, and any {@link ClassLoader}
051 * supported by Spring's {@link ReflectiveLoadTimeWeaver} (such as Liberty's).
052 *
053 * @author Juergen Hoeller
054 * @author Ramnivas Laddad
055 * @author Costin Leau
056 * @since 2.5
057 * @see org.springframework.context.ConfigurableApplicationContext#LOAD_TIME_WEAVER_BEAN_NAME
058 */
059public class DefaultContextLoadTimeWeaver implements LoadTimeWeaver, BeanClassLoaderAware, DisposableBean {
060
061        protected final Log logger = LogFactory.getLog(getClass());
062
063        @Nullable
064        private LoadTimeWeaver loadTimeWeaver;
065
066
067        public DefaultContextLoadTimeWeaver() {
068        }
069
070        public DefaultContextLoadTimeWeaver(ClassLoader beanClassLoader) {
071                setBeanClassLoader(beanClassLoader);
072        }
073
074
075        @Override
076        public void setBeanClassLoader(ClassLoader classLoader) {
077                LoadTimeWeaver serverSpecificLoadTimeWeaver = createServerSpecificLoadTimeWeaver(classLoader);
078                if (serverSpecificLoadTimeWeaver != null) {
079                        if (logger.isDebugEnabled()) {
080                                logger.debug("Determined server-specific load-time weaver: " +
081                                                serverSpecificLoadTimeWeaver.getClass().getName());
082                        }
083                        this.loadTimeWeaver = serverSpecificLoadTimeWeaver;
084                }
085                else if (InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) {
086                        logger.debug("Found Spring's JVM agent for instrumentation");
087                        this.loadTimeWeaver = new InstrumentationLoadTimeWeaver(classLoader);
088                }
089                else {
090                        try {
091                                this.loadTimeWeaver = new ReflectiveLoadTimeWeaver(classLoader);
092                                if (logger.isDebugEnabled()) {
093                                        logger.debug("Using reflective load-time weaver for class loader: " +
094                                                        this.loadTimeWeaver.getInstrumentableClassLoader().getClass().getName());
095                                }
096                        }
097                        catch (IllegalStateException ex) {
098                                throw new IllegalStateException(ex.getMessage() + " Specify a custom LoadTimeWeaver or start your " +
099                                                "Java virtual machine with Spring's agent: -javaagent:spring-instrument-{version}.jar");
100                        }
101                }
102        }
103
104        /*
105         * This method never fails, allowing to try other possible ways to use an
106         * server-agnostic weaver. This non-failure logic is required since
107         * determining a load-time weaver based on the ClassLoader name alone may
108         * legitimately fail due to other mismatches.
109         */
110        @Nullable
111        protected LoadTimeWeaver createServerSpecificLoadTimeWeaver(ClassLoader classLoader) {
112                String name = classLoader.getClass().getName();
113                try {
114                        if (name.startsWith("org.apache.catalina")) {
115                                return new TomcatLoadTimeWeaver(classLoader);
116                        }
117                        else if (name.startsWith("org.glassfish")) {
118                                return new GlassFishLoadTimeWeaver(classLoader);
119                        }
120                        else if (name.startsWith("org.jboss.modules")) {
121                                return new JBossLoadTimeWeaver(classLoader);
122                        }
123                        else if (name.startsWith("com.ibm.ws.classloader")) {
124                                return new WebSphereLoadTimeWeaver(classLoader);
125                        }
126                        else if (name.startsWith("weblogic")) {
127                                return new WebLogicLoadTimeWeaver(classLoader);
128                        }
129                }
130                catch (Exception ex) {
131                        if (logger.isInfoEnabled()) {
132                                logger.info("Could not obtain server-specific LoadTimeWeaver: " + ex.getMessage());
133                        }
134                }
135                return null;
136        }
137
138        @Override
139        public void destroy() {
140                if (this.loadTimeWeaver instanceof InstrumentationLoadTimeWeaver) {
141                        if (logger.isDebugEnabled()) {
142                                logger.debug("Removing all registered transformers for class loader: " +
143                                                this.loadTimeWeaver.getInstrumentableClassLoader().getClass().getName());
144                        }
145                        ((InstrumentationLoadTimeWeaver) this.loadTimeWeaver).removeTransformers();
146                }
147        }
148
149
150        @Override
151        public void addTransformer(ClassFileTransformer transformer) {
152                Assert.state(this.loadTimeWeaver != null, "Not initialized");
153                this.loadTimeWeaver.addTransformer(transformer);
154        }
155
156        @Override
157        public ClassLoader getInstrumentableClassLoader() {
158                Assert.state(this.loadTimeWeaver != null, "Not initialized");
159                return this.loadTimeWeaver.getInstrumentableClassLoader();
160        }
161
162        @Override
163        public ClassLoader getThrowawayClassLoader() {
164                Assert.state(this.loadTimeWeaver != null, "Not initialized");
165                return this.loadTimeWeaver.getThrowawayClassLoader();
166        }
167
168}