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 98a117228bc..3934e71d9bc 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 @@ -53,6 +53,10 @@ /** * Factory for creating {@link SftpSession} instances. + *
+ * The {@link #createSftpClient(ClientSession, SftpVersionSelector, SftpErrorDataHandler)} + * can be overridden to provide a custom {@link SftpClient}. + * The {@link ConcurrentSftpClient} is used by default. * * @author Josh Long * @author Mario Gray @@ -65,6 +69,7 @@ * @author Krzysztof Debski * @author Auke Zaaiman * @author Christian Tzolov + * @author Adama Sorho * * @since 2.0 */ @@ -282,9 +287,7 @@ public SftpSession getSession() { try { boolean freshSftpClient = false; if (sftpClient == null || !sftpClient.isOpen()) { - sftpClient = - new ConcurrentSftpClient(initClientSession(), this.sftpVersionSelector, - SftpErrorDataHandler.EMPTY); + sftpClient = createSftpClient(initClientSession(), this.sftpVersionSelector, SftpErrorDataHandler.EMPTY); freshSftpClient = true; } sftpSession = new SftpSession(sftpClient); @@ -293,8 +296,8 @@ public SftpSession getSession() { this.sharedSftpClient = sftpClient; } } - catch (Exception e) { - throw new IllegalStateException("failed to create SFTP Session", e); + catch (Exception ex) { + throw new IllegalStateException("failed to create SFTP Session", ex); } finally { if (this.sharedSessionLock != null) { @@ -397,15 +400,32 @@ public void resetSharedSession() { this.sharedSftpClient = null; } + /** + * Can be overridden to provide a custom {@link SftpClient} to {@link #getSession()}. + * @param clientSession the {@link ClientSession} + * @param initialVersionSelector the initial {@link SftpVersionSelector} + * @param errorDataHandler the {@link SftpErrorDataHandler} to handle incoming data + * through the error stream. + * @return {@link SftpClient} + * @throws IOException if failed to initialize + * @since 6.1.3 + */ + protected SftpClient createSftpClient( + ClientSession clientSession, SftpVersionSelector initialVersionSelector, + SftpErrorDataHandler errorDataHandler) throws IOException { + + return new ConcurrentSftpClient(clientSession, initialVersionSelector, errorDataHandler); + } + /** * The {@link DefaultSftpClient} extension to lock the {@link #send(int, Buffer)} * for concurrent interaction. */ - private static class ConcurrentSftpClient extends DefaultSftpClient { + protected static class ConcurrentSftpClient extends DefaultSftpClient { private final Lock sendLock = new ReentrantLock(); - ConcurrentSftpClient(ClientSession clientSession, SftpVersionSelector initialVersionSelector, + protected ConcurrentSftpClient(ClientSession clientSession, SftpVersionSelector initialVersionSelector, SftpErrorDataHandler errorDataHandler) throws IOException { super(clientSession, initialVersionSelector, errorDataHandler); diff --git a/src/reference/antora/modules/ROOT/pages/sftp/session-factory.adoc b/src/reference/antora/modules/ROOT/pages/sftp/session-factory.adoc index aea8376f5c8..9045f489bc3 100644 --- a/src/reference/antora/modules/ROOT/pages/sftp/session-factory.adoc +++ b/src/reference/antora/modules/ROOT/pages/sftp/session-factory.adoc @@ -38,13 +38,26 @@ When using this feature, you must wrap the session factory in a caching session If the cache is reset, the session is disconnected only when the last channel is closed. -The connection is refreshed if it is found to be disconnected when a new operation obtains a session. +The connection is refreshed if it to be disconnected when a new operation obtains a session. ===== Now all you need to do is inject this SFTP session factory into your adapters. NOTE: A more practical way to provide values for the SFTP session factory is to use Spring's https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-factory-placeholderconfigurer[property placeholder support]. +Starting with version 6.1.3, the `DefaultSftpSessionFactory` introduces a `createSftpClient(...)` to support a custom `SftpClient`. +See a sample below of how to override `createSftpChannelSubsystem()` method in your custom `SftpClient` to add, for example, some custom `RequestHandler` for SFTP sub-system requests and replies: + +[source, java] +---- +@Override +protected ChannelSubsystem createSftpChannelSubsystem(ClientSession clientSession) { + ChannelSubsystem sftpChannelSubsystem = super.createSftpChannelSubsystem(clientSession); + sftpChannelSubsystem.addRequestHandler((channel, request, wantReply, buffer) -> ...); + return sftpChannelSubsystem; +} +---- + [[sftp-session-factory-properties]] == Configuration Properties diff --git a/src/reference/antora/modules/ROOT/pages/whats-new.adoc b/src/reference/antora/modules/ROOT/pages/whats-new.adoc index 40e73c67e75..a573c495f56 100644 --- a/src/reference/antora/modules/ROOT/pages/whats-new.adoc +++ b/src/reference/antora/modules/ROOT/pages/whats-new.adoc @@ -72,3 +72,9 @@ See xref:mongodb.adoc#mongodb-message-store[MongoDB Message Store] for an exampl `FtpLastModifiedFileListFilter`, `SftpLastModifiedFileListFilter` and `SmbLastModifiedFileListFilter` have been introduced to allow files filtering based on a last-modified strategy respectively for `FTP`, `SFTP` and `SMB`. See xref:ftp/inbound.adoc#ftp-inbound[FTP Inbound Channel Adapter], xref:sftp/inbound.adoc#sftp-inbound[SFTP Inbound Channel Adapter], and xref:smb.adoc#smb-inbound[SMB Inbound Channel Adapter] for more information. + +[[x6.2-sftp-changes]] +=== SFTP Support Changes + +A new `DefaultSftpSessionFactory.createSftpClient(...)` method has been introduced to support a custom `SftpClient` when overridden. +See xref:sftp/session-factory.adoc#sftp-session-factory[SFTP Session Factory] for more information.