GH-9380: Add DefaultSftpSessionFactory.setSshClientConfigurer()

Fixes: #9380

Expose a `Consumer<SshClient> sshClientConfigurer` option for the `DefaultSftpSessionFactory`
to further customize an internal `SshClient` instance.
This commit is contained in:
Artem Bilan
2024-08-14 15:56:22 -04:00
parent e332ce988a
commit 57c98e1611
4 changed files with 48 additions and 4 deletions

View File

@@ -24,6 +24,7 @@ import java.time.Duration;
import java.util.Collection;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
import org.apache.sshd.client.SshClient;
import org.apache.sshd.client.auth.keyboard.UserInteraction;
@@ -79,7 +80,8 @@ import org.springframework.util.Assert;
*
* @since 2.0
*/
public class DefaultSftpSessionFactory implements SessionFactory<SftpClient.DirEntry>, SharedSessionCapable, DisposableBean {
public class DefaultSftpSessionFactory
implements SessionFactory<SftpClient.DirEntry>, SharedSessionCapable, DisposableBean {
private final Lock lock = new ReentrantLock();
@@ -119,6 +121,9 @@ public class DefaultSftpSessionFactory implements SessionFactory<SftpClient.DirE
private volatile SftpClient sharedSftpClient;
private Consumer<SshClient> sshClientConfigurer = (sshClient) -> {
};
public DefaultSftpSessionFactory() {
this(false);
}
@@ -283,6 +288,20 @@ public class DefaultSftpSessionFactory implements SessionFactory<SftpClient.DirE
this.sftpVersionSelector = sftpVersionSelector;
}
/**
* Set a {@link Consumer} as a callback to further customize an internal {@link SshClient} instance.
* For example, to set custom values for its properties using {@link PropertyResolverUtils#updateProperty} API.
* @param sshClientConfigurer the {@link Consumer} to configure an internal {@link SshClient} instance.
* @since 6.4
* @see SshClient
* @see PropertyResolverUtils#updateProperty
*/
public void setSshClientConfigurer(Consumer<SshClient> sshClientConfigurer) {
Assert.state(this.isInnerClient, "Cannot mutate externally provided SshClient");
Assert.notNull(sshClientConfigurer, "'sshClientConfigurer' must noy be null");
this.sshClientConfigurer = sshClientConfigurer;
}
@Override
public SftpSession getSession() {
SftpSession sftpSession;
@@ -392,6 +411,7 @@ public class DefaultSftpSessionFactory implements SessionFactory<SftpClient.DirE
}
}
this.sshClient.setUserInteraction(this.userInteraction);
this.sshClientConfigurer.accept(this.sshClient);
}
}
@@ -424,7 +444,7 @@ public class DefaultSftpSessionFactory implements SessionFactory<SftpClient.DirE
}
@Override
public void destroy() throws Exception {
public void destroy() {
if (this.isInnerClient && this.sshClient != null && this.sshClient.isStarted()) {
this.sshClient.stop();
}

View File

@@ -31,7 +31,9 @@ import org.apache.sshd.client.auth.password.PasswordIdentityProvider;
import org.apache.sshd.client.channel.ClientChannel;
import org.apache.sshd.client.keyverifier.AcceptAllServerKeyVerifier;
import org.apache.sshd.client.session.ClientSession;
import org.apache.sshd.common.PropertyResolverUtils;
import org.apache.sshd.common.SshException;
import org.apache.sshd.core.CoreModuleProperties;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
import org.apache.sshd.sftp.client.SftpClient;
@@ -206,7 +208,7 @@ public class SftpSessionFactoryTests {
}
@Test
void customTimeoutIsApplied() throws Exception {
void customPropertiesAreApplied() throws Exception {
try (SshServer server = SshServer.setUpDefaultServer()) {
server.setPasswordAuthenticator((arg0, arg1, arg2) -> true);
server.setPort(0);
@@ -221,10 +223,15 @@ public class SftpSessionFactoryTests {
sftpSessionFactory.setPassword("pass");
sftpSessionFactory.setAllowUnknownKeys(true);
sftpSessionFactory.setTimeout(15_000);
sftpSessionFactory.setSshClientConfigurer((sshClient) -> {
sshClient.setNioWorkers(27);
PropertyResolverUtils.updateProperty(sshClient, CoreModuleProperties.MAX_PACKET_SIZE.getName(), 48 * 1024);
});
ClientChannel clientChannel = sftpSessionFactory.getSession().getClientInstance().getClientChannel();
assertThat(AbstractSftpClient.SFTP_CLIENT_CMD_TIMEOUT.getRequired(clientChannel)).hasSeconds(15);
assertThat(CoreModuleProperties.MAX_PACKET_SIZE.getRequired(clientChannel)).isEqualTo(48 * 1024);
sftpSessionFactory.destroy();
}
@@ -260,4 +267,5 @@ public class SftpSessionFactoryTests {
sftpSessionFactory.destroy();
}
}
}

View File

@@ -107,3 +107,13 @@ If `false`, a pre-populated `knownHosts` file is required.
`userInteraction`::A custom `org.apache.sshd.client.auth.keyboard.UserInteraction` to be used during authentication.
Starting with version 6.4, the `DefaultSftpSessionFactory` expose a `Consumer<SshClient>` configurer property to further customize an internal `SshClient`.
For example, this is how to change a default number of NIO workers and packet size for the client:
[source, java]
----
sftpSessionFactory.setSshClientConfigurer((sshClient) -> {
sshClient.setNioWorkers(27);
PropertyResolverUtils.updateProperty(sshClient, CoreModuleProperties.MAX_PACKET_SIZE.getName(), 48 * 1024);
});
----

View File

@@ -59,4 +59,10 @@ See xref:redis.adoc[Redis Support] for more information.
=== Groovy Changes
The `ControlBusFactoryBean` (and respective `<int-groovy:control-bus>` XML tag) has been deprecated (for removal) in favor of new introduced `ControlBusFactoryBean` based on a new model implemented in the `ControlBusCommandRegistry`.
See xref:control-bus.adoc[Control Bus] for more information.
See xref:control-bus.adoc[Control Bus] for more information.
[[x6.4-sftp-changes]]
=== SFTP Support Changes
The `DefaultSftpSessionFactory` now exposes a `Consumer<SshClient>` configurer property to further customize an internal `SshClient`.
See xref:sftp/session-factory.adoc[SFTP Session Factory] for more information.