Skip to content

Commit 8d85a87

Browse files
committed
spring-projectsGH-3572: Migrate SFTP from jsch to sshd-sftp
Fixes spring-projects#3572 * Rework SFTP module from the JSch API to more modern `sshd-sftp` * Migrate generics of most API from `ChannelSftp.LsEntry` to the `SftpClient.DirEntry` * Rework `DefaultSftpSessionFactory` to deal with an `SshClient` and create `SftpClient` wrapped to the `SftpSession` * Implement a `ResourceKnownHostsServerKeyVerifier` to load `known-hosts` from any possible resource * Implement an expected `SftpSession.list()` with just file name to take or pattern matching * Remove some unused tests and their config * Remove tests for custom `UserInfo` since we don't provide any custom out-of-the-box * Test a new `ResourceKnownHostsServerKeyVerifier` against default `known-hosts` file
1 parent d825ade commit 8d85a87

File tree

59 files changed

+1037
-2216
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1037
-2216
lines changed

build.gradle

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,6 @@ ext {
7676
jmsApiVersion = '3.0.0'
7777
jpaApiVersion = '3.0.3'
7878
jrubyVersion = '9.3.8.0'
79-
jschVersion = '0.1.55'
8079
jsonpathVersion = '2.7.0'
8180
junit4Version = '4.13.2'
8281
junitJupiterVersion = '5.9.0'
@@ -896,13 +895,11 @@ project('spring-integration-sftp') {
896895
description = 'Spring Integration SFTP Support'
897896
dependencies {
898897
api project(':spring-integration-file')
899-
api "com.jcraft:jsch:$jschVersion"
900898
api 'org.springframework:spring-context-support'
901-
optionalApi ("org.apache.sshd:sshd-sftp:$apacheSshdVersion") {
899+
api ("org.apache.sshd:sshd-sftp:$apacheSshdVersion") {
902900
exclude group: 'org.slf4j', module: 'jcl-over-slf4j'
903901
}
904902

905-
testImplementation "org.apache.sshd:sshd-core:$apacheSshdVersion"
906903
testImplementation project(':spring-integration-event')
907904
testImplementation project(':spring-integration-file').sourceSets.test.output
908905
}

spring-integration-file/src/main/java/org/springframework/integration/file/dsl/RemoteFileInboundChannelAdapterSpec.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2020 the original author or authors.
2+
* Copyright 2016-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -290,7 +290,7 @@ public S scanner(DirectoryScanner scanner) {
290290
* @return the spec.
291291
* @since 5.2.9
292292
*/
293-
public S remoteComparator(Comparator<F> remoteComparator) {
293+
public S remoteComparator(Comparator<? extends F> remoteComparator) {
294294
this.synchronizer.setComparator(remoteComparator);
295295
return _this();
296296
}

spring-integration-file/src/main/java/org/springframework/integration/file/remote/synchronizer/AbstractInboundFileSynchronizer.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -151,8 +151,9 @@ protected Comparator<F> getComparator() {
151151
* @param comparator the comparator.
152152
* @since 5.1
153153
*/
154-
public void setComparator(@Nullable Comparator<F> comparator) {
155-
this.comparator = comparator;
154+
@SuppressWarnings("unchecked")
155+
public void setComparator(@Nullable Comparator<? extends F> comparator) {
156+
this.comparator = (Comparator<F>) comparator;
156157
}
157158

158159
/**

spring-integration-sftp/src/main/java/org/springframework/integration/sftp/dsl/Sftp.java

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import java.io.File;
2020
import java.util.Comparator;
2121

22+
import org.apache.sshd.sftp.client.SftpClient;
23+
2224
import org.springframework.integration.file.remote.MessageSessionCallback;
2325
import org.springframework.integration.file.remote.RemoteFileTemplate;
2426
import org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway;
@@ -27,9 +29,6 @@
2729
import org.springframework.integration.sftp.gateway.SftpOutboundGateway;
2830
import org.springframework.integration.sftp.session.SftpRemoteFileTemplate;
2931

30-
import com.jcraft.jsch.ChannelSftp;
31-
import com.jcraft.jsch.ChannelSftp.LsEntry;
32-
3332
/**
3433
* The factory for SFTP components.
3534
*
@@ -46,7 +45,7 @@ public final class Sftp {
4645
* @param sessionFactory the session factory.
4746
* @return the spec.
4847
*/
49-
public static SftpInboundChannelAdapterSpec inboundAdapter(SessionFactory<ChannelSftp.LsEntry> sessionFactory) {
48+
public static SftpInboundChannelAdapterSpec inboundAdapter(SessionFactory<SftpClient.DirEntry> sessionFactory) {
5049
return inboundAdapter(sessionFactory, null);
5150
}
5251

@@ -56,7 +55,7 @@ public static SftpInboundChannelAdapterSpec inboundAdapter(SessionFactory<Channe
5655
* @param receptionOrderComparator the comparator.
5756
* @return the spec.
5857
*/
59-
public static SftpInboundChannelAdapterSpec inboundAdapter(SessionFactory<ChannelSftp.LsEntry> sessionFactory,
58+
public static SftpInboundChannelAdapterSpec inboundAdapter(SessionFactory<SftpClient.DirEntry> sessionFactory,
6059
Comparator<File> receptionOrderComparator) {
6160

6261
return new SftpInboundChannelAdapterSpec(sessionFactory, receptionOrderComparator);
@@ -69,7 +68,7 @@ public static SftpInboundChannelAdapterSpec inboundAdapter(SessionFactory<Channe
6968
* @return the spec.
7069
*/
7170
public static SftpStreamingInboundChannelAdapterSpec inboundStreamingAdapter(
72-
RemoteFileTemplate<LsEntry> remoteFileTemplate) {
71+
RemoteFileTemplate<SftpClient.DirEntry> remoteFileTemplate) {
7372

7473
return inboundStreamingAdapter(remoteFileTemplate, null);
7574
}
@@ -82,8 +81,8 @@ public static SftpStreamingInboundChannelAdapterSpec inboundStreamingAdapter(
8281
* @return the spec.
8382
*/
8483
public static SftpStreamingInboundChannelAdapterSpec inboundStreamingAdapter(
85-
RemoteFileTemplate<LsEntry> remoteFileTemplate,
86-
Comparator<LsEntry> receptionOrderComparator) {
84+
RemoteFileTemplate<SftpClient.DirEntry> remoteFileTemplate,
85+
Comparator<SftpClient.DirEntry> receptionOrderComparator) {
8786

8887
return new SftpStreamingInboundChannelAdapterSpec(remoteFileTemplate, receptionOrderComparator);
8988
}
@@ -93,7 +92,7 @@ public static SftpStreamingInboundChannelAdapterSpec inboundStreamingAdapter(
9392
* @param sessionFactory the session factory.
9493
* @return the spec.
9594
*/
96-
public static SftpMessageHandlerSpec outboundAdapter(SessionFactory<ChannelSftp.LsEntry> sessionFactory) {
95+
public static SftpMessageHandlerSpec outboundAdapter(SessionFactory<SftpClient.DirEntry> sessionFactory) {
9796
return new SftpMessageHandlerSpec(sessionFactory);
9897
}
9998

@@ -103,7 +102,7 @@ public static SftpMessageHandlerSpec outboundAdapter(SessionFactory<ChannelSftp.
103102
* @param fileExistsMode the file exists mode.
104103
* @return the spec.
105104
*/
106-
public static SftpMessageHandlerSpec outboundAdapter(SessionFactory<ChannelSftp.LsEntry> sessionFactory,
105+
public static SftpMessageHandlerSpec outboundAdapter(SessionFactory<SftpClient.DirEntry> sessionFactory,
107106
FileExistsMode fileExistsMode) {
108107

109108
return outboundAdapter(new SftpRemoteFileTemplate(sessionFactory), fileExistsMode);
@@ -141,7 +140,7 @@ public static SftpMessageHandlerSpec outboundAdapter(SftpRemoteFileTemplate sftp
141140
* @param expression the remoteFilePath SpEL expression.
142141
* @return the {@link SftpOutboundGatewaySpec}
143142
*/
144-
public static SftpOutboundGatewaySpec outboundGateway(SessionFactory<ChannelSftp.LsEntry> sessionFactory,
143+
public static SftpOutboundGatewaySpec outboundGateway(SessionFactory<SftpClient.DirEntry> sessionFactory,
145144
AbstractRemoteFileOutboundGateway.Command command, String expression) {
146145

147146
return outboundGateway(sessionFactory, command.getCommand(), expression);
@@ -157,7 +156,7 @@ public static SftpOutboundGatewaySpec outboundGateway(SessionFactory<ChannelSftp
157156
* @return the {@link SftpOutboundGatewaySpec}
158157
* @see RemoteFileTemplate
159158
*/
160-
public static SftpOutboundGatewaySpec outboundGateway(SessionFactory<ChannelSftp.LsEntry> sessionFactory,
159+
public static SftpOutboundGatewaySpec outboundGateway(SessionFactory<SftpClient.DirEntry> sessionFactory,
161160
String command, String expression) {
162161

163162
return new SftpOutboundGatewaySpec(new SftpOutboundGateway(sessionFactory, command, expression));
@@ -172,7 +171,7 @@ public static SftpOutboundGatewaySpec outboundGateway(SessionFactory<ChannelSftp
172171
* @return the {@link SftpOutboundGatewaySpec}
173172
* @see RemoteFileTemplate
174173
*/
175-
public static SftpOutboundGatewaySpec outboundGateway(RemoteFileTemplate<ChannelSftp.LsEntry> remoteFileTemplate,
174+
public static SftpOutboundGatewaySpec outboundGateway(RemoteFileTemplate<SftpClient.DirEntry> remoteFileTemplate,
176175
AbstractRemoteFileOutboundGateway.Command command, String expression) {
177176

178177
return outboundGateway(remoteFileTemplate, command.getCommand(), expression);
@@ -187,7 +186,7 @@ public static SftpOutboundGatewaySpec outboundGateway(RemoteFileTemplate<Channel
187186
* @return the {@link SftpOutboundGatewaySpec}
188187
* @see RemoteFileTemplate
189188
*/
190-
public static SftpOutboundGatewaySpec outboundGateway(RemoteFileTemplate<ChannelSftp.LsEntry> remoteFileTemplate,
189+
public static SftpOutboundGatewaySpec outboundGateway(RemoteFileTemplate<SftpClient.DirEntry> remoteFileTemplate,
191190
String command, String expression) {
192191

193192
return new SftpOutboundGatewaySpec(new SftpOutboundGateway(remoteFileTemplate, command, expression));
@@ -201,8 +200,8 @@ public static SftpOutboundGatewaySpec outboundGateway(RemoteFileTemplate<Channel
201200
* @return the {@link SftpOutboundGatewaySpec}
202201
* @see MessageSessionCallback
203202
*/
204-
public static SftpOutboundGatewaySpec outboundGateway(SessionFactory<ChannelSftp.LsEntry> sessionFactory,
205-
MessageSessionCallback<ChannelSftp.LsEntry, ?> messageSessionCallback) {
203+
public static SftpOutboundGatewaySpec outboundGateway(SessionFactory<SftpClient.DirEntry> sessionFactory,
204+
MessageSessionCallback<SftpClient.DirEntry, ?> messageSessionCallback) {
206205

207206
return new SftpOutboundGatewaySpec(new SftpOutboundGateway(sessionFactory, messageSessionCallback));
208207
}

spring-integration-sftp/src/main/java/org/springframework/integration/sftp/dsl/SftpInboundChannelAdapterSpec.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2014-2020 the original author or authors.
2+
* Copyright 2014-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@
1919
import java.io.File;
2020
import java.util.Comparator;
2121

22+
import org.apache.sshd.sftp.client.SftpClient;
23+
2224
import org.springframework.integration.file.dsl.RemoteFileInboundChannelAdapterSpec;
2325
import org.springframework.integration.file.filters.CompositeFileListFilter;
2426
import org.springframework.integration.file.filters.FileListFilter;
@@ -30,8 +32,6 @@
3032
import org.springframework.integration.sftp.inbound.SftpInboundFileSynchronizer;
3133
import org.springframework.integration.sftp.inbound.SftpInboundFileSynchronizingMessageSource;
3234

33-
import com.jcraft.jsch.ChannelSftp;
34-
3535
/**
3636
* A {@link RemoteFileInboundChannelAdapterSpec} for an {@link SftpInboundFileSynchronizingMessageSource}.
3737
*
@@ -40,10 +40,10 @@
4040
* @since 5.0
4141
*/
4242
public class SftpInboundChannelAdapterSpec
43-
extends RemoteFileInboundChannelAdapterSpec<ChannelSftp.LsEntry, SftpInboundChannelAdapterSpec,
44-
SftpInboundFileSynchronizingMessageSource> {
43+
extends RemoteFileInboundChannelAdapterSpec<SftpClient.DirEntry, SftpInboundChannelAdapterSpec,
44+
SftpInboundFileSynchronizingMessageSource> {
4545

46-
protected SftpInboundChannelAdapterSpec(SessionFactory<ChannelSftp.LsEntry> sessionFactory,
46+
protected SftpInboundChannelAdapterSpec(SessionFactory<SftpClient.DirEntry> sessionFactory,
4747
Comparator<File> comparator) {
4848

4949
super(new SftpInboundFileSynchronizer(sessionFactory));
@@ -70,9 +70,10 @@ public SftpInboundChannelAdapterSpec regexFilter(String regex) {
7070
return filter(composeFilters(new SftpRegexPatternFileListFilter(regex)));
7171
}
7272

73-
private CompositeFileListFilter<ChannelSftp.LsEntry> composeFilters(FileListFilter<ChannelSftp.LsEntry>
73+
private CompositeFileListFilter<SftpClient.DirEntry> composeFilters(FileListFilter<SftpClient.DirEntry>
7474
fileListFilter) {
75-
CompositeFileListFilter<ChannelSftp.LsEntry> compositeFileListFilter = new CompositeFileListFilter<>();
75+
76+
CompositeFileListFilter<SftpClient.DirEntry> compositeFileListFilter = new CompositeFileListFilter<>();
7677
compositeFileListFilter.addFilters(fileListFilter,
7778
new SftpPersistentAcceptOnceFileListFilter(new SimpleMetadataStore(), "sftpMessageSource"));
7879
return compositeFileListFilter;

spring-integration-sftp/src/main/java/org/springframework/integration/sftp/dsl/SftpMessageHandlerSpec.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@
1616

1717
package org.springframework.integration.sftp.dsl;
1818

19+
import org.apache.sshd.sftp.client.SftpClient;
20+
1921
import org.springframework.integration.file.dsl.FileTransferringMessageHandlerSpec;
2022
import org.springframework.integration.file.remote.session.SessionFactory;
2123
import org.springframework.integration.file.support.FileExistsMode;
2224
import org.springframework.integration.sftp.outbound.SftpMessageHandler;
2325
import org.springframework.integration.sftp.session.SftpRemoteFileTemplate;
2426

25-
import com.jcraft.jsch.ChannelSftp;
26-
2727
/**
2828
* @author Artem Bilan
2929
* @author Joaquin Santana
@@ -32,9 +32,9 @@
3232
* @since 5.0
3333
*/
3434
public class SftpMessageHandlerSpec
35-
extends FileTransferringMessageHandlerSpec<ChannelSftp.LsEntry, SftpMessageHandlerSpec> {
35+
extends FileTransferringMessageHandlerSpec<SftpClient.DirEntry, SftpMessageHandlerSpec> {
3636

37-
protected SftpMessageHandlerSpec(SessionFactory<ChannelSftp.LsEntry> sessionFactory) {
37+
protected SftpMessageHandlerSpec(SessionFactory<SftpClient.DirEntry> sessionFactory) {
3838
this.target = new SftpMessageHandler(sessionFactory);
3939
}
4040

spring-integration-sftp/src/main/java/org/springframework/integration/sftp/dsl/SftpOutboundGatewaySpec.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,24 @@
1616

1717
package org.springframework.integration.sftp.dsl;
1818

19+
import org.apache.sshd.sftp.client.SftpClient;
20+
1921
import org.springframework.integration.file.dsl.RemoteFileOutboundGatewaySpec;
2022
import org.springframework.integration.file.remote.gateway.AbstractRemoteFileOutboundGateway;
2123
import org.springframework.integration.sftp.filters.SftpRegexPatternFileListFilter;
2224
import org.springframework.integration.sftp.filters.SftpSimplePatternFileListFilter;
2325

24-
import com.jcraft.jsch.ChannelSftp;
25-
2626
/**
2727
* @author Artem Bilan
2828
* @author Gary Russell
2929
*
3030
* @since 5.0
3131
*/
3232
public class SftpOutboundGatewaySpec
33-
extends RemoteFileOutboundGatewaySpec<ChannelSftp.LsEntry, SftpOutboundGatewaySpec> {
33+
extends RemoteFileOutboundGatewaySpec<SftpClient.DirEntry, SftpOutboundGatewaySpec> {
3434

3535

36-
protected SftpOutboundGatewaySpec(AbstractRemoteFileOutboundGateway<ChannelSftp.LsEntry> outboundGateway) {
36+
protected SftpOutboundGatewaySpec(AbstractRemoteFileOutboundGateway<SftpClient.DirEntry> outboundGateway) {
3737
super(outboundGateway);
3838
}
3939

spring-integration-sftp/src/main/java/org/springframework/integration/sftp/dsl/SftpStreamingInboundChannelAdapterSpec.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2020 the original author or authors.
2+
* Copyright 2016-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,6 +18,8 @@
1818

1919
import java.util.Comparator;
2020

21+
import org.apache.sshd.sftp.client.SftpClient;
22+
2123
import org.springframework.integration.file.dsl.RemoteFileStreamingInboundChannelAdapterSpec;
2224
import org.springframework.integration.file.filters.CompositeFileListFilter;
2325
import org.springframework.integration.file.filters.FileListFilter;
@@ -28,20 +30,18 @@
2830
import org.springframework.integration.sftp.filters.SftpSimplePatternFileListFilter;
2931
import org.springframework.integration.sftp.inbound.SftpStreamingMessageSource;
3032

31-
import com.jcraft.jsch.ChannelSftp.LsEntry;
32-
3333
/**
3434
* @author Gary Russell
3535
*
3636
* @since 5.0
3737
*
3838
*/
3939
public class SftpStreamingInboundChannelAdapterSpec
40-
extends RemoteFileStreamingInboundChannelAdapterSpec<LsEntry, SftpStreamingInboundChannelAdapterSpec,
41-
SftpStreamingMessageSource> {
40+
extends RemoteFileStreamingInboundChannelAdapterSpec<SftpClient.DirEntry,
41+
SftpStreamingInboundChannelAdapterSpec, SftpStreamingMessageSource> {
4242

43-
protected SftpStreamingInboundChannelAdapterSpec(RemoteFileTemplate<LsEntry> remoteFileTemplate,
44-
Comparator<LsEntry> comparator) {
43+
protected SftpStreamingInboundChannelAdapterSpec(RemoteFileTemplate<SftpClient.DirEntry> remoteFileTemplate,
44+
Comparator<SftpClient.DirEntry> comparator) {
4545

4646
this.target = new SftpStreamingMessageSource(remoteFileTemplate, comparator);
4747
}
@@ -68,8 +68,10 @@ public SftpStreamingInboundChannelAdapterSpec regexFilter(String regex) {
6868
return filter(composeFilters(new SftpRegexPatternFileListFilter(regex)));
6969
}
7070

71-
private CompositeFileListFilter<LsEntry> composeFilters(FileListFilter<LsEntry> fileListFilter) {
72-
CompositeFileListFilter<LsEntry> compositeFileListFilter = new CompositeFileListFilter<>();
71+
private CompositeFileListFilter<SftpClient.DirEntry> composeFilters(
72+
FileListFilter<SftpClient.DirEntry> fileListFilter) {
73+
74+
CompositeFileListFilter<SftpClient.DirEntry> compositeFileListFilter = new CompositeFileListFilter<>();
7375
compositeFileListFilter.addFilters(fileListFilter,
7476
new SftpPersistentAcceptOnceFileListFilter(new SimpleMetadataStore(), "sftpStreamingMessageSource"));
7577
return compositeFileListFilter;

spring-integration-sftp/src/main/java/org/springframework/integration/sftp/filters/SftpPersistentAcceptOnceFileListFilter.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,40 +16,42 @@
1616

1717
package org.springframework.integration.sftp.filters;
1818

19+
import org.apache.sshd.sftp.client.SftpClient;
20+
1921
import org.springframework.integration.file.filters.AbstractPersistentAcceptOnceFileListFilter;
2022
import org.springframework.integration.metadata.ConcurrentMetadataStore;
2123

22-
import com.jcraft.jsch.ChannelSftp.LsEntry;
23-
2424
/**
2525
* Persistent file list filter using the server's file timestamp to detect if we've already
2626
* 'seen' this file.
2727
*
2828
* @author Gary Russell
2929
* @author David Liu
30+
* @author Artem Bilan
3031
*
3132
* @since 3.0
3233
*
3334
*/
34-
public class SftpPersistentAcceptOnceFileListFilter extends AbstractPersistentAcceptOnceFileListFilter<LsEntry> {
35+
public class SftpPersistentAcceptOnceFileListFilter
36+
extends AbstractPersistentAcceptOnceFileListFilter<SftpClient.DirEntry> {
3537

3638
public SftpPersistentAcceptOnceFileListFilter(ConcurrentMetadataStore store, String prefix) {
3739
super(store, prefix);
3840
}
3941

4042
@Override
41-
protected long modified(LsEntry file) {
42-
return ((long) file.getAttrs().getMTime()) * 1000; // NOSONAR magic number
43+
protected long modified(SftpClient.DirEntry file) {
44+
return file.getAttributes().getModifyTime().toMillis();
4345
}
4446

4547
@Override
46-
protected String fileName(LsEntry file) {
48+
protected String fileName(SftpClient.DirEntry file) {
4749
return file.getFilename();
4850
}
4951

5052
@Override
51-
protected boolean isDirectory(LsEntry file) {
52-
return file.getAttrs().isDir();
53+
protected boolean isDirectory(SftpClient.DirEntry file) {
54+
return file.getAttributes().isDirectory();
5355
}
5456

5557
}

0 commit comments

Comments
 (0)