Recently I’ve had the need from java to run a job on a remote server that can take a variable amount of time (anywhere from a few seconds to a few hours) and needed to detect when it is complete and get a file it generated when completed. This was a little challanging at first, as I had never used ssh from java, but I knew a little searching would yield results.
After a little searching, I found Java Secure Channel (JSch), a handy library with a BSD license and used in Ant and eclipse. The only sad thing is that there is zero documentation outside of a few examples, and my knowledge of streams (specifically PipedInputStream) was sorely behind as I hadn’t used them directly, since, well, college.
A little confusion abounded for awhile … how the heck am I supposed to detect the end of data coming over a pipe!? I mean, you can detect when it closes, but in my case I want to stay connected and issue more commands and close when I please, which means I cannot simply detect when the pipe is closed.
I struggled for a bit, then decided … I’ll just append ” && echo
public void send(String command) throws IOException {
command += " && echo \"" + TERMINATOR + "\"\n";
toServer.write(command.getBytes());
lastCommand = new String(command);
}
Getting the server response was a bit tricky. Since the command shows up when reading from the server, I need to detect the 2nd time it shows up and break out of the loop on that. After a little time I hacked up the following:
public String getServerResponse() throws IOException, InterruptedException {
StringBuilder builder = new StringBuilder();
int count = 0;
String line = "";
for (int i = 0; true; i++) {
line = fromServer.readLine();
builder.append(line).append("\n");
if (line.contains(TERMINATOR) && (++count > 1)) {
break;
}
}
String result = builder.toString();
int beginIndex = result.indexOf(TERMINATOR+"\"") + ((TERMINATOR+"\"").length());
result = result.substring(beginIndex);
return result.replaceAll(escape(TERMINATOR), "").trim();
}
This makes so many assumptions, but I guess that as I keep working on it I can find a better way. Anyway, below is the final result of the SshClient class I came up with:
package jamesstuff.ssh;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.regex.Pattern;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.UserInfo;
public class SshClient {
BufferedReader fromServer;
Pattern alphaNumeric = Pattern.compile("([^a-zA-z0-9])");
OutputStream toServer;
String lastCommand = "";
Channel channel;
private static final String TERMINATOR = "zDonez";
public void connect(String username, String password, String host, int port)
throws JSchException, IOException {
JSch shell = new JSch();
Session session = shell.getSession(username, host, port);
MyUserInfo ui = new MyUserInfo();
ui.setPassword(password);
session.setUserInfo(ui);
session.connect();
channel = session.openChannel("shell");
fromServer = new BufferedReader(new InputStreamReader(channel.getInputStream()));
toServer = channel.getOutputStream();
channel.connect();
if(isConnected()){
send("echo \"\"");
}
}
public boolean isConnected() {
// TODO Auto-generated method stub
return (channel != null && channel.isConnected());
}
public void disconnect() {
if (isConnected()) {
channel.disconnect();
}
}
public void send(String command) throws IOException {
command += "; echo \"" + TERMINATOR + "\"\n";
toServer.write(command.getBytes());
lastCommand = new String(command);
}
public String getServerResponse() throws IOException, InterruptedException {
StringBuilder builder = new StringBuilder();
int count = 0;
String line = "";
for (int i = 0; true; i++) {
line = fromServer.readLine();
builder.append(line).append("\n");
if (line.contains(TERMINATOR) && (++count > 1)) {
break;
}
}
String result = builder.toString();
int beginIndex = result.indexOf(TERMINATOR+"\"") + ((TERMINATOR+"\"").length());
result = result.substring(beginIndex);
return result.replaceAll(escape(TERMINATOR), "").trim();
}
private String escape(String subjectString){
return alphaNumeric.matcher(subjectString).replaceAll("\\\\$1");
}
private static class MyUserInfo implements UserInfo {
private String password;
public void setPassword(String password) {
this.password = password;
}
public String getPassphrase() {
// TODO Auto-generated method stub
return null;
}
public String getPassword() {
// TODO Auto-generated method stub
return password;
}
public boolean promptPassword(String arg0) {
// TODO Auto-generated method stub
return true;
}
public boolean promptPassphrase(String arg0) {
// TODO Auto-generated method stub
return true;
}
public boolean promptYesNo(String arg0) {
// TODO Auto-generated method stub
return true;
}
public void showMessage(String arg0) {
// TODO Auto-generated method stub
System.out.println(arg0);
}
}
}


thanks for posting this code. the echo is a great idea.
I don’t really understand where you will call the getServerResponse method ?
Ah! At last an example which demystifies the JSch Shell IO streams! I have been trying to crack this one for the last two days as I have to do something very similar to what you are doing!
Thanks very much James!
This script doesn’t execute the commands on the remote server. At least not when I’m testing it with something like this:
—
ssh.connect(“usr”, “pwd”, “host”, 22);
ssh.sendSimple(“myservice start”);
System.out.println(ssh.getServerResponse());
ssh.disconnect();
System.exit(0);
—
I’ve also used the Exec.java example in jsch and it works otherwise, but then the scripts won’t see the needed system variables and I don’t know how to convert it to using shell channel instead of exec…
Thanks for the example! Curl does not support an SSH shell interface. I like your method for handling the response.
Thanks again
What is the TERMILATOR doing?? Is it the command prompt?
I could not get the command prompt in “getServerResponse” method.
Can you plz let me know how to do that
@dip TERMINATOR is simply a constant that defines the terminating string in the server output,used to indicate that output is done.
Thanks James for your kind reply.
I am a new to Java and trying to implement SSH using your example.
I am using your code like—-
con.connect(“user”, “passwd”, “server”, 22);
System.out.println(con.getServerResponse());
if (con.isConnected()) {
con.send(“ls -l”);
}
System.out.println(con.getServerResponse());
But I am not receiving any response for the cmd “ls -l”
Plz let me know if my understanding is correct.
I have put some traces in
line no 68. if (line.contains(TERMINATOR) && (++count > 1)) {
but it never executes this trace
I think you need
toServer.flush()
after:
toServer.write(command.getBytes());
in your send method.
Also, not sure what the i counter in:
for (int i = 0; true; i++)
in your getServerResponse method is for?
Otherwise, your code was very helpful. Thank you. james
I think I have this working as advertised except for the fact that I’m not sure what to use as my TERMINATOR value. For example I am trying to execute a simple ls command — what is the terminator in that case?
I am not getting the expected output from an ls command executed using the SshClient code above.
My test code looks like this:
SshClient sshClient = new SshClient();
sshClient.connect(“username”, “passwd”, “host.com”, 22);
if (sshClient.isConnected()) {
sshClient.send(“ls -l”);
System.out.println(“RESPONSE: ” +
sshClient.getServerResponse());
}
The output I am seeing looks like this:
RESPONSE: ls -l; echo “”
So it appears that I am getting the original command that I sent as the response, instead of the actual output of the ls command.
Can anyone suggest what’s going wrong? Thanks in advance for your help!
–James
Thanks for the nice working code. As someone posted already I too need to add toServer.flush() to make it work in my case.
When I refactored your class like this, It’s work. The key is use Piped streams, thanks for your ideas:
public class SshClient {
public static void main(String[] args) throws JSchException, IOException, InterruptedException {
SshClient client = new SshClient();
client.connect(“dakhoa”, “1qaz@WSX”, “zw3-easvtm-01″, 22);
client.send(“date /t /r”);
System.out.println(client.getServerResponse());
// client.send(“time /t”);
// System.out.println(client.getServerResponse());
client.disconnect();
}
PipedInputStream fromServer;
Pattern alphaNumeric = Pattern.compile(“([^a-zA-z0-9])”);
PipedOutputStream toServer;
String lastCommand = “”;
Channel channel;
Session session;
private static final String TERMINATOR = “zDonez”;
public void connect(String username, String password, String host, int port)
throws JSchException, IOException, InterruptedException {
JSch shell = new JSch();
session = shell.getSession(username, host, port);
MyUserInfo ui = new MyUserInfo();
ui.setPassword(password);
session.setUserInfo(ui);
session.connect();
channel = session.openChannel(“shell”);
PipedOutputStream po = new PipedOutputStream();
fromServer = new PipedInputStream(po);
channel.setOutputStream(po);
toServer = new PipedOutputStream();
PipedInputStream pi = new PipedInputStream(toServer);
channel.setInputStream(pi);
channel.connect();
Thread.sleep(100);
getServerResponse();
}
public boolean isConnected() {
// TODO Auto-generated method stub
return (channel != null && channel.isConnected());
}
public void disconnect() {
if (isConnected()) {
channel.disconnect();
session.disconnect();
}
}
public void send(String command) throws IOException, InterruptedException {
command += “; echo \”" + TERMINATOR + “\” \n”;
toServer.write(command.getBytes());
Thread.sleep(100);
lastCommand = new String(command);
}
public String getServerResponse() throws IOException, InterruptedException {
StringBuffer builder = new StringBuffer();
int count = 0;
String line = “”;
BufferedReader reader = new BufferedReader(new InputStreamReader(fromServer));
for (int i = 0; true; i++) {
line = reader.readLine();
builder.append(line).append(“\n”);
if (line.length() == 0) { //|| line.indexOf(TERMINATOR) != -1 && (++count > 1)) {
break;
}
}
String result = builder.toString();
int beginIndex = result.indexOf(TERMINATOR + “\”") + ((TERMINATOR + “\”").length());
result = result.substring(beginIndex);
return result.replaceAll(escape(TERMINATOR), “”).trim();
}
private String escape(String subjectString) {
return alphaNumeric.matcher(subjectString).replaceAll(“\\\\$1″);
}
private static class MyUserInfo implements UserInfo {
private String password;
public void setPassword(String password) {
this.password = password;
}
public String getPassphrase() {
// TODO Auto-generated method stub
return null;
}
public String getPassword() {
// TODO Auto-generated method stub
return password;
}
public boolean promptPassword(String arg0) {
// TODO Auto-generated method stub
return true;
}
public boolean promptPassphrase(String arg0) {
// TODO Auto-generated method stub
return true;
}
public boolean promptYesNo(String arg0) {
// TODO Auto-generated method stub
return true;
}
public void showMessage(String arg0) {
// TODO Auto-generated method stub
System.out.println(arg0);
}
}
}
With the piped version, there appears to be a buffer size cap of 1024 … is there anyway to get around it? I am trying to use this on a Cisco device and the results from a \show version\ exceeds 1024.
Just FYI I found a simpler and more reliable way of sending a command and receiving a response:
(presumes the session exists and is open)
public String execute(String command) throws IOException, JSchException {
ChannelExec channel = (ChannelExec) session.openChannel(“exec”);
channel.setCommand(command);
channel.connect();
BufferedReader reader = new BufferedReader(new InputStreamReader(channel.getInputStream()));
StringBuilder responseBuilder = new StringBuilder();
while (!channel.isClosed() && !channel.isEOF()) {
String line = reader.readLine();
if (line != null) {
responseBuilder.append(line).append(“\n”);
}
}
return responseBuilder.toString();
}
As I can’t edit the previous comment…
I forgot to thank the author for pointing me in the right direction.
JSch is cool but it really lacks documentation.
BTW the fact that it always opens a channel does not seem to affect performance (in my tests)
I tried David Alves’s version. It works. Thanks.
while trying to delete a remote file using “rm” the exception SftpException is thrown with the message: 4:
any ideas please???
Sorry guys, I really can’t help with any problems you might have with this library. This post was written over 4 years ago and the last time I even used this library was exactly that… 4 years ago.
Perhaps contacting the author or using any mailing list for the library if it’s available can help you with any problems you have. The ant guys use this for one of their bundled ant tasks, so you may get answers from their mailing lists as well.
Regards,
James
I just modify the code above and it works. Here is the modification :
SshClient.java
package com.rozi;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.regex.Pattern;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.UserInfo;
import java.io.*;
import com.jcraft.jsch.*;
public class SshClient {
private JSch shell;
private Session session;
private Channel channel;
private static OutputStream out;
private static InputStream in;
public void connect(String username, String password, String host, int port)
throws JSchException, IOException, InterruptedException {
shell = new JSch();
session = shell.getSession(username, host, port);
session.setPassword(password);
session.setConfig(“StrictHostKeyChecking”, “no”);
session.connect();
channel=session.openChannel(“shell”);
channel.setInputStream(null);
channel.setOutputStream(null);
in=channel.getInputStream();
out =channel.getOutputStream();
((ChannelShell)channel).setPtyType(“vt102″);
channel.connect();
}
public String send(String command) throws IOException, InterruptedException {
byte[] tmp=new byte[1024];
out.write((command+”;echo \”z4a3ce4f3317Z\”").getBytes());
out.write((“\n”).getBytes());
out.flush();
String result = “”;
while(true){
while(in.available()>0){
int i=in.read(tmp, 0, 1024);
if(i<0)
break;
result = result + (new String(tmp, 0, i));
}
if(result.indexOf(“z4a3ce4f3317Z”) != -1){
break;
}
try{Thread.sleep(100);}catch(Exception ee){}
}
return result;
}
public boolean isConnected() {
return (channel != null && channel.isConnected());
}
public void disconnect() {
if (isConnected()) {
channel.disconnect();
session.disconnect();
}
}
}
—————————————————
Here is the test file. Test.java
import com.rozi.SshClient;
public class Test {
public static void main(String[] args) {
try{
SshClient client = new SshClient();
client.connect(“avsg”, “xbbnm”, “192.168.8.302″, 22);
client.send(“cd /home/vik/mori”);
System.out.println(client.send(“ls -lar”));
System.out.println(client.send(“touch kl”));
System.out.println(client.send(“chmod 754 kl”));
client.disconnect();
}catch(Exception e){
System.out.println(e);
}
}
}
Thanks a lot for the code !! Morz Design !!
I used the code provided by ” Morz Design ” and in the output I get the following
$ pwd;echo”z4a3ce4f3317Z”
ksh: echoz4a3ce4f3317Z: not found.
$
bs.sh abc;echo”z4a3ce4f3317Z
In main method I have provided the commands like pwd ,bs.sh abc
I am unable to print the result of bs.sh abc The Build is successfull after “bs.sh abc;echo”z4a3ce4f3317Z”
I need to know what is “z4a3ce4f3317Z” and when I comment this line “out.write((command+”;echo \”z4a3ce4f3317Z\””).getBytes());” and “if(result.indexOf(“z4a3ce4f3317Z”) != -1){
break;
}”
in send method in class SshClient its in run mode for a long time I need to manually terminate it !!
Kindly help me in understanding whats the problem.
I need to print the full ouptput of this shell script “bs.sh abc” [where abc is uiinput parameter]
Awaiting for reply through my mail or here !!
Thanks in advance !! !!
I think the ouput of bs.sh abc is terminated by “;echo”z4a3ce4f3317Z”
I am unable to grasp what is “;echo”z4a3ce4f3317Z”
Kindly enlighten me !!
And sorry for double posting I am unable to edit my post !!
Small Update what I found and what I need :
1.”z4a3ce4f3317Z”is a delimiter to detect the end of the data ! Sorry I read the post in a Haste [later on read calmly].I made a mistake by ommiting a whitespace in “”;echo”z4a3ce4f3317Z” edited like this : “out.write((command + “; echo \”z4a3ce4f3317Z\”").getBytes());”
2.
The Below Block works fine if the output of the Command is small
try{Thread.sleep(100);}catch(Exception ee){}
}
When I tried this
try{Thread.sleep(60000);}catch(Exception ee){}
}
I need to wait for a minute [because I have specified Thread.sleep(60000); ]to get the full output of the command bs.sh abc along with the delimieter “z4a3ce4f3317Z”
The Thread.sleep(60000) . makes my module to sleep for 1Min and later on the entire output is displayed.I need to display the output line by line .I tried all the possiblities of printing the output but still it prints after waiting for 1Minute.
3.
Is there any way to prevent the display of the escape string “z4a3ce4f3317Z” it gets displayed like this “bs.sh abc; echo “z4a3ce4f3317Z” “
This is more for people that stumble across this website in hopes of trying to get this to work. Anyways, you might want to change the following lines in Morz Design’s code:
out.write((command+”;echo \”z4a3ce4f3317Z\””).getBytes());
if(result.indexOf(“z4a3ce4f3317Z”) != -1){
to this:
out.write((command+”;echo $((1+1))\”z4a3ce4f3317Z\””).getBytes());
if(result.indexOf(“2z4a3ce4f3317Z”) != -1){
That way, you can be sure that you don’t catch your command being echoed in the output, and return after sleeping 100 seconds.
I tried to change the code little bit and i was able to get results.
change this:
out.write((command+”;echo $((1+1))\”z4a3ce4f3317Z\””).getBytes());
if(result.indexOf(“2z4a3ce4f3317Z”) != -1){
To this:
out.write((command).getBytes());
if(result.indexOf(command) != -1){
and also increase the Thread.sleep(500);
Great work by you people…..Its helps me a lot….Cheers..
Thanks for the post. I was able to make a utility in my organisation based on the concepts used in the code. Thank you.