Sunday, December 13, 2009

My Favorite Runtime.exec Wrapper

If you want to run another process in java (like a python script), the internet should lead you to Runtime.exec. This method doesn't block, so you might be tempted to call Process.waitFor to make sure it's done. However, sometimes this method will hang forever when the process writes to stderr or returns != 0. That seems like a bug to me, so the wrapper below works around that by catching exceptions from Process.exitValue instead of asking sun to do the waiting for us. Furthermore, the wrapper below reports stdout and stderr to you via StringBuffer arguments.
/**
* Executes a command and waits for the process to exit.
* The process is queried every 100 milliseconds for completion.
* stdoutBuffer and stderrBuffer can be the same object.
*
* @param cmd the command to be passed to {@link Runtime#exec(String[])}.
* @param stdoutBuffer buffer to which the process's stdout will be written. can be null.
* @param stderrBuffer buffer to which the process's stderr will be written. can be null.
* @return the exit value of the process
* @throws RuntimeException IOException and InterruptedException are wrapped in RuntimeException and thrown
*/
public static int exec(String[] cmd, StringBuffer stdoutBuffer, StringBuffer stderrBuffer)
{
if (stdoutBuffer == null)
stdoutBuffer = new StringBuffer();
if (stderrBuffer == null)
stderrBuffer = new StringBuffer();
try {
Process p = Runtime.getRuntime().exec(cmd);
InputStreamReader stdout = new InputStreamReader(p.getInputStream());
InputStreamReader stderr = new InputStreamReader(p.getErrorStream());
while (true) {
int exitValue = -1;
boolean done;
try {
exitValue = p.exitValue();
done = true;
} catch (IllegalThreadStateException e) {
done = false;
}
while (stdout.ready())
stdoutBuffer.append((char)stdout.read());
while (stderr.ready())
stderrBuffer.append((char)stderr.read());
if (done)
return exitValue;
Thread.sleep(100);
}
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}

No comments: