INT-4334: Adds a DirectoryScanner to RemoteFileMS

JIRA: https://jira.springsource.org/browse/INT-4334

* Add a `DirectoryScanner` setter named `setScanner` in
`AbstractInboundFileSynchronizingMessageSource` to set the
`FileReadingMessageSource`'s scanner.
* Add tests to `AbstractRemoteFileSynchronizerTests`.

Polish.

Updates xsd, tests and documentation.

Fix xsd formatting.

Addresses review comments.

* Some code style polishing
This commit is contained in:
Venil Noronha
2017-09-18 00:51:37 -07:00
committed by Artem Bilan
parent 2427a7d426
commit 797e5045bf
10 changed files with 140 additions and 25 deletions

View File

@@ -37,6 +37,7 @@ import org.springframework.util.StringUtils;
* @author Mark Fisher
* @author Gary Russell
* @author Artem Bilan
* @author Venil Noronha
*
* @since 2.0
*/
@@ -75,6 +76,7 @@ public abstract class AbstractRemoteFileInboundChannelAdapterParser extends Abst
messageSourceBuilder.addConstructorArgReference(comparator);
}
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(messageSourceBuilder, element, "local-filter");
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(messageSourceBuilder, element, "scanner");
IntegrationNamespaceUtils.setValueIfAttributeDefined(messageSourceBuilder, element, "local-directory");
IntegrationNamespaceUtils.setValueIfAttributeDefined(messageSourceBuilder, element,
"auto-create-local-directory");

View File

@@ -26,6 +26,7 @@ import java.util.regex.Pattern;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.context.Lifecycle;
import org.springframework.integration.endpoint.AbstractFetchLimitingMessageSource;
import org.springframework.integration.file.DirectoryScanner;
import org.springframework.integration.file.FileReadingMessageSource;
import org.springframework.integration.file.RecursiveDirectoryScanner;
import org.springframework.integration.file.filters.AcceptOnceFileListFilter;
@@ -59,6 +60,7 @@ import org.springframework.util.Assert;
* @author Oleg Zhurakousky
* @author Gary Russell
* @author Artem Bilan
* @author Venil Noronha
*/
public abstract class AbstractInboundFileSynchronizingMessageSource<F>
extends AbstractFetchLimitingMessageSource<File> implements Lifecycle {
@@ -88,6 +90,10 @@ public abstract class AbstractInboundFileSynchronizingMessageSource<F>
private volatile FileListFilter<File> localFileListFilter;
/**
* Whether the {@link DirectoryScanner} was explicitly set.
*/
private volatile boolean scannerExplicitlySet = false;
public AbstractInboundFileSynchronizingMessageSource(AbstractInboundFileSynchronizer<F> synchronizer) {
this(synchronizer, null);
@@ -145,6 +151,17 @@ public abstract class AbstractInboundFileSynchronizingMessageSource<F>
}
}
/**
* Switch the local {@link FileReadingMessageSource} to use a custom
* {@link DirectoryScanner}.
* @param scanner the {@link DirectoryScanner} to use.
* @since 5.0
*/
public void setScanner(DirectoryScanner scanner) {
this.fileSource.setScanner(scanner);
this.scannerExplicitlySet = true;
}
@Override
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
@@ -167,7 +184,12 @@ public abstract class AbstractInboundFileSynchronizingMessageSource<F>
new SimpleMetadataStore(), getComponentName());
}
FileListFilter<File> filter = buildFilter();
if (!this.fileSource.isUseWatchService()) {
if (this.scannerExplicitlySet) {
Assert.state(!this.fileSource.isUseWatchService(),
"'useWatchService' and 'scanner' are mutually exclusive.");
this.fileSource.getScanner().setFilter(filter);
}
else if (!this.fileSource.isUseWatchService()) {
RecursiveDirectoryScanner directoryScanner = new RecursiveDirectoryScanner();
directoryScanner.setFilter(filter);
this.fileSource.setScanner(directoryScanner);

View File

@@ -33,6 +33,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.integration.file.HeadDirectoryScanner;
import org.springframework.integration.file.filters.AcceptOnceFileListFilter;
import org.springframework.integration.file.remote.session.Session;
import org.springframework.integration.file.remote.session.SessionFactory;
@@ -41,6 +42,7 @@ import org.springframework.messaging.MessagingException;
/**
* @author Gary Russell
* @author Artem Bilan
* @author Venil Noronha
*
* @since 4.0.4
*
@@ -116,21 +118,7 @@ public class AbstractRemoteFileSynchronizerTests {
public void testMaxFetchSizeSource() throws Exception {
final AtomicInteger count = new AtomicInteger();
AbstractInboundFileSynchronizer<String> sync = createLimitingSynchronizer(count);
AbstractInboundFileSynchronizingMessageSource<String> source =
new AbstractInboundFileSynchronizingMessageSource<String>(sync) {
@Override
public String getComponentType() {
return "foo";
}
};
source.setLocalDirectory(new File(System.getProperty("java.io.tmpdir") + File.separator + UUID.randomUUID()));
source.setAutoCreateLocalDirectory(true);
source.setBeanFactory(mock(BeanFactory.class));
source.setMaxFetchSize(1);
source.setBeanName("maxFetchSizeSource");
AbstractInboundFileSynchronizingMessageSource<String> source = createSource(sync);
source.afterPropertiesSet();
source.start();
@@ -143,6 +131,60 @@ public class AbstractRemoteFileSynchronizerTests {
source.stop();
}
@Test
public void testExclusiveScanner() throws Exception {
final AtomicInteger count = new AtomicInteger();
AbstractInboundFileSynchronizingMessageSource<String> source = createSource(count);
source.setScanner(new HeadDirectoryScanner(1));
source.afterPropertiesSet();
source.start();
source.receive();
assertEquals(1, count.get());
}
@Test
public void testExclusiveWatchService() throws Exception {
final AtomicInteger count = new AtomicInteger();
AbstractInboundFileSynchronizingMessageSource<String> source = createSource(count);
source.setUseWatchService(true);
source.afterPropertiesSet();
source.start();
source.receive();
assertEquals(1, count.get());
}
@Test(expected = IllegalStateException.class)
public void testScannerAndWatchServiceConflict() throws Exception {
final AtomicInteger count = new AtomicInteger();
AbstractInboundFileSynchronizingMessageSource<String> source = createSource(count);
source.setUseWatchService(true);
source.setScanner(new HeadDirectoryScanner(1));
source.afterPropertiesSet();
}
private AbstractInboundFileSynchronizingMessageSource<String> createSource(AtomicInteger count) {
return createSource(createLimitingSynchronizer(count));
}
private AbstractInboundFileSynchronizingMessageSource<String> createSource(
AbstractInboundFileSynchronizer<String> sync) {
AbstractInboundFileSynchronizingMessageSource<String> source =
new AbstractInboundFileSynchronizingMessageSource<String>(sync) {
@Override
public String getComponentType() {
return "foo";
}
};
source.setMaxFetchSize(1);
source.setLocalDirectory(new File(System.getProperty("java.io.tmpdir") + File.separator + UUID.randomUUID()));
source.setAutoCreateLocalDirectory(true);
source.setBeanFactory(mock(BeanFactory.class));
source.setBeanName("fooSource");
return source;
}
private AbstractInboundFileSynchronizer<String> createLimitingSynchronizer(final AtomicInteger count) {
SessionFactory<String> sf = new StringSessionFactory();
AbstractInboundFileSynchronizer<String> sync = new AbstractInboundFileSynchronizer<String>(sf) {
@@ -194,7 +236,7 @@ public class AbstractRemoteFileSynchronizerTests {
@Override
public String[] list(String path) throws IOException {
return new String[] {"foo", "bar", "baz"};
return new String[] { "foo", "bar", "baz" };
}
@Override