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