From 67613f905fa167d7e7dd7344bf974ebdb08745d3 Mon Sep 17 00:00:00 2001 From: benoit Date: Fri, 18 May 2018 11:35:48 +0200 Subject: [PATCH] SPR-16838 PathMatchingResourcePatternResolver should enforce consistent alphabetical sorting of directory content A sort was added in SPR-14085 but the default File#compareTo depends upon the underlying system. The ordering of the ressources returned by PathMatchingResourcePatternResolver#retrieveMatchingFiles may differ between OS (Windows, Unix). This leads to subtle bugs hard to track. This PR replace the default sort with one based on the file name. --- .../PathMatchingResourcePatternResolver.java | 5 +++- ...hMatchingResourcePatternResolverTests.java | 28 ++++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) 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 502e9867fdd7..e84c8dce7be9 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 @@ -790,7 +790,10 @@ protected void doRetrieveMatchingFiles(String fullPattern, File dir, Set r } return; } - Arrays.sort(dirContents); + + // enforce consistent alphabetical sorting to avoid different order between OS + Arrays.sort(dirContents, (f1, f2) -> f1.getName().compareTo(f2.getName())); + for (File content : dirContents) { String currPath = StringUtils.replace(content.getAbsolutePath(), File.separator, "/"); if (content.isDirectory() && getPathMatcher().matchStart(fullPattern, currPath + "/")) { 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 4e6237eae648..990db17c2eaf 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 @@ -16,15 +16,18 @@ package org.springframework.core.io.support; +import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Set; import org.junit.Ignore; +import org.junit.Rule; import org.junit.Test; - +import org.junit.rules.TemporaryFolder; import org.springframework.core.io.Resource; import org.springframework.util.StringUtils; @@ -56,6 +59,8 @@ public class PathMatchingResourcePatternResolverTests { private PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + @Rule + public TemporaryFolder folder = new TemporaryFolder(); @Test(expected = FileNotFoundException.class) public void invalidPrefixWithPatternElementInIt() throws IOException { @@ -117,6 +122,27 @@ public void rootPatternRetrievalInJarFiles() throws IOException { } assertTrue("Could not find aspectj_1_5_0.dtd in the root of the aspectjweaver jar", found); } + + + @Test + public void testConsistentFileOrder() throws IOException { + + List expectedFileNames = new ArrayList<>(); + + expectedFileNames.add(folder.newFile("A.txt").getName()); + expectedFileNames.add(folder.newFile("B.txt").getName()); + expectedFileNames.add(folder.newFile("P.txt").getName()); + File newFolder = folder.newFolder("message"); + expectedFileNames.add(File.createTempFile("Message", ".txt", newFolder).getName()); + + Set matchingFiles = resolver.retrieveMatchingFiles(folder.getRoot(), "**/*.txt"); + assertEquals(expectedFileNames.size(), matchingFiles.size()); + int i = 0; + for (File file : matchingFiles) { + assertEquals(expectedFileNames.get(i), file.getName()); + i++; + } + } private void assertProtocolAndFilenames(Resource[] resources, String protocol, String... filenames)