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 31f1ef9eee3e..ba8bfc17e678 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 @@ -729,7 +729,7 @@ protected JarFile getJarFile(String jarFileUrl) throws IOException { /** * Find all resources in the file system of the supplied root directory that * match the given location sub pattern via the Ant-style PathMatcher. - * @param rootDirResource the root directory as Resource + * @param rootDirResource the root directory as a Resource * @param subPattern the sub pattern to match (below the root directory) * @return a mutable Set of matching Resource instances * @throws IOException in case of I/O errors @@ -738,50 +738,47 @@ protected JarFile getJarFile(String jarFileUrl) throws IOException { protected Set doFindPathMatchingFileResources(Resource rootDirResource, String subPattern) throws IOException { - Set result = new HashSet<>(); + + URI rootDirUri = rootDirResource.getURI(); + String rootDir = rootDirUri.getRawPath(); + if (!"file".equals(rootDirUri.getScheme()) && rootDir.startsWith("/")) { + rootDir = stripLeadingSlash(rootDir); + rootDir = stripTrailingSlash(rootDir); + if (rootDir.isEmpty()) { + rootDir = "/"; + } + } FileSystem fileSystem; try { - fileSystem = FileSystems.getFileSystem(rootDirResource.getURI().resolve("/")); + fileSystem = FileSystems.getFileSystem(rootDirUri.resolve("/")); } catch (Exception ex) { - fileSystem = FileSystems.newFileSystem(rootDirResource.getURI().resolve("/"), Map.of(), + fileSystem = FileSystems.newFileSystem(rootDirUri.resolve("/"), Map.of(), ClassUtils.getDefaultClassLoader()); } - String rootPath = rootDirResource.getURI().getRawPath(); - if (!("file").equals(rootDirResource.getURI().getScheme()) && rootPath.startsWith("/")) { - rootPath = rootPath.substring(1); - if (rootPath.length()==0) { - return result; - } - if (rootPath.endsWith("/")) { - rootPath = rootPath.substring(0, rootPath.length()-1); - } - if (rootPath.length()==0) { - return result; - } - } - Path path = fileSystem.getPath(rootPath); - Path patternPath = path.resolve(subPattern); - try (Stream files = Files.walk(path)) { - files.forEach(file -> { - if (getPathMatcher().match(patternPath.toString(), file.toString())) { - try { - result.add(convertToResource(file.toUri())); - } - catch (Exception ex) { - // ignore - } + + Path rootPath = fileSystem.getPath(rootDir); + String resourcePattern = rootPath.resolve(subPattern).toString(); + Predicate resourcePatternMatches = path -> getPathMatcher().match(resourcePattern, path.toString()); + Set result = new HashSet<>(); + try (Stream files = Files.walk(rootPath)) { + files.filter(resourcePatternMatches).sorted().forEach(file -> { + try { + result.add(convertToResource(file.toUri())); + } + catch (Exception ex) { + // TODO Introduce logging } }); } catch (NoSuchFileException ex) { - // ignore + // TODO Introduce logging } try { fileSystem.close(); } catch (UnsupportedOperationException ex) { - // ignore + // TODO Introduce logging } return result; } @@ -876,6 +873,10 @@ private static String stripLeadingSlash(String path) { return (path.startsWith("/") ? path.substring(1) : path); } + private static String stripTrailingSlash(String path) { + return (path.endsWith("/") ? path.substring(0, path.length() - 1) : path); + } + /** * Inner delegate class, avoiding a hard JBoss VFS API dependency at runtime. diff --git a/spring-core/src/test/java/org/springframework/core/io/support/PathMatchingResourcePatternResolverTests.java b/spring-core/src/test/java/org/springframework/core/io/support/PathMatchingResourcePatternResolverTests.java index 0410d11e920f..25febc2d72be 100644 --- a/spring-core/src/test/java/org/springframework/core/io/support/PathMatchingResourcePatternResolverTests.java +++ b/spring-core/src/test/java/org/springframework/core/io/support/PathMatchingResourcePatternResolverTests.java @@ -137,6 +137,7 @@ private void assertFilenames(String pattern, String... filenames) throws IOExcep Resource[] resources = resolver.getResources(pattern); List actualNames = Arrays.stream(resources) .map(Resource::getFilename) + // Need to decode within GraalVM native image to get %23 converted to #. .map(filename -> URLDecoder.decode(filename, UTF_8)) .sorted() .toList();