General defensiveness about the bootstrap ClassLoader (i.e. null ClassLoader)
Issue: SPR-11721
This commit is contained in:
parent
ec7d80b851
commit
c05ab3e2e8
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -50,9 +50,9 @@ public class ClassPathResource extends AbstractFileResolvingResource {
|
|||
|
||||
|
||||
/**
|
||||
* Create a new ClassPathResource for ClassLoader usage.
|
||||
* A leading slash will be removed, as the ClassLoader
|
||||
* resource access methods will not accept it.
|
||||
* Create a new {@code ClassPathResource} for {@code ClassLoader} usage.
|
||||
* A leading slash will be removed, as the ClassLoader resource access
|
||||
* methods will not accept it.
|
||||
* <p>The thread context class loader will be used for
|
||||
* loading the resource.
|
||||
* @param path the absolute path within the class path
|
||||
|
@ -64,9 +64,9 @@ public class ClassPathResource extends AbstractFileResolvingResource {
|
|||
}
|
||||
|
||||
/**
|
||||
* Create a new ClassPathResource for ClassLoader usage.
|
||||
* A leading slash will be removed, as the ClassLoader
|
||||
* resource access methods will not accept it.
|
||||
* Create a new {@code ClassPathResource} for {@code ClassLoader} usage.
|
||||
* A leading slash will be removed, as the ClassLoader resource access
|
||||
* methods will not accept it.
|
||||
* @param path the absolute path within the classpath
|
||||
* @param classLoader the class loader to load the resource with,
|
||||
* or {@code null} for the thread context class loader
|
||||
|
@ -83,9 +83,9 @@ public class ClassPathResource extends AbstractFileResolvingResource {
|
|||
}
|
||||
|
||||
/**
|
||||
* Create a new ClassPathResource for Class usage.
|
||||
* The path can be relative to the given class,
|
||||
* or absolute within the classpath via a leading slash.
|
||||
* Create a new {@code ClassPathResource} for {@code Class} usage.
|
||||
* The path can be relative to the given class, or absolute within
|
||||
* the classpath via a leading slash.
|
||||
* @param path relative or absolute path within the class path
|
||||
* @param clazz the class to load resources with
|
||||
* @see java.lang.Class#getResourceAsStream
|
||||
|
@ -97,8 +97,8 @@ public class ClassPathResource extends AbstractFileResolvingResource {
|
|||
}
|
||||
|
||||
/**
|
||||
* Create a new ClassPathResource with optional ClassLoader and Class.
|
||||
* Only for internal usage.
|
||||
* Create a new {@code ClassPathResource} with optional {@code ClassLoader}
|
||||
* and {@code Class}. Only for internal usage.
|
||||
* @param path relative or absolute path within the classpath
|
||||
* @param classLoader the class loader to load the resource with, if any
|
||||
* @param clazz the class to load resources with, if any
|
||||
|
@ -109,6 +109,7 @@ public class ClassPathResource extends AbstractFileResolvingResource {
|
|||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the path for this resource (as resource path within the class path).
|
||||
*/
|
||||
|
@ -120,9 +121,10 @@ public class ClassPathResource extends AbstractFileResolvingResource {
|
|||
* Return the ClassLoader that this resource will be obtained from.
|
||||
*/
|
||||
public final ClassLoader getClassLoader() {
|
||||
return (this.classLoader != null ? this.classLoader : this.clazz.getClassLoader());
|
||||
return (this.clazz != null ? this.clazz.getClassLoader() : this.classLoader);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This implementation checks for the resolution of a resource URL.
|
||||
* @see java.lang.ClassLoader#getResource(String)
|
||||
|
@ -130,14 +132,23 @@ public class ClassPathResource extends AbstractFileResolvingResource {
|
|||
*/
|
||||
@Override
|
||||
public boolean exists() {
|
||||
URL url;
|
||||
return (resolveURL() != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a URL for the underlying class path resource.
|
||||
* @return the resolved URL, or {@code null} if not resolvable
|
||||
*/
|
||||
protected URL resolveURL() {
|
||||
if (this.clazz != null) {
|
||||
url = this.clazz.getResource(this.path);
|
||||
return this.clazz.getResource(this.path);
|
||||
}
|
||||
else if (this.classLoader != null) {
|
||||
return this.classLoader.getResource(this.path);
|
||||
}
|
||||
else {
|
||||
url = this.classLoader.getResource(this.path);
|
||||
return ClassLoader.getSystemResource(this.path);
|
||||
}
|
||||
return (url != null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -151,9 +162,12 @@ public class ClassPathResource extends AbstractFileResolvingResource {
|
|||
if (this.clazz != null) {
|
||||
is = this.clazz.getResourceAsStream(this.path);
|
||||
}
|
||||
else {
|
||||
else if (this.classLoader != null) {
|
||||
is = this.classLoader.getResourceAsStream(this.path);
|
||||
}
|
||||
else {
|
||||
is = ClassLoader.getSystemResourceAsStream(this.path);
|
||||
}
|
||||
if (is == null) {
|
||||
throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
|
||||
}
|
||||
|
@ -161,19 +175,14 @@ public class ClassPathResource extends AbstractFileResolvingResource {
|
|||
}
|
||||
|
||||
/**
|
||||
* This implementation returns a URL for the underlying class path resource.
|
||||
* This implementation returns a URL for the underlying class path resource,
|
||||
* if available.
|
||||
* @see java.lang.ClassLoader#getResource(String)
|
||||
* @see java.lang.Class#getResource(String)
|
||||
*/
|
||||
@Override
|
||||
public URL getURL() throws IOException {
|
||||
URL url;
|
||||
if (this.clazz != null) {
|
||||
url = this.clazz.getResource(this.path);
|
||||
}
|
||||
else {
|
||||
url = this.classLoader.getResource(this.path);
|
||||
}
|
||||
URL url = resolveURL();
|
||||
if (url == null) {
|
||||
throw new FileNotFoundException(getDescription() + " cannot be resolved to URL because it does not exist");
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -70,7 +70,9 @@ public interface ResourceLoader {
|
|||
* <p>Clients which need to access the ClassLoader directly can do so
|
||||
* in a uniform manner with the ResourceLoader, rather than relying
|
||||
* on the thread context ClassLoader.
|
||||
* @return the ClassLoader (never {@code null})
|
||||
* @return the ClassLoader (only {@code null} if even the system
|
||||
* ClassLoader isn't accessible)
|
||||
* @see org.springframework.util.ClassUtils#getDefaultClassLoader()
|
||||
*/
|
||||
ClassLoader getClassLoader();
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -229,7 +229,8 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
|||
|
||||
/**
|
||||
* Return the ClassLoader that this pattern resolver works with
|
||||
* (never {@code null}).
|
||||
* (only {@code null} if even the system ClassLoader isn't accessible).
|
||||
* @see org.springframework.util.ClassUtils#getDefaultClassLoader()
|
||||
*/
|
||||
@Override
|
||||
public ClassLoader getClassLoader() {
|
||||
|
@ -301,7 +302,8 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol
|
|||
if (path.startsWith("/")) {
|
||||
path = path.substring(1);
|
||||
}
|
||||
Enumeration<URL> resourceUrls = getClassLoader().getResources(path);
|
||||
ClassLoader cl = getClassLoader();
|
||||
Enumeration<URL> resourceUrls = (cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path));
|
||||
Set<Resource> result = new LinkedHashSet<Resource>(16);
|
||||
while (resourceUrls.hasMoreElements()) {
|
||||
URL url = resourceUrls.nextElement();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -170,12 +170,13 @@ public abstract class PropertiesLoaderUtils {
|
|||
*/
|
||||
public static Properties loadAllProperties(String resourceName, ClassLoader classLoader) throws IOException {
|
||||
Assert.notNull(resourceName, "Resource name must not be null");
|
||||
ClassLoader clToUse = classLoader;
|
||||
if (clToUse == null) {
|
||||
clToUse = ClassUtils.getDefaultClassLoader();
|
||||
ClassLoader classLoaderToUse = classLoader;
|
||||
if (classLoaderToUse == null) {
|
||||
classLoaderToUse = ClassUtils.getDefaultClassLoader();
|
||||
}
|
||||
Properties props = new Properties();
|
||||
Enumeration<URL> urls = clToUse.getResources(resourceName);
|
||||
Enumeration<URL> urls = (classLoaderToUse != null ? classLoaderToUse.getResources(resourceName) :
|
||||
ClassLoader.getSystemResources(resourceName));
|
||||
while (urls.hasMoreElements()) {
|
||||
URL url = urls.nextElement();
|
||||
URLConnection con = url.openConnection();
|
||||
|
|
|
@ -67,16 +67,17 @@ public abstract class SpringFactoriesLoader {
|
|||
*/
|
||||
public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) {
|
||||
Assert.notNull(factoryClass, "'factoryClass' must not be null");
|
||||
if (classLoader == null) {
|
||||
classLoader = SpringFactoriesLoader.class.getClassLoader();
|
||||
ClassLoader classLoaderToUse = classLoader;
|
||||
if (classLoaderToUse == null) {
|
||||
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
|
||||
}
|
||||
List<String> factoryNames = loadFactoryNames(factoryClass, classLoader);
|
||||
List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
|
||||
}
|
||||
List<T> result = new ArrayList<T>(factoryNames.size());
|
||||
for (String factoryName : factoryNames) {
|
||||
result.add(instantiateFactory(factoryName, factoryClass, classLoader));
|
||||
result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
|
||||
}
|
||||
OrderComparator.sort(result);
|
||||
return result;
|
||||
|
@ -86,7 +87,8 @@ public abstract class SpringFactoriesLoader {
|
|||
String factoryClassName = factoryClass.getName();
|
||||
try {
|
||||
List<String> result = new ArrayList<String>();
|
||||
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
|
||||
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
|
||||
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
|
||||
while (urls.hasMoreElements()) {
|
||||
URL url = urls.nextElement();
|
||||
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
|
||||
|
|
|
@ -142,12 +142,14 @@ public abstract class ClassUtils {
|
|||
* ClassLoader, if available; the ClassLoader that loaded the ClassUtils
|
||||
* class will be used as fallback.
|
||||
* <p>Call this method if you intend to use the thread context ClassLoader
|
||||
* in a scenario where you absolutely need a non-null ClassLoader reference:
|
||||
* in a scenario where you clearly prefer a non-null ClassLoader reference:
|
||||
* for example, for class path resource loading (but not necessarily for
|
||||
* {@code Class.forName}, which accepts a {@code null} ClassLoader
|
||||
* reference as well).
|
||||
* @return the default ClassLoader (never {@code null})
|
||||
* @return the default ClassLoader (only {@code null} if even the system
|
||||
* ClassLoader isn't accessible)
|
||||
* @see Thread#getContextClassLoader()
|
||||
* @see ClassLoader#getSystemClassLoader()
|
||||
*/
|
||||
public static ClassLoader getDefaultClassLoader() {
|
||||
ClassLoader cl = null;
|
||||
|
@ -155,11 +157,20 @@ public abstract class ClassUtils {
|
|||
cl = Thread.currentThread().getContextClassLoader();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// Cannot access thread context ClassLoader - falling back to system class loader...
|
||||
// Cannot access thread context ClassLoader - falling back...
|
||||
}
|
||||
if (cl == null) {
|
||||
// No thread context class loader -> use class loader of this class.
|
||||
cl = ClassUtils.class.getClassLoader();
|
||||
if (cl == null) {
|
||||
// getClassLoader() returning null indicates the bootstrap ClassLoader
|
||||
try {
|
||||
cl = ClassLoader.getSystemClassLoader();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
|
||||
}
|
||||
}
|
||||
}
|
||||
return cl;
|
||||
}
|
||||
|
@ -185,7 +196,7 @@ public abstract class ClassUtils {
|
|||
|
||||
/**
|
||||
* Replacement for {@code Class.forName()} that also returns Class instances
|
||||
* for primitives (e.g."int") and array class names (e.g. "String[]").
|
||||
* for primitives (e.g. "int") and array class names (e.g. "String[]").
|
||||
* Furthermore, it is also capable of resolving inner class names in Java source
|
||||
* style (e.g. "java.lang.Thread.State" instead of "java.lang.Thread$State").
|
||||
* @param name the name of the Class
|
||||
|
@ -228,19 +239,19 @@ public abstract class ClassUtils {
|
|||
return Array.newInstance(elementClass, 0).getClass();
|
||||
}
|
||||
|
||||
ClassLoader classLoaderToUse = classLoader;
|
||||
if (classLoaderToUse == null) {
|
||||
classLoaderToUse = getDefaultClassLoader();
|
||||
ClassLoader clToUse = classLoader;
|
||||
if (clToUse == null) {
|
||||
clToUse = getDefaultClassLoader();
|
||||
}
|
||||
try {
|
||||
return classLoaderToUse.loadClass(name);
|
||||
return (clToUse != null ? clToUse.loadClass(name) : Class.forName(name));
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
int lastDotIndex = name.lastIndexOf('.');
|
||||
if (lastDotIndex != -1) {
|
||||
String innerClassName = name.substring(0, lastDotIndex) + '$' + name.substring(lastDotIndex + 1);
|
||||
try {
|
||||
return classLoaderToUse.loadClass(innerClassName);
|
||||
return (clToUse != null ? clToUse.loadClass(innerClassName) : Class.forName(innerClassName));
|
||||
}
|
||||
catch (ClassNotFoundException ex2) {
|
||||
// swallow - let original exception get through
|
||||
|
|
|
@ -116,7 +116,8 @@ public abstract class ResourceUtils {
|
|||
Assert.notNull(resourceLocation, "Resource location must not be null");
|
||||
if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) {
|
||||
String path = resourceLocation.substring(CLASSPATH_URL_PREFIX.length());
|
||||
URL url = ClassUtils.getDefaultClassLoader().getResource(path);
|
||||
ClassLoader cl = ClassUtils.getDefaultClassLoader();
|
||||
URL url = (cl != null ? cl.getResource(path) : ClassLoader.getSystemResource(path));
|
||||
if (url == null) {
|
||||
String description = "class path resource [" + path + "]";
|
||||
throw new FileNotFoundException(
|
||||
|
@ -156,7 +157,8 @@ public abstract class ResourceUtils {
|
|||
if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) {
|
||||
String path = resourceLocation.substring(CLASSPATH_URL_PREFIX.length());
|
||||
String description = "class path resource [" + path + "]";
|
||||
URL url = ClassUtils.getDefaultClassLoader().getResource(path);
|
||||
ClassLoader cl = ClassUtils.getDefaultClassLoader();
|
||||
URL url = (cl != null ? cl.getResource(path) : ClassLoader.getSystemResource(path));
|
||||
if (url == null) {
|
||||
throw new FileNotFoundException(
|
||||
description + " cannot be resolved to absolute file path " +
|
||||
|
|
Loading…
Reference in New Issue