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 ” and read until I see that token, escape, remove the token, and return the data. So I wrote a small and dirty SshClient class that would allow me to send and recieve responses. The send method simply added the appropriate marker and saves the command so that it knows to strip it from the response:
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:
Read More »