package com.buglabs.util.shell.pub;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Writer;
/**
* Default implementation of ShellSession.
*
* @author kgilmer
*/
public class ShellSession implements IShellSession {
private volatile boolean interrupt = false;
private boolean disposed = false;
/**
* This terminator string is used to determine when content from the output
* stream is completed for a given command.
*/
public static final String TERMINATOR = "234o987dsfkcqiuwey18837032843259d";
public static final String LT = System.getProperty("line.separator");
private Process process;
private OutputStream pos = null;
private static final String SHELL_PATH = "/bin/sh";
private final File root;
private final Writer out;
/**
* @param root directory to run the shell session
* @param out where to direct output
* @throws IOException on File I/O error
*/
public ShellSession(File root, Writer out) throws IOException {
this.root = root;
if (out == null) {
this.out = new NullWriter();
} else {
this.out = out;
}
initializeShell();
}
/**
* @param root base directory
* @throws IOException on File I/O error
*/
public ShellSession(File root) throws IOException {
this.root = root;
this.out = new NullWriter();
initializeShell();
}
/**
* @throws IOException on File I/O error
*/
private void initializeShell() throws IOException {
if (!disposed) {
process = Runtime.getRuntime().exec(SHELL_PATH);
pos = process.getOutputStream();
if (root != null) {
out.write(execute("cd " + root.getAbsolutePath()));
}
}
}
/*
* (non-Javadoc)
*
* @see shellservice.IShellService#execute(java.lang.String)
*/
synchronized public String execute(String command) throws IOException {
if (disposed) {
throw new IOException("This shell session has been disposed.");
}
String errorMessage = null;
interrupt = false;
out.write(command);
sendToProcessAndTerminate(command);
if (process.getErrorStream().available() > 0) {
byte[] msg = new byte[process.getErrorStream().available()];
process.getErrorStream().read(msg, 0, msg.length);
out.write(new String(msg));
errorMessage = "Error while executing: " + command + LT + new String(msg);
}
BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuffer sb = new StringBuffer();
String line = null;
while (((line = br.readLine()) != null) && !line.equals(TERMINATOR) && !interrupt) {
sb.append(line);
sb.append(LT);
out.write(line);
out.write(LT);
}
if (interrupt) {
process.destroy();
initializeShell();
interrupt = false;
}
if (errorMessage != null) {
throw new IOException(errorMessage);
}
return sb.toString();
}
/*
* (non-Javadoc)
*
* @see shellservice.IShellService#execute(java.lang.String,
* shell.pub.ICommandResponseHandler)
*/
public synchronized void execute(String command, ICommandResponseHandler handler) throws IOException {
execute(command, TERMINATOR, handler);
}
/*
* (non-Javadoc)
*
* @see shellservice.IShellService#execute(java.lang.String,
* java.lang.String, shell.pub.ICommandResponseHandler)
*/
public synchronized void execute(String command, String terminator, ICommandResponseHandler handler) throws IOException {
if (disposed) {
throw new IOException("This shell session has been disposed.");
}
interrupt = false;
sendToProcessAndTerminate(command);
BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
InputStream errIs = process.getErrorStream();
String std = null;
do {
if (errIs.available() > 0) {
byte[] msg = new byte[errIs.available()];
errIs.read(msg, 0, msg.length);
out.write(new String(msg));
handler.response(new String(msg), true);
}
std = br.readLine();
if (std != null && !std.equals(terminator)) {
out.write(std);
handler.response(std, false);
}
} while (std != null && !std.equals(terminator) && !interrupt);
if (interrupt) {
process.destroy();
initializeShell();
interrupt = false;
}
}
/**
* Send command string to shell process and add special terminator string so
* reader knows when output is complete.
*
* @param command command to execute
* @throws IOException on File I/O error
*/
private void sendToProcessAndTerminate(String command) throws IOException {
pos.write(command.getBytes());
pos.write(LT.getBytes());
pos.write("echo ".getBytes());
pos.write(TERMINATOR.getBytes());
pos.write(LT.getBytes());
pos.flush();
}
/* (non-Javadoc)
* @see com.buglabs.util.shell.pub.IShellSession#interrupt()
*/
public void interrupt() {
interrupt = true;
}
/* (non-Javadoc)
* @see com.buglabs.util.shell.pub.IShellSession#dispose()
*/
public void dispose() {
this.disposed = true;
interrupt();
}
/**
* A Writer that does nothing.
*
* @author kgilmer
*/
private class NullWriter extends Writer {
/* (non-Javadoc)
* @see java.io.Writer#close()
*/
public void close() throws IOException {
}
/* (non-Javadoc)
* @see java.io.Writer#flush()
*/
public void flush() throws IOException {
}
/* (non-Javadoc)
* @see java.io.Writer#write(char[], int, int)
*/
public void write(char[] cbuf, int off, int len) throws IOException {
}
}
}