Skip to content

Commit 4aa2f91

Browse files
authored
GH-3572: Migrate SFTP from jsch to sshd-sftp (#3892)
* GH-3572: Migrate SFTP from `jsch` to `sshd-sftp` Fixes #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 * * Some tests improvements * * Improve generics handling for `FileUtils`
1 parent 6917663 commit 4aa2f91

File tree

61 files changed

+1050
-2234
lines changed

Some content is hidden

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

61 files changed

+1050
-2234
lines changed

build.gradle

+1-4
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'
@@ -900,13 +899,11 @@ project('spring-integration-sftp') {
900899
description = 'Spring Integration SFTP Support'
901900
dependencies {
902901
api project(':spring-integration-file')
903-
api "com.jcraft:jsch:$jschVersion"
904902
api 'org.springframework:spring-context-support'
905-
optionalApi ("org.apache.sshd:sshd-sftp:$apacheSshdVersion") {
903+
api ("org.apache.sshd:sshd-sftp:$apacheSshdVersion") {
906904
exclude group: 'org.slf4j', module: 'jcl-over-slf4j'
907905
}
908906

909-
testImplementation "org.apache.sshd:sshd-core:$apacheSshdVersion"
910907
testImplementation project(':spring-integration-event')
911908
testImplementation project(':spring-integration-file').sourceSets.test.output
912909
}

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

+2-2
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/AbstractRemoteFileStreamingMessageSource.java

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2021 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.
@@ -62,11 +62,11 @@
6262
public abstract class AbstractRemoteFileStreamingMessageSource<F>
6363
extends AbstractFetchLimitingMessageSource<InputStream> implements ManageableLifecycle {
6464

65-
private final RemoteFileTemplate<F> remoteFileTemplate;
65+
private final RemoteFileTemplate<? extends F> remoteFileTemplate;
6666

6767
private final BlockingQueue<AbstractFileInfo<F>> toBeReceived = new LinkedBlockingQueue<>();
6868

69-
private final Comparator<F> comparator;
69+
private final Comparator<? extends F> comparator;
7070

7171
private final AtomicBoolean running = new AtomicBoolean();
7272

@@ -86,8 +86,8 @@ public abstract class AbstractRemoteFileStreamingMessageSource<F>
8686
*/
8787
private FileListFilter<F> filter;
8888

89-
protected AbstractRemoteFileStreamingMessageSource(RemoteFileTemplate<F> template,
90-
@Nullable Comparator<F> comparator) {
89+
protected AbstractRemoteFileStreamingMessageSource(RemoteFileTemplate<? extends F> template,
90+
@Nullable Comparator<? extends F> comparator) {
9191

9292
Assert.notNull(template, "'template' must not be null");
9393
this.remoteFileTemplate = template;
@@ -143,7 +143,7 @@ public void setFileInfoJson(boolean fileInfoJson) {
143143
this.fileInfoJson = fileInfoJson;
144144
}
145145

146-
protected RemoteFileTemplate<F> getRemoteFileTemplate() {
146+
protected RemoteFileTemplate<? extends F> getRemoteFileTemplate() {
147147
return this.remoteFileTemplate;
148148
}
149149

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

+4-4
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.
@@ -123,7 +123,7 @@ public abstract class AbstractInboundFileSynchronizer<F>
123123
private BeanFactory beanFactory;
124124

125125
@Nullable
126-
private Comparator<F> comparator;
126+
private Comparator<? extends F> comparator;
127127

128128
private MetadataStore remoteFileMetadataStore = new SimpleMetadataStore();
129129

@@ -141,7 +141,7 @@ public AbstractInboundFileSynchronizer(SessionFactory<F> sessionFactory) {
141141
}
142142

143143
@Nullable
144-
protected Comparator<F> getComparator() {
144+
protected Comparator<? extends F> getComparator() {
145145
return this.comparator;
146146
}
147147

@@ -151,7 +151,7 @@ protected Comparator<F> getComparator() {
151151
* @param comparator the comparator.
152152
* @since 5.1
153153
*/
154-
public void setComparator(@Nullable Comparator<F> comparator) {
154+
public void setComparator(@Nullable Comparator<? extends F> comparator) {
155155
this.comparator = comparator;
156156
}
157157

spring-integration-file/src/main/java/org/springframework/integration/file/support/FileUtils.java

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2017-2019 the original author or authors.
2+
* Copyright 2017-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.
@@ -29,6 +29,7 @@
2929
* Utilities for operations on Files.
3030
*
3131
* @author Gary Russell
32+
* @author Artem Bilan
3233
*
3334
* @since 5.0
3435
*
@@ -47,22 +48,22 @@ public final class FileUtils {
4748
* @since 5.0.7
4849
*/
4950
@SuppressWarnings("unchecked")
50-
public static <F> F[] purgeUnwantedElements(F[] fileArray, Predicate<F> predicate,
51-
@Nullable Comparator<F> comparator) {
51+
public static <F> F[] purgeUnwantedElements(F[] fileArray, Predicate<? extends F> predicate,
52+
@Nullable Comparator<? extends F> comparator) {
5253

5354
if (ObjectUtils.isEmpty(fileArray)) {
5455
return fileArray;
5556
}
5657
else {
5758
if (comparator == null) {
5859
return Arrays.stream(fileArray)
59-
.filter(predicate.negate())
60+
.filter((Predicate<? super F>) predicate.negate())
6061
.toArray(size -> (F[]) Array.newInstance(fileArray[0].getClass(), size));
6162
}
6263
else {
6364
return Arrays.stream(fileArray)
64-
.filter(predicate.negate())
65-
.sorted(comparator)
65+
.filter((Predicate<? super F>) predicate.negate())
66+
.sorted((Comparator<? super F>) comparator)
6667
.toArray(size -> (F[]) Array.newInstance(fileArray[0].getClass(), size));
6768
}
6869
}

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

+15-16
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

+9-8
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

+4-4
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

+4-4
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

0 commit comments

Comments
 (0)