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 be84de592932546b6696ace1561fee0151de1a99..bdb8d4fec0fa471ccee7dd970e330b384bafdec9 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 @@ -144,6 +144,9 @@ import org.springframework.util.StringUtils; * root of expanded directories. This originates from a limitation in the JDK's * {@code ClassLoader.getResources()} method which only returns file system * locations for a passed-in empty String (indicating potential roots to search). + * This {@code ResourcePatternResolver} implementation is trying to mitigate the + * jar root lookup limitation through {@link URLClassLoader} introspection and + * "java.class.path" manifest evaluation; however, without portability guarantees. * *

WARNING: Ant-style patterns with "classpath:" resources are not * guaranteed to find matching resources if the root package to search is available @@ -156,9 +159,9 @@ import org.springframework.util.StringUtils; * classpath:com/mycompany/**/service-context.xml * * is used to try to resolve it, the resolver will work off the (first) URL - * returned by {@code getResource("com/mycompany");}. If this base package - * node exists in multiple classloader locations, the actual end resource may - * not be underneath. Therefore, preferably, use "{@code classpath*:}" with the same + * returned by {@code getResource("com/mycompany");}. If this base package node + * exists in multiple classloader locations, the actual end resource may not be + * underneath. Therefore, preferably, use "{@code classpath*:}" with the same * Ant-style pattern in such a case, which will search all class path * locations that contain the root package. * @@ -166,6 +169,7 @@ import org.springframework.util.StringUtils; * @author Colin Sampaleanu * @author Marius Bogoevici * @author Costin Leau + * @author Phil Webb * @since 1.0.2 * @see #CLASSPATH_ALL_URL_PREFIX * @see org.springframework.util.AntPathMatcher @@ -316,6 +320,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol * Called by {@link #findAllClassPathResources(String)}. * @param path the absolute path within the classpath (never a leading slash) * @return a mutable Set of matching Resource instances + * @since 4.1.1 */ protected Set doFindAllClassPathResources(String path) throws IOException { Set result = new LinkedHashSet(16); @@ -350,6 +355,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol * given set of resources in the form of pointers to the root of the jar file content. * @param classLoader the ClassLoader to search (including its ancestors) * @param result the set of resources to add jar roots to + * @since 4.1.1 */ protected void addAllClassLoaderJarRoots(ClassLoader classLoader, Set result) { if (classLoader instanceof URLClassLoader) { @@ -379,8 +385,15 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol } } } + + if (classLoader == ClassLoader.getSystemClassLoader()) { + // "java.class.path" manifest evaluation... + addClassPathManifestEntries(result); + } + if (classLoader != null) { try { + // Hierarchy traversal... addAllClassLoaderJarRoots(classLoader.getParent(), result); } catch (Exception ex) { @@ -392,6 +405,41 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol } } + /** + * Determine jar file references from the "java.class.path." manifest property and add them + * to the given set of resources in the form of pointers to the root of the jar file content. + * @param result the set of resources to add jar roots to + * @since 4.3 + */ + protected void addClassPathManifestEntries(Set result) { + try { + String javaClassPathProperty = System.getProperty("java.class.path"); + for (String url : StringUtils.delimitedListToStringArray( + javaClassPathProperty, System.getProperty("path.separator"))) { + try { + if (url.endsWith(ResourceUtils.JAR_FILE_EXTENSION)) { + UrlResource jarResource = new UrlResource(ResourceUtils.JAR_URL_PREFIX + + ResourceUtils.FILE_URL_PREFIX + url + ResourceUtils.JAR_URL_SEPARATOR); + if (jarResource.exists()) { + result.add(jarResource); + } + } + } + catch (MalformedURLException ex) { + if (logger.isDebugEnabled()) { + logger.debug("Cannot search for matching files underneath [" + url + + "] because it cannot be converted to a valid 'jar:' URL: " + ex.getMessage()); + } + } + } + } + catch (Exception ex) { + if (logger.isDebugEnabled()) { + logger.debug("Failed to evaluate 'java.class.path' manifest entries: " + ex); + } + } + } + /** * Find all resources that match the given location pattern via the * Ant-style PathMatcher. Supports resources in jar files and zip files