Jsch SFTP client unable to create new native thread - java

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.

Related

Hard Time Keeping JSCH Session Alive

I am trying to run a sequence of commands on a CSICO router using JSCH and SSH. But the problem I am having is after executing the first command, the second one just doesn't execute. The session seems to go down after running the first command. It throws this exception:
com.jcraft.jsch.JSchException: Packet corrupt
at com.jcraft.jsch.Session.start_discard(Session.java:1067)
at com.jcraft.jsch.Session.read(Session.java:937)
at com.jcraft.jsch.Session.connect(Session.java:309)
at com.jcraft.jsch.Session.connect(Session.java:183)
at com.att.ncaas.device.connection.ssh.SSHConnectionJSch.connect(SSHConnectionJSch.java:120)
The code look likes this:
public void connect () {
ChannelExec channelExec = null;
JSch jSch = new JSch();
Session session = null;
try {
session = jSch.getSession(userid, ipAddress, port);
// Set the password.
session.setPassword("XXXXXXXXX");
Properties properties = new Properties();
properties.put("StrictHostKeyChecking", "no");
// Set the Ciphers.
/*
* properties.put("cipher.s2c", "aes128-cbc"); properties.put("cipher.c2s",
* "aes128-cbc"); properties.put("CheckCiphers", "aes128-cbc");
*/
session.setConfig(properties);
// Finally make the connection.
session.connect();
channelExec = (ChannelExec) session.openChannel("exec");
// Command 1
channelExec.setCommand("show run");
ByteArrayOutputStream responseStream = new ByteArrayOutputStream();
channelExec.setOutputStream(responseStream);
channelExec.connect();
while (channelExec.isConnected()) {
Thread.sleep(1000);
}
String responseString = new String(responseStream.toByteArray());
System.out.println("Show Run Output:\n");
System.out.println(responseString);
if (session.isConnected()) {
System.out.println("Wooooooooooooooooooohoooooooooooooooooooooooo!!!!");
} else {
session.connect();
}
// Command 2
channelExec.setCommand("show version");
channelExec.connect();
while (channelExec.isConnected()) {
Thread.sleep(1000);
}
responseString = new String(responseStream.toByteArray());
System.out.println("Show Version Output:\n");
System.out.println(responseString);
} catch (JSchException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (session != null) {
session.disconnect();
}
if (channelExec != null) {
channelExec.disconnect();
}
}
}
When it comes for the second session.connect() inside the if block, it throws the error. The only other thing I noticed is the when I put the ciphers in, this part:
// Set the Ciphers.
properties.put("cipher.s2c", "aes128-cbc");
properties.put("cipher.c2s", "aes128-cbc");
properties.put("CheckCiphers", "aes128-cbc");
I get a different error on the same line. I get:
com.jcraft.jsch.JSchException: Session.connect: java.io.IOException: End of IO Stream Read
at com.jcraft.jsch.Session.connect(Session.java:565)
at com.jcraft.jsch.Session.connect(Session.java:183)
Any pointers on how to keep the session alive between two ChannelExec execute would really helpful. Thanks.
First, you cannot reuse the "exec" channel this way. You have to open a new channel for the new command.
And note that Cisco is known not to support multiple commands in one "exec" channel, even if specified together upfront:
Error passing multiple commands to Cisco CLI via plink
I would not be surprised if it did not support multiple "exec" channels in one connection either.
Did you test with an SSH client, if it is even possible, to do what you are trying to implement?

Sending Jsch commands

Beating my head against a wall here.
I am trying in the constructor to establish a link and log on. Then in the begin method to execute a java application on the remote computer. Finally in terminate, to kill the java process.
No exceptions show up, the command just doesn't execute. I can see the user has logged in if I go to the remote terminal and run a last. The user has only a login time of 0 though. I can also get an error if I change the password to the wrong password.
But the remote java app isn't running. Remote app runs fine when I log in directly from that terminal or ssh in and run it from the command line.
Ideas? Thanks in advance.
//Constants for the login information
String HOST="<HOSTNAME>";
String USER="<userid>";
String PASSWORD="<password>";
//Constants for command strings
String START_COMMAND="java -Djava.library.path=/usr/local/share/OpenCV/java -jar <filename>";
String TERMINATE_COMMAND="killall java";
Channel channel;
Session session;
InputStream in;
public VisionProcessing(){
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
JSch jsch = new JSch();
try {
System.out.println("Trying login");
session = jsch.getSession(USER, HOST, 22);
session.setPassword(PASSWORD);
session.setConfig(config); //XXX No idea what this does
session.connect();
channel=session.openChannel("exec");
((ChannelExec)channel).setPty(true);
channel.connect();//This is important
if (channel.isConnected()){
System.out.println("Connected");
} else {
System.out.println("Not Connected");
}
} catch (JSchException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("Exception logging in");
}
}
public boolean beginTargeting() {
try {
StringBuilder outputBuffer = new StringBuilder();
Channel channel = session.openChannel("exec");
InputStream commandOutput = channel.getInputStream();
int readByte = commandOutput.read();
while(readByte != 0xffffffff)
{
outputBuffer.append((char)readByte);
readByte = commandOutput.read();
}
System.out.println("Begin");
if (channel.isConnected()){
System.out.println("Connected");
((ChannelExec)channel).setCommand(START_COMMAND);
} else {
System.out.println("Not Connected");
}
channel.connect();//This is important
channel.disconnect();
}catch(IOException ioX)
{
System.out.println(ioX);
}
catch(JSchException jschX)
{
System.out.println(jschX);
}
return true;
}
public boolean terminateTargeting() {
try {
System.out.println("Terminate");
channel=session.openChannel("exec");
((ChannelExec)channel).setCommand(TERMINATE_COMMAND);
channel.connect();//This is important
System.out.println("Terminating");
channel.setInputStream(null);
channel.disconnect();
session.disconnect();
System.out.println("DONE");
}catch(Exception ee){
System.out.println(ee);
}
return true;
}

I am trying to post multiple file using SFTP connection but not able to post more than 2 files

I am trying to post multiple file using SFTP connection. I am using below code to connect SFTP server and post file on server.
My program running properly but for only 2 file not more than that, third file on words its generates Authentication error "com.jcraft.jsch.JSchException: Auth fail".
Could you please somebody guide me for that.
public boolean putSftp(String input , String fileName) {
JSch jsch = new JSch();
Session session = null;
try {
System.out.println("Input : "+input+": File Name :"+fileName+" :-----------Logon on to the Server---------- : "+ datetime);
session = jsch.getSession(USER_NAME, FTP_HOSTNAME, 22);
session.setConfig("StrictHostKeyChecking", "no");
session.setPassword(PASSWORD);
session.connect();
Channel channel = session.openChannel("sftp");
channel.connect();
ChannelSftp sftpChannel = (ChannelSftp) channel;
sftpChannel.cd(TO_DIRECTORY);
sftpChannel.put(input+fileName, fileName);
System.out.println("File sucessfully posted on server....:"+datetime);
sftpChannel.exit();
session.disconnect();
result = true;
}catch (JSchException e) {
System.out.println("Here JSCH...!!! : ");
e.printStackTrace();
}catch (SftpException e) {
System.out.println("Here SFTP...!!! :");
e.printStackTrace();
} catch (Exception e){
System.out.println("Here Exception...!!!"+e);
}finally {
System.out.println("In Side of SFTP Method....."+result);
}
return result;
}

JSCH: closing channel and session

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.

Copy a file on remote server from one directory to other using Jsch

I am trying to copy a file on server from one directory to another one using Jsch. I am using SFTP protocol put and get methods to accomplish this task. I am doing this way as I dont have shell access to the server. Below is my code sample and the exception that I am getting. Can someone please let me know how to resolve it.
OutputStream outputStream = null;
InputStream inputStream = null;
try
{
JSch jsch = new JSch();
session = jsch.getSession(USER,HOST,PORT);
session.setPassword(PASS);
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
config.put("PreferredAuthentications", "password");
session.setConfig(config);
session.connect();
channel = session.openChannel("sftp");
channel.connect();
channelSftp = (ChannelSftp)channel;
inputStream = channelSftp.get(fromFilename);
channelSftp.put(inputStream,toFilename);
} catch(Exception e){
e.printStackTrace();
} finally {
if(inputStream != null)
inputStream.close();
if(outputStream != null)
outputStream.close();
channelSftp.exit();
channel.disconnect();
session.disconnect();
}
Here is the exception
4: java.io.IOException: error: 4: RequestQueue: unknown request id 12
at com.jcraft.jsch.ChannelSftp._put(ChannelSftp.java:689)
at com.jcraft.jsch.ChannelSftp.put(ChannelSftp.java:540)
at com.jcraft.jsch.ChannelSftp.put(ChannelSftp.java:492)
You have to do the get() operation in a one channel and put() operation in another channel. That works. Here is my code.
public void cp (Session session, String source, String target) throws Exception {
log.info("COMMAND: cp " + source + " " + target);
if (!session.isConnected()) {
log.error("Session is not connected");
throw new Exception("Session is not connected...");
}
Channel upChannel = null;
Channel downChannel = null;
ChannelSftp uploadChannel = null;
ChannelSftp downloadChannel = null;
try {
upChannel = session.openChannel("sftp");
downChannel = session.openChannel("sftp");
upChannel.connect();
downChannel.connect();
uploadChannel = (ChannelSftp) upChannel;
downloadChannel = (ChannelSftp) downChannel;
FileProgressMonitor monitor = new FileProgressMonitor();
InputStream inputStream = uploadChannel.get(source);
downloadChannel.put(inputStream, target, monitor);
} catch (JSchException e) {
log.error("Auth failure", e);
throw new Exception(e);
} finally {
if (upChannel == null || downChannel == null) {
System.out.println("Channel is null ...");
}else if (uploadChannel != null && !uploadChannel.isClosed()){
uploadChannel.exit();
downloadChannel.exit();
uploadChannel.disconnect();
downloadChannel.disconnect();
}else if (!upChannel.isClosed()) {
upChannel.disconnect();
downChannel.disconnect();
}
session.disconnect();
}
}

Categories

Resources