diff --git a/src/main/java/org/apache/commons/io/FileUtils.java b/src/main/java/org/apache/commons/io/FileUtils.java index b4794b61439..679aabc6699 100644 --- a/src/main/java/org/apache/commons/io/FileUtils.java +++ b/src/main/java/org/apache/commons/io/FileUtils.java @@ -23,6 +23,7 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; +import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -57,6 +58,7 @@ import java.time.chrono.ChronoLocalDateTime; import java.time.chrono.ChronoZonedDateTime; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -65,6 +67,7 @@ import java.util.List; import java.util.Objects; import java.util.Set; +import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.zip.CRC32; @@ -2241,6 +2244,11 @@ public static LineIterator lineIterator(final File file, final String charsetNam } } + private static List list(final File directory, final Predicate predicate, final String[] extensions) { + return Arrays.stream(extensions != null ? directory.listFiles((FilenameFilter) new SuffixFileFilter(extensions)) : directory.listFiles()) + .filter(predicate).collect(Collectors.toList()); + } + private static AccumulatorPathVisitor listAccumulate(final File directory, final IOFileFilter fileFilter, final IOFileFilter dirFilter, final FileVisitOption... options) throws IOException { final boolean isDirFilterSet = dirFilter != null; @@ -2322,9 +2330,13 @@ public static Collection listFiles(final File directory, final IOFileFilte * @return a collection of {@link File} with the matching files */ public static Collection listFiles(final File directory, final String[] extensions, final boolean recursive) { - try (Stream fileStream = Uncheck.get(() -> streamFiles(directory, recursive, extensions))) { - return toList(fileStream); + // IO-856 + if (recursive) { + final List list = list(directory, File::isFile, extensions); + list(directory, File::isDirectory, null).forEach(d -> list.addAll(listFiles(d, extensions, true))); + return list; } + return list(directory, File::isFile, extensions); } /** diff --git a/src/test/java/org/apache/commons/io/FileUtilsListFilesTest.java b/src/test/java/org/apache/commons/io/FileUtilsListFilesTest.java index 20e3c8d77a1..5a85763e792 100644 --- a/src/test/java/org/apache/commons/io/FileUtilsListFilesTest.java +++ b/src/test/java/org/apache/commons/io/FileUtilsListFilesTest.java @@ -18,6 +18,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -44,8 +45,6 @@ import org.apache.commons.lang3.function.Consumers; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.EnabledOnOs; -import org.junit.jupiter.api.condition.OS; import org.junit.jupiter.api.io.TempDir; /** @@ -204,12 +203,12 @@ public void testListFilesByExtension() { files = FileUtils.listFiles(temporaryFolder, extensions, true); fileNames = filesToFilenames(files); - assertEquals(4, fileNames.size()); + assertEquals(4, fileNames.size(), fileNames::toString); assertTrue(fileNames.contains("dummy-file.txt")); assertFalse(fileNames.contains("dummy-index.html")); files = FileUtils.listFiles(temporaryFolder, null, false); - assertEquals(2, files.size()); + assertEquals(2, files.size(), files::toString); fileNames = filesToFilenames(files); assertTrue(fileNames.contains("dummy-build.xml")); assertTrue(fileNames.contains("README")); @@ -238,7 +237,6 @@ public void testListFilesWithDeletion() throws IOException { * Tests IO-856 ListFiles should not fail on vanishing files. */ @Test - @EnabledOnOs(value = OS.WINDOWS) public void testListFilesWithDeletionThreaded() throws ExecutionException, InterruptedException { // test for IO-856 // create random directory in tmp, create the directory if it does not exist @@ -251,11 +249,13 @@ public void testListFilesWithDeletionThreaded() throws ExecutionException, Inter final byte[] bytes = "TEST".getBytes(StandardCharsets.UTF_8); final CompletableFuture c1 = CompletableFuture.runAsync(() -> { final long endTime = System.currentTimeMillis() + waitTime; + int count = 0; while (System.currentTimeMillis() < endTime) { final File file = new File(tempDir.getAbsolutePath(), UUID.randomUUID() + ".deletetester"); file.deleteOnExit(); try { Files.write(file.toPath(), bytes); + count++; } catch (final Exception e) { fail("Could not create test file: '" + file.getAbsolutePath() + "': " + e, e); } @@ -263,18 +263,24 @@ public void testListFilesWithDeletionThreaded() throws ExecutionException, Inter fail("Could not delete test file: '" + file.getAbsolutePath() + "'"); } } + // System.out.printf("Created %,d%n", count); }); final CompletableFuture c2 = CompletableFuture.runAsync(() -> { final long endTime = System.currentTimeMillis() + waitTime; + int max = 0; try { while (System.currentTimeMillis() < endTime) { - FileUtils.listFiles(tempDir, new String[] { "\\.deletetester" }, false); + Collection files = FileUtils.listFiles(tempDir, new String[] { "\\.deletetester" }, false); + assertNotNull(files); + max = Math.max(max, files.size()); } } catch (final Exception e) { + System.out.printf("List size max %,d%n", max); fail("IO-856 test failure: " + e, e); // The exception can be hidden. e.printStackTrace(); } + // System.out.printf("List size max %,d%n", max); }); // wait for the threads to finish c1.get();