INT-4046: Add FtpRemoteFileTemplate.ExistsMode

JIRA: https://jira.spring.io/browse/INT-4046

Since not all FTP servers provide proper `STAT` command implementation,
plus the `NLIST` doesn't work properly for directories cases, introduce the `FtpRemoteFileTemplate.ExistsMode`
to let:
* to perform `STAT` by default (previous) behavior;
* to switch to `NLIST` for `FtpRemoteFileTemplate` internal use;
* perform the full `NLIST` and `FTPClient.changeWorkingDirectory()` algorithm if needed.

* Improve (S)Ftp components to use proper `RemoteFileTemplate` for internal instantiation
* Introduce `FtpMessageHandler` to wrap `FtpRemoteFileTemplate` with the proper `NLIST` `ExistsMode`
* Cover `NLIST` switching from the `FtpOutboundChannelAdapterParser` and `FtpOutboundGatewayParser`
* Document the `FtpRemoteFileTemplate.ExistsMode`

* Fix typo in the recently introduced `RemoteFileOperations.getSession()` method name
* Add JavaDoc to `Session.exists()`
* Add `NLIST` support for the `FtpSession.exists()` to meet the API requirements

**Cherry-pick to 4.2.x and 4.1.x**

Addressing PR comments

Doc Polishing

Conflicts:
	spring-integration-file/src/main/java/org/springframework/integration/file/remote/AbstractRemoteFileStreamingMessageSource.java
	spring-integration-file/src/main/java/org/springframework/integration/file/remote/RemoteFileOperations.java
	spring-integration-file/src/main/java/org/springframework/integration/file/remote/RemoteFileTemplate.java
	spring-integration-ftp/src/main/java/org/springframework/integration/ftp/session/FtpSession.java
	spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundChannelAdapterParserTests.java
	spring-integration-sftp/src/main/java/org/springframework/integration/sftp/gateway/SftpOutboundGateway.java
	spring-integration-sftp/src/main/java/org/springframework/integration/sftp/outbound/SftpMessageHandler.java
	spring-integration-sftp/src/main/java/org/springframework/integration/sftp/outbound/package-info.java
This commit is contained in:
Artem Bilan
2016-06-06 12:25:26 -04:00
parent f6d6fc9e4e
commit 80e1a56653
23 changed files with 327 additions and 71 deletions

View File

@@ -84,9 +84,14 @@ public abstract class AbstractRemoteFileOutboundGatewayParser extends AbstractCo
builder.addPropertyValue("localFilenameGeneratorExpression", localFileGeneratorExpressionBuilder.getBeanDefinition());
}
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "mode", "fileExistsMode");
postProcessBuilder(builder, element);
return builder;
}
protected void postProcessBuilder(BeanDefinitionBuilder builder, Element element) {
// no-op
}
protected void configureFilter(BeanDefinitionBuilder builder, Element element, ParserContext parserContext,
String filterAttribute, String patternPrefix, String propertyName) {
String filter = element.getAttribute(filterAttribute);

View File

@@ -38,7 +38,7 @@ public abstract class RemoteFileOutboundChannelAdapterParser extends AbstractOut
@Override
protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
BeanDefinitionBuilder handlerBuilder = BeanDefinitionBuilder.genericBeanDefinition(FileTransferringMessageHandler.class);
BeanDefinitionBuilder handlerBuilder = BeanDefinitionBuilder.genericBeanDefinition(handlerClass());
BeanDefinition templateDefinition = FileParserUtils.parseRemoteFileTemplate(element, parserContext, true,
getTemplateClass());
@@ -48,9 +48,18 @@ public abstract class RemoteFileOutboundChannelAdapterParser extends AbstractOut
if (StringUtils.hasText(mode)) {
handlerBuilder.addConstructorArgValue(mode);
}
postProcessBuilder(handlerBuilder, element);
return handlerBuilder.getBeanDefinition();
}
protected Class<?> handlerClass() {
return FileTransferringMessageHandler.class;
}
protected void postProcessBuilder(BeanDefinitionBuilder builder, Element element) {
// no-op
}
protected abstract Class<? extends RemoteFileOperations<?>> getTemplateClass();
}

View File

@@ -340,8 +340,14 @@ public class RemoteFileTemplate<F> implements RemoteFileOperations<F>, Initializ
}
@Override
public boolean exists(String path) {
throw new UnsupportedOperationException("exists() is not supported by the generic template");
public boolean exists(final String path) {
return this.execute(new SessionCallback<F, Boolean>() {
@Override
public Boolean doInSession(Session<F> session) throws IOException {
return session.exists(path);
}
});
}
@Override

View File

@@ -65,7 +65,7 @@ import org.springframework.util.StringUtils;
*/
public abstract class AbstractRemoteFileOutboundGateway<F> extends AbstractReplyProducingMessageHandler {
private final RemoteFileTemplate<F> remoteFileTemplate;
protected final RemoteFileTemplate<F> remoteFileTemplate;
protected final Command command;

View File

@@ -38,7 +38,7 @@ import org.springframework.util.Assert;
*/
public class FileTransferringMessageHandler<F> extends AbstractMessageHandler {
private final RemoteFileTemplate<F> remoteFileTemplate;
protected final RemoteFileTemplate<F> remoteFileTemplate;
private final FileExistsMode mode;

View File

@@ -66,6 +66,12 @@ public interface Session<F> {
boolean isOpen();
/**
* Check if the remote file or directory exists.
* @param path the remote path.
* @return {@code true} or {@code false} if remote path exists or not.
* @throws IOException an IO exception during remote interaction.
*/
boolean exists(String path) throws IOException;
String[] listNames(String path) throws IOException;

View File

@@ -23,9 +23,15 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.Mockito;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.integration.file.remote.session.Session;
@@ -34,13 +40,6 @@ import org.springframework.integration.file.support.FileExistsMode;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.support.GenericMessage;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.Mockito;
/**
* @author Gary Russell
* @since 4.1.7
@@ -61,19 +60,7 @@ public class RemoteFileTemplateTests {
@Before
public void setUp() throws Exception {
SessionFactory<Object> sessionFactory = mock(SessionFactory.class);
this.template = new RemoteFileTemplate<Object>(sessionFactory) {
@Override
public boolean exists(String path) {
try {
return sessionFactory.getSession().exists(path);
}
catch (IOException e) {
return false;
}
}
};
this.template = new RemoteFileTemplate<Object>(sessionFactory);
this.template.setRemoteDirectoryExpression(new LiteralExpression("/foo"));
this.template.setBeanFactory(mock(BeanFactory.class));
this.template.afterPropertiesSet();