INT-1614 created SftpSession interface

This commit is contained in:
Mark Fisher
2010-11-19 10:12:55 -05:00
parent 9f410d5068
commit 8076b68bf2
6 changed files with 173 additions and 132 deletions

View File

@@ -123,7 +123,7 @@ public class SftpInboundSynchronizer extends AbstractInboundRemoteFileSystemSych
try {
session = sessionPool.getSession();
logger.trace("Pooled SftpSession " + this.sessionPool + " from the pool");
session.start();
session.connect();
this.checkThatRemotePathExists(remotePath, session);
ChannelSftp channelSftp = session.getChannel();
Collection<ChannelSftp.LsEntry> beforeFilter = channelSftp.ls(remotePath);

View File

@@ -171,7 +171,7 @@ public class SftpSendingMessageHandler extends AbstractMessageHandler {
}
InputStream fileInputStream = null;
try {
session.start();
session.connect();
ChannelSftp sftp = session.getChannel();
fileInputStream = new FileInputStream(file);
String baseOfRemotePath = "";

View File

@@ -0,0 +1,165 @@
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.sftp.session;
import java.io.InputStream;
import org.apache.commons.lang.StringUtils;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.UserInfo;
/**
* Default SftpSession implementation.
*
* @author Josh Long
* @author Mario Gray
* @author Mark Fisher
* @since 2.0
*/
public class DefaultSftpSession implements SftpSession {
private volatile ChannelSftp channel;
private volatile Session targetSession;
private String privateKey;
private String privateKeyPassphrase;
private volatile UserInfo userInfo;
/**
* @param userName the name of the account being logged into.
* @param hostName this should be the host. I found values like <code>foo.com</code> work, where
* <code>http://foo.com</code> don't.
* @param userPassword if you are not using key based authentication, then you are likely being prompted
* for a password each time you login. This is that password. It is <em>not</em> the
* passphrase for the private key!
* @param port the default is 22, and if you specify N<0 for this value we'll default it to 22
* @param knownHostsFile this is the known hosts file. If you don't specify it, jsch does some magic to work
* without your specification. If you have it in a non well-known location, however,
* this property is for you. An example: <code>/home/user/.ssh/known_hosts</code>
* @param knownHostsInputStream this is the known hosts file. If you don't specify it, jsch does some magic to work
* without your specification. If you have it in a non well-known location, however,
* this property is for you. An example: <code>/home/user/.ssh/known_hosts</code>. Note
* that you may specify this <em>or</em> the #knownHostsFile - not both!
* @param privateKey this is usually used when you want passwordless automation (obviously, for this
* integration it's useless since this lets you specify a password once, anyway, but
* still good to have if required). This file might be ~/.ssh/id_dsa, or a
* <code>.pem</code> for your remote server (for example, on EC2)
* @param pvKeyPassPhrase sometimes, to be extra secure, the private key itself is extra encrypted. In order
* to surmount that, we need the private key passphrase. Specify that here.
* @throws Exception thrown if any of a myriad of scenarios plays out
*/
public DefaultSftpSession(String userName, String hostName, String userPassword, int port, String knownHostsFile,
InputStream knownHostsInputStream, String privateKey, String pvKeyPassPhrase) throws Exception {
JSch jSch = new JSch();
if (port <= 0) {
port = 22;
}
this.privateKey = privateKey;
this.privateKeyPassphrase = pvKeyPassPhrase;
if (!StringUtils.isEmpty(knownHostsFile)) {
jSch.setKnownHosts(knownHostsFile);
}
else if (null != knownHostsInputStream) {
jSch.setKnownHosts(knownHostsInputStream);
}
// private key
if (privateKey != null) {
if (!StringUtils.isEmpty(privateKeyPassphrase)) {
jSch.addIdentity(this.privateKey, privateKeyPassphrase);
}
else {
jSch.addIdentity(this.privateKey);
}
}
this.targetSession = jSch.getSession(userName, hostName, port);
if (!StringUtils.isEmpty(userPassword)) {
this.targetSession.setPassword(userPassword);
}
this.userInfo = new OptimisticUserInfoImpl(userPassword);
this.targetSession.setUserInfo(userInfo);
this.targetSession.connect();
this.channel = (ChannelSftp) this.targetSession.openChannel("sftp");
}
public ChannelSftp getChannel() {
return channel;
}
public void connect() {
if (!channel.isConnected()) {
try {
channel.connect();
}
catch (JSchException e) {
throw new IllegalStateException("failed to connect", e);
}
}
}
public void disconnect() {
if (targetSession.isConnected()) {
targetSession.disconnect();
}
}
/**
* this is a simple, optimistic implementation of this interface. It simply returns in the positive where possible
* and handles interactive authentication (ie, 'Please enter your password: ' prompts are dispatched automatically using this)
*/
private static class OptimisticUserInfoImpl implements UserInfo {
private String password;
public OptimisticUserInfoImpl(String password) {
this.password = password;
}
public String getPassphrase() {
return null; // pass
}
public String getPassword() {
return password;
}
public boolean promptPassphrase(String string) {
return true;
}
public boolean promptPassword(String string) {
return true;
}
public boolean promptYesNo(String string) {
return true;
}
public void showMessage(String string) {
}
}
}

View File

@@ -25,7 +25,6 @@ import org.springframework.context.SmartLifecycle;
import org.springframework.util.Assert;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.Session;
/**
* This approach - of having a SessionPool ({@link SftpSessionPool}) that has an
@@ -120,10 +119,7 @@ public class QueuedSftpSessionPool implements SftpSessionPool, SmartLifecycle {
if (channel.isConnected()) {
channel.disconnect();
}
Session session = sftpSession.getSession();
if (session.isConnected()) {
session.disconnect();
}
sftpSession.disconnect();
}
}
catch (Throwable e) {

View File

@@ -16,14 +16,7 @@
package org.springframework.integration.sftp.session;
import java.io.InputStream;
import org.apache.commons.lang.StringUtils;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.UserInfo;
/**
* There are many ways to create a {@link SftpSession} just as there are many ways to SSH into a remote system.
@@ -36,125 +29,12 @@ import com.jcraft.jsch.UserInfo;
* @author Mario Gray
* @since 2.0
*/
public class SftpSession {
public interface SftpSession {
private volatile ChannelSftp channel;
ChannelSftp getChannel();
private volatile Session session;
void connect();
private String privateKey;
private String privateKeyPassphrase;
private volatile UserInfo userInfo;
/**
* @param userName the name of the account being logged into.
* @param hostName this should be the host. I found values like <code>foo.com</code> work, where
* <code>http://foo.com</code> don't.
* @param userPassword if you are not using key based authentication, then you are likely being prompted
* for a password each time you login. This is that password. It is <em>not</em> the
* passphrase for the private key!
* @param port the default is 22, and if you specify N<0 for this value we'll default it to 22
* @param knownHostsFile this is the known hosts file. If you don't specify it, jsch does some magic to work
* without your specification. If you have it in a non well-known location, however,
* this property is for you. An example: <code>/home/user/.ssh/known_hosts</code>
* @param knownHostsInputStream this is the known hosts file. If you don't specify it, jsch does some magic to work
* without your specification. If you have it in a non well-known location, however,
* this property is for you. An example: <code>/home/user/.ssh/known_hosts</code>. Note
* that you may specify this <em>or</em> the #knownHostsFile - not both!
* @param privateKey this is usually used when you want passwordless automation (obviously, for this
* integration it's useless since this lets you specify a password once, anyway, but
* still good to have if required). This file might be ~/.ssh/id_dsa, or a
* <code>.pem</code> for your remote server (for example, on EC2)
* @param pvKeyPassPhrase sometimes, to be extra secure, the private key itself is extra encrypted. In order
* to surmount that, we need the private key passphrase. Specify that here.
* @throws Exception thrown if any of a myriad of scenarios plays out
*/
public SftpSession(String userName, String hostName, String userPassword, int port, String knownHostsFile,
InputStream knownHostsInputStream, String privateKey, String pvKeyPassPhrase) throws Exception {
JSch jSch = new JSch();
if (port <= 0) {
port = 22;
}
this.privateKey = privateKey;
this.privateKeyPassphrase = pvKeyPassPhrase;
if (!StringUtils.isEmpty(knownHostsFile)) {
jSch.setKnownHosts(knownHostsFile);
}
else if (null != knownHostsInputStream) {
jSch.setKnownHosts(knownHostsInputStream);
}
// private key
if (privateKey != null) {
if (!StringUtils.isEmpty(privateKeyPassphrase)) {
jSch.addIdentity(this.privateKey, privateKeyPassphrase);
}
else {
jSch.addIdentity(this.privateKey);
}
}
this.session = jSch.getSession(userName, hostName, port);
if (!StringUtils.isEmpty(userPassword)) {
this.session.setPassword(userPassword);
}
this.userInfo = new OptimisticUserInfoImpl(userPassword);
this.session.setUserInfo(userInfo);
this.session.connect();
this.channel = (ChannelSftp) this.session.openChannel("sftp");
}
public ChannelSftp getChannel() {
return channel;
}
public Session getSession() {
return session;
}
public void start() throws Exception {
if (!channel.isConnected()) {
channel.connect();
}
}
/**
* this is a simple, optimistic implementation of this interface. It simply returns in the positive where possible
* and handles interactive authentication (ie, 'Please enter your password: ' prompts are dispatched automatically using this)
*/
private static class OptimisticUserInfoImpl implements UserInfo {
private String password;
public OptimisticUserInfoImpl(String password) {
this.password = password;
}
public String getPassphrase() {
return null; // pass
}
public String getPassword() {
return password;
}
public boolean promptPassphrase(String string) {
return true;
}
public boolean promptPassword(String string) {
return true;
}
public boolean promptYesNo(String string) {
return true;
}
public void showMessage(String string) {
}
}
void disconnect();
}

View File

@@ -84,7 +84,7 @@ public class SimpleSftpSessionFactory implements SftpSessionFactory {
if (privateKey != null){
privateKeyToPass = privateKey.getFile().getAbsolutePath();
}
return new SftpSession(this.user, this.host, this.password, this.port, this.knownHosts, null, privateKeyToPass, this.privateKeyPassphrase);
return new DefaultSftpSession(this.user, this.host, this.password, this.port, this.knownHosts, null, privateKeyToPass, this.privateKeyPassphrase);
}
catch (Exception e) {
throw new IllegalStateException("failed to create SFTP Session", e);