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