001/*
002 * Copyright 2002-2016 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.core;
018
019import java.io.IOException;
020import java.io.InputStream;
021
022import org.springframework.lang.UsesJava7;
023import org.springframework.util.FileCopyUtils;
024
025/**
026 * {@code ClassLoader} that does <i>not</i> always delegate to the parent loader
027 * as normal class loaders do. This enables, for example, instrumentation to be
028 * forced in the overriding ClassLoader, or a "throwaway" class loading behavior
029 * where selected application classes are temporarily loaded in the overriding
030 * {@code ClassLoader} for introspection purposes before eventually loading an
031 * instrumented version of the class in the given parent {@code ClassLoader}.
032 *
033 * @author Rod Johnson
034 * @author Juergen Hoeller
035 * @since 2.0.1
036 */
037@UsesJava7
038public class OverridingClassLoader extends DecoratingClassLoader {
039
040        /** Packages that are excluded by default */
041        public static final String[] DEFAULT_EXCLUDED_PACKAGES = new String[]
042                        {"java.", "javax.", "sun.", "oracle.", "javassist.", "org.aspectj.", "net.sf.cglib."};
043
044        private static final String CLASS_FILE_SUFFIX = ".class";
045
046        static {
047                if (parallelCapableClassLoaderAvailable) {
048                        ClassLoader.registerAsParallelCapable();
049                }
050        }
051
052
053        private final ClassLoader overrideDelegate;
054
055
056        /**
057         * Create a new OverridingClassLoader for the given ClassLoader.
058         * @param parent the ClassLoader to build an overriding ClassLoader for
059         */
060        public OverridingClassLoader(ClassLoader parent) {
061                this(parent, null);
062        }
063
064        /**
065         * Create a new OverridingClassLoader for the given ClassLoader.
066         * @param parent the ClassLoader to build an overriding ClassLoader for
067         * @param overrideDelegate the ClassLoader to delegate to for overriding
068         * @since 4.3
069         */
070        public OverridingClassLoader(ClassLoader parent, ClassLoader overrideDelegate) {
071                super(parent);
072                this.overrideDelegate = overrideDelegate;
073                for (String packageName : DEFAULT_EXCLUDED_PACKAGES) {
074                        excludePackage(packageName);
075                }
076        }
077
078
079        @Override
080        public Class<?> loadClass(String name) throws ClassNotFoundException {
081                if (this.overrideDelegate != null && isEligibleForOverriding(name)) {
082                        return this.overrideDelegate.loadClass(name);
083                }
084                return super.loadClass(name);
085        }
086
087        @Override
088        protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
089                if (isEligibleForOverriding(name)) {
090                        Class<?> result = loadClassForOverriding(name);
091                        if (result != null) {
092                                if (resolve) {
093                                        resolveClass(result);
094                                }
095                                return result;
096                        }
097                }
098                return super.loadClass(name, resolve);
099        }
100
101        /**
102         * Determine whether the specified class is eligible for overriding
103         * by this class loader.
104         * @param className the class name to check
105         * @return whether the specified class is eligible
106         * @see #isExcluded
107         */
108        protected boolean isEligibleForOverriding(String className) {
109                return !isExcluded(className);
110        }
111
112        /**
113         * Load the specified class for overriding purposes in this ClassLoader.
114         * <p>The default implementation delegates to {@link #findLoadedClass},
115         * {@link #loadBytesForClass} and {@link #defineClass}.
116         * @param name the name of the class
117         * @return the Class object, or {@code null} if no class defined for that name
118         * @throws ClassNotFoundException if the class for the given name couldn't be loaded
119         */
120        protected Class<?> loadClassForOverriding(String name) throws ClassNotFoundException {
121                Class<?> result = findLoadedClass(name);
122                if (result == null) {
123                        byte[] bytes = loadBytesForClass(name);
124                        if (bytes != null) {
125                                result = defineClass(name, bytes, 0, bytes.length);
126                        }
127                }
128                return result;
129        }
130
131        /**
132         * Load the defining bytes for the given class,
133         * to be turned into a Class object through a {@link #defineClass} call.
134         * <p>The default implementation delegates to {@link #openStreamForClass}
135         * and {@link #transformIfNecessary}.
136         * @param name the name of the class
137         * @return the byte content (with transformers already applied),
138         * or {@code null} if no class defined for that name
139         * @throws ClassNotFoundException if the class for the given name couldn't be loaded
140         */
141        protected byte[] loadBytesForClass(String name) throws ClassNotFoundException {
142                InputStream is = openStreamForClass(name);
143                if (is == null) {
144                        return null;
145                }
146                try {
147                        // Load the raw bytes.
148                        byte[] bytes = FileCopyUtils.copyToByteArray(is);
149                        // Transform if necessary and use the potentially transformed bytes.
150                        return transformIfNecessary(name, bytes);
151                }
152                catch (IOException ex) {
153                        throw new ClassNotFoundException("Cannot load resource for class [" + name + "]", ex);
154                }
155        }
156
157        /**
158         * Open an InputStream for the specified class.
159         * <p>The default implementation loads a standard class file through
160         * the parent ClassLoader's {@code getResourceAsStream} method.
161         * @param name the name of the class
162         * @return the InputStream containing the byte code for the specified class
163         */
164        protected InputStream openStreamForClass(String name) {
165                String internalName = name.replace('.', '/') + CLASS_FILE_SUFFIX;
166                return getParent().getResourceAsStream(internalName);
167        }
168
169
170        /**
171         * Transformation hook to be implemented by subclasses.
172         * <p>The default implementation simply returns the given bytes as-is.
173         * @param name the fully-qualified name of the class being transformed
174         * @param bytes the raw bytes of the class
175         * @return the transformed bytes (never {@code null};
176         * same as the input bytes if the transformation produced no changes)
177         */
178        protected byte[] transformIfNecessary(String name, byte[] bytes) {
179                return bytes;
180        }
181
182}