diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/config/AbstractRemoteFileOutboundGatewayParser.java b/spring-integration-file/src/main/java/org/springframework/integration/file/config/AbstractRemoteFileOutboundGatewayParser.java index d23f377baf..fa37d618ee 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/config/AbstractRemoteFileOutboundGatewayParser.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/config/AbstractRemoteFileOutboundGatewayParser.java @@ -78,9 +78,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); diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/config/RemoteFileOutboundChannelAdapterParser.java b/spring-integration-file/src/main/java/org/springframework/integration/file/config/RemoteFileOutboundChannelAdapterParser.java index f1af695d3e..bf39a3c034 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/config/RemoteFileOutboundChannelAdapterParser.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/config/RemoteFileOutboundChannelAdapterParser.java @@ -39,7 +39,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()); @@ -49,9 +49,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> getTemplateClass(); } diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/remote/RemoteFileTemplate.java b/spring-integration-file/src/main/java/org/springframework/integration/file/remote/RemoteFileTemplate.java index 926e151e71..053ac0294a 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/remote/RemoteFileTemplate.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/remote/RemoteFileTemplate.java @@ -269,8 +269,14 @@ public class RemoteFileTemplate implements RemoteFileOperations, 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() { + + @Override + public Boolean doInSession(Session session) throws IOException { + return session.exists(path); + } + }); } @Override diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/remote/gateway/AbstractRemoteFileOutboundGateway.java b/spring-integration-file/src/main/java/org/springframework/integration/file/remote/gateway/AbstractRemoteFileOutboundGateway.java index 83825d586a..795fee88be 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/remote/gateway/AbstractRemoteFileOutboundGateway.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/remote/gateway/AbstractRemoteFileOutboundGateway.java @@ -62,7 +62,7 @@ import org.springframework.util.StringUtils; */ public abstract class AbstractRemoteFileOutboundGateway extends AbstractReplyProducingMessageHandler { - private final RemoteFileTemplate remoteFileTemplate; + protected final RemoteFileTemplate remoteFileTemplate; protected final Command command; diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/remote/handler/FileTransferringMessageHandler.java b/spring-integration-file/src/main/java/org/springframework/integration/file/remote/handler/FileTransferringMessageHandler.java index 1903239fe3..df3decf4bd 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/remote/handler/FileTransferringMessageHandler.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/remote/handler/FileTransferringMessageHandler.java @@ -38,7 +38,7 @@ import org.springframework.util.Assert; */ public class FileTransferringMessageHandler extends AbstractMessageHandler { - private final RemoteFileTemplate remoteFileTemplate; + protected final RemoteFileTemplate remoteFileTemplate; private final FileExistsMode mode; diff --git a/spring-integration-file/src/main/java/org/springframework/integration/file/remote/session/Session.java b/spring-integration-file/src/main/java/org/springframework/integration/file/remote/session/Session.java index 4549c2e7b1..3da476a5eb 100644 --- a/spring-integration-file/src/main/java/org/springframework/integration/file/remote/session/Session.java +++ b/spring-integration-file/src/main/java/org/springframework/integration/file/remote/session/Session.java @@ -66,6 +66,12 @@ public interface Session { 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; diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/remote/RemoteFileTemplateTests.java b/spring-integration-file/src/test/java/org/springframework/integration/file/remote/RemoteFileTemplateTests.java index 5094707134..1b9aef48ca 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/remote/RemoteFileTemplateTests.java +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/remote/RemoteFileTemplateTests.java @@ -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 sessionFactory = mock(SessionFactory.class); - this.template = new RemoteFileTemplate(sessionFactory) { - - @Override - public boolean exists(String path) { - try { - return sessionFactory.getSession().exists(path); - } - catch (IOException e) { - return false; - } - } - - }; + this.template = new RemoteFileTemplate(sessionFactory); this.template.setRemoteDirectoryExpression(new LiteralExpression("/foo")); this.template.setBeanFactory(mock(BeanFactory.class)); this.template.afterPropertiesSet(); diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpOutboundChannelAdapterParser.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpOutboundChannelAdapterParser.java index 201206352b..391bd6dc28 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpOutboundChannelAdapterParser.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpOutboundChannelAdapterParser.java @@ -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> 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); + } + } diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpOutboundGatewayParser.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpOutboundGatewayParser.java index 47d4d64b8d..33f9424be2 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpOutboundGatewayParser.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpOutboundGatewayParser.java @@ -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); + } + } diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/gateway/FtpOutboundGateway.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/gateway/FtpOutboundGateway.java index 0a80125af6..f0e80bce35 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/gateway/FtpOutboundGateway.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/gateway/FtpOutboundGateway.java @@ -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.ftp.session.FtpFileInfo; +import org.springframework.integration.ftp.session.FtpRemoteFileTemplate; /** * Outbound Gateway for performing remote file operations via FTP/FTPS. @@ -38,7 +39,8 @@ public class FtpOutboundGateway extends AbstractRemoteFileOutboundGateway sessionFactory, String command, String expression) { - super(sessionFactory, command, expression); + this(new FtpRemoteFileTemplate(sessionFactory), command, expression); + ((FtpRemoteFileTemplate) this.remoteFileTemplate).setExistsMode(FtpRemoteFileTemplate.ExistsMode.NLST); } public FtpOutboundGateway(RemoteFileTemplate remoteFileTemplate, String command, String expression) { diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/outbound/FtpMessageHandler.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/outbound/FtpMessageHandler.java new file mode 100644 index 0000000000..dddb1e3c75 --- /dev/null +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/outbound/FtpMessageHandler.java @@ -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 { + + public FtpMessageHandler(SessionFactory sessionFactory) { + this(new FtpRemoteFileTemplate(sessionFactory)); + ((FtpRemoteFileTemplate) this.remoteFileTemplate).setExistsMode(FtpRemoteFileTemplate.ExistsMode.NLST); + } + + public FtpMessageHandler(FtpRemoteFileTemplate remoteFileTemplate) { + super(remoteFileTemplate); + } + + public FtpMessageHandler(RemoteFileTemplate remoteFileTemplate, FileExistsMode mode) { + super(remoteFileTemplate, mode); + } + +} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/outbound/package-info.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/outbound/package-info.java new file mode 100644 index 0000000000..196ff128f7 --- /dev/null +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/outbound/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides classes for the FTP outbound channel adapter. + */ +package org.springframework.integration.ftp.outbound; diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/session/FtpRemoteFileTemplate.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/session/FtpRemoteFileTemplate.java index 2f0c61ffba..20cb1043f3 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/session/FtpRemoteFileTemplate.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/session/FtpRemoteFileTemplate.java @@ -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 { + private ExistsMode existsMode = ExistsMode.STAT; + public FtpRemoteFileTemplate(SessionFactory sessionFactory) { super(sessionFactory); } @@ -47,6 +52,19 @@ public class FtpRemoteFileTemplate extends RemoteFileTemplate { return doExecuteWithClient((ClientCallback) 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 doExecuteWithClient(final ClientCallback callback) { return execute(new SessionCallback() { @@ -57,21 +75,76 @@ public class FtpRemoteFileTemplate extends RemoteFileTemplate { }); } + /** + * 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. + *

The mode can be switched with the {@link #setExistsMode(ExistsMode)} property. + *

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() { + return doExecuteWithClient(new ClientCallback() { @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)}. + *

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. + *

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 + } } diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/session/FtpSession.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/session/FtpSession.java index c2f85dc419..18be15dda0 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/session/FtpSession.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/session/FtpSession.java @@ -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. @@ -192,17 +193,21 @@ public class FtpSession implements Session { 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; diff --git a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundChannelAdapterParserTests.java b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundChannelAdapterParserTests.java index 2cf3627fbc..b522a97ed3 100644 --- a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundChannelAdapterParserTests.java +++ b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundChannelAdapterParserTests.java @@ -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; diff --git a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundGatewayParserTests.java b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundGatewayParserTests.java index 50e9e35e3d..79fb15ae7a 100644 --- a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundGatewayParserTests.java +++ b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/config/FtpOutboundGatewayParserTests.java @@ -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 options = TestUtils.getPropertyValue(gateway, "options", Set.class); diff --git a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/outbound/FtpServerOutboundTests.java b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/outbound/FtpServerOutboundTests.java index 4393df6cb0..2fdba8a6fc 100644 --- a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/outbound/FtpServerOutboundTests.java +++ b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/outbound/FtpServerOutboundTests.java @@ -21,6 +21,7 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; @@ -321,13 +322,14 @@ public class FtpServerOutboundTests { @Test public void testInt3412FileMode() { + FtpRemoteFileTemplate template = new FtpRemoteFileTemplate(ftpSessionFactory); + assertFalse(template.exists("ftpTarget/appending.txt")); Message 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); diff --git a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/session/FtpRemoteFileTemplateTests.java b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/session/FtpRemoteFileTemplateTests.java index e8f4dd96af..927d9dbc0e 100644 --- a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/session/FtpRemoteFileTemplateTests.java +++ b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/session/FtpRemoteFileTemplateTests.java @@ -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 diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpOutboundChannelAdapterParser.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpOutboundChannelAdapterParser.java index ff6344e6fb..6c346d2a00 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpOutboundChannelAdapterParser.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/config/SftpOutboundChannelAdapterParser.java @@ -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> getTemplateClass() { return SftpRemoteFileTemplate.class; diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/gateway/SftpOutboundGateway.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/gateway/SftpOutboundGateway.java index 9648eb02d9..963de43d58 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/gateway/SftpOutboundGateway.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/gateway/SftpOutboundGateway.java @@ -26,6 +26,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; @@ -38,7 +39,7 @@ import com.jcraft.jsch.ChannelSftp.LsEntry; public class SftpOutboundGateway extends AbstractRemoteFileOutboundGateway { public SftpOutboundGateway(SessionFactory sessionFactory, String command, String expression) { - super(sessionFactory, command, expression); + this(new SftpRemoteFileTemplate(sessionFactory), command, expression); } public SftpOutboundGateway(RemoteFileTemplate remoteFileTemplate, String command, String expression) { diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/outbound/SftpMessageHandler.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/outbound/SftpMessageHandler.java new file mode 100644 index 0000000000..5cb529578f --- /dev/null +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/outbound/SftpMessageHandler.java @@ -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 { + + /** + * @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 sessionFactory) { + this(new SftpRemoteFileTemplate(sessionFactory)); + } + +} diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/outbound/package-info.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/outbound/package-info.java new file mode 100644 index 0000000000..ade8d224fb --- /dev/null +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/outbound/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides classes for the SFTP outbound channel adapter. + */ +package org.springframework.integration.sftp.outbound; diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpRemoteFileTemplate.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpRemoteFileTemplate.java index 1a32b8e1e6..0bfe2dea16 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpRemoteFileTemplate.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpRemoteFileTemplate.java @@ -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 { }); } - @Override - public boolean exists(final String path) { - return executeWithClient(new ClientCallback() { - - @Override - public Boolean doWithClient(ChannelSftp client) { - try { - return client.stat(path) != null; - } - catch (SftpException e) { - return false; - } - } - }); - } - - - }