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:
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user