diff --git a/spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java b/spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java index 70fc8a83e6..dacfb011ad 100644 --- a/spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java +++ b/spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java @@ -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. *

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"); } diff --git a/spring-core/src/main/java/org/springframework/core/io/ResourceLoader.java b/spring-core/src/main/java/org/springframework/core/io/ResourceLoader.java index 958746178f..b753535ab0 100644 --- a/spring-core/src/main/java/org/springframework/core/io/ResourceLoader.java +++ b/spring-core/src/main/java/org/springframework/core/io/ResourceLoader.java @@ -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 { *

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(); diff --git a/spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java b/spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java index 1def34100a..6eeef89270 100644 --- a/spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java +++ b/spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java @@ -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 resourceUrls = getClassLoader().getResources(path); + ClassLoader cl = getClassLoader(); + Enumeration resourceUrls = (cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path)); Set result = new LinkedHashSet(16); while (resourceUrls.hasMoreElements()) { URL url = resourceUrls.nextElement(); diff --git a/spring-core/src/main/java/org/springframework/core/io/support/PropertiesLoaderUtils.java b/spring-core/src/main/java/org/springframework/core/io/support/PropertiesLoaderUtils.java index b05cfc5bb9..b9789557df 100644 --- a/spring-core/src/main/java/org/springframework/core/io/support/PropertiesLoaderUtils.java +++ b/spring-core/src/main/java/org/springframework/core/io/support/PropertiesLoaderUtils.java @@ -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 urls = clToUse.getResources(resourceName); + Enumeration urls = (classLoaderToUse != null ? classLoaderToUse.getResources(resourceName) : + ClassLoader.getSystemResources(resourceName)); while (urls.hasMoreElements()) { URL url = urls.nextElement(); URLConnection con = url.openConnection(); diff --git a/spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java b/spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java index 6c33d76817..358366815e 100644 --- a/spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java +++ b/spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java @@ -67,16 +67,17 @@ public abstract class SpringFactoriesLoader { */ public static List loadFactories(Class 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 factoryNames = loadFactoryNames(factoryClass, classLoader); + List factoryNames = loadFactoryNames(factoryClass, classLoaderToUse); if (logger.isTraceEnabled()) { logger.trace("Loaded [" + factoryClass.getName() + "] names: " + factoryNames); } List result = new ArrayList(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 result = new ArrayList(); - Enumeration urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION); + Enumeration 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)); diff --git a/spring-core/src/main/java/org/springframework/util/ClassUtils.java b/spring-core/src/main/java/org/springframework/util/ClassUtils.java index 48a7826337..01f33f2296 100644 --- a/spring-core/src/main/java/org/springframework/util/ClassUtils.java +++ b/spring-core/src/main/java/org/springframework/util/ClassUtils.java @@ -142,12 +142,14 @@ public abstract class ClassUtils { * ClassLoader, if available; the ClassLoader that loaded the ClassUtils * class will be used as fallback. *

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 diff --git a/spring-core/src/main/java/org/springframework/util/ResourceUtils.java b/spring-core/src/main/java/org/springframework/util/ResourceUtils.java index 8ab4f5bba5..54d07645b0 100644 --- a/spring-core/src/main/java/org/springframework/util/ResourceUtils.java +++ b/spring-core/src/main/java/org/springframework/util/ResourceUtils.java @@ -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 " +