Skip to content

Commit f16e6c4

Browse files
committed
General defensiveness about the bootstrap ClassLoader (i.e. null ClassLoader)
Issue: SPR-11721 (cherry picked from commit c05ab3e)
1 parent 4dbc0c2 commit f16e6c4

File tree

7 files changed

+81
-52
lines changed

7 files changed

+81
-52
lines changed

spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java

+35-26
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -50,9 +50,9 @@ public class ClassPathResource extends AbstractFileResolvingResource {
5050

5151

5252
/**
53-
* Create a new ClassPathResource for ClassLoader usage.
54-
* A leading slash will be removed, as the ClassLoader
55-
* resource access methods will not accept it.
53+
* Create a new {@code ClassPathResource} for {@code ClassLoader} usage.
54+
* A leading slash will be removed, as the ClassLoader resource access
55+
* methods will not accept it.
5656
* <p>The thread context class loader will be used for
5757
* loading the resource.
5858
* @param path the absolute path within the class path
@@ -64,9 +64,9 @@ public ClassPathResource(String path) {
6464
}
6565

6666
/**
67-
* Create a new ClassPathResource for ClassLoader usage.
68-
* A leading slash will be removed, as the ClassLoader
69-
* resource access methods will not accept it.
67+
* Create a new {@code ClassPathResource} for {@code ClassLoader} usage.
68+
* A leading slash will be removed, as the ClassLoader resource access
69+
* methods will not accept it.
7070
* @param path the absolute path within the classpath
7171
* @param classLoader the class loader to load the resource with,
7272
* or {@code null} for the thread context class loader
@@ -83,9 +83,9 @@ public ClassPathResource(String path, ClassLoader classLoader) {
8383
}
8484

8585
/**
86-
* Create a new ClassPathResource for Class usage.
87-
* The path can be relative to the given class,
88-
* or absolute within the classpath via a leading slash.
86+
* Create a new {@code ClassPathResource} for {@code Class} usage.
87+
* The path can be relative to the given class, or absolute within
88+
* the classpath via a leading slash.
8989
* @param path relative or absolute path within the class path
9090
* @param clazz the class to load resources with
9191
* @see java.lang.Class#getResourceAsStream
@@ -97,8 +97,8 @@ public ClassPathResource(String path, Class<?> clazz) {
9797
}
9898

9999
/**
100-
* Create a new ClassPathResource with optional ClassLoader and Class.
101-
* Only for internal usage.
100+
* Create a new {@code ClassPathResource} with optional {@code ClassLoader}
101+
* and {@code Class}. Only for internal usage.
102102
* @param path relative or absolute path within the classpath
103103
* @param classLoader the class loader to load the resource with, if any
104104
* @param clazz the class to load resources with, if any
@@ -109,6 +109,7 @@ protected ClassPathResource(String path, ClassLoader classLoader, Class<?> clazz
109109
this.clazz = clazz;
110110
}
111111

112+
112113
/**
113114
* Return the path for this resource (as resource path within the class path).
114115
*/
@@ -120,24 +121,34 @@ public final String getPath() {
120121
* Return the ClassLoader that this resource will be obtained from.
121122
*/
122123
public final ClassLoader getClassLoader() {
123-
return (this.classLoader != null ? this.classLoader : this.clazz.getClassLoader());
124+
return (this.clazz != null ? this.clazz.getClassLoader() : this.classLoader);
124125
}
125126

127+
126128
/**
127129
* This implementation checks for the resolution of a resource URL.
128130
* @see java.lang.ClassLoader#getResource(String)
129131
* @see java.lang.Class#getResource(String)
130132
*/
131133
@Override
132134
public boolean exists() {
133-
URL url;
135+
return (resolveURL() != null);
136+
}
137+
138+
/**
139+
* Resolves a URL for the underlying class path resource.
140+
* @return the resolved URL, or {@code null} if not resolvable
141+
*/
142+
protected URL resolveURL() {
134143
if (this.clazz != null) {
135-
url = this.clazz.getResource(this.path);
144+
return this.clazz.getResource(this.path);
145+
}
146+
else if (this.classLoader != null) {
147+
return this.classLoader.getResource(this.path);
136148
}
137149
else {
138-
url = this.classLoader.getResource(this.path);
150+
return ClassLoader.getSystemResource(this.path);
139151
}
140-
return (url != null);
141152
}
142153

143154
/**
@@ -151,29 +162,27 @@ public InputStream getInputStream() throws IOException {
151162
if (this.clazz != null) {
152163
is = this.clazz.getResourceAsStream(this.path);
153164
}
154-
else {
165+
else if (this.classLoader != null) {
155166
is = this.classLoader.getResourceAsStream(this.path);
156167
}
168+
else {
169+
is = ClassLoader.getSystemResourceAsStream(this.path);
170+
}
157171
if (is == null) {
158172
throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
159173
}
160174
return is;
161175
}
162176

163177
/**
164-
* This implementation returns a URL for the underlying class path resource.
178+
* This implementation returns a URL for the underlying class path resource,
179+
* if available.
165180
* @see java.lang.ClassLoader#getResource(String)
166181
* @see java.lang.Class#getResource(String)
167182
*/
168183
@Override
169184
public URL getURL() throws IOException {
170-
URL url;
171-
if (this.clazz != null) {
172-
url = this.clazz.getResource(this.path);
173-
}
174-
else {
175-
url = this.classLoader.getResource(this.path);
176-
}
185+
URL url = resolveURL();
177186
if (url == null) {
178187
throw new FileNotFoundException(getDescription() + " cannot be resolved to URL because it does not exist");
179188
}

spring-core/src/main/java/org/springframework/core/io/ResourceLoader.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -70,7 +70,9 @@ public interface ResourceLoader {
7070
* <p>Clients which need to access the ClassLoader directly can do so
7171
* in a uniform manner with the ResourceLoader, rather than relying
7272
* on the thread context ClassLoader.
73-
* @return the ClassLoader (never {@code null})
73+
* @return the ClassLoader (only {@code null} if even the system
74+
* ClassLoader isn't accessible)
75+
* @see org.springframework.util.ClassUtils#getDefaultClassLoader()
7476
*/
7577
ClassLoader getClassLoader();
7678

spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -229,7 +229,8 @@ public ResourceLoader getResourceLoader() {
229229

230230
/**
231231
* Return the ClassLoader that this pattern resolver works with
232-
* (never {@code null}).
232+
* (only {@code null} if even the system ClassLoader isn't accessible).
233+
* @see org.springframework.util.ClassUtils#getDefaultClassLoader()
233234
*/
234235
@Override
235236
public ClassLoader getClassLoader() {
@@ -301,7 +302,8 @@ protected Resource[] findAllClassPathResources(String location) throws IOExcepti
301302
if (path.startsWith("/")) {
302303
path = path.substring(1);
303304
}
304-
Enumeration<URL> resourceUrls = getClassLoader().getResources(path);
305+
ClassLoader cl = getClassLoader();
306+
Enumeration<URL> resourceUrls = (cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path));
305307
Set<Resource> result = new LinkedHashSet<Resource>(16);
306308
while (resourceUrls.hasMoreElements()) {
307309
URL url = resourceUrls.nextElement();

spring-core/src/main/java/org/springframework/core/io/support/PropertiesLoaderUtils.java

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -170,12 +170,13 @@ public static Properties loadAllProperties(String resourceName) throws IOExcepti
170170
*/
171171
public static Properties loadAllProperties(String resourceName, ClassLoader classLoader) throws IOException {
172172
Assert.notNull(resourceName, "Resource name must not be null");
173-
ClassLoader clToUse = classLoader;
174-
if (clToUse == null) {
175-
clToUse = ClassUtils.getDefaultClassLoader();
173+
ClassLoader classLoaderToUse = classLoader;
174+
if (classLoaderToUse == null) {
175+
classLoaderToUse = ClassUtils.getDefaultClassLoader();
176176
}
177177
Properties props = new Properties();
178-
Enumeration<URL> urls = clToUse.getResources(resourceName);
178+
Enumeration<URL> urls = (classLoaderToUse != null ? classLoaderToUse.getResources(resourceName) :
179+
ClassLoader.getSystemResources(resourceName));
179180
while (urls.hasMoreElements()) {
180181
URL url = urls.nextElement();
181182
URLConnection con = url.openConnection();

spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java

+7-5
Original file line numberDiff line numberDiff line change
@@ -67,16 +67,17 @@ public abstract class SpringFactoriesLoader {
6767
*/
6868
public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) {
6969
Assert.notNull(factoryClass, "'factoryClass' must not be null");
70-
if (classLoader == null) {
71-
classLoader = SpringFactoriesLoader.class.getClassLoader();
70+
ClassLoader classLoaderToUse = classLoader;
71+
if (classLoaderToUse == null) {
72+
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
7273
}
73-
List<String> factoryNames = loadFactoryNames(factoryClass, classLoader);
74+
List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);
7475
if (logger.isTraceEnabled()) {
7576
logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames);
7677
}
7778
List<T> result = new ArrayList<T>(factoryNames.size());
7879
for (String factoryName : factoryNames) {
79-
result.add(instantiateFactory(factoryName, factoryClass, classLoader));
80+
result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
8081
}
8182
OrderComparator.sort(result);
8283
return result;
@@ -86,7 +87,8 @@ public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader c
8687
String factoryClassName = factoryClass.getName();
8788
try {
8889
List<String> result = new ArrayList<String>();
89-
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
90+
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
91+
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
9092
while (urls.hasMoreElements()) {
9193
URL url = urls.nextElement();
9294
Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));

spring-core/src/main/java/org/springframework/util/ClassUtils.java

+20-9
Original file line numberDiff line numberDiff line change
@@ -142,24 +142,35 @@ private static void registerCommonClasses(Class<?>... commonClasses) {
142142
* ClassLoader, if available; the ClassLoader that loaded the ClassUtils
143143
* class will be used as fallback.
144144
* <p>Call this method if you intend to use the thread context ClassLoader
145-
* in a scenario where you absolutely need a non-null ClassLoader reference:
145+
* in a scenario where you clearly prefer a non-null ClassLoader reference:
146146
* for example, for class path resource loading (but not necessarily for
147147
* {@code Class.forName}, which accepts a {@code null} ClassLoader
148148
* reference as well).
149-
* @return the default ClassLoader (never {@code null})
149+
* @return the default ClassLoader (only {@code null} if even the system
150+
* ClassLoader isn't accessible)
150151
* @see Thread#getContextClassLoader()
152+
* @see ClassLoader#getSystemClassLoader()
151153
*/
152154
public static ClassLoader getDefaultClassLoader() {
153155
ClassLoader cl = null;
154156
try {
155157
cl = Thread.currentThread().getContextClassLoader();
156158
}
157159
catch (Throwable ex) {
158-
// Cannot access thread context ClassLoader - falling back to system class loader...
160+
// Cannot access thread context ClassLoader - falling back...
159161
}
160162
if (cl == null) {
161163
// No thread context class loader -> use class loader of this class.
162164
cl = ClassUtils.class.getClassLoader();
165+
if (cl == null) {
166+
// getClassLoader() returning null indicates the bootstrap ClassLoader
167+
try {
168+
cl = ClassLoader.getSystemClassLoader();
169+
}
170+
catch (Throwable ex) {
171+
// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
172+
}
173+
}
163174
}
164175
return cl;
165176
}
@@ -185,7 +196,7 @@ public static ClassLoader overrideThreadContextClassLoader(ClassLoader classLoad
185196

186197
/**
187198
* Replacement for {@code Class.forName()} that also returns Class instances
188-
* for primitives (e.g."int") and array class names (e.g. "String[]").
199+
* for primitives (e.g. "int") and array class names (e.g. "String[]").
189200
* Furthermore, it is also capable of resolving inner class names in Java source
190201
* style (e.g. "java.lang.Thread.State" instead of "java.lang.Thread$State").
191202
* @param name the name of the Class
@@ -228,19 +239,19 @@ public static Class<?> forName(String name, ClassLoader classLoader) throws Clas
228239
return Array.newInstance(elementClass, 0).getClass();
229240
}
230241

231-
ClassLoader classLoaderToUse = classLoader;
232-
if (classLoaderToUse == null) {
233-
classLoaderToUse = getDefaultClassLoader();
242+
ClassLoader clToUse = classLoader;
243+
if (clToUse == null) {
244+
clToUse = getDefaultClassLoader();
234245
}
235246
try {
236-
return classLoaderToUse.loadClass(name);
247+
return (clToUse != null ? clToUse.loadClass(name) : Class.forName(name));
237248
}
238249
catch (ClassNotFoundException ex) {
239250
int lastDotIndex = name.lastIndexOf('.');
240251
if (lastDotIndex != -1) {
241252
String innerClassName = name.substring(0, lastDotIndex) + '$' + name.substring(lastDotIndex + 1);
242253
try {
243-
return classLoaderToUse.loadClass(innerClassName);
254+
return (clToUse != null ? clToUse.loadClass(innerClassName) : Class.forName(innerClassName));
244255
}
245256
catch (ClassNotFoundException ex2) {
246257
// swallow - let original exception get through

spring-core/src/main/java/org/springframework/util/ResourceUtils.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ public static URL getURL(String resourceLocation) throws FileNotFoundException {
116116
Assert.notNull(resourceLocation, "Resource location must not be null");
117117
if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) {
118118
String path = resourceLocation.substring(CLASSPATH_URL_PREFIX.length());
119-
URL url = ClassUtils.getDefaultClassLoader().getResource(path);
119+
ClassLoader cl = ClassUtils.getDefaultClassLoader();
120+
URL url = (cl != null ? cl.getResource(path) : ClassLoader.getSystemResource(path));
120121
if (url == null) {
121122
String description = "class path resource [" + path + "]";
122123
throw new FileNotFoundException(
@@ -156,7 +157,8 @@ public static File getFile(String resourceLocation) throws FileNotFoundException
156157
if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) {
157158
String path = resourceLocation.substring(CLASSPATH_URL_PREFIX.length());
158159
String description = "class path resource [" + path + "]";
159-
URL url = ClassUtils.getDefaultClassLoader().getResource(path);
160+
ClassLoader cl = ClassUtils.getDefaultClassLoader();
161+
URL url = (cl != null ? cl.getResource(path) : ClassLoader.getSystemResource(path));
160162
if (url == null) {
161163
throw new FileNotFoundException(
162164
description + " cannot be resolved to absolute file path " +

0 commit comments

Comments
 (0)