From eb721b1e9f4dd9fe6dc682e3966c7ea53fceef06 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 18 Aug 2014 12:54:14 -0700 Subject: [PATCH] Find 'messages*.properties' in all jar URLs Update the the PathMatchingResourcePatternResolver used in the MessageSourceAutoConfiguration condition to deal with the fact that `classpath*:` patterns do not work with URLClassLoaders when the pattern doesn't include a folder. The ExtendedPathMatchingResourcePatternResolver works by searching all classpath URLs when the `findAllClassPathResources` method is called with an empty location. Fixes gh-1378 --- .../MessageSourceAutoConfiguration.java | 74 ++++++++++++++++++- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/MessageSourceAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/MessageSourceAutoConfiguration.java index 6163d590e4..a2438b271e 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/MessageSourceAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/MessageSourceAutoConfiguration.java @@ -17,7 +17,14 @@ package org.springframework.boot.autoconfigure; import java.io.IOException; - +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration.ResourceBundleCondition; import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -118,7 +125,7 @@ public class MessageSourceAutoConfiguration { private Resource[] getResources(ClassLoader classLoader, String name) { try { - return new PathMatchingResourcePatternResolver(classLoader) + return new ExtendedPathMatchingResourcePatternResolver(classLoader) .getResources("classpath*:" + name + "*.properties"); } catch (IOException ex) { @@ -128,4 +135,67 @@ public class MessageSourceAutoConfiguration { } + /** + * Extended version of {@link PathMatchingResourcePatternResolver} to deal with the + * fact that "{@code classpath*:...*.properties}" patterns don't work with + * {@link URLClassLoader}s. + */ + private static class ExtendedPathMatchingResourcePatternResolver extends + PathMatchingResourcePatternResolver { + + private static final Log logger = LogFactory + .getLog(PathMatchingResourcePatternResolver.class); + + public ExtendedPathMatchingResourcePatternResolver(ClassLoader classLoader) { + super(classLoader); + } + + @Override + protected Resource[] findAllClassPathResources(String location) + throws IOException { + String path = location; + if (path.startsWith("/")) { + path = path.substring(1); + } + if ("".equals(path)) { + Set result = new LinkedHashSet(16); + result.addAll(Arrays.asList(super.findAllClassPathResources(location))); + addAllClassLoaderJarUrls(getClassLoader(), result); + return result.toArray(new Resource[result.size()]); + } + return super.findAllClassPathResources(location); + } + + private void addAllClassLoaderJarUrls(ClassLoader classLoader, + Set result) { + if (classLoader != null) { + if (classLoader instanceof URLClassLoader) { + addAllClassLoaderJarUrls(((URLClassLoader) classLoader).getURLs(), + result); + } + addAllClassLoaderJarUrls(classLoader.getParent(), result); + } + } + + private void addAllClassLoaderJarUrls(URL[] urls, Set result) { + for (URL url : urls) { + if ("file".equals(url.getProtocol()) + && url.toString().toLowerCase().endsWith(".jar")) { + try { + URL jarUrl = new URL("jar:" + url.toString() + "!/"); + jarUrl.openConnection(); + result.add(convertClassLoaderURL(jarUrl)); + } + catch (Exception ex) { + if (logger.isWarnEnabled()) { + logger.warn("Cannot search for matching files underneath " + + url + " because it cannot be accessed as a JAR", ex); + } + } + } + } + } + + } + }