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.util.Collections;
020import java.util.Set;
021import java.util.concurrent.ConcurrentHashMap;
022
023import org.springframework.lang.UsesJava7;
024import org.springframework.util.Assert;
025import org.springframework.util.ClassUtils;
026
027/**
028 * Base class for decorating ClassLoaders such as {@link OverridingClassLoader}
029 * and {@link org.springframework.instrument.classloading.ShadowingClassLoader},
030 * providing common handling of excluded packages and classes.
031 *
032 * @author Juergen Hoeller
033 * @author Rod Johnson
034 * @since 2.5.2
035 */
036@UsesJava7
037public abstract class DecoratingClassLoader extends ClassLoader {
038
039        /**
040         * Java 7+ {@code ClassLoader.registerAsParallelCapable()} available?
041         * @since 4.1.2
042         */
043        protected static final boolean parallelCapableClassLoaderAvailable =
044                        ClassUtils.hasMethod(ClassLoader.class, "registerAsParallelCapable");
045
046        static {
047                if (parallelCapableClassLoaderAvailable) {
048                        ClassLoader.registerAsParallelCapable();
049                }
050        }
051
052
053        private final Set<String> excludedPackages =
054                        Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(8));
055
056        private final Set<String> excludedClasses =
057                        Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(8));
058
059
060        /**
061         * Create a new DecoratingClassLoader with no parent ClassLoader.
062         */
063        public DecoratingClassLoader() {
064        }
065
066        /**
067         * Create a new DecoratingClassLoader using the given parent ClassLoader
068         * for delegation.
069         */
070        public DecoratingClassLoader(ClassLoader parent) {
071                super(parent);
072        }
073
074
075        /**
076         * Add a package name to exclude from decoration (e.g. overriding).
077         * <p>Any class whose fully-qualified name starts with the name registered
078         * here will be handled by the parent ClassLoader in the usual fashion.
079         * @param packageName the package name to exclude
080         */
081        public void excludePackage(String packageName) {
082                Assert.notNull(packageName, "Package name must not be null");
083                this.excludedPackages.add(packageName);
084        }
085
086        /**
087         * Add a class name to exclude from decoration (e.g. overriding).
088         * <p>Any class name registered here will be handled by the parent
089         * ClassLoader in the usual fashion.
090         * @param className the class name to exclude
091         */
092        public void excludeClass(String className) {
093                Assert.notNull(className, "Class name must not be null");
094                this.excludedClasses.add(className);
095        }
096
097        /**
098         * Determine whether the specified class is excluded from decoration
099         * by this class loader.
100         * <p>The default implementation checks against excluded packages and classes.
101         * @param className the class name to check
102         * @return whether the specified class is eligible
103         * @see #excludePackage
104         * @see #excludeClass
105         */
106        protected boolean isExcluded(String className) {
107                if (this.excludedClasses.contains(className)) {
108                        return true;
109                }
110                for (String packageName : this.excludedPackages) {
111                        if (className.startsWith(packageName)) {
112                                return true;
113                        }
114                }
115                return false;
116        }
117
118}