001/*
002 * Copyright 2012-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 *      http://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.boot.web.embedded.tomcat;
018
019import java.io.IOException;
020import java.net.URL;
021import java.util.Collections;
022import java.util.Enumeration;
023
024import org.apache.catalina.loader.ParallelWebappClassLoader;
025import org.apache.commons.logging.Log;
026import org.apache.commons.logging.LogFactory;
027
028/**
029 * Extension of Tomcat's {@link ParallelWebappClassLoader} that does not consider the
030 * {@link ClassLoader#getSystemClassLoader() system classloader}. This is required to
031 * ensure that any custom context class loader is always used (as is the case with some
032 * executable archives).
033 *
034 * @author Phillip Webb
035 * @since 2.0.0
036 */
037public class TomcatEmbeddedWebappClassLoader extends ParallelWebappClassLoader {
038
039        private static final Log logger = LogFactory
040                        .getLog(TomcatEmbeddedWebappClassLoader.class);
041
042        static {
043                ClassLoader.registerAsParallelCapable();
044        }
045
046        public TomcatEmbeddedWebappClassLoader() {
047        }
048
049        public TomcatEmbeddedWebappClassLoader(ClassLoader parent) {
050                super(parent);
051        }
052
053        @Override
054        public URL findResource(String name) {
055                return null;
056        }
057
058        @Override
059        public Enumeration<URL> findResources(String name) throws IOException {
060                return Collections.emptyEnumeration();
061        }
062
063        @Override
064        public Class<?> loadClass(String name, boolean resolve)
065                        throws ClassNotFoundException {
066                synchronized (getClassLoadingLock(name)) {
067                        Class<?> result = findExistingLoadedClass(name);
068                        result = (result != null) ? result : doLoadClass(name);
069                        if (result == null) {
070                                throw new ClassNotFoundException(name);
071                        }
072                        return resolveIfNecessary(result, resolve);
073                }
074        }
075
076        private Class<?> findExistingLoadedClass(String name) {
077                Class<?> resultClass = findLoadedClass0(name);
078                resultClass = (resultClass != null) ? resultClass : findLoadedClass(name);
079                return resultClass;
080        }
081
082        private Class<?> doLoadClass(String name) throws ClassNotFoundException {
083                checkPackageAccess(name);
084                if ((this.delegate || filter(name, true))) {
085                        Class<?> result = loadFromParent(name);
086                        return (result != null) ? result : findClassIgnoringNotFound(name);
087                }
088                Class<?> result = findClassIgnoringNotFound(name);
089                return (result != null) ? result : loadFromParent(name);
090        }
091
092        private Class<?> resolveIfNecessary(Class<?> resultClass, boolean resolve) {
093                if (resolve) {
094                        resolveClass(resultClass);
095                }
096                return (resultClass);
097        }
098
099        @Override
100        protected void addURL(URL url) {
101                // Ignore URLs added by the Tomcat 8 implementation (see gh-919)
102                if (logger.isTraceEnabled()) {
103                        logger.trace("Ignoring request to add " + url + " to the tomcat classloader");
104                }
105        }
106
107        private Class<?> loadFromParent(String name) {
108                if (this.parent == null) {
109                        return null;
110                }
111                try {
112                        return Class.forName(name, false, this.parent);
113                }
114                catch (ClassNotFoundException ex) {
115                        return null;
116                }
117        }
118
119        private Class<?> findClassIgnoringNotFound(String name) {
120                try {
121                        return findClass(name);
122                }
123                catch (ClassNotFoundException ex) {
124                        return null;
125                }
126        }
127
128        private void checkPackageAccess(String name) throws ClassNotFoundException {
129                if (this.securityManager != null && name.lastIndexOf('.') >= 0) {
130                        try {
131                                this.securityManager
132                                                .checkPackageAccess(name.substring(0, name.lastIndexOf('.')));
133                        }
134                        catch (SecurityException ex) {
135                                throw new ClassNotFoundException("Security Violation, attempt to use "
136                                                + "Restricted Class: " + name, ex);
137                        }
138                }
139        }
140
141}