diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/remote/synchronizer/AbstractInboundFileSynchronizingMessageSource.java b/spring-integration-file/src/main/java/org/springframework/integration/file/remote/synchronizer/AbstractInboundFileSynchronizingMessageSource.java index 89c1fb799c..814dcaba7b 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/remote/synchronizer/AbstractInboundFileSynchronizingMessageSource.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/remote/synchronizer/AbstractInboundFileSynchronizingMessageSource.java @@ -20,8 +20,8 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.net.URI; -import java.util.Arrays; import java.util.Comparator; +import java.util.List; import java.util.regex.Pattern; import org.jspecify.annotations.Nullable; @@ -33,7 +33,7 @@ import org.springframework.integration.file.DirectoryScanner; import org.springframework.integration.file.FileHeaders; import org.springframework.integration.file.FileReadingMessageSource; -import org.springframework.integration.file.filters.CompositeFileListFilter; +import org.springframework.integration.file.filters.ChainFileListFilter; import org.springframework.integration.file.filters.FileListFilter; import org.springframework.integration.file.filters.FileSystemPersistentAcceptOnceFileListFilter; import org.springframework.integration.file.filters.RegexPatternFileListFilter; @@ -51,9 +51,7 @@ * from an 'inbound' adapter). *

* The base class supports configuration of whether the remote file system and - * local file system's directories should be created on start (what 'creating a - * directory' means to the specific adapter is of course implementation - * specific). + * local file system's directories should be created on start. *

* This class is to be used as a pair with an implementation of * {@link AbstractInboundFileSynchronizer}. The synchronizer must @@ -94,7 +92,8 @@ public abstract class AbstractInboundFileSynchronizingMessageSource @SuppressWarnings("NullAway.Init") private File localDirectory; - private @Nullable FileListFilter localFileListFilter; + @SuppressWarnings("NullAway.Init") + private FileListFilter localFileListFilter; /** * Whether the {@link DirectoryScanner} was explicitly set. @@ -196,9 +195,7 @@ protected void onInit() { this.fileSource.setDirectory(this.localDirectory); initFiltersAndScanner(); BeanFactory beanFactory = getBeanFactory(); - if (beanFactory != null) { - this.fileSource.setBeanFactory(beanFactory); - } + this.fileSource.setBeanFactory(beanFactory); this.fileSource.afterPropertiesSet(); this.synchronizer.afterPropertiesSet(); } @@ -263,7 +260,7 @@ public boolean isRunning() { * @param maxFetchSize the maximum files to fetch. */ @Override - public @Nullable final AbstractIntegrationMessageBuilder doReceive(int maxFetchSize) { + public final @Nullable AbstractIntegrationMessageBuilder doReceive(int maxFetchSize) { AbstractIntegrationMessageBuilder messageBuilder = this.fileSource.doReceive(); if (messageBuilder == null) { this.synchronizer.synchronizeToLocalDirectory(this.localDirectory, maxFetchSize); @@ -284,9 +281,9 @@ public boolean isRunning() { } private FileListFilter buildFilter() { - Pattern completePattern = Pattern.compile("^.*(?( - Arrays.asList(this.localFileListFilter, new RegexPatternFileListFilter(completePattern))); + var ignoreTemporaryFilesPattern = Pattern.compile("^.*(?( + List.of(new RegexPatternFileListFilter(ignoreTemporaryFilesPattern), this.localFileListFilter)); } /** diff --git a/src/reference/antora/modules/ROOT/pages/ftp/inbound.adoc b/src/reference/antora/modules/ROOT/pages/ftp/inbound.adoc index 049561093d..03e6224d07 100644 --- a/src/reference/antora/modules/ROOT/pages/ftp/inbound.adoc +++ b/src/reference/antora/modules/ROOT/pages/ftp/inbound.adoc @@ -87,7 +87,7 @@ metadata store on every update (if the store implements `Flushable`). IMPORTANT: Further, if you use a distributed `MetadataStore` (such as xref:redis.adoc#redis-metadata-store[Redis]), you can have multiple instances of the same adapter or application and be sure that each file is processed only once. -The actual local filter is a `CompositeFileListFilter` that contains the supplied filter and a pattern filter that prevents processing files that are in the process of being downloaded (based on the `temporary-file-suffix`). +The actual local filter is a `ChainFileListFilter` that contains a pattern filter that prevents processing files that are in the process of being downloaded (based on the `temporary-file-suffix`) and the supplied filter. Files are downloaded with this suffix (the default is `.writing`), and the file is renamed to its final name when the transfer is complete, making it 'visible' to the filter. The `remote-file-separator` attribute lets you configure a file separator character to use if the default '/' is not applicable for your particular environment. diff --git a/src/reference/antora/modules/ROOT/pages/sftp/inbound.adoc b/src/reference/antora/modules/ROOT/pages/sftp/inbound.adoc index 6bb6672a4e..2f2864c34c 100644 --- a/src/reference/antora/modules/ROOT/pages/sftp/inbound.adoc +++ b/src/reference/antora/modules/ROOT/pages/sftp/inbound.adoc @@ -69,7 +69,7 @@ You can handle any other use-cases by using `CompositeFileListFilter` (or `Chain The above discussion refers to filtering the files before retrieving them. Once the files have been retrieved, an additional filter is applied to the files on the file system. -By default, this is an`AcceptOnceFileListFilter`, which, as discussed in this section, retains state in memory and does not consider the file's modified time. +By default, this is an `AcceptOnceFileListFilter`, which, as discussed in this section, retains state in memory and does not consider the file's modified time. Unless your application removes files after processing, the adapter re-processes the files on disk by default after an application restarts. Also, if you configure the `filter` to use a `SftpPersistentAcceptOnceFileListFilter` and the remote file timestamp changes (causing it to be re-fetched), the default local filter does not allow this new file to be processed. @@ -86,7 +86,7 @@ metadata store on every update (if the store implements `Flushable`). IMPORTANT: Further, if you use a distributed `MetadataStore` (such as xref:redis.adoc#redis-metadata-store[Redis Metadata Store]), you can have multiple instances of the same adapter or application and be sure that one and only one instance processes a file. -The actual local filter is a `CompositeFileListFilter` that contains the supplied filter and a pattern filter that prevents processing files that are in the process of being downloaded (based on the `temporary-file-suffix`). +The actual local filter is a `ChainFileListFilter` that contains a pattern filter that prevents processing files that are in the process of being downloaded (based on the `temporary-file-suffix`) and the supplied filter. Files are downloaded with this suffix (the default is `.writing`), and the files are renamed to their final names when the transfer is complete, making them 'visible' to the filter. See the https://github.com/spring-projects/spring-integration/tree/main/spring-integration-core/src/main/resources/org/springframework/integration/config[schema] for more detail on these attributes.