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}