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);
}
}
}
No tags

Eileen · November 14, 2006 at 11:53 am
thanks for posting this code. the echo is a great idea.
Adedib · April 11, 2007 at 9:38 am
I don’t really understand where you will call the getServerResponse method ?
Abhik · August 5, 2007 at 4:59 am
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!
Blacktek · October 30, 2007 at 8:52 am
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…
sathish · March 6, 2012 at 2:41 am
Hi,
I need help in automation of ssh. I am trying to send commands automatically so that it will fire commands and give me the output of it. As we all know that, ssh is for getting into the node and fire some commands and give output for that command.
I am using ssh factory jar file and i am trying to send commands automatically.
kindly check for the below code which i have tried:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import com.jscape.inet.ssh.Ssh;
import com.jscape.inet.ssh.SshAdapter;
import com.jscape.inet.ssh.SshConnectedEvent;
import com.jscape.inet.ssh.SshDataReceivedEvent;
import com.jscape.inet.ssh.SshDisconnectedEvent;
import com.jscape.inet.ssh.SshException;
import com.jscape.inet.ssh.SshScript;
import com.jscape.inet.ssh.SshTask;
import com.jscape.inet.ssh.SshTaskEndEvent;
import com.jscape.inet.ssh.SshTaskStartEvent;
import com.jscape.inet.ssh.SshTaskTimeoutException;
import com.jscape.inet.ssh.connection.channels.SessionCli ent;
import com.jscape.inet.ssh.util.SshParameters;
public class SshScriptTutorial extends SshAdapter {
public SshScriptTutorial() {}
public void executeSshScript(String hostname, String username, String password)
throws SshException, IOException, InterruptedException
{
// assumes that SSH shell prompt is “$” .. this MUST match exactly
String shellPrompt = “>”;
// initialize and create new Ssh instance
SshParameters sshParams = new SshParameters(hostname,username,password);
Ssh ssh = new Ssh(sshParams);
// register this class to receive Ssh events
ssh.addSshListener(this);
// create new script object and bind to the given ssh object
SshScript script = new SshScript(ssh);
// add tasks to script object
script.addTask(new SshTask(shellPrompt, “show host”, shellPrompt));
script.addTask(new SshTask(shellPrompt, “ssh ssgpun”, shellPrompt));
// while sending password, it is not able to fire this.
script.addTask(new SshTask(shellPrompt, “password”, “:”)); // trying to send password to the server.
// connect to SSH server and execute script
ssh.connect();
// wait until last task is complete
while(!script.isComplete()) {
try {
Thread.sleep(500);
} catch(Exception e) {}
}
// disconnect from server
// ssh.disconnect();
}
public void connected(SshConnectedEvent event) {
System.out.println(“Connected to host: ” + event.getHost());
}
public void disconnected(SshDisconnectedEvent event) {
System.out.println(“Disconnected from host: ” + event.getHost());
}
public void dataReceived(SshDataReceivedEvent event) {
System.out.print(event.getData());
}
public static void main(String[] args) {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
// System.out.print(“Enter SSH hostname: “);
// String hostname = reader.readLine();
// System.out.print(“Enter SSH username: “);
// String username = reader.readLine();
String hostname = “hostname”; // ip of server
String username = “username”;
System.out.print(“Enter SSH password: “);
String password = reader.readLine();
SshScriptTutorial tutorial = new SshScriptTutorial();
tutorial.executeSshScript(hostname, username, password);
// System.out.print(“Hi”);
String cus_pass = reader.readLine();
} catch(Exception e) {
e.printStackTrace();
}
}
}
Output log appears as,
Enter SSH password: 4732885288
Connected to host: 150.236.14.11
egw-pnq > show host
Node IP number Reverse SSH tunnel
__________________________________________________ _____________________
ssgpun 192.168.181.2 yes
egw-pnq > ssh ssgpun
Connecting to ssgpun as user
Password:
Till this i am able to send commands automatically. when i am trying to send password, it is not able to fire it.
I feel that this might be due to setting shell prompt or delay problem. I am not sure. I dont know how to solve it.
If any1 knows, kindly help me out in this step.
email id: mg.sathish2@gmail.com
Rai · May 20, 2008 at 11:19 am
Thanks for the example! Curl does not support an SSH shell interface. I like your method for handling the response.
Thanks again
dip · June 24, 2008 at 7:59 am
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
Admin comment by James Carr · June 24, 2008 at 8:21 am
@dip TERMINATOR is simply a constant that defines the terminating string in the server output,used to indicate that output is done.
dip · June 25, 2008 at 9:24 am
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.
dip · June 25, 2008 at 9:27 am
I have put some traces in
line no 68. if (line.contains(TERMINATOR) && (++count > 1)) {
but it never executes this trace
james · September 13, 2008 at 11:31 am
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
James Adams · December 16, 2008 at 2:24 pm
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?
James Adams · December 16, 2008 at 2:43 pm
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
Senthil · April 24, 2009 at 9:50 am
Thanks for the nice working code. As someone posted already I too need to add toServer.flush() to make it work in my case.
Khoa · June 9, 2009 at 5:46 am
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);
}
}
}
James Hunt · June 12, 2009 at 1:58 pm
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.
David Alves · August 30, 2009 at 12:05 am
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();
}
David Alves · August 30, 2009 at 12:12 am
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)
Simon · February 10, 2010 at 2:08 pm
I tried David Alves’s version. It works. Thanks.
Andy · February 26, 2010 at 9:18 am
while trying to delete a remote file using “rm” the exception SftpException is thrown with the message: 4:
any ideas please???
Admin comment by James Carr · February 26, 2010 at 10:10 am
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
Morz Design · May 25, 2010 at 5:32 am
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);
}
}
}
Mani · February 9, 2011 at 7:52 am
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 !! !!
Mani · February 9, 2011 at 7:54 am
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 !!
Mani · February 9, 2011 at 9:47 am
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” “
Pat · March 16, 2011 at 9:18 am
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.
Jai Shankar · May 13, 2011 at 1:04 pm
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);
krishna Ravuri · October 13, 2011 at 8:58 am
Great work by you people…..Its helps me a lot….Cheers..
Debashish Bhattacharjee · December 21, 2011 at 8:45 am
Thanks for the post. I was able to make a utility in my organisation based on the concepts used in the code. Thank you.