Skip to content

Commit 38fc85a

Browse files
artembilangaryrussell
authored andcommitted
Optimize DefaultSftpSessionFactory (#2896)
* Optimize DefaultSftpSessionFactory Related to https://build.spring.io/browse/INT-MASTERSPRING40-677/ Doesn't look like `DefaultSftpSessionFactory.getSession()` needs locking around `sharedJschSession` * change the logic in the `getSession()` to store a `sharedJschSession` into the local variable and if it is `null` or not connected, create a new `JSchSessionWrapper`, connect it and store to the `sharedJschSession` back into the `sharedJschSession` property if `this.isSharedSession`. This way we always deal with `sharedJschSession` anyway if it is valid or create a new fresh one if that is invalid. Without locking we always get an actual state of the `sharedJschSession` and don't fall into the race condition when `sharedJschSession` is invalid, but we can't connect to the SFTP channel from the `sftpSession.connect()` * * Wrap `sharedJschSession` initialization to the lock * * Store `sharedJschSession` back when it is really fresh and while the lock
1 parent 0ad731d commit 38fc85a

File tree

1 file changed

+57
-65
lines changed

1 file changed

+57
-65
lines changed

spring-integration-sftp/src/main/java/org/springframework/integration/sftp/session/DefaultSftpSessionFactory.java

Lines changed: 57 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
import java.io.IOException;
2020
import java.util.Arrays;
2121
import java.util.Properties;
22-
import java.util.concurrent.locks.ReadWriteLock;
23-
import java.util.concurrent.locks.ReentrantReadWriteLock;
22+
import java.util.concurrent.locks.Lock;
23+
import java.util.concurrent.locks.ReentrantLock;
2424

2525
import org.apache.commons.logging.Log;
2626
import org.apache.commons.logging.LogFactory;
@@ -64,51 +64,51 @@ public class DefaultSftpSessionFactory implements SessionFactory<LsEntry>, Share
6464
JSch.setLogger(new JschLogger());
6565
}
6666

67-
private final ReadWriteLock sharedSessionLock = new ReentrantReadWriteLock();
68-
6967
private final UserInfo userInfoWrapper = new UserInfoWrapper();
7068

7169
private final JSch jsch;
7270

7371
private final boolean isSharedSession;
7472

75-
private volatile String host;
73+
private final Lock sharedSessionLock;
7674

77-
private volatile int port = 22; // the default
75+
private String host;
7876

79-
private volatile String user;
77+
private int port = 22; // the default
8078

81-
private volatile String password;
79+
private String user;
8280

83-
private volatile String knownHosts;
81+
private String password;
8482

85-
private volatile Resource privateKey;
83+
private String knownHosts;
8684

87-
private volatile String privateKeyPassphrase;
85+
private Resource privateKey;
8886

89-
private volatile Properties sessionConfig;
87+
private String privateKeyPassphrase;
9088

91-
private volatile Proxy proxy;
89+
private Properties sessionConfig;
9290

93-
private volatile SocketFactory socketFactory;
91+
private Proxy proxy;
9492

95-
private volatile Integer timeout;
93+
private SocketFactory socketFactory;
9694

97-
private volatile String clientVersion;
95+
private Integer timeout;
9896

99-
private volatile String hostKeyAlias;
97+
private String clientVersion;
10098

101-
private volatile Integer serverAliveInterval;
99+
private String hostKeyAlias;
102100

103-
private volatile Integer serverAliveCountMax;
101+
private Integer serverAliveInterval;
104102

105-
private volatile Boolean enableDaemonThread;
103+
private Integer serverAliveCountMax;
106104

107-
private volatile JSchSessionWrapper sharedJschSession;
105+
private Boolean enableDaemonThread;
108106

109-
private volatile UserInfo userInfo;
107+
private UserInfo userInfo;
110108

111-
private volatile boolean allowUnknownKeys = false;
109+
private boolean allowUnknownKeys = false;
110+
111+
private volatile JSchSessionWrapper sharedJschSession;
112112

113113

114114
public DefaultSftpSessionFactory() {
@@ -130,6 +130,12 @@ public DefaultSftpSessionFactory(boolean isSharedSession) {
130130
public DefaultSftpSessionFactory(JSch jsch, boolean isSharedSession) {
131131
this.jsch = jsch;
132132
this.isSharedSession = isSharedSession;
133+
if (this.isSharedSession) {
134+
this.sharedSessionLock = new ReentrantLock();
135+
}
136+
else {
137+
this.sharedSessionLock = null;
138+
}
133139
}
134140

135141
/**
@@ -349,57 +355,41 @@ public void setAllowUnknownKeys(boolean allowUnknownKeys) {
349355

350356
@Override
351357
public SftpSession getSession() {
352-
Assert.hasText(this.host, "host must not be empty");
353-
Assert.hasText(this.user, "user must not be empty");
354-
Assert.isTrue(StringUtils.hasText(this.userInfoWrapper.getPassword()) || this.privateKey != null,
355-
"either a password or a private key is required");
358+
JSchSessionWrapper jschSession = this.sharedJschSession;
359+
SftpSession sftpSession;
360+
if (this.sharedSessionLock != null) {
361+
this.sharedSessionLock.lock();
362+
}
356363
try {
357-
JSchSessionWrapper jschSession;
358-
SftpSession sftpSession;
359-
if (this.isSharedSession) {
360-
this.sharedSessionLock.readLock().lock();
361-
try {
362-
if (this.sharedJschSession == null || !this.sharedJschSession.isConnected()) {
363-
this.sharedSessionLock.readLock().unlock();
364-
this.sharedSessionLock.writeLock().lock();
365-
try {
366-
if (this.sharedJschSession == null || !this.sharedJschSession.isConnected()) {
367-
this.sharedJschSession = new JSchSessionWrapper(initJschSession());
368-
try {
369-
this.sharedJschSession.getSession().connect();
370-
}
371-
catch (JSchException e) {
372-
throw new IllegalStateException("failed to connect", e);
373-
}
374-
}
375-
}
376-
finally {
377-
this.sharedSessionLock.readLock().lock();
378-
this.sharedSessionLock.writeLock().unlock();
379-
}
380-
}
381-
jschSession = this.sharedJschSession;
382-
sftpSession = new SftpSession(jschSession);
383-
sftpSession.connect();
384-
}
385-
finally {
386-
this.sharedSessionLock.readLock().unlock();
387-
}
388-
}
389-
else {
364+
boolean freshJschSession = false;
365+
if (jschSession == null || !jschSession.isConnected()) {
390366
jschSession = new JSchSessionWrapper(initJschSession());
391-
sftpSession = new SftpSession(jschSession);
392-
sftpSession.connect();
367+
freshJschSession = true;
368+
}
369+
sftpSession = new SftpSession(jschSession);
370+
sftpSession.connect();
371+
if (this.isSharedSession && freshJschSession) {
372+
this.sharedJschSession = jschSession;
393373
}
394-
jschSession.addChannel();
395-
return sftpSession;
396374
}
397375
catch (Exception e) {
398376
throw new IllegalStateException("failed to create SFTP Session", e);
399377
}
378+
finally {
379+
if (this.sharedSessionLock != null) {
380+
this.sharedSessionLock.unlock();
381+
}
382+
}
383+
jschSession.addChannel();
384+
return sftpSession;
400385
}
401386

402387
private com.jcraft.jsch.Session initJschSession() throws JSchException, IOException {
388+
Assert.hasText(this.host, "host must not be empty");
389+
Assert.hasText(this.user, "user must not be empty");
390+
Assert.isTrue(StringUtils.hasText(this.userInfoWrapper.getPassword()) || this.privateKey != null,
391+
"either a password or a private key is required");
392+
403393
if (this.port <= 0) {
404394
this.port = 22;
405395
}
@@ -564,18 +554,20 @@ public void showMessage(String message) {
564554
@Override
565555
public String[] promptKeyboardInteractive(String destination, String name, String instruction, String[] prompt,
566556
boolean[] echo) {
557+
567558
if (hasDelegate() && getDelegate() instanceof UIKeyboardInteractive) {
568559
return ((UIKeyboardInteractive) getDelegate()).promptKeyboardInteractive(destination, name,
569560
instruction, prompt, echo);
570561
}
571562
else {
572563
if (logger.isDebugEnabled()) {
573564
logger.debug("No UIKeyboardInteractive provided - " + destination + ":" + name + ":" + instruction
574-
+ ":" + Arrays.asList(prompt) + ":" + Arrays.asList(echo));
565+
+ ":" + Arrays.asList(prompt) + ":" + Arrays.toString(echo));
575566
}
576567
return null;
577568
}
578569
}
570+
579571
}
580572

581573
}

0 commit comments

Comments
 (0)