JSCH: closing channel and session - java

I'm doing some SFTP operation with JSCH:
JSch jsch = new JSch();
Session session = null;
ChannelSftp sftpChannel = null;
try {
session = jsch.getSession(user, host, Integer.valueOf(port));
session.setConfig(JSCH_OPTIONS);
session.setPassword(password);
session.connect();
Channel channel = session.openChannel(SFTP_CHANNEL_ID);
channel.connect();
sftpChannel = (ChannelSftp) channel;
// some sftp operations
} catch (Exception e) {
log.error("Error while SFTP session", e);
} finally {
sftpChannel.exit();
session.disconnect();
}
My question is: when I'm done is it enough to call disconnect() on the session object, or the exit() on channel is a must before?
Thanks!
Update: I checked the behavior and there are no errors, but I'm not quite sure the sockets/etc are cleaned up correctly.

Related

How to mock connection for SFTP Connection

My program opens sftp connection and connects to the server to get a file which is then processed.
I am writing a test case for this method, trying to mock the connection.
My actual class is:
public void getFile() {
Session session = null;
Channel channel = null;
ChannelSftp channelSftp = null;
String path = ftpurl;
try {
JSch jsch = new JSch();
session = jsch.getSession(username, host, port);
session.setConfig("StrictHostKeyChecking", "no");session.setPassword(JaspytPasswordEncryptor.getDecryptedString(jaspytEncryptionKey, jaspytEncryptionAlgorithm, password));
session.connect();
channel = session.openChannel("sftp");
channel.connect();
channelSftp = (ChannelSftp) channel;
channelSftp.cd(path);
Vector<ChannelSftp.LsEntry> list = channelSftp.ls("*.csv");
for (ChannelSftp.LsEntry entry : list) {
if (entry.getFilename().startsWith("A...")) {
findByFileName(entry.getFilename());
}
}
channelSftp.exit();
session.disconnect();
} catch (JSchException e) {
LOGGER.error("JSch exception"+e);
} catch (SftpException e) {
LOGGER.error("Sftp Exception"+e);
}
}
Test class so far:
#Test
public void getNamesTestValid() throws IOException, JSchException {
JSch jsch = new JSch();
Hashtable config = new Hashtable();
config.put("StrictHostKeyChecking", "no");
JSch.setConfig(config);
Session session = jsch.getSession( "remote-username", "localhost", 22999);
session.setPassword("remote-password");
session.connect();
Channel channel = session.openChannel( "sftp" );
channel.connect();
ChannelSftp sftpChannel = (ChannelSftp) channel;
Mockito.when(fileRepository.findByFileName(fileName)).thenReturn(fileDetail);
scheduler.getCSVFileNames();
}
When trying to mock the connection, it searches for the actual port and the error is port invalid, connection refused.
I only want to mock the connection.
My other doubt is after mocking the connection from where should i read the file details.
That's because you're creating an actual connection with new Jsch(). You need to mock the connection with a library such as Mockito. For example:
#RunWith(MockitoJUnitRunner.class)
class Test {
#Mock(answer = Answers.RETURNS_DEEP_STUBS)
private JSch mockJsch;
..
void test() {
Session mockSession = mockJsch.getSession("username", "localhost", 22999);
..
}
}

Jsch SFTP client unable to create new native thread

I use Jsch as SFTP client to read and write XML files from a remote SFTP directory.
I use a 5 second job to check if new files available for drafts, after 30 or 40 min loop I get the following error
Caused by: java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method) [rt.jar:1.7.0_65]
at java.lang.Thread.start(Thread.java:714) [rt.jar:1.7.0_65]
at com.jcraft.jsch.Session.connect(Session.java:528) [jsch-0.1.53.jar:]
at com.jcraft.jsch.Session.connect(Session.java:183) [jsch-0.1.53.jar:]
This is the source code used to create connexion
public InputStream getFile(String path){
Session session = null;
Channel channel = null;
try {
ChannelSftp sftp = openConnexion(session, channel);
return sftp.get(path);
} catch (SftpException e) {
new RuntimeException("Error detected during get file from SFTP specific path : " + e.getMessage(), e);
} finally {
closeConnexion(session, channel);
}
}
private ChannelSftp openConnexion(Session session, Channel channel) {
try {
JSch ssh = new JSch();
session = ssh.getSession("user", "hostname", 22);
session.setPassword("password");
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
channel = session.openChannel(SFTP_CHANNEL);
channel.connect();
ChannelSftp sftp = (ChannelSftp) channel;
return sftp;
} catch (JSchException e) {
throw new RuntimeException("Error detected during open SFTP connexion : " + e.getMessage(), e);
}
}
private void closeConnexion(Session session, Channel channel) {
if (channel != null) {
channel.disconnect();
}
if (session != null) {
session.disconnect();
}
}
I tried to increase the size of JVM thread stack and also increase the limits of native process allowed by unix => same error.
I used the following command to do that :
ulimit -u unlimited
I tried to create a pool of jsch session, jsch session when it is not disconnected, it is unusable => "SFTP Error 4"
My job is runned into war deployed on jboss-as-7, this is the JVM option :
JAVA_OPTS="-Xms1024m -Xmx1024m -XX:MaxPermSize=256m -Xss1024k"
Do you have a suggestion for this kind of treatment?
Thank you !
The problem is that you're not closing the channel and session after each loop, which will leak at least the thread that's used to perform the download over SFTP.
The attempt to close the session and channel in the finally block, if it worked, would unfortunately invalidate the InputStream that you're trying to read from; preventing you from processing the file properly.
I'm going to refactor the code slightly, which should address the resource exhaustion issue, with comments:
// session and channel are at the object scope
Session session = null;
Channel channel = null;
public InputStream getFile(String path){
// First, close any existing connections.
try {
closeConnexion();
} catch (SftpException e) {
// You can try to handle an issue here; but it's
// probably not worth it
}
try {
ChannelSftp sftp = openConnexion();
return sftp.get(path);
} catch (SftpException e) {
new RuntimeException("Error detected during get file from SFTP specific path : " + e.getMessage(), e);
} finally {
}
}
private ChannelSftp openConnexion() {
try {
JSch ssh = new JSch();
// use the object's session variable
session = ssh.getSession("user", "hostname", 22);
session.setPassword("password");
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
// use the object's channel object
channel = session.openChannel(SFTP_CHANNEL);
channel.connect();
ChannelSftp sftp = (ChannelSftp) channel;
return sftp;
} catch (JSchException e) {
throw new RuntimeException("Error detected during open SFTP connexion : " + e.getMessage(), e);
}
}
private void closeConnexion() {
// close object's channel and session
if (channel != null) {
channel.disconnect();
channel = null;
}
if (session != null) {
session.disconnect();
session = null;
}
}
If I was to re-design this, I would return a container class rather than an InputStream that contained the channel, session and InputStream. The container class would have a 'close' method, which would close the InputStream, channel and session, and then I wouldn't store the channel and session in the object.

Transfer a file through SFTP with a different user using JSch in java

I am currently doing a SFTP file transfer using JSch as follows,
public void send (String fileName) {
String SFTPHOST = "host:IP";
int SFTPPORT = 22;
String SFTPUSER = "username";
String SFTPPASS = "password";
String SFTPWORKINGDIR = "file/to/transfer";
Session session = null;
Channel channel = null;
ChannelSftp channelSftp = null;
System.out.println("preparing the host information for sftp.");
try {
JSch jsch = new JSch();
session = jsch.getSession(SFTPUSER, SFTPHOST, SFTPPORT);
session.setPassword(SFTPPASS);
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
System.out.println("Host connected.");
channel = session.openChannel("sftp");
channel.connect();
System.out.println("sftp channel opened and connected.");
channelSftp = (ChannelSftp) channel;
channelSftp.cd(SFTPWORKINGDIR);
File f = new File(fileName);
channelSftp.put(new FileInputStream(f), f.getName());
log.info("File transfered successfully to host.");
} catch (Exception ex) {
System.out.println("Exception found while tranfer the response.");
}
finally{
channelSftp.exit();
System.out.println("sftp Channel exited.");
channel.disconnect();
System.out.println("Channel disconnected.");
session.disconnect();
System.out.println("Host Session disconnected.");
}
}
I need to do this transfer as a different user i.e I need to trigger pbrun first and then perform the transfer using the new user. I could not find such an option in JSch or ChannelSftp. Is it possible to do the same using the mentioned APIs.

How to reput on JSch SFTP?

I'm using JSch to upload files to a SFTP. It works but sometimes the TCP connection is shut down while a file is being uploaded resulting on a truncated file on the server.
I found out that the reput command on SFTP servers resumes the upload. How can I send a reput command with JSch? Is it even possible?
Here's my code:
public void upload(File file) throws Exception
{
JSch jsch = new JSch();
Session session = jsch.getSession(USER, HOST, PORT);
session.setPassword(PASSWORD);
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
Channel channel=session.openChannel("sftp");
channel.connect();
ChannelSftp sftpChannel = (ChannelSftp)channel;
sftpChannel.put(file.getAbsolutePath(), file.getName());
channel.disconnect();
session.disconnect();
}
I found a way. Use the "put" method with the RESUME parameter:
sftpChannel.put(file.getAbsolutePath(), file.getName(), ChannelSftp.RESUME);
My code became:
public static void upload(File file, boolean retry) {
try
{
System.out.println("Uplodaing file " + file.getName());
JSch jsch = new JSch();
Session session = jsch.getSession(USER, HOST, PORT);
session.setPassword(PASSWORD);
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
Channel channel = session.openChannel("sftp");
channel.connect();
ChannelSftp sftpChannel = (ChannelSftp) channel;
if (!retry)
sftpChannel.put(file.getAbsolutePath(), file.getName(), ChannelSftp.OVERWRITE);
else
sftpChannel.put(file.getAbsolutePath(), file.getName(), ChannelSftp.RESUME);
channel.disconnect();
session.disconnect();
}
catch (Exception e)
{
e.printStackTrace();
upload(file, true);
}
}

Not able to connect to Secured Channel when doing SFTP with JSch

I am trying to implement JSch to retrieve a file from remote windows sftp server to Linux.
Session session = null;
Channel channel = null;
ChannelSftp channelSftp = null;
try{
JSch jsch = new JSch();
session = jsch.getSession("userName","hostName",22);
session.setPassword("password");
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
System.out.println(session.sendKeepAliveMsg());
channel = session.openChannel("sftp");
channel.connect();
}catch(Exception e){
e.printstacktrace();
}
I am getting following exception while running this code.
com.jcraft.jsch.JSchException: java.io.IOException: inputstream is closed
at com.jcraft.jsch.ChannelSftp.start(ChannelSftp.java:288)
at com.jcraft.jsch.Channel.connect(Channel.java:152)
When I debug I found:
start();
method in Channel class is throwing the exception. Is there anyway I can prevent this? I don't understand why the method is there without doing nothing.
Try to cast your channel into ChannelSft before the connect:
Channel channel = session.openChannel("sftp");
ChannelSftp channelSftp = (ChannelSftp) channel;
channelSftp.connect();

Categories

Resources