001/*
002 * Copyright 2002-2012 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;
018
019import java.lang.instrument.ClassFileTransformer;
020import java.lang.instrument.IllegalClassFormatException;
021import java.lang.instrument.Instrumentation;
022import java.security.ProtectionDomain;
023import java.util.ArrayList;
024import java.util.List;
025
026import org.springframework.instrument.InstrumentationSavingAgent;
027import org.springframework.util.Assert;
028import org.springframework.util.ClassUtils;
029
030/**
031 * {@link LoadTimeWeaver} relying on VM {@link Instrumentation}.
032 *
033 * <p>Start the JVM specifying the Java agent to be used, like as follows:
034 *
035 * <p><code class="code">-javaagent:path/to/org.springframework.instrument.jar</code>
036 *
037 * <p>where {@code org.springframework.instrument.jar} is a JAR file containing
038 * the {@link InstrumentationSavingAgent} class, as shipped with Spring.
039 *
040 * <p>In Eclipse, for example, set the "Run configuration"'s JVM args to be of the form:
041 *
042 * <p><code class="code">-javaagent:${project_loc}/lib/org.springframework.instrument.jar</code>
043 *
044 * @author Rod Johnson
045 * @author Juergen Hoeller
046 * @since 2.0
047 * @see InstrumentationSavingAgent
048 */
049public class InstrumentationLoadTimeWeaver implements LoadTimeWeaver {
050
051        private static final boolean AGENT_CLASS_PRESENT = ClassUtils.isPresent(
052                        "org.springframework.instrument.InstrumentationSavingAgent",
053                        InstrumentationLoadTimeWeaver.class.getClassLoader());
054
055
056        private final ClassLoader classLoader;
057
058        private final Instrumentation instrumentation;
059
060        private final List<ClassFileTransformer> transformers = new ArrayList<ClassFileTransformer>(4);
061
062
063        /**
064         * Create a new InstrumentationLoadTimeWeaver for the default ClassLoader.
065         */
066        public InstrumentationLoadTimeWeaver() {
067                this(ClassUtils.getDefaultClassLoader());
068        }
069
070        /**
071         * Create a new InstrumentationLoadTimeWeaver for the given ClassLoader.
072         * @param classLoader the ClassLoader that registered transformers are supposed to apply to
073         */
074        public InstrumentationLoadTimeWeaver(ClassLoader classLoader) {
075                Assert.notNull(classLoader, "ClassLoader must not be null");
076                this.classLoader = classLoader;
077                this.instrumentation = getInstrumentation();
078        }
079
080
081        @Override
082        public void addTransformer(ClassFileTransformer transformer) {
083                Assert.notNull(transformer, "Transformer must not be null");
084                FilteringClassFileTransformer actualTransformer =
085                                new FilteringClassFileTransformer(transformer, this.classLoader);
086                synchronized (this.transformers) {
087                        if (this.instrumentation == null) {
088                                throw new IllegalStateException(
089                                                "Must start with Java agent to use InstrumentationLoadTimeWeaver. See Spring documentation.");
090                        }
091                        this.instrumentation.addTransformer(actualTransformer);
092                        this.transformers.add(actualTransformer);
093                }
094        }
095
096        /**
097         * We have the ability to weave the current class loader when starting the
098         * JVM in this way, so the instrumentable class loader will always be the
099         * current loader.
100         */
101        @Override
102        public ClassLoader getInstrumentableClassLoader() {
103                return this.classLoader;
104        }
105
106        /**
107         * This implementation always returns a {@link SimpleThrowawayClassLoader}.
108         */
109        @Override
110        public ClassLoader getThrowawayClassLoader() {
111                return new SimpleThrowawayClassLoader(getInstrumentableClassLoader());
112        }
113
114        /**
115         * Remove all registered transformers, in inverse order of registration.
116         */
117        public void removeTransformers() {
118                synchronized (this.transformers) {
119                        if (!this.transformers.isEmpty()) {
120                                for (int i = this.transformers.size() - 1; i >= 0; i--) {
121                                        this.instrumentation.removeTransformer(this.transformers.get(i));
122                                }
123                                this.transformers.clear();
124                        }
125                }
126        }
127
128
129        /**
130         * Check whether an Instrumentation instance is available for the current VM.
131         * @see #getInstrumentation()
132         */
133        public static boolean isInstrumentationAvailable() {
134                return (getInstrumentation() != null);
135        }
136
137        /**
138         * Obtain the Instrumentation instance for the current VM, if available.
139         * @return the Instrumentation instance, or {@code null} if none found
140         * @see #isInstrumentationAvailable()
141         */
142        private static Instrumentation getInstrumentation() {
143                if (AGENT_CLASS_PRESENT) {
144                        return InstrumentationAccessor.getInstrumentation();
145                }
146                else {
147                        return null;
148                }
149        }
150
151
152        /**
153         * Inner class to avoid InstrumentationSavingAgent dependency.
154         */
155        private static class InstrumentationAccessor {
156
157                public static Instrumentation getInstrumentation() {
158                        return InstrumentationSavingAgent.getInstrumentation();
159                }
160        }
161
162
163        /**
164         * Decorator that only applies the given target transformer to a specific ClassLoader.
165         */
166        private static class FilteringClassFileTransformer implements ClassFileTransformer {
167
168                private final ClassFileTransformer targetTransformer;
169
170                private final ClassLoader targetClassLoader;
171
172                public FilteringClassFileTransformer(ClassFileTransformer targetTransformer, ClassLoader targetClassLoader) {
173                        this.targetTransformer = targetTransformer;
174                        this.targetClassLoader = targetClassLoader;
175                }
176
177                @Override
178                public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
179                                ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
180
181                        if (!this.targetClassLoader.equals(loader)) {
182                                return null;
183                        }
184                        return this.targetTransformer.transform(
185                                        loader, className, classBeingRedefined, protectionDomain, classfileBuffer);
186                }
187
188                @Override
189                public String toString() {
190                        return "FilteringClassFileTransformer for: " + this.targetTransformer.toString();
191                }
192        }
193
194}