diff --git a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/session/AbstractFtpSessionFactory.java b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/session/AbstractFtpSessionFactory.java index b07d1decaee..5ab775219f0 100644 --- a/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/session/AbstractFtpSessionFactory.java +++ b/spring-integration-ftp/src/main/java/org/springframework/integration/ftp/session/AbstractFtpSessionFactory.java @@ -63,6 +63,12 @@ public abstract class AbstractFtpSessionFactory implements protected String controlEncoding = FTP.DEFAULT_CONTROL_ENCODING; + private Integer connectTimeout; + + private Integer defaultTimeout; + + private Integer dataTimeout; + /** * File types defined by {@link org.apache.commons.net.ftp.FTP} constants: @@ -133,6 +139,30 @@ public void setClientMode(int clientMode) { this.clientMode = clientMode; } + /** + * Set the connect timeout for the socket. + * @param connectTimeout the timeout + */ + public void setConnectTimeout(int connectTimeout) { + this.connectTimeout = connectTimeout; + } + + /** + * Set the (socket option) timeout on the command socket. + * @param defaultTimeout the timeout. + */ + public void setDefaultTimeout(int defaultTimeout) { + this.defaultTimeout = defaultTimeout; + } + + /** + * Set the (socket option) timeout on the data connection. + * @param dataTimeout the timeout. + */ + public void setDataTimeout(int dataTimeout) { + this.dataTimeout = dataTimeout; + } + @Override public Session getSession() { try { @@ -148,6 +178,15 @@ private T createClient() throws SocketException, IOException { Assert.notNull(client, "client must not be null"); client.configure(this.config); Assert.hasText(this.username, "username is required"); + if (this.connectTimeout != null) { + client.setConnectTimeout(this.connectTimeout); + } + if (this.defaultTimeout != null) { + client.setDefaultTimeout(this.defaultTimeout); + } + if (this.dataTimeout != null) { + client.setDataTimeout(this.dataTimeout); + } this.postProcessClientBeforeConnect(client); diff --git a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/session/SessionFactoryTests.java b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/session/SessionFactoryTests.java index 7e68c79c0c2..21b7fa59610 100644 --- a/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/session/SessionFactoryTests.java +++ b/spring-integration-ftp/src/test/java/org/springframework/integration/ftp/session/SessionFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -15,6 +15,12 @@ */ package org.springframework.integration.ftp.session; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + import java.lang.reflect.Field; import java.util.Random; import java.util.concurrent.ExecutorService; @@ -22,30 +28,50 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import org.junit.Assert; import org.apache.commons.net.ftp.FTPClient; +import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; import org.mockito.Mockito; -import org.springframework.messaging.MessagingException; + import org.springframework.integration.file.remote.session.CachingSessionFactory; import org.springframework.integration.file.remote.session.Session; import org.springframework.integration.file.remote.session.SessionFactory; import org.springframework.integration.test.util.TestUtils; - -import static org.junit.Assert.fail; - -import static org.junit.Assert.assertEquals; +import org.springframework.messaging.MessagingException; /** * @author Oleg Zhurakousky * @author Gunnar Hillert + * @author Gary Russell * */ @SuppressWarnings({"rawtypes","unchecked"}) public class SessionFactoryTests { + @Test + public void testTimeouts() throws Exception { + final FTPClient client = mock(FTPClient.class); + DefaultFtpSessionFactory sessionFactory = new DefaultFtpSessionFactory() { + + @Override + protected FTPClient createClientInstance() { + return client; + } + }; + sessionFactory.setUsername("foo"); + sessionFactory.setConnectTimeout(123); + sessionFactory.setDefaultTimeout(456); + sessionFactory.setDataTimeout(789); + doReturn(200).when(client).getReplyCode(); + doReturn(true).when(client).login("foo", null); + sessionFactory.getSession(); + verify(client).setConnectTimeout(123); + verify(client).setDefaultTimeout(456); + verify(client).setDataTimeout(789); + } + @Test public void testWithControlEncoding() { DefaultFtpSessionFactory sessionFactory = new DefaultFtpSessionFactory(); @@ -164,6 +190,7 @@ public void testConnectionLimit() throws Exception{ final AtomicInteger failures = new AtomicInteger(); for (int i = 0; i < 30; i++) { executor.execute(new Runnable() { + @Override public void run() { try { Session session = factory.getSession(); diff --git a/src/reference/docbook/ftp.xml b/src/reference/docbook/ftp.xml index 83fec62b0a8..6ea347dd533 100644 --- a/src/reference/docbook/ftp.xml +++ b/src/reference/docbook/ftp.xml @@ -104,15 +104,19 @@ xsi:schemaLocation="http://www.springframework.org/schema/integration/ftp - Advanced Configuration + Advanced Configuration DefaultFtpSessionFactory provides an abstraction over the underlying client API which, since Spring Integration 2.0, is Apache Commons Net. This spares you from the low level configuration details - of the org.apache.commons.net.ftp.FTPClient. However there are times when access to lower level FTPClient details is - necessary to achieve more advanced configuration (e.g., setting data timeout, default timeout etc.). For that purpose, AbstractFtpSessionFactory + of the org.apache.commons.net.ftp.FTPClient. Several common properties are exposed on the + session factory (since version 4.0, this now includes connectTimeout, + defaultTimeout and dataTimeout). + However there are times when access to lower level FTPClient configuration is + necessary to achieve more advanced configuration (e.g., setting the port range for active mode etc.). + For that purpose, AbstractFtpSessionFactory (the base class for all FTP Session Factories) exposes hooks, in the form of the two post-processing methods below. diff --git a/src/reference/docbook/whats-new.xml b/src/reference/docbook/whats-new.xml index c2959304ec0..f723c8cb000 100644 --- a/src/reference/docbook/whats-new.xml +++ b/src/reference/docbook/whats-new.xml @@ -312,5 +312,15 @@ . +
+ FTP Timeouts + + The DefaultFtpSessionFactory now exposes the + connectTimeout, defaultTimeout and dataTimeout + properties, avoiding the need to subclass the factory just to set these common + properties. The postProcess* methods are still available for more + advanced configuration. See for more information. + +