diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/filters/ChainFileListFilter.java b/spring-integration-file/src/main/java/org/springframework/integration/file/filters/ChainFileListFilter.java index cc9c968aa3..aba8233529 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/filters/ChainFileListFilter.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/filters/ChainFileListFilter.java @@ -23,13 +23,15 @@ import org.springframework.util.Assert; /** * The {@link CompositeFileListFilter} extension which chains the result - * of the previous filter to the next one. + * of the previous filter to the next one. If a filter in the chain returns + * an empty list, the remaining filters are not invoked. + * @param The type that will be filtered. * * @author Artem Bilan + * @author Gary Russell * * @since 4.3.7 * - * @param The type that will be filtered. */ public class ChainFileListFilter extends CompositeFileListFilter { @@ -38,6 +40,9 @@ public class ChainFileListFilter extends CompositeFileListFilter { Assert.notNull(files, "'files' should not be null"); List leftOver = Arrays.asList(files); for (FileListFilter fileFilter : this.fileFilters) { + if (leftOver.size() == 0) { + break; + } @SuppressWarnings("unchecked") F[] fileArray = (F[]) leftOver.toArray(); leftOver = fileFilter.filterFiles(fileArray); diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/CompositeFileListFilterTests.java b/spring-integration-file/src/test/java/org/springframework/integration/file/CompositeFileListFilterTests.java index ad11db8e33..57a0fc645d 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/CompositeFileListFilterTests.java +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/CompositeFileListFilterTests.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.isA; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.hamcrest.MockitoHamcrest.argThat; @@ -102,7 +103,7 @@ public class CompositeFileListFilterTests { assertEquals(noFiles, compositeFileFilter.filterFiles(new File[] { this.fileMock })); verify(fileFilterMock1).filterFiles(argThat(arrayWithSize(1))); - verify(fileFilterMock2).filterFiles(argThat(arrayWithSize(0))); + verify(fileFilterMock2, never()).filterFiles(isA(File[].class)); compositeFileFilter.close(); } diff --git a/src/reference/asciidoc/file.adoc b/src/reference/asciidoc/file.adoc index 8f916896ed..74a3ee4d20 100644 --- a/src/reference/asciidoc/file.adoc +++ b/src/reference/asciidoc/file.adoc @@ -118,6 +118,7 @@ Say we have files `a.txt`, `a.done`, and `b.txt`. The pattern filter only passes `a.txt` and `b.txt`, the "done" filter will see all three files and only pass `a.txt`. The final result of the composite filter is only `a.txt` is released. +NOTE: With the `ChainFileListFilter`, if any filter in the chain returns an empty list, the remaining filters are not invoked. Starting with _version 5.0_ an `ExpressionFileListFilter` has been introduced to allow to execute SpEL expression against file as a context evaluation root object. For this purpose all the XML components for file handling (local and remote), alongside with an existing `filter` attribute, have been supplied with the `filter-expression` option: