diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/DefaultSftpSessionFactory.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/DefaultSftpSessionFactory.java index 9fc89142e1..ec9c3718f0 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/DefaultSftpSessionFactory.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/DefaultSftpSessionFactory.java @@ -17,6 +17,7 @@ package org.springframework.integration.sftp.session; import java.io.IOException; +import java.time.Duration; import java.util.Arrays; import java.util.Properties; import java.util.concurrent.locks.Lock; @@ -108,6 +109,8 @@ public class DefaultSftpSessionFactory implements SessionFactory, Share private boolean allowUnknownKeys = false; + private Duration channelConnectTimeout; + private volatile JSchSessionWrapper sharedJschSession; @@ -353,6 +356,16 @@ public class DefaultSftpSessionFactory implements SessionFactory, Share this.allowUnknownKeys = allowUnknownKeys; } + /** + * Set the connect timeout. + * @param timeout the timeout to set. + * @since 5.2 + */ + public void setChannelConnectTimeout(Duration timeout) { + Assert.notNull(timeout, "'connectTimeout' cannot be null"); + this.channelConnectTimeout = timeout; + } + @Override public SftpSession getSession() { JSchSessionWrapper jschSession = this.sharedJschSession; @@ -367,6 +380,8 @@ public class DefaultSftpSessionFactory implements SessionFactory, Share freshJschSession = true; } sftpSession = new SftpSession(jschSession); + JavaUtils.INSTANCE + .acceptIfNotNull(this.channelConnectTimeout, sftpSession::setChannelConnectTimeout); sftpSession.connect(); if (this.isSharedSession && freshJschSession) { this.sharedJschSession = jschSession; diff --git a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSession.java b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSession.java index 7955b6a985..aee02da36b 100644 --- a/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSession.java +++ b/spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/SftpSession.java @@ -19,6 +19,7 @@ package org.springframework.integration.sftp.session; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.Vector; @@ -51,12 +52,16 @@ public class SftpSession implements Session { private static final String SESSION_IS_NOT_CONNECTED = "session is not connected"; + private static final Duration DEFAULT_CHANNEL_CONNECT_TIMEOUT = Duration.ofSeconds(5); + private final Log logger = LogFactory.getLog(this.getClass()); private final com.jcraft.jsch.Session jschSession; private final JSchSessionWrapper wrapper; + private int channelConnectTimeout = (int) DEFAULT_CHANNEL_CONNECT_TIMEOUT.toMillis(); + private volatile ChannelSftp channel; private volatile boolean closed; @@ -74,6 +79,16 @@ public class SftpSession implements Session { this.wrapper = wrapper; } + /** + * Set the connect timeout. + * @param timeout the timeout to set. + * @since 5.2 + */ + public void setChannelConnectTimeout(Duration timeout) { + Assert.notNull(timeout, "'timeout' cannot be null"); + this.channelConnectTimeout = (int) timeout.toMillis(); + } + @Override public boolean remove(String path) throws IOException { Assert.state(this.channel != null, SESSION_IS_NOT_CONNECTED); @@ -146,7 +161,7 @@ public class SftpSession implements Session { } @Override - public boolean finalizeRaw() throws IOException { + public boolean finalizeRaw() { return true; } @@ -258,7 +273,7 @@ public class SftpSession implements Session { this.channel.lstat(path); return true; } - catch (SftpException e) { + catch (@SuppressWarnings("unused") SftpException e) { // ignore } return false; @@ -271,7 +286,7 @@ public class SftpSession implements Session { } this.channel = (ChannelSftp) this.jschSession.openChannel("sftp"); if (this.channel != null && !this.channel.isConnected()) { - this.channel.connect(); + this.channel.connect(this.channelConnectTimeout); } } catch (JSchException e) { @@ -295,7 +310,7 @@ public class SftpSession implements Session { this.channel.lstat(this.channel.getHome()); return true; } - catch (Exception e) { + catch (@SuppressWarnings("unused") Exception e) { return false; } } diff --git a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/outbound/SftpOutboundTests.java b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/outbound/SftpOutboundTests.java index bb22f63057..06e7966ae9 100644 --- a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/outbound/SftpOutboundTests.java +++ b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/outbound/SftpOutboundTests.java @@ -20,6 +20,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.willAnswer; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -380,7 +381,7 @@ public class SftpOutboundTests { } private void noopConnect(ChannelSftp channel1) throws JSchException { - doAnswer(invocation -> null).when(channel1).connect(); + doNothing().when(channel1).connect(5000); } public static class TestSftpSessionFactory extends DefaultSftpSessionFactory { diff --git a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/session/SftpSessionFactoryTests.java b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/session/SftpSessionFactoryTests.java index 5fcf76258d..7d5d10b8ba 100644 --- a/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/session/SftpSessionFactoryTests.java +++ b/spring-integration-sftp/src/test/java/org/springframework/integration/sftp/session/SftpSessionFactoryTests.java @@ -25,6 +25,7 @@ import static org.mockito.Mockito.when; import java.io.File; import java.io.IOException; import java.net.ConnectException; +import java.time.Duration; import java.util.Collections; import org.apache.sshd.server.SshServer; @@ -153,8 +154,11 @@ public class SftpSessionFactoryTests { SshServer server = SshServer.setUpDefaultServer(); try { DefaultSftpSessionFactory f = createServerAndClient(server); + f.setChannelConnectTimeout(Duration.ofSeconds(6)); f.setAllowUnknownKeys(true); - f.getSession().close(); + SftpSession session = f.getSession(); + assertThat(TestUtils.getPropertyValue(session, "channelConnectTimeout", Integer.class)).isEqualTo(6_000); + session.close(); } finally { server.stop(true);