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();

View File

@@ -15,22 +15,46 @@
*/
package org.springframework.integration.ftp.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.integration.file.config.RemoteFileOutboundChannelAdapterParser;
import org.springframework.integration.file.remote.RemoteFileOperations;
import org.springframework.integration.ftp.outbound.FtpMessageHandler;
import org.springframework.integration.ftp.session.FtpRemoteFileTemplate;
/**
* Parser for FTP Outbound Channel Adapters.
*
* @author Gary Russell
* @author Artem Bilan
* @since 4.1
*
*/
public class FtpOutboundChannelAdapterParser extends RemoteFileOutboundChannelAdapterParser {
@Override
protected Class<?> handlerClass() {
return FtpMessageHandler.class;
}
@Override
protected Class<? extends RemoteFileOperations<?>> getTemplateClass() {
return FtpRemoteFileTemplate.class;
}
@Override
protected void postProcessBuilder(BeanDefinitionBuilder builder, Element element) {
BeanDefinition templateDefinition = (BeanDefinition) builder.getRawBeanDefinition()
.getConstructorArgumentValues()
.getIndexedArgumentValues()
.values()
.iterator()
.next()
.getValue();
templateDefinition.getPropertyValues()
.add("existsMode", FtpRemoteFileTemplate.ExistsMode.NLST);
}
}

View File

@@ -15,6 +15,10 @@
*/
package org.springframework.integration.ftp.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.integration.file.config.AbstractRemoteFileOutboundGatewayParser;
import org.springframework.integration.file.remote.RemoteFileOperations;
import org.springframework.integration.ftp.filters.FtpRegexPatternFileListFilter;
@@ -50,4 +54,17 @@ public class FtpOutboundGatewayParser extends AbstractRemoteFileOutboundGatewayP
return FtpRemoteFileTemplate.class;
}
@Override
protected void postProcessBuilder(BeanDefinitionBuilder builder, Element element) {
BeanDefinition templateDefinition = (BeanDefinition) builder.getRawBeanDefinition()
.getConstructorArgumentValues()
.getIndexedArgumentValues()
.values()
.iterator()
.next()
.getValue();
templateDefinition.getPropertyValues()
.add("existsMode", FtpRemoteFileTemplate.ExistsMode.NLST);
}
}

View File

@@ -28,6 +28,7 @@ import org.springframework.integration.file.remote.RemoteFileTemplate;
import org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway;
import org.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.integration.ftp.session.FtpFileInfo;
import org.springframework.integration.ftp.session.FtpRemoteFileTemplate;
/**
* Outbound Gateway for performing remote file operations via FTP/FTPS.
@@ -46,7 +47,8 @@ public class FtpOutboundGateway extends AbstractRemoteFileOutboundGateway<FTPFil
*/
public FtpOutboundGateway(SessionFactory<FTPFile> sessionFactory,
MessageSessionCallback<FTPFile, ?> messageSessionCallback) {
super(sessionFactory, messageSessionCallback);
this(new FtpRemoteFileTemplate(sessionFactory), messageSessionCallback);
((FtpRemoteFileTemplate) this.remoteFileTemplate).setExistsMode(FtpRemoteFileTemplate.ExistsMode.NLST);
}
/**
@@ -68,7 +70,8 @@ public class FtpOutboundGateway extends AbstractRemoteFileOutboundGateway<FTPFil
* @param expression the filename expression.
*/
public FtpOutboundGateway(SessionFactory<FTPFile> sessionFactory, String command, String expression) {
super(sessionFactory, command, expression);
this(new FtpRemoteFileTemplate(sessionFactory), command, expression);
((FtpRemoteFileTemplate) this.remoteFileTemplate).setExistsMode(FtpRemoteFileTemplate.ExistsMode.NLST);
}
/**

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.ftp.outbound;
import org.apache.commons.net.ftp.FTPFile;
import org.springframework.integration.file.remote.RemoteFileTemplate;
import org.springframework.integration.file.remote.handler.FileTransferringMessageHandler;
import org.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.integration.file.support.FileExistsMode;
import org.springframework.integration.ftp.session.FtpRemoteFileTemplate;
/**
* The FTP specific {@link FileTransferringMessageHandler} extension.
* Based on the {@link FtpRemoteFileTemplate}.
*
* @author Artem Bilan
* @since 4.1.9
* @see FtpRemoteFileTemplate
*/
public class FtpMessageHandler extends FileTransferringMessageHandler<FTPFile> {
public FtpMessageHandler(SessionFactory<FTPFile> sessionFactory) {
this(new FtpRemoteFileTemplate(sessionFactory));
((FtpRemoteFileTemplate) this.remoteFileTemplate).setExistsMode(FtpRemoteFileTemplate.ExistsMode.NLST);
}
public FtpMessageHandler(FtpRemoteFileTemplate remoteFileTemplate) {
super(remoteFileTemplate);
}
public FtpMessageHandler(RemoteFileTemplate<FTPFile> remoteFileTemplate, FileExistsMode mode) {
super(remoteFileTemplate, mode);
}
}

View File

@@ -0,0 +1,4 @@
/**
* Provides classes for the FTP outbound channel adapter.
*/
package org.springframework.integration.ftp.outbound;

View File

@@ -26,17 +26,22 @@ import org.springframework.integration.file.remote.SessionCallback;
import org.springframework.integration.file.remote.session.Session;
import org.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.messaging.MessagingException;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* FTP version of {@code RemoteFileTemplate} providing type-safe access to
* the underlying FTPClient object.
*
* @author Gary Russell
* @author Artem Bilan
* @since 4.1
*
*/
public class FtpRemoteFileTemplate extends RemoteFileTemplate<FTPFile> {
private ExistsMode existsMode = ExistsMode.STAT;
public FtpRemoteFileTemplate(SessionFactory<FTPFile> sessionFactory) {
super(sessionFactory);
}
@@ -47,6 +52,19 @@ public class FtpRemoteFileTemplate extends RemoteFileTemplate<FTPFile> {
return doExecuteWithClient((ClientCallback<FTPClient, T>) callback);
}
/**
* Specify an {@link ExistsMode} for {@link #exists(String)} operation.
* Defaults to {@link ExistsMode#STAT}.
* When used internally by framework components for file operation,
* switched to {@link ExistsMode#NLST}.
* @param existsMode the {@link ExistsMode} to use.
* @since 4.1.9
*/
public void setExistsMode(ExistsMode existsMode) {
Assert.notNull(existsMode, "'existsMode' must not be null.");
this.existsMode = existsMode;
}
protected <T> T doExecuteWithClient(final ClientCallback<FTPClient, T> callback) {
return execute(new SessionCallback<FTPFile, T>() {
@@ -57,21 +75,76 @@ public class FtpRemoteFileTemplate extends RemoteFileTemplate<FTPFile> {
});
}
/**
* This particular FTP implementation is based on the {@link FTPClient#getStatus(String)}
* by default, but since not all FTP servers properly implement the {@code STAT} command,
* the framework internal {@link FtpRemoteFileTemplate} instances are switched to the
* {@link FTPClient#listNames(String)} for only files operations.
* <p> The mode can be switched with the {@link #setExistsMode(ExistsMode)} property.
* <p> Any custom implementation can be done in an extension of the {@link FtpRemoteFileTemplate}.
* @param path the remote file path to check.
* @return true or false if remote file exists or not.
*/
@Override
public boolean exists(final String path) {
return executeWithClient(new ClientCallback<FTPClient, Boolean>() {
return doExecuteWithClient(new ClientCallback<FTPClient, Boolean>() {
@Override
public Boolean doWithClient(FTPClient client) {
try {
return client.getStatus(path) != null;
switch (FtpRemoteFileTemplate.this.existsMode) {
case STAT:
return client.getStatus(path) != null;
case NLST:
String[] names = client.listNames(path);
return !ObjectUtils.isEmpty(names);
case NLST_AND_DIRS:
return FtpRemoteFileTemplate.this.sessionFactory.getSession().exists(path);
default:
throw new IllegalStateException("Unsupported 'existsMode': " +
FtpRemoteFileTemplate.this.existsMode);
}
}
catch (IOException e) {
throw new MessagingException("Failed to stat " + path, e);
throw new MessagingException("Failed to check the remote path for " + path, e);
}
}
});
}
/**
* The {@link #exists(String)} operation mode.
* @since 4.1.9
*/
public enum ExistsMode {
/**
* Perform the {@code STAT} FTP command.
* Default.
*/
STAT,
/**
* Perform the {@code NLST} FTP command.
* Used as default internally by framework components for files only operations.
*/
NLST,
/**
* Perform the {@code NLST} FTP command and fall back to
* {@link FTPClient#changeWorkingDirectory(String)}.
* <p> This technique is required when you want to check if a directory exists
* and the server does not support {@code STAT} - it requires 4 requests/replies.
* <p> If you are only checking for an existing file, {@code NLST} is preferred
* (unless {@code STAT} is supported).
* @see FtpSession#exists(String)
*/
NLST_AND_DIRS
}
}

View File

@@ -29,6 +29,7 @@ import org.apache.commons.net.ftp.FTPReply;
import org.springframework.integration.file.remote.session.Session;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* Implementation of {@link Session} for FTP.
@@ -195,17 +196,21 @@ public class FtpSession implements Session<FTPFile> {
public boolean exists(String path) throws IOException{
Assert.hasText(path, "'path' must not be empty");
String currentWorkingPath = this.client.printWorkingDirectory();
Assert.state(currentWorkingPath != null, "working directory cannot be determined, therefore exists check can not be completed");
boolean exists = false;
String[] names = this.client.listNames(path);
boolean exists = !ObjectUtils.isEmpty(names);
try {
if (this.client.changeWorkingDirectory(path)) {
exists = true;
if (!exists) {
String currentWorkingPath = this.client.printWorkingDirectory();
Assert.state(currentWorkingPath != null,
"working directory cannot be determined; exists check can not be completed");
try {
exists = this.client.changeWorkingDirectory(path);
}
}
finally {
this.client.changeWorkingDirectory(currentWorkingPath);
finally {
this.client.changeWorkingDirectory(currentWorkingPath);
}
}
return exists;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,6 +38,7 @@ import org.springframework.integration.file.remote.handler.FileTransferringMessa
import org.springframework.integration.file.remote.session.CachingSessionFactory;
import org.springframework.integration.file.support.FileExistsMode;
import org.springframework.integration.ftp.session.DefaultFtpSessionFactory;
import org.springframework.integration.ftp.session.FtpRemoteFileTemplate;
import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.support.MessageBuilder;
@@ -91,7 +92,8 @@ public class FtpOutboundChannelAdapterParserTests {
assertEquals(ftpChannel, TestUtils.getPropertyValue(ftpOutbound, "inputChannel"));
assertEquals("ftpOutbound", ftpOutbound.getComponentName());
FileTransferringMessageHandler<?> handler = TestUtils.getPropertyValue(ftpOutbound, "handler", FileTransferringMessageHandler.class);
String remoteFileSeparator = (String) TestUtils.getPropertyValue(handler, "remoteFileTemplate.remoteFileSeparator");
String remoteFileSeparator = (String) TestUtils.getPropertyValue(handler,
"remoteFileTemplate.remoteFileSeparator");
assertNotNull(remoteFileSeparator);
assertEquals(".foo", TestUtils.getPropertyValue(handler, "remoteFileTemplate.temporaryFileSuffix", String.class));
assertEquals("", remoteFileSeparator);
@@ -99,6 +101,8 @@ public class FtpOutboundChannelAdapterParserTests {
assertEquals("UTF-8", TestUtils.getPropertyValue(handler, "remoteFileTemplate.charset"));
assertNotNull(TestUtils.getPropertyValue(handler, "remoteFileTemplate.directoryExpressionProcessor"));
assertNotNull(TestUtils.getPropertyValue(handler, "remoteFileTemplate.temporaryDirectoryExpressionProcessor"));
assertEquals(FtpRemoteFileTemplate.ExistsMode.NLST,
TestUtils.getPropertyValue(handler, "remoteFileTemplate.existsMode"));
Object sfProperty = TestUtils.getPropertyValue(handler, "remoteFileTemplate.sessionFactory");
assertEquals(DefaultFtpSessionFactory.class, sfProperty.getClass());
DefaultFtpSessionFactory sessionFactory = (DefaultFtpSessionFactory) sfProperty;

View File

@@ -41,6 +41,7 @@ import org.springframework.integration.file.remote.gateway.AbstractRemoteFileOut
import org.springframework.integration.file.remote.session.CachingSessionFactory;
import org.springframework.integration.file.support.FileExistsMode;
import org.springframework.integration.ftp.gateway.FtpOutboundGateway;
import org.springframework.integration.ftp.session.FtpRemoteFileTemplate;
import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.support.MessageBuilder;
@@ -93,7 +94,7 @@ public class FtpOutboundGatewayParserTests {
assertNotNull(TestUtils.getPropertyValue(gateway, "remoteFileTemplate.sessionFactory"));
assertNotNull(TestUtils.getPropertyValue(gateway, "outputChannel"));
assertEquals("local-test-dir", TestUtils.getPropertyValue(gateway, "localDirectoryExpression.literalValue"));
assertFalse((Boolean) TestUtils.getPropertyValue(gateway, "autoCreateLocalDirectory"));
assertFalse(TestUtils.getPropertyValue(gateway, "autoCreateLocalDirectory", Boolean.class));
assertNotNull(TestUtils.getPropertyValue(gateway, "filter"));
assertEquals(Command.LS, TestUtils.getPropertyValue(gateway, "command"));
@@ -105,7 +106,8 @@ public class FtpOutboundGatewayParserTests {
Long sendTimeout = TestUtils.getPropertyValue(gateway, "messagingTemplate.sendTimeout", Long.class);
assertEquals(Long.valueOf(777), sendTimeout);
assertTrue(TestUtils.getPropertyValue(gateway, "requiresReply", Boolean.class));
assertThat(TestUtils.getPropertyValue(gateway, "mputFilter"), Matchers.instanceOf(RegexPatternFileListFilter.class));
assertThat(TestUtils.getPropertyValue(gateway, "mputFilter"),
Matchers.instanceOf(RegexPatternFileListFilter.class));
assertEquals(FileExistsMode.APPEND, TestUtils.getPropertyValue(gateway, "fileExistsMode"));
}
@@ -115,10 +117,13 @@ public class FtpOutboundGatewayParserTests {
"handler", FtpOutboundGateway.class);
assertEquals("X", TestUtils.getPropertyValue(gateway, "remoteFileTemplate.remoteFileSeparator"));
assertNotNull(TestUtils.getPropertyValue(gateway, "remoteFileTemplate.sessionFactory"));
assertTrue(TestUtils.getPropertyValue(gateway, "remoteFileTemplate.sessionFactory") instanceof CachingSessionFactory);
assertThat(TestUtils.getPropertyValue(gateway, "remoteFileTemplate.sessionFactory"),
Matchers.instanceOf(CachingSessionFactory.class));
assertEquals(FtpRemoteFileTemplate.ExistsMode.NLST,
TestUtils.getPropertyValue(gateway, "remoteFileTemplate.existsMode"));
assertNotNull(TestUtils.getPropertyValue(gateway, "outputChannel"));
assertEquals("local-test-dir", TestUtils.getPropertyValue(gateway, "localDirectoryExpression.literalValue"));
assertFalse((Boolean) TestUtils.getPropertyValue(gateway, "autoCreateLocalDirectory"));
assertFalse(TestUtils.getPropertyValue(gateway, "autoCreateLocalDirectory", Boolean.class));
assertEquals(Command.GET, TestUtils.getPropertyValue(gateway, "command"));
@SuppressWarnings("unchecked")
Set<String> options = TestUtils.getPropertyValue(gateway, "options", Set.class);

View File

@@ -348,13 +348,14 @@ public class FtpServerOutboundTests {
@Test
public void testInt3412FileMode() {
FtpRemoteFileTemplate template = new FtpRemoteFileTemplate(ftpSessionFactory);
assertFalse(template.exists("ftpTarget/appending.txt"));
Message<String> m = MessageBuilder.withPayload("foo")
.setHeader(FileHeaders.FILENAME, "appending.txt")
.build();
appending.send(m);
appending.send(m);
FtpRemoteFileTemplate template = new FtpRemoteFileTemplate(ftpSessionFactory);
assertLength6(template);
ignoring.send(m);

View File

@@ -71,7 +71,7 @@ public class FtpRemoteFileTemplateTests {
}
@Test
public void testINT3412AppendStatRmdir() {
public void testINT3412AppendStatRmdir() throws IOException {
FtpRemoteFileTemplate template = new FtpRemoteFileTemplate(sessionFactory);
DefaultFileNameGenerator fileNameGenerator = new DefaultFileNameGenerator();
fileNameGenerator.setExpression("'foobar.txt'");
@@ -114,7 +114,7 @@ public class FtpRemoteFileTemplateTests {
assertTrue(session.rmdir("foo/"));
}
});
assertFalse(template.exists("foo"));
assertFalse(sessionFactory.getSession().exists("foo"));
}
@Test

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014 the original author or authors.
* Copyright 2014-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,6 +17,7 @@ package org.springframework.integration.sftp.config;
import org.springframework.integration.file.config.RemoteFileOutboundChannelAdapterParser;
import org.springframework.integration.file.remote.RemoteFileOperations;
import org.springframework.integration.sftp.outbound.SftpMessageHandler;
import org.springframework.integration.sftp.session.SftpRemoteFileTemplate;
/**
@@ -28,6 +29,12 @@ import org.springframework.integration.sftp.session.SftpRemoteFileTemplate;
*/
public class SftpOutboundChannelAdapterParser extends RemoteFileOutboundChannelAdapterParser {
@Override
protected Class<?> handlerClass() {
return SftpMessageHandler.class;
}
@Override
protected Class<? extends RemoteFileOperations<?>> getTemplateClass() {
return SftpRemoteFileTemplate.class;

View File

@@ -27,6 +27,7 @@ import org.springframework.integration.file.remote.RemoteFileTemplate;
import org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway;
import org.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.integration.sftp.session.SftpFileInfo;
import org.springframework.integration.sftp.session.SftpRemoteFileTemplate;
import com.jcraft.jsch.ChannelSftp.LsEntry;
@@ -47,7 +48,7 @@ public class SftpOutboundGateway extends AbstractRemoteFileOutboundGateway<LsEnt
*/
public SftpOutboundGateway(SessionFactory<LsEntry> sessionFactory,
MessageSessionCallback<LsEntry, ?> messageSessionCallback) {
super(sessionFactory, messageSessionCallback);
this(new SftpRemoteFileTemplate(sessionFactory), messageSessionCallback);
}
/**
@@ -69,7 +70,7 @@ public class SftpOutboundGateway extends AbstractRemoteFileOutboundGateway<LsEnt
* @param expression the filename expression.
*/
public SftpOutboundGateway(SessionFactory<LsEntry> sessionFactory, String command, String expression) {
super(sessionFactory, command, expression);
this(new SftpRemoteFileTemplate(sessionFactory), command, expression);
}
/**

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.sftp.outbound;
import org.springframework.integration.file.remote.handler.FileTransferringMessageHandler;
import org.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.integration.file.support.FileExistsMode;
import org.springframework.integration.sftp.session.SftpRemoteFileTemplate;
import com.jcraft.jsch.ChannelSftp.LsEntry;
/**
* Subclass of {@link FileTransferringMessageHandler} for SFTP.
*
* @author Gary Russell
* @since 4.3
*
*/
public class SftpMessageHandler extends FileTransferringMessageHandler<LsEntry> {
/**
* @param remoteFileTemplate the template.
* @see FileTransferringMessageHandler#FileTransferringMessageHandler
* (org.springframework.integration.file.remote.RemoteFileTemplate)
*/
public SftpMessageHandler(SftpRemoteFileTemplate remoteFileTemplate) {
super(remoteFileTemplate);
}
/**
*
* @param remoteFileTemplate the template.
* @param mode the file exists mode.
* @see FileTransferringMessageHandler#FileTransferringMessageHandler
* (org.springframework.integration.file.remote.RemoteFileTemplate, FileExistsMode)
*/
public SftpMessageHandler(SftpRemoteFileTemplate remoteFileTemplate, FileExistsMode mode) {
super(remoteFileTemplate, mode);
}
/**
* @param sessionFactory the session factory.
* @see FileTransferringMessageHandler#FileTransferringMessageHandler
* (SessionFactory)
*/
public SftpMessageHandler(SessionFactory<LsEntry> sessionFactory) {
this(new SftpRemoteFileTemplate(sessionFactory));
}
}

View File

@@ -0,0 +1,4 @@
/**
* Provides classes for the SFTP outbound channel adapter.
*/
package org.springframework.integration.sftp.outbound;

View File

@@ -25,7 +25,6 @@ import org.springframework.integration.file.remote.session.SessionFactory;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import com.jcraft.jsch.SftpException;
/**
* SFTP version of {@code RemoteFileTemplate} providing type-safe access to
@@ -57,22 +56,4 @@ public class SftpRemoteFileTemplate extends RemoteFileTemplate<LsEntry> {
});
}
@Override
public boolean exists(final String path) {
return executeWithClient(new ClientCallback<ChannelSftp, Boolean>() {
@Override
public Boolean doWithClient(ChannelSftp client) {
try {
return client.stat(path) != null;
}
catch (SftpException e) {
return false;
}
}
});
}
}