From 02faab51dc63a9c39303aa84d6bb0529846f8ff2 Mon Sep 17 00:00:00 2001 From: Josh Long Date: Sat, 28 Aug 2010 18:19:17 +0000 Subject: [PATCH] re-adding src from branch to include FTPS support. --- .../ftp/AbstractFtpClientFactory.java | 147 ++++++++++ .../integration/ftp/ClientFactorySupport.java | 85 ++++++ .../ftp/DefaultFtpClientFactory.java | 32 +++ .../ftp/DefaultFtpsClientFactory.java | 193 ++++++++++++++ .../integration/ftp/FtpClientFactory.java | 34 +++ .../integration/ftp/FtpClientPool.java | 46 ++++ .../integration/ftp/FtpFileEntryNamer.java | 16 ++ .../ftp/FtpSendingMessageHandler.java | 199 ++++++++++++++ .../FtpSendingMessageHandlerFactoryBean.java | 86 ++++++ .../FtpsSendingMessageHandlerFactoryBean.java | 89 +++++++ .../integration/ftp/QueuedFtpClientPool.java | 117 ++++++++ ...geSendingConsumerBeanDefinitionParser.java | 25 ++ .../FtpMessageSourceBeanDefinitionParser.java | 40 +++ .../ftp/config/FtpNamespaceHandler.java | 54 ++++ .../ftp/config/FtpNamespaceParserSupport.java | 38 +++ ...geSendingConsumerBeanDefinitionParser.java | 26 ++ ...FtpsMessageSourceBeanDefinitionParser.java | 39 +++ .../ftp/config/FtpsNamespaceHandler.java | 36 +++ ...tpInboundRemoteFileSystemSynchronizer.java | 118 ++++++++ ...eFileSystemSynchronizingMessageSource.java | 35 +++ ...SynchronizingMessageSourceFactoryBean.java | 173 ++++++++++++ ...SynchronizingMessageSourceFactoryBean.java | 116 ++++++++ .../src/main/resources/META-INF/MANIFEST.MF | 0 .../main/resources/META-INF/spring.handlers | 3 + .../main/resources/META-INF/spring.schemas | 4 + .../ftp/config/spring-integration-ftp-2.0.xsd | 249 +++++++++++++++++ .../config/spring-integration-ftps-2.0.xsd | 252 ++++++++++++++++++ .../integration/ftp/FtpsExample.java | 9 + .../ftp/InboundFtpFileServiceActivator.java | 34 +++ .../ftp/InboundFtpsFileServiceActivator.java | 34 +++ .../integration/ftp/OutboundFtpExample.java | 16 ++ .../integration/ftp/OutboundFtpsExample.java | 16 ++ .../test/resources/inbound-ftp-context.xml | 34 +++ .../test/resources/inbound-ftps-context.xml | 42 +++ .../test/resources/outbound-ftp-context.xml | 38 +++ .../test/resources/outbound-ftps-context.xml | 39 +++ 36 files changed, 2514 insertions(+) create mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/AbstractFtpClientFactory.java create mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/ClientFactorySupport.java create mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/DefaultFtpClientFactory.java create mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/DefaultFtpsClientFactory.java create mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpClientFactory.java create mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpClientPool.java create mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpFileEntryNamer.java create mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpSendingMessageHandler.java create mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpSendingMessageHandlerFactoryBean.java create mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpsSendingMessageHandlerFactoryBean.java create mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/QueuedFtpClientPool.java create mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpMessageSendingConsumerBeanDefinitionParser.java create mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpMessageSourceBeanDefinitionParser.java create mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpNamespaceHandler.java create mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpNamespaceParserSupport.java create mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsMessageSendingConsumerBeanDefinitionParser.java create mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsMessageSourceBeanDefinitionParser.java create mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsNamespaceHandler.java create mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/impl/FtpInboundRemoteFileSystemSynchronizer.java create mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/impl/FtpInboundRemoteFileSystemSynchronizingMessageSource.java create mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/impl/FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java create mode 100644 spring-integration-ftp/src/main/java/org/springframework/integration/ftp/impl/FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean.java create mode 100644 spring-integration-ftp/src/main/resources/META-INF/MANIFEST.MF create mode 100644 spring-integration-ftp/src/main/resources/META-INF/spring.handlers create mode 100644 spring-integration-ftp/src/main/resources/META-INF/spring.schemas create mode 100644 spring-integration-ftp/src/main/resources/org/springframework/integration/ftp/config/spring-integration-ftp-2.0.xsd create mode 100644 spring-integration-ftp/src/main/resources/org/springframework/integration/ftp/config/spring-integration-ftps-2.0.xsd create mode 100644 spring-integration-ftp/src/test/java/org/springframework/integration/ftp/FtpsExample.java create mode 100644 spring-integration-ftp/src/test/java/org/springframework/integration/ftp/InboundFtpFileServiceActivator.java create mode 100644 spring-integration-ftp/src/test/java/org/springframework/integration/ftp/InboundFtpsFileServiceActivator.java create mode 100644 spring-integration-ftp/src/test/java/org/springframework/integration/ftp/OutboundFtpExample.java create mode 100644 spring-integration-ftp/src/test/java/org/springframework/integration/ftp/OutboundFtpsExample.java create mode 100644 spring-integration-ftp/src/test/resources/inbound-ftp-context.xml create mode 100644 spring-integration-ftp/src/test/resources/inbound-ftps-context.xml create mode 100644 spring-integration-ftp/src/test/resources/outbound-ftp-context.xml create mode 100644 spring-integration-ftp/src/test/resources/outbound-ftps-context.xml diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/AbstractFtpClientFactory.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/AbstractFtpClientFactory.java new file mode 100644 index 0000000000..bbad3735fd --- /dev/null +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/AbstractFtpClientFactory.java @@ -0,0 +1,147 @@ +package org.springframework.integration.ftp; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.commons.net.ftp.FTP; +import org.apache.commons.net.ftp.FTPClient; +import org.apache.commons.net.ftp.FTPClientConfig; +import org.apache.commons.net.ftp.FTPReply; +import org.springframework.integration.MessagingException; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +import java.io.IOException; +import java.net.SocketException; + + +abstract public class AbstractFtpClientFactory implements FtpClientFactory { + private static final Log logger = LogFactory.getLog(FtpClientFactory.class); + private static final String DEFAULT_REMOTE_WORKING_DIRECTORY = "/"; + protected FTPClientConfig config; + protected String username; + protected String host; + protected String password; + protected int port = FTP.DEFAULT_PORT; + protected String remoteWorkingDirectory = DEFAULT_REMOTE_WORKING_DIRECTORY; + protected int clientMode = FTPClient.ACTIVE_LOCAL_DATA_CONNECTION_MODE; + protected int fileType = FTP.BINARY_FILE_TYPE; + + public void setFileType(int fileType) { + this.fileType = fileType; + } + + public void setConfig(FTPClientConfig config) { + Assert.notNull(config); + this.config = config; + } + + public void setHost(String host) { + Assert.hasText(host); + this.host = host; + } + + public void setPort(int port) { + Assert.isTrue(port > 0, "Port number should be > 0"); + this.port = port; + } + + public void setUsername(String user) { + Assert.hasText(user, "'user' should be a nonempty string"); + this.username = user; + } + + public void setPassword(String pass) { + Assert.notNull(pass, "password should not be null"); + this.password = pass; + } + + public void setRemoteWorkingDirectory(String remoteWorkingDirectory) { + Assert.notNull(remoteWorkingDirectory, "remote directory should not be null"); + this.remoteWorkingDirectory = remoteWorkingDirectory.replaceAll("^$", "/"); + } + + /** + * Set client mode for example + * FTPClient.ACTIVE_LOCAL_CONNECTION_MODE (default) Only local + * modes are supported. + */ + public void setClientMode(int clientMode) { + this.clientMode = clientMode; + } + + protected abstract T createSingleInstanceOfClient(); + + /** + * this is a hook to setup the state of the {@link org.apache.commons.net.ftp.FTPClient} impl *after* the + * implementation's {@link org.apache.commons.net.ftp.FTPClient#connect(String)} method's been called but before any + * action's been taken. + * + * @param t the ftp client instance on which to act + * @throws IOException if anything should go wrong + */ + protected void onAfterConnect(T t) throws IOException { + // NOOP + } + + public T getClient() throws SocketException, IOException { + T client = createSingleInstanceOfClient(); + client.configure(config); + + if (!StringUtils.hasText(username)) { + throw new MessagingException("username is required"); + } + + client.connect(host); + onAfterConnect(client); + + if (!FTPReply.isPositiveCompletion(client.getReplyCode())) { + throw new MessagingException("Connecting to server [" + host + ":" + port + "] failed, please check the connection"); + } + + if (logger.isDebugEnabled()) { + logger.debug("Connected to server [" + host + ":" + port + "]"); + } + + if (!client.login(username, password)) { + throw new MessagingException("Login failed. Please check the username and password."); + } + + setClientMode(client); + + client.setFileType(this.fileType); + + if (logger.isDebugEnabled()) { + logger.debug("login successful"); + } + + if (!remoteWorkingDirectory.equals(client.printWorkingDirectory()) && !client.changeWorkingDirectory(remoteWorkingDirectory)) { + throw new MessagingException("Could not change directory to '" + remoteWorkingDirectory + "'. Please check the path."); + } + + if (logger.isDebugEnabled()) { + logger.debug("working directory is: " + client.printWorkingDirectory()); + } + + return client; + } + + /** + * Sets the mode of the connection. Only local modes are supported. + */ + protected void setClientMode(FTPClient client) { + switch (clientMode) { + case FTPClient.ACTIVE_LOCAL_DATA_CONNECTION_MODE: + client.enterLocalActiveMode(); + + break; + + case FTPClient.PASSIVE_LOCAL_DATA_CONNECTION_MODE: + client.enterLocalPassiveMode(); + + break; + + default: + break; + } + } +} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/ClientFactorySupport.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/ClientFactorySupport.java new file mode 100644 index 0000000000..6ba29e5e87 --- /dev/null +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/ClientFactorySupport.java @@ -0,0 +1,85 @@ +package org.springframework.integration.ftp; + +import org.springframework.util.StringUtils; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.TrustManager; + + +/** + * Factors out the client factory creaton + * + * @author Josh Long + */ +public class ClientFactorySupport { + public static DefaultFtpsClientFactory ftpsClientFactory(String host, int port, String remoteDir, String user, String pw, int fileType, int clientMode, String prot, String protocol, + String authValue, Boolean implicit, TrustManager trustManager, KeyManager keyManager, Boolean sessionCreation, Boolean useClientMode, Boolean wantsClientAuth, Boolean needClientAuth , String [] cipherSuites) { + DefaultFtpsClientFactory defaultFtpClientFactory = new DefaultFtpsClientFactory(); + defaultFtpClientFactory.setHost(host); + defaultFtpClientFactory.setPassword(pw); + defaultFtpClientFactory.setPort((port)); + defaultFtpClientFactory.setRemoteWorkingDirectory(remoteDir); + defaultFtpClientFactory.setUsername(user); + defaultFtpClientFactory.setFileType(fileType); + defaultFtpClientFactory.setClientMode(clientMode); + + if(cipherSuites !=null) + defaultFtpClientFactory.setCipherSuites( cipherSuites ); + + + + if (StringUtils.hasText(prot)) { + defaultFtpClientFactory.setProt(prot); + } + + if (StringUtils.hasText(protocol)) { + defaultFtpClientFactory.setProtocol(protocol); + } + + if (StringUtils.hasText(authValue)) { + defaultFtpClientFactory.setAuthValue(authValue); + } + + if (null != implicit) { + defaultFtpClientFactory.setImplicit(implicit); + } + + if (trustManager != null) { + defaultFtpClientFactory.setTrustManager(trustManager); + } + + if (keyManager != null) { + defaultFtpClientFactory.setKeyManager(keyManager); + } + + if (needClientAuth != null) { + defaultFtpClientFactory.setNeedClientAuth(needClientAuth); + } + + if (wantsClientAuth != null) { + defaultFtpClientFactory.setWantsClientAuth(wantsClientAuth); + } + + if (sessionCreation != null) { + defaultFtpClientFactory.setSessionCreation(sessionCreation); + } + + if (useClientMode != null) { + defaultFtpClientFactory.setUseClientMode(useClientMode); + } + + return defaultFtpClientFactory; + } + + public static DefaultFtpClientFactory ftpClientFactory(String host, int port, String remoteDir, String user, String pw, int clientMode) { + DefaultFtpClientFactory defaultFtpClientFactory = new DefaultFtpClientFactory(); + defaultFtpClientFactory.setHost(host); + defaultFtpClientFactory.setPassword(pw); + defaultFtpClientFactory.setPort(port); + defaultFtpClientFactory.setRemoteWorkingDirectory(remoteDir); + defaultFtpClientFactory.setUsername(user); + defaultFtpClientFactory.setClientMode(clientMode); + + return defaultFtpClientFactory; + } +} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/DefaultFtpClientFactory.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/DefaultFtpClientFactory.java new file mode 100644 index 0000000000..c608e29578 --- /dev/null +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/DefaultFtpClientFactory.java @@ -0,0 +1,32 @@ +/* + * Copyright 2002-2008 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; + +import org.apache.commons.net.ftp.FTPClient; + + +/** + * Default implementation of FtpClientFactory. + * + * @author iwein + * @author Josh Long + */ +public class DefaultFtpClientFactory extends AbstractFtpClientFactory { + @Override + protected FTPClient createSingleInstanceOfClient() { + return new FTPClient(); + } +} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/DefaultFtpsClientFactory.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/DefaultFtpsClientFactory.java new file mode 100644 index 0000000000..9a7d539ce0 --- /dev/null +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/DefaultFtpsClientFactory.java @@ -0,0 +1,193 @@ +package org.springframework.integration.ftp; + +import org.apache.commons.lang.SystemUtils; +import org.apache.commons.net.ftp.FTPClient; +import org.apache.commons.net.ftp.FTPSClient; + +import org.springframework.beans.factory.config.PropertiesFactoryBean; + +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.Resource; + +import org.springframework.util.StringUtils; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +import java.net.SocketException; + +import java.security.NoSuchAlgorithmException; + +import java.util.Properties; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.TrustManager; + + +/** + * provides a working FTPS implementation + * + * @author Josh Long + */ +public class DefaultFtpsClientFactory extends AbstractFtpClientFactory { + private Boolean useClientMode; + private Boolean sessionCreation; + private String authValue; + private TrustManager trustManager; + private String[] cipherSuites; + private String[] protocols; + private KeyManager keyManager; + private Boolean needClientAuth; + private Boolean wantsClientAuth; + private boolean implicit = false; + private String prot = "P"; + private String protocol; + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public static void main(String[] args) throws Throwable { + File file = new File(SystemUtils.getUserHome(), "Desktop/ftp.properties"); + Resource r = new FileSystemResource(file); + PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean(); + propertiesFactoryBean.setLocation(r); + propertiesFactoryBean.afterPropertiesSet(); + + Properties props = propertiesFactoryBean.getObject(); + + String user = props.getProperty("ftp.username"); + String pw = props.getProperty("ftp.password"); + String host = props.getProperty("ftp.host"); + + if (!file.exists()) { + throw new RuntimeException("doesn't exist"); + } + + DefaultFtpsClientFactory defaultFtpsClientFactory = new DefaultFtpsClientFactory(); + defaultFtpsClientFactory.setUsername(user); + defaultFtpsClientFactory.setImplicit(false); + defaultFtpsClientFactory.setPassword(pw); + defaultFtpsClientFactory.setClientMode(FTPClient.PASSIVE_LOCAL_DATA_CONNECTION_MODE); + defaultFtpsClientFactory.setHost(host); + + FTPSClient ftpClient = defaultFtpsClientFactory.getClient(); + + InputStream fileStream = r.getInputStream(); + ftpClient.storeFile("pushed.java", fileStream); + fileStream.close(); + ftpClient.disconnect(); + } + + public void setUseClientMode(Boolean useClientMode) { + this.useClientMode = useClientMode; + } + + public void setSessionCreation(Boolean sessionCreation) { + this.sessionCreation = sessionCreation; + } + + public void setAuthValue(String authValue) { + this.authValue = authValue; + } + + public void setTrustManager(TrustManager trustManager) { + this.trustManager = trustManager; + } + + public void setCipherSuites(String[] cipherSuites) { + this.cipherSuites = cipherSuites; + } + + public void setProtocols(String[] protocols) { + this.protocols = protocols; + } + + public void setKeyManager(KeyManager keyManager) { + this.keyManager = keyManager; + } + + public void setNeedClientAuth(Boolean needClientAuth) { + this.needClientAuth = needClientAuth; + } + + public void setWantsClientAuth(Boolean wantsClientAuth) { + this.wantsClientAuth = wantsClientAuth; + } + + public void setProt(String prot) { + this.prot = prot; + } + + @Override + protected void onAfterConnect(FTPSClient ftpsClient) + throws IOException { + ftpsClient.execPBSZ(0); + ftpsClient.execPROT(this.prot); + } + + @Override + public FTPSClient getClient() throws SocketException, IOException { + FTPSClient ftpsClient = super.getClient(); + + if (StringUtils.hasText(this.authValue)) { + ftpsClient.setAuthValue(authValue); + } + + if (this.trustManager != null) { + ftpsClient.setTrustManager(this.trustManager); + } + + if (this.cipherSuites != null) { + ftpsClient.setEnabledCipherSuites(this.cipherSuites); + } + + if (this.protocols != null) { + ftpsClient.setEnabledProtocols(this.protocols); + } + + if (this.sessionCreation != null) { + ftpsClient.setEnabledSessionCreation(this.sessionCreation); + } + + if (this.useClientMode != null) { + ftpsClient.setUseClientMode(this.useClientMode); + } + + if (this.sessionCreation != null) { + ftpsClient.setEnabledSessionCreation(this.sessionCreation); + } + + if (this.keyManager != null) { + ftpsClient.setKeyManager(keyManager); + } + + if (this.needClientAuth != null) { + ftpsClient.setNeedClientAuth(this.needClientAuth); + } + + if (this.wantsClientAuth != null) { + ftpsClient.setWantClientAuth(this.wantsClientAuth); + } + + return ftpsClient; + } + + public void setImplicit(boolean implicit) { + this.implicit = implicit; + } + + @Override + protected FTPSClient createSingleInstanceOfClient() { + try { + if (StringUtils.hasText(this.protocol)) { + return new FTPSClient(this.protocol, this.implicit); + } + + return new FTPSClient(this.implicit); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } +} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpClientFactory.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpClientFactory.java new file mode 100644 index 0000000000..472c25d12a --- /dev/null +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpClientFactory.java @@ -0,0 +1,34 @@ +/* + * Copyright 2002-2008 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; + +import org.apache.commons.net.ftp.FTPClient; + +import java.io.IOException; + + +/** + * Factory for {@link FTPClient}. + * + * @author Iwein Fuld + */ +public interface FtpClientFactory { + /** + * @return Fully configured and connected FTPClient. Never null. + * @throws IOException thrown when a networking IO subsystem error occurs + */ + T getClient() throws IOException; +} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpClientPool.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpClientPool.java new file mode 100644 index 0000000000..35c7559399 --- /dev/null +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpClientPool.java @@ -0,0 +1,46 @@ +/* + * Copyright 2002-2008 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; + +import org.apache.commons.net.ftp.FTPClient; + + +/** + * A pool of {@link FTPClient} instances. The pool can be used to control the + * number of open FTP connections and reuse these connections efficiently. + * + * @author Iwein Fuld + */ +public interface FtpClientPool extends FtpClientFactory { + /** + * Releases the client back to the pool. When calling this method the caller + * is no longer responsible for the connection. The pool is free to do with + * it as it sees fit, which means either recycling or disconnecting it most + * probably. + *

+ * The caller should NOT disconnect the client before calling this method. + *

+ * The caller is NOT expected to use the client after calling this method. + * Doing so can lead to unexpected behavior. + * + * @param client the {@link FTPClient} to release. Implementations of this + * method are recommended to deal gracefully with a null + * argument, although the endpoint implementations in + * org.springframework.integration.ftp will never pass in + * null. + */ + void releaseClient(FTPClient client); +} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpFileEntryNamer.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpFileEntryNamer.java new file mode 100644 index 0000000000..12017bafea --- /dev/null +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpFileEntryNamer.java @@ -0,0 +1,16 @@ +package org.springframework.integration.ftp; + +import org.apache.commons.net.ftp.FTPFile; +import org.springframework.integration.file.entries.EntryNamer; + + +/** + * A {@link org.springframework.integration.file.entries.EntryNamer} for {@link org.apache.commons.net.ftp.FTPFile} objects + * + * @author Josh Long + */ +public class FtpFileEntryNamer implements EntryNamer { + public String nameOf(FTPFile entry) { + return entry.getName(); + } +} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpSendingMessageHandler.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpSendingMessageHandler.java new file mode 100644 index 0000000000..f6cd54ea07 --- /dev/null +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpSendingMessageHandler.java @@ -0,0 +1,199 @@ +/* + * Copyright 2002-2008 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; + +import org.apache.commons.lang.SystemUtils; +import org.apache.commons.net.ftp.FTPClient; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.Resource; +import org.springframework.integration.Message; +import org.springframework.integration.MessageDeliveryException; +import org.springframework.integration.MessageHandlingException; +import org.springframework.integration.MessageRejectedException; +import org.springframework.integration.core.MessageHandler; +import org.springframework.integration.file.DefaultFileNameGenerator; +import org.springframework.integration.file.FileNameGenerator; +import org.springframework.util.Assert; +import org.springframework.util.FileCopyUtils; + +import java.io.*; +import java.net.SocketException; + + +/** + * A {@link org.springframework.integration.core.MessageHandler} implementation that sends files to an FTP server. + * + * @author Iwein Fuld + * @author Mark Fisher + * @author Josh Long + */ +public class FtpSendingMessageHandler implements MessageHandler, InitializingBean { + + private FtpClientPool ftpClientPool; + + public FtpSendingMessageHandler() { + } + + public FtpSendingMessageHandler(FtpClientPool ftpClientPool) { + this.ftpClientPool = ftpClientPool; + } + + public void setFtpClientPool(FtpClientPool ftpClientPool) { + this.ftpClientPool = ftpClientPool; + } + + public void afterPropertiesSet() throws Exception { + Assert.notNull(ftpClientPool, "'ftpClientPool' must not be null"); + Assert.notNull(temporaryBufferFolder, "'temporaryBufferFolder' must not be null"); + temporaryBufferFolderFile = this.temporaryBufferFolder.getFile(); + } + + /* Ugh this needs to be put in a convenient place accessible for all the file:, sftp:, and ftp:* adapters */ + + private File handleFileMessage(File sourceFile, File tempFile, File resultFile) + throws IOException { + if (sourceFile.renameTo(resultFile)) { + return resultFile; + } + + FileCopyUtils.copy(sourceFile, tempFile); + tempFile.renameTo(resultFile); + + return resultFile; + } + + private File handleByteArrayMessage(byte[] bytes, File tempFile, File resultFile) + throws IOException { + FileCopyUtils.copy(bytes, tempFile); + tempFile.renameTo(resultFile); + + return resultFile; + } + + private File handleStringMessage(String content, File tempFile, File resultFile, String charset) + throws IOException { + OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(tempFile), charset); + FileCopyUtils.copy(content, writer); + tempFile.renameTo(resultFile); + + return resultFile; + } + + private static final String TEMPORARY_FILE_SUFFIX = ".writing"; + private FileNameGenerator fileNameGenerator = new DefaultFileNameGenerator(); + private File temporaryBufferFolderFile; + private Resource temporaryBufferFolder = new FileSystemResource(SystemUtils.getJavaIoTmpDir()); + + public void setTemporaryBufferFolder(Resource temporaryBufferFolder) { + this.temporaryBufferFolder = temporaryBufferFolder; + } + + public void setFileNameGenerator(FileNameGenerator fileNameGenerator) { + this.fileNameGenerator = fileNameGenerator; + } + + private File redeemForStorableFile(Message msg) throws MessageDeliveryException { + try { + Object payload = msg.getPayload(); + String generateFileName = this.fileNameGenerator.generateFileName(msg); + File tempFile = new File(temporaryBufferFolderFile, generateFileName + TEMPORARY_FILE_SUFFIX); + File resultFile = new File(temporaryBufferFolderFile, generateFileName); + File sendableFile; + if (payload instanceof String) + sendableFile = this.handleStringMessage((String) payload, tempFile, resultFile, this.charset); + else if (payload instanceof File) + sendableFile = this.handleFileMessage((File) payload, tempFile, resultFile); + else if (payload instanceof byte[]) + sendableFile = this.handleByteArrayMessage((byte[]) payload, tempFile, resultFile); + else sendableFile = null; + return sendableFile; + } catch (Throwable th) { + throw new MessageDeliveryException(msg); + } + + } + + private String charset; + + public void setCharset(String charset) { + this.charset = charset; + } + /* Ugh this needs to be put in a convenient place accessible for all the file:, sftp:, and ftp:* adapters */ + + + public void handleMessage(Message message) throws MessageRejectedException, + MessageHandlingException, MessageDeliveryException { + + + Assert.notNull(message, "'message' must not be null"); + + Object payload = message.getPayload(); + + Assert.notNull(payload, "Message payload must not be null"); + + File file = this.redeemForStorableFile(message); + + if ((file != null) && file.exists()) { + FTPClient client = null; + boolean sentSuccesfully; + + try { + client = getFtpClient(); + sentSuccesfully = sendFile(file, client); + } catch (FileNotFoundException e) { + throw new MessageDeliveryException(message, "File [" + file + "] not found in local working directory; it was moved or deleted unexpectedly", e); + } catch (IOException e) { + throw new MessageDeliveryException(message, "Error transferring file [" + file + "] from local working directory to remote FTP directory", e); + } catch (Exception e) { + throw new MessageDeliveryException(message, "Error handling message for file [" + file + "]", e); + } finally { + if (file.exists()) + try { + file.delete(); + } catch (Throwable th) { + /// noop + } + if (client != null) { + ftpClientPool.releaseClient(client); + } + } + + if (!sentSuccesfully) { + throw new MessageDeliveryException(message, "Failed to store file '" + file + "'"); + } + + } + + } + + private boolean sendFile(File file, FTPClient client) + throws FileNotFoundException, IOException { + FileInputStream fileInputStream = new FileInputStream(file); + boolean sent = client.storeFile(file.getName(), fileInputStream); + fileInputStream.close(); + + return sent; + } + + private FTPClient getFtpClient() throws SocketException, IOException { + FTPClient client; + client = this.ftpClientPool.getClient(); + Assert.state(client != null, FtpClientPool.class.getSimpleName() + " returned 'null' client this most likely a bug in the pool implementation."); + + return client; + } +} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpSendingMessageHandlerFactoryBean.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpSendingMessageHandlerFactoryBean.java new file mode 100644 index 0000000000..26782253f5 --- /dev/null +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpSendingMessageHandlerFactoryBean.java @@ -0,0 +1,86 @@ +package org.springframework.integration.ftp; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.AbstractFactoryBean; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.ResourceLoaderAware; + +import org.springframework.core.io.ResourceLoader; + + +/** + * A factory bean implementation that handles constructing an outbound FTP adapter. + * + * @author Josh Long + */ +public class FtpSendingMessageHandlerFactoryBean extends AbstractFactoryBean implements ResourceLoaderAware, ApplicationContextAware { + protected int port; + protected String username; + protected String password; + protected String host; + protected String remoteDirectory; + protected int clientMode; + + // private vars + private ResourceLoader resourceLoader; + private ApplicationContext applicationContext; + + public void setClientMode(int clientMode) { + this.clientMode = clientMode; + } + + public void setResourceLoader(ResourceLoader resourceLoader) { + this.resourceLoader = resourceLoader; + } + + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException { + this.applicationContext = applicationContext; + } + + @Override + public Class getObjectType() { + return FtpSendingMessageHandler.class; + } + + protected AbstractFtpClientFactory clientFactory() { + return ClientFactorySupport.ftpClientFactory( this.host, this.port , this.remoteDirectory , this.username , this.password , this.clientMode ); + } + + @Override + protected FtpSendingMessageHandler createInstance() + throws Exception { + // the dependencies for the outbound-adapter are much simpler + // they only require an instance of the pool + AbstractFtpClientFactory defaultFtpClientFactory = clientFactory(); + + QueuedFtpClientPool queuedFtpClientPool = new QueuedFtpClientPool(15, defaultFtpClientFactory); + + FtpSendingMessageHandler ftpSendingMessageHandler = new FtpSendingMessageHandler(queuedFtpClientPool); + ftpSendingMessageHandler.afterPropertiesSet(); + + return ftpSendingMessageHandler; + } + + public void setPort(int port) { + this.port = port; + } + + public void setUsername(String username) { + this.username = username; + } + + public void setPassword(String password) { + this.password = password; + } + + public void setHost(String host) { + this.host = host; + } + + public void setRemoteDirectory(String remoteDirectory) { + this.remoteDirectory = remoteDirectory; + } +} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpsSendingMessageHandlerFactoryBean.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpsSendingMessageHandlerFactoryBean.java new file mode 100644 index 0000000000..d74ccfb216 --- /dev/null +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/FtpsSendingMessageHandlerFactoryBean.java @@ -0,0 +1,89 @@ +package org.springframework.integration.ftp; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.TrustManager; + +public class FtpsSendingMessageHandlerFactoryBean extends FtpSendingMessageHandlerFactoryBean { + /** + * Sets whether the connection is implicit. Local testing reveals this to be a good choice. + */ + protected volatile Boolean implicit = Boolean.FALSE; + + /** + * "TLS" or "SSL" + */ + protected volatile String protocol; + + /** + * "P" + */ + protected volatile String prot; + private KeyManager keyManager; + private TrustManager trustManager; + protected volatile String authValue; + private Boolean sessionCreation; + private Boolean useClientMode; + private Boolean needClientAuth; + private Boolean wantsClientAuth; + private String[] cipherSuites; + + public void setImplicit(Boolean implicit) { + this.implicit = implicit; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public void setProt(String prot) { + this.prot = prot; + } + + public void setKeyManager(KeyManager keyManager) { + this.keyManager = keyManager; + } + + public void setTrustManager(TrustManager trustManager) { + this.trustManager = trustManager; + } + + public void setAuthValue(String authValue) { + this.authValue = authValue; + } + + public void setSessionCreation(Boolean sessionCreation) { + this.sessionCreation = sessionCreation; + } + + public void setUseClientMode(Boolean useClientMode) { + this.useClientMode = useClientMode; + } + + public void setNeedClientAuth(Boolean needClientAuth) { + this.needClientAuth = needClientAuth; + } + + public void setWantsClientAuth(Boolean wantsClientAuth) { + this.wantsClientAuth = wantsClientAuth; + } + + public void setCipherSuites(String[] cipherSuites) { + this.cipherSuites = cipherSuites; + } + + private int fileType ; + + public void setFileType(int fileType) { + this.fileType = fileType; + } + + @Override + protected AbstractFtpClientFactory clientFactory() { + DefaultFtpsClientFactory factory = ClientFactorySupport.ftpsClientFactory( + this.host, (this.port), this.remoteDirectory, this.username, this.password, this.fileType, + this.clientMode, this.prot, this.protocol, this.authValue, this.implicit, this.trustManager, this.keyManager, this.sessionCreation, this.useClientMode, this.wantsClientAuth, + this.needClientAuth, this.cipherSuites); + + return factory; + } +} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/QueuedFtpClientPool.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/QueuedFtpClientPool.java new file mode 100644 index 0000000000..50f5c21fb2 --- /dev/null +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/QueuedFtpClientPool.java @@ -0,0 +1,117 @@ +/* + * Copyright 2002-2008 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; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.commons.net.ftp.FTPClient; +import org.springframework.util.Assert; + +import java.io.IOException; +import java.net.SocketException; +import java.util.Queue; +import java.util.concurrent.ArrayBlockingQueue; + + +/** + * FtpClientPool implementation based on a Queue. This implementation has a + * default pool size of 5, but this is configurable with a constructor argument. + *

+ * This implementation pools released clients, but gives no guarantee to the + * number of clients open at the same time. + * + * @author Iwein Fuld + */ +public class QueuedFtpClientPool implements FtpClientPool { + private static final Log log = LogFactory.getLog(QueuedFtpClientPool.class); + private static final int DEFAULT_POOL_SIZE = 5; + private final Queue pool; + private final FtpClientFactory factory; + + public QueuedFtpClientPool(FtpClientFactory factory) { + this(DEFAULT_POOL_SIZE, factory); + } + + /** + * @param maxPoolSize the maximum size of the pool + */ + public QueuedFtpClientPool(int maxPoolSize, FtpClientFactory factory) { + Assert.notNull(factory); + this.factory = factory; + pool = new ArrayBlockingQueue(maxPoolSize); + } + + /** + * Returns an active FTPClient connected to the configured server. When no + * clients are available in the queue a new client is created with the + * factory. + *

+ * It is possible that released clients are disconnected by the remote + * server (@see {@link FTPClient#sendNoOp()}. In this case getClient is + * called recursively to obtain a client that is still alive. For this + * reason large pools are not recommended in poor networking conditions. + */ + public FTPClient getClient() throws SocketException, IOException { + FTPClient client = pool.poll(); + + if (client == null) { + client = factory.getClient(); + } + + return prepareClient(client); + } + + /** + * Prepares the client before it is returned through + * getClient(). The default implementation will check the + * connection using a noOp and replace the client with a new one if it + * encounters a problem. + *

+ * In more exotic environments subclasses can override this method to + * implement their own preparation strategy. + * + * @param client the unprepared client + * @return + * @throws SocketException + * @throws IOException + */ + protected FTPClient prepareClient(FTPClient client) + throws SocketException, IOException { + return isClientAlive(client) ? client : getClient(); + } + + private boolean isClientAlive(FTPClient client) { + try { + if (client.sendNoOp()) { + return true; + } + } catch (IOException e) { + log.warn("Client [" + client + "] discarded: ", e); + } + + return false; + } + + public void releaseClient(FTPClient client) { + if ((client != null) && !pool.offer(client)) { + try { + client.disconnect(); + } catch (IOException e) { + log.warn("Error disconnecting ftpclient", e); + } + } + } +} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpMessageSendingConsumerBeanDefinitionParser.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpMessageSendingConsumerBeanDefinitionParser.java new file mode 100644 index 0000000000..d0d848c5db --- /dev/null +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpMessageSendingConsumerBeanDefinitionParser.java @@ -0,0 +1,25 @@ +package org.springframework.integration.ftp.config; + +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser; +import org.springframework.integration.ftp.FtpSendingMessageHandlerFactoryBean; +import org.w3c.dom.Element; + + +/** + * Logic for parsing the ftp:outbound-channel-adapter + * + * @author Josh Long + */ +public class FtpMessageSendingConsumerBeanDefinitionParser extends AbstractOutboundChannelAdapterParser { + @Override + protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FtpSendingMessageHandlerFactoryBean.class.getName()); + + FtpNamespaceParserSupport.configureCoreFtpClient(builder, element, parserContext); + + return builder.getBeanDefinition(); + } +} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpMessageSourceBeanDefinitionParser.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpMessageSourceBeanDefinitionParser.java new file mode 100644 index 0000000000..1638a166d2 --- /dev/null +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpMessageSourceBeanDefinitionParser.java @@ -0,0 +1,40 @@ +package org.springframework.integration.ftp.config; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; +import org.springframework.beans.factory.xml.ParserContext; + +import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.integration.ftp.impl.FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean; + +import org.w3c.dom.Element; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + + +/** + * Logic that configures an ftp:inbound-channel-adapter + * + * @author Josh Long + */ +public class FtpMessageSourceBeanDefinitionParser extends AbstractPollingInboundChannelAdapterParser { + private Set receiveAttrs = new HashSet(Arrays.asList("auto-delete-remote-files-on-sync,filename-pattern,local-working-directory".split(","))); + + @Override + @SuppressWarnings("unused") + protected String parseSource(Element element, ParserContext parserContext) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.class.getName()); + + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "filter"); + + for (String a : receiveAttrs) + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, a); + + FtpNamespaceParserSupport.configureCoreFtpClient(builder, element, parserContext); + + return BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(), parserContext.getRegistry()); + } +} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpNamespaceHandler.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpNamespaceHandler.java new file mode 100644 index 0000000000..c44fade980 --- /dev/null +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpNamespaceHandler.java @@ -0,0 +1,54 @@ +/* + * Copyright 2010 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.config; + +import org.apache.commons.net.ftp.FTP; +import org.springframework.beans.factory.xml.NamespaceHandlerSupport; + +import java.util.HashMap; +import java.util.Map; + + +/** + * Provides namespace support for using FTP + *

+ * This is *heavily* influenced by the good work done by Iwein before. + * + * @author Josh Long + */ +@SuppressWarnings("unused") +public class FtpNamespaceHandler extends NamespaceHandlerSupport { + static public Map FILE_TYPES = new HashMap(); + static public Map CLIENT_MODES = new HashMap(); + + static { + // file types + FILE_TYPES.put("ebcdic-file-type" , FTP.EBCDIC_FILE_TYPE); + FILE_TYPES.put("ascii-file-type" , FTP.ASCII_FILE_TYPE); + FILE_TYPES.put("binary-file-type" , FTP.BINARY_FILE_TYPE); + + // client modes + CLIENT_MODES.put("active-local-data-connection-mode", 0); + CLIENT_MODES.put("active-remote-data-connection-mode", 1); + CLIENT_MODES.put("passive-local-data-connection-mode", 2); + CLIENT_MODES.put("passive-remote-data-connection-mode", 3); + } + + public void init() { + registerBeanDefinitionParser("inbound-channel-adapter", new FtpMessageSourceBeanDefinitionParser()); + registerBeanDefinitionParser("outbound-channel-adapter", new FtpMessageSendingConsumerBeanDefinitionParser()); + } +} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpNamespaceParserSupport.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpNamespaceParserSupport.java new file mode 100644 index 0000000000..747eb673b6 --- /dev/null +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpNamespaceParserSupport.java @@ -0,0 +1,38 @@ +package org.springframework.integration.ftp.config; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.w3c.dom.Element; + + +/** + * A lot of parsers need to support the same set of core attributes, so I'm hiding that logic here + * + * @author Josh Long + * + */ +public class FtpNamespaceParserSupport { + /** + * lots of values are supported across all adapters, let this code handle it initially + * + * @param builder a builder + * @param element an element + * @param parserContext a parser context + */ + public static void configureCoreFtpClient(BeanDefinitionBuilder builder, Element element, ParserContext parserContext) { + for (String p : "auto-create-directories,username,port,password,host,remote-directory".split(",")) { + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, p); + } + + if (element.hasAttribute("file-type")) { + int fileType = FtpNamespaceHandler.FILE_TYPES.get(element.getAttribute("file-type")); + builder.addPropertyValue("fileType", fileType); + } + + if (element.hasAttribute("client-mode")) { + int clientMode = FtpNamespaceHandler.CLIENT_MODES.get(element.getAttribute("client-mode")); + builder.addPropertyValue("clientMode", clientMode); + } + } +} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsMessageSendingConsumerBeanDefinitionParser.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsMessageSendingConsumerBeanDefinitionParser.java new file mode 100644 index 0000000000..9506fd0bd8 --- /dev/null +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsMessageSendingConsumerBeanDefinitionParser.java @@ -0,0 +1,26 @@ +package org.springframework.integration.ftp.config; + +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser; +import org.springframework.integration.ftp.FtpSendingMessageHandlerFactoryBean; +import org.springframework.integration.ftp.FtpsSendingMessageHandlerFactoryBean; +import org.w3c.dom.Element; + + +/** + * Logic for parsing the ftp:outbound-channel-adapter + * + * @author Josh Long + */ +public class FtpsMessageSendingConsumerBeanDefinitionParser extends AbstractOutboundChannelAdapterParser { + @Override + protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FtpsSendingMessageHandlerFactoryBean.class.getName()); + + FtpNamespaceParserSupport.configureCoreFtpClient(builder, element, parserContext); + + return builder.getBeanDefinition(); + } +} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsMessageSourceBeanDefinitionParser.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsMessageSourceBeanDefinitionParser.java new file mode 100644 index 0000000000..1bf8481d7c --- /dev/null +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsMessageSourceBeanDefinitionParser.java @@ -0,0 +1,39 @@ +package org.springframework.integration.ftp.config; + +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.config.xml.AbstractPollingInboundChannelAdapterParser; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.integration.ftp.impl.FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean; +import org.springframework.integration.ftp.impl.FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean; +import org.w3c.dom.Element; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + + +/** + * Logic that configures an ftp:inbound-channel-adapter + * + * @author Josh Long + */ +public class FtpsMessageSourceBeanDefinitionParser extends AbstractPollingInboundChannelAdapterParser { + private Set receiveAttrs = new HashSet(Arrays.asList("auto-delete-remote-files-on-sync,filename-pattern,local-working-directory".split(","))); + + @Override + @SuppressWarnings("unused") + protected String parseSource(Element element, ParserContext parserContext) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean.class.getName()); + + IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "filter"); + + for (String a : receiveAttrs) + IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, a); + + FtpNamespaceParserSupport.configureCoreFtpClient(builder, element, parserContext); + + return BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(), parserContext.getRegistry()); + } +} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsNamespaceHandler.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsNamespaceHandler.java new file mode 100644 index 0000000000..cfa64598b5 --- /dev/null +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/config/FtpsNamespaceHandler.java @@ -0,0 +1,36 @@ +/* + * Copyright 2010 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.config; + +/** + * Provides namespace support for using FTP + *

+ * This is *heavily* influenced by the good work done by Iwein before. + * + * + * @author Josh Long + */ +@SuppressWarnings("unused") +public class FtpsNamespaceHandler extends FtpNamespaceHandler { + + @Override + public void init() { + this.registerBeanDefinitionParser( "inbound-channel-adapter", new FtpsMessageSourceBeanDefinitionParser()); + + // todo test this + this.registerBeanDefinitionParser( "outbound-channel-adapter", new FtpsMessageSendingConsumerBeanDefinitionParser()); + } +} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/impl/FtpInboundRemoteFileSystemSynchronizer.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/impl/FtpInboundRemoteFileSystemSynchronizer.java new file mode 100644 index 0000000000..2b9cfc1c19 --- /dev/null +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/impl/FtpInboundRemoteFileSystemSynchronizer.java @@ -0,0 +1,118 @@ +package org.springframework.integration.ftp.impl; + +import org.apache.commons.net.ftp.FTPClient; +import org.apache.commons.net.ftp.FTPFile; +import org.springframework.core.io.Resource; +import org.springframework.integration.MessagingException; +import org.springframework.integration.file.AbstractInboundRemoteFileSystemSychronizer; +import org.springframework.integration.file.AbstractInboundRemoteFileSystemSynchronizingMessageSource; +import org.springframework.integration.ftp.FtpClientPool; +import org.springframework.scheduling.Trigger; +import org.springframework.scheduling.support.PeriodicTrigger; +import org.springframework.util.Assert; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Collection; + + +/** + * An FTP-adapter implementation of {@link org.springframework.integration.file.AbstractInboundRemoteFileSystemSychronizer} + * + * @author Josh Long + */ +public class FtpInboundRemoteFileSystemSynchronizer extends AbstractInboundRemoteFileSystemSychronizer { + protected FtpClientPool clientPool; + + @Override + protected void onInit() throws Exception { + Assert.notNull(this.clientPool, "clientPool can't be null"); + + if (this.shouldDeleteSourceFile) { + this.entryAcknowledgmentStrategy = new DeletionEntryAcknowledgmentStrategy(); + } + } + + /** + * The {@link org.springframework.integration.ftp.FtpClientPool} that holds references to {@link org.apache.commons.net.ftp.FTPClient} instances + * + * @param clientPool the {@link org.springframework.integration.ftp.FtpClientPool} + */ + public void setClientPool(FtpClientPool clientPool) { + this.clientPool = clientPool; + } + + protected boolean copyFileToLocalDirectory(FTPClient client, FTPFile ftpFile, Resource localDirectory) + throws IOException, FileNotFoundException { + String remoteFileName = ftpFile.getName(); + String localFileName = localDirectory.getFile().getPath() + "/" + remoteFileName; + File localFile = new File(localFileName); + + if (!localFile.exists()) { + String tempFileName = localFileName + AbstractInboundRemoteFileSystemSynchronizingMessageSource.INCOMPLETE_EXTENSION; + File file = new File(tempFileName); + FileOutputStream fos = new FileOutputStream(file); + + try { + client.retrieveFile(remoteFileName, fos); + + // Perhaps we have some dispatch of hte source file to do? + acknowledge(client, ftpFile); + } catch (Throwable th) { + throw new RuntimeException(th); + } finally { + fos.close(); + } + + file.renameTo(localFile); + + return true; + } else { + return false; + } + } + + @Override + protected void syncRemoteToLocalFileSystem() { + try { + FTPClient client = this.clientPool.getClient(); + Assert.state(client != null, FtpClientPool.class.getSimpleName() + " returned a 'null' client. " + "This most likely a bug in the pool implementation."); + + Collection fileList = this.filter.filterEntries(client.listFiles()); + + try { + for (FTPFile ftpFile : fileList) { + if ((ftpFile != null) && ftpFile.isFile()) { + copyFileToLocalDirectory(client, ftpFile, this.localDirectory); + } + } + } finally { + this.clientPool.releaseClient(client); + } + } catch (IOException e) { + throw new MessagingException("Problem occurred while synchronizing remote to local directory", e); + } + } + + @Override + protected Trigger getTrigger() { + return new PeriodicTrigger(10 * 1000); + } + + /** + * An ackowledgment strategy that deletes + */ + class DeletionEntryAcknowledgmentStrategy implements AbstractInboundRemoteFileSystemSychronizer.EntryAcknowledgmentStrategy { + public void acknowledge(Object useful, FTPFile msg) + throws Exception { + FTPClient ftpClient = (FTPClient) useful; + if ((msg != null) && ftpClient.deleteFile(msg.getName())) { + if (logger.isDebugEnabled()) { + logger.debug("deleted " + msg.getName()); + } + } + } + } +} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/impl/FtpInboundRemoteFileSystemSynchronizingMessageSource.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/impl/FtpInboundRemoteFileSystemSynchronizingMessageSource.java new file mode 100644 index 0000000000..c868fd0c8c --- /dev/null +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/impl/FtpInboundRemoteFileSystemSynchronizingMessageSource.java @@ -0,0 +1,35 @@ +package org.springframework.integration.ftp.impl; + +import org.apache.commons.net.ftp.FTPFile; +import org.springframework.integration.file.AbstractInboundRemoteFileSystemSynchronizingMessageSource; +import org.springframework.integration.ftp.FtpClientPool; + + +/** + * a {@link org.springframework.integration.core.MessageSource} implementation for FTP + * + * @author Josh Long + */ +public class FtpInboundRemoteFileSystemSynchronizingMessageSource extends AbstractInboundRemoteFileSystemSynchronizingMessageSource { + private volatile FtpClientPool clientPool; + + public void setClientPool(FtpClientPool clientPool) { + this.clientPool = clientPool; + } + + @Override + protected void doStart() { + this.synchronizer.start(); + } + + @Override + protected void doStop() { + this.synchronizer.stop(); + } + + @Override + protected void onInit() throws Exception { + super.onInit(); + this.synchronizer.setClientPool(this.clientPool); + } +} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/impl/FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/impl/FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java new file mode 100644 index 0000000000..2713aee47b --- /dev/null +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/impl/FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean.java @@ -0,0 +1,173 @@ +package org.springframework.integration.ftp.impl; + +import org.apache.commons.lang.SystemUtils; +import org.apache.commons.net.ftp.FTP; +import org.apache.commons.net.ftp.FTPClient; +import org.apache.commons.net.ftp.FTPFile; +import org.springframework.beans.factory.config.AbstractFactoryBean; +import org.springframework.context.ResourceLoaderAware; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceEditor; +import org.springframework.core.io.ResourceLoader; +import org.springframework.integration.file.entries.CompositeEntryListFilter; +import org.springframework.integration.file.entries.EntryListFilter; +import org.springframework.integration.file.entries.PatternMatchingEntryListFilter; +import org.springframework.integration.ftp.*; +import org.springframework.util.StringUtils; + +import java.io.File; + + +/** + * Factory to make building the namespace easier + * + * @author Josh Long + */ +public class FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean extends AbstractFactoryBean implements ResourceLoaderAware { + protected volatile String port; + protected volatile String autoCreateDirectories; + protected volatile String filenamePattern; + protected volatile String username; + protected volatile String password; + protected volatile String host; + protected volatile String remoteDirectory; + protected volatile String localWorkingDirectory; + protected volatile ResourceLoader resourceLoader; + protected volatile Resource localDirectoryResource; + protected volatile EntryListFilter filter; + protected volatile int clientMode = FTPClient.ACTIVE_LOCAL_DATA_CONNECTION_MODE; + protected volatile int fileType = FTP.BINARY_FILE_TYPE; + + public void setFileType(int fileType) { + this.fileType = fileType; + } + + private volatile String autoDeleteRemoteFilesOnSync; + + @SuppressWarnings("unused") + public void setAutoDeleteRemoteFilesOnSync(String autoDeleteRemoteFilesOnSync) { + this.autoDeleteRemoteFilesOnSync = autoDeleteRemoteFilesOnSync; + } + + @Override + public Class getObjectType() { + return FtpInboundRemoteFileSystemSynchronizingMessageSource.class; + } + + private Resource fromText(String path) { + ResourceEditor resourceEditor = new ResourceEditor(this.resourceLoader); + resourceEditor.setAsText(path); + return (Resource) resourceEditor.getValue(); + } + + protected AbstractFtpClientFactory defaultClientFactory() throws Exception { + return ClientFactorySupport.ftpClientFactory( this.host , Integer.parseInt(this.port) , this.remoteDirectory , this.username ,this.password, this.clientMode ); + } + + protected String defaultFtpInboundFolderName = "ftpInbound"; + + @Override + protected FtpInboundRemoteFileSystemSynchronizingMessageSource createInstance() + throws Exception { + boolean autoCreatDirs = Boolean.parseBoolean(this.autoCreateDirectories); + boolean ackRemoteDir = Boolean.parseBoolean(this.autoDeleteRemoteFilesOnSync); + + FtpInboundRemoteFileSystemSynchronizingMessageSource ftpRemoteFileSystemSynchronizingMessageSource = new FtpInboundRemoteFileSystemSynchronizingMessageSource(); + ftpRemoteFileSystemSynchronizingMessageSource.setAutoCreateDirectories(autoCreatDirs); + + if (!StringUtils.hasText(this.localWorkingDirectory)) { + File tmp = new File(SystemUtils.getJavaIoTmpDir(), defaultFtpInboundFolderName); + this.localWorkingDirectory = "file://" + tmp.getAbsolutePath(); + } + + this.localDirectoryResource = this.fromText(this.localWorkingDirectory); + + FtpFileEntryNamer ftpFileEntryNamer = new FtpFileEntryNamer(); + CompositeEntryListFilter compositeFtpFileListFilter = new CompositeEntryListFilter(); + + if (StringUtils.hasText(this.filenamePattern)) { + PatternMatchingEntryListFilter ftpFilePatternMatchingEntryListFilter = new PatternMatchingEntryListFilter(ftpFileEntryNamer, filenamePattern); + compositeFtpFileListFilter.addFilter(ftpFilePatternMatchingEntryListFilter); + } + + if (this.filter != null) { + compositeFtpFileListFilter.addFilter(this.filter); + } + + QueuedFtpClientPool queuedFtpClientPool = new QueuedFtpClientPool(15, defaultClientFactory()); + + FtpInboundRemoteFileSystemSynchronizer ftpRemoteFileSystemSynchronizer = new FtpInboundRemoteFileSystemSynchronizer(); + ftpRemoteFileSystemSynchronizer.setClientPool(queuedFtpClientPool); + ftpRemoteFileSystemSynchronizer.setLocalDirectory(this.localDirectoryResource); + ftpRemoteFileSystemSynchronizer.setShouldDeleteSourceFile(ackRemoteDir); + + ftpRemoteFileSystemSynchronizer.setFilter(compositeFtpFileListFilter); + ftpRemoteFileSystemSynchronizingMessageSource.setRemotePredicate(compositeFtpFileListFilter); + + ftpRemoteFileSystemSynchronizingMessageSource.setSynchronizer(ftpRemoteFileSystemSynchronizer); + ftpRemoteFileSystemSynchronizingMessageSource.setClientPool(queuedFtpClientPool); + + ftpRemoteFileSystemSynchronizingMessageSource.setLocalDirectory(this.localDirectoryResource); + ftpRemoteFileSystemSynchronizingMessageSource.setBeanFactory(this.getBeanFactory()); + ftpRemoteFileSystemSynchronizingMessageSource.setAutoStartup(true); + ftpRemoteFileSystemSynchronizingMessageSource.afterPropertiesSet(); + ftpRemoteFileSystemSynchronizingMessageSource.start(); + + return ftpRemoteFileSystemSynchronizingMessageSource; + } + + @SuppressWarnings("unused") + public void setPort(String port) { + this.port = port; + } + + @SuppressWarnings("unused") + public void setAutoCreateDirectories(String autoCreateDirectories) { + this.autoCreateDirectories = autoCreateDirectories; + } + + @SuppressWarnings("unused") + public void setFilenamePattern(String filenamePattern) { + this.filenamePattern = filenamePattern; + } + + @SuppressWarnings("unused") + public void setUsername(String username) { + this.username = username; + } + + @SuppressWarnings("unused") + public void setPassword(String password) { + this.password = password; + } + + @SuppressWarnings("unused") + public void setHost(String host) { + this.host = host; + } + + @SuppressWarnings("unused") + public void setRemoteDirectory(String remoteDirectory) { + this.remoteDirectory = remoteDirectory; + } + + @SuppressWarnings("unused") + public void setLocalWorkingDirectory(String localWorkingDirectory) { + this.localWorkingDirectory = localWorkingDirectory; + } + + @SuppressWarnings("unused") + public void setFilter(EntryListFilter filter) { + this.filter = filter; + } + + @SuppressWarnings("unused") + public void setClientMode(int clientMode) { + this.clientMode = clientMode; + } + + @SuppressWarnings("unused") + public void setResourceLoader(ResourceLoader resourceLoader) { + this.resourceLoader = resourceLoader; + } +} diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/impl/FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/impl/FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean.java new file mode 100644 index 0000000000..76dfa16115 --- /dev/null +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/impl/FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean.java @@ -0,0 +1,116 @@ +package org.springframework.integration.ftp.impl; + +import org.apache.commons.lang.SystemUtils; +import org.apache.commons.net.ftp.FTP; +import org.apache.commons.net.ftp.FTPClient; +import org.apache.commons.net.ftp.FTPFile; + +import org.springframework.beans.factory.config.AbstractFactoryBean; + +import org.springframework.context.ResourceLoaderAware; + +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceEditor; +import org.springframework.core.io.ResourceLoader; + +import org.springframework.integration.file.entries.CompositeEntryListFilter; +import org.springframework.integration.file.entries.EntryListFilter; +import org.springframework.integration.file.entries.PatternMatchingEntryListFilter; +import org.springframework.integration.ftp.*; + +import org.springframework.util.StringUtils; + +import java.io.File; +import java.io.IOException; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.TrustManager; + + +/** + * Factory to make building the namespace easier + * + * @author Josh Long + */ +public class FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean extends FtpRemoteFileSystemSynchronizingMessageSourceFactoryBean { + /** + * Sets whether the connection is implicit. Local testing reveals this to be a good choice. + */ + protected volatile Boolean implicit = Boolean.FALSE; + + /** + * "TLS" or "SSL" + */ + protected volatile String protocol; + + /** + * "P" + */ + protected volatile String prot; + private KeyManager keyManager; + private TrustManager trustManager; + protected volatile String authValue; + private Boolean sessionCreation; + private Boolean useClientMode; + private Boolean needClientAuth; + private Boolean wantsClientAuth; + private String[] cipherSuites; + + public FtpsRemoteFileSystemSynchronizingMessageSourceFactoryBean() { + this.defaultFtpInboundFolderName = "ftpsInbound"; + this.clientMode = FTPClient.PASSIVE_LOCAL_DATA_CONNECTION_MODE; + } + + public void setKeyManager(KeyManager keyManager) { + this.keyManager = keyManager; + } + + public void setTrustManager(TrustManager trustManager) { + this.trustManager = trustManager; + } + + public void setImplicit(Boolean implicit) { + this.implicit = implicit; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public void setProt(String prot) { + this.prot = prot; + } + + public void setAuthValue(String authValue) { + this.authValue = authValue; + } + + public void setSessionCreation(Boolean sessionCreation) { + this.sessionCreation = sessionCreation; + } + + public void setUseClientMode(Boolean useClientMode) { + this.useClientMode = useClientMode; + } + + public void setNeedClientAuth(Boolean needClientAuth) { + this.needClientAuth = needClientAuth; + } + + public void setWantsClientAuth(Boolean wantsClientAuth) { + this.wantsClientAuth = wantsClientAuth; + } + + protected AbstractFtpClientFactory defaultClientFactory() + throws Exception { + DefaultFtpsClientFactory factory = ClientFactorySupport.ftpsClientFactory(this.host, Integer.parseInt(this.port), this.remoteDirectory, this.username, this.password, this.fileType, + this.clientMode, this.prot, this.protocol, this.authValue, this.implicit, this.trustManager, this.keyManager, this.sessionCreation, this.useClientMode, this.wantsClientAuth, + this.needClientAuth, this.cipherSuites); + + return factory; + } + + public void setCipherSuites(String[] cipherSuites) { + this.cipherSuites = cipherSuites; + } +} diff --git a/spring-integration-ftp/src/main/resources/META-INF/MANIFEST.MF b/spring-integration-ftp/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 0000000000..e69de29bb2 diff --git a/spring-integration-ftp/src/main/resources/META-INF/spring.handlers b/spring-integration-ftp/src/main/resources/META-INF/spring.handlers new file mode 100644 index 0000000000..7d2711fc92 --- /dev/null +++ b/spring-integration-ftp/src/main/resources/META-INF/spring.handlers @@ -0,0 +1,3 @@ +http\://www.springframework.org/schema/integration/ftp=org.springframework.integration.ftp.config.FtpNamespaceHandler +http\://www.springframework.org/schema/integration/ftps=org.springframework.integration.ftp.config.FtpsNamespaceHandler + diff --git a/spring-integration-ftp/src/main/resources/META-INF/spring.schemas b/spring-integration-ftp/src/main/resources/META-INF/spring.schemas new file mode 100644 index 0000000000..5103c6c85f --- /dev/null +++ b/spring-integration-ftp/src/main/resources/META-INF/spring.schemas @@ -0,0 +1,4 @@ +http\://www.springframework.org/schema/integration/ftp/spring-integration-ftp-2.0.xsd=org/springframework/integration/ftp/config/spring-integration-ftp-2.0.xsd +http\://www.springframework.org/schema/integration/ftp/spring-integration-ftp.xsd=org/springframework/integration/ftp/config/spring-integration-ftp-2.0.xsd +http\://www.springframework.org/schema/integration/ftp/spring-integration-ftps-2.0.xsd=org/springframework/integration/ftp/config/spring-integration-ftps-2.0.xsd +http\://www.springframework.org/schema/integration/ftp/spring-integration-ftps.xsd=org/springframework/integration/ftp/config/spring-integration-ftps-2.0.xsd diff --git a/spring-integration-ftp/src/main/resources/org/springframework/integration/ftp/config/spring-integration-ftp-2.0.xsd b/spring-integration-ftp/src/main/resources/org/springframework/integration/ftp/config/spring-integration-ftp-2.0.xsd new file mode 100644 index 0000000000..4b7473aca0 --- /dev/null +++ b/spring-integration-ftp/src/main/resources/org/springframework/integration/ftp/config/spring-integration-ftp-2.0.xsd @@ -0,0 +1,249 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-ftp/src/main/resources/org/springframework/integration/ftp/config/spring-integration-ftps-2.0.xsd b/spring-integration-ftp/src/main/resources/org/springframework/integration/ftp/config/spring-integration-ftps-2.0.xsd new file mode 100644 index 0000000000..2bba87f6fd --- /dev/null +++ b/spring-integration-ftp/src/main/resources/org/springframework/integration/ftp/config/spring-integration-ftps-2.0.xsd @@ -0,0 +1,252 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/FtpsExample.java b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/FtpsExample.java new file mode 100644 index 0000000000..136ed912f1 --- /dev/null +++ b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/FtpsExample.java @@ -0,0 +1,9 @@ +package org.springframework.integration.ftp; + +/** + * the goal here is to sketch out what a simple FTPS client looks like + */ +public class FtpsExample { + + +} diff --git a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/InboundFtpFileServiceActivator.java b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/InboundFtpFileServiceActivator.java new file mode 100644 index 0000000000..1d494fec74 --- /dev/null +++ b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/InboundFtpFileServiceActivator.java @@ -0,0 +1,34 @@ +package org.springframework.integration.ftp; + +import org.apache.commons.lang.StringUtils; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.Message; +import org.springframework.integration.annotation.ServiceActivator; + +import java.io.File; + + +/** + * Simple component to test the inbound integration + * + * @author Josh Long + */ + +public class InboundFtpFileServiceActivator { + + @ServiceActivator + public void onNewRemoteFTPFile(Message file) + throws Throwable { + System.out.println(StringUtils.repeat("=", 100)); + System.out.println("A new file has appeared: " + file.getPayload().getAbsolutePath()); + + for (String h : file.getHeaders().keySet()) + System.out.println(String.format("%s = %s", h, file.getHeaders().get(h))); + } + + public static void main(String[] args) throws Throwable { + ClassPathXmlApplicationContext classPathXmlApplicationContext = + new ClassPathXmlApplicationContext("inbound-ftp-context.xml"); + classPathXmlApplicationContext.start(); + } +} diff --git a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/InboundFtpsFileServiceActivator.java b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/InboundFtpsFileServiceActivator.java new file mode 100644 index 0000000000..85e2e219dd --- /dev/null +++ b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/InboundFtpsFileServiceActivator.java @@ -0,0 +1,34 @@ +package org.springframework.integration.ftp; + +import org.apache.commons.lang.StringUtils; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.Message; +import org.springframework.integration.annotation.ServiceActivator; + +import java.io.File; + + +/** + * Simple component to test the inbound integration + * + * @author Josh Long + */ + +public class InboundFtpsFileServiceActivator { + + @ServiceActivator + public void onNewRemoteFTPFile(Message file) + throws Throwable { + System.out.println(StringUtils.repeat("=", 100)); + System.out.println("A new file has appeared: " + file.getPayload().getAbsolutePath()); + + for (String h : file.getHeaders().keySet()) + System.out.println(String.format("%s = %s", h, file.getHeaders().get(h))); + } + + public static void main(String[] args) throws Throwable { + ClassPathXmlApplicationContext classPathXmlApplicationContext = + new ClassPathXmlApplicationContext("inbound-ftps-context.xml"); + classPathXmlApplicationContext.start(); + } +} diff --git a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/OutboundFtpExample.java b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/OutboundFtpExample.java new file mode 100644 index 0000000000..9a01ff1100 --- /dev/null +++ b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/OutboundFtpExample.java @@ -0,0 +1,16 @@ +package org.springframework.integration.ftp; + +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * This simple example demonstrates sending a file to a remote FTP server using the ftp:outbound-channel-adapter + *

+ * It reads files from a directory on your computer and systematically puts them on the remote FTP server, + * + * @author Josh Long + */ +public class OutboundFtpExample { + public static void main(String[] args) throws Throwable { + ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("outbound-ftp-context.xml"); + } +} diff --git a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/OutboundFtpsExample.java b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/OutboundFtpsExample.java new file mode 100644 index 0000000000..3fe0060c53 --- /dev/null +++ b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/OutboundFtpsExample.java @@ -0,0 +1,16 @@ +package org.springframework.integration.ftp; + +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * This simple example demonstrates sending a file to a remote FTP server using the ftp:outbound-channel-adapter + *

+ * It reads files from a directory on your computer and systematically puts them on the remote FTP server, + * + * @author Josh Long + */ +public class OutboundFtpsExample { + public static void main(String[] args) throws Throwable { + ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("outbound-ftps-context.xml"); + } +} diff --git a/spring-integration-ftp/src/test/resources/inbound-ftp-context.xml b/spring-integration-ftp/src/test/resources/inbound-ftp-context.xml new file mode 100644 index 0000000000..cbffef591d --- /dev/null +++ b/spring-integration-ftp/src/test/resources/inbound-ftp-context.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-ftp/src/test/resources/inbound-ftps-context.xml b/spring-integration-ftp/src/test/resources/inbound-ftps-context.xml new file mode 100644 index 0000000000..2b0ca1806a --- /dev/null +++ b/spring-integration-ftp/src/test/resources/inbound-ftps-context.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-ftp/src/test/resources/outbound-ftp-context.xml b/spring-integration-ftp/src/test/resources/outbound-ftp-context.xml new file mode 100644 index 0000000000..278b3b2110 --- /dev/null +++ b/spring-integration-ftp/src/test/resources/outbound-ftp-context.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + diff --git a/spring-integration-ftp/src/test/resources/outbound-ftps-context.xml b/spring-integration-ftp/src/test/resources/outbound-ftps-context.xml new file mode 100644 index 0000000000..6eb2275a70 --- /dev/null +++ b/spring-integration-ftp/src/test/resources/outbound-ftps-context.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + +