I want to launch a process from Java, read its output, and get its return code. But while it's executing, I want to be able to cancel it. I start out by launching the process:
ProcessBuilder pb = new ProcessBuilder(args);
pb.redirectErrorStream(true);
Process proc = pb.start();
If I call proc.waitFor(), I can't do anything until the process exits. So I'm assuming I need to something like this:
while (true) {
see if process has exited
capture some output from the process
decide if I want to cancel it, and if so, cancel it
sleep for a while
}
Is this right? Can someone give me an example of how to do this in Java?
Here's an example of what I think you want to do:
ProcessBuilder pb = new ProcessBuilder(args);
pb.redirectErrorStream(true);
Process proc = pb.start();
InputStream is = proc.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
int exit = -1;
while ((line = br.readLine()) != null) {
// Outputs your process execution
System.out.println(line);
try {
exit = proc.exitValue();
if (exit == 0) {
// Process finished
}
} catch (IllegalThreadStateException t) {
// The process has not yet finished.
// Should we stop it?
if (processMustStop())
// processMustStop can return true
// after time out, for example.
proc.destroy();
}
}
You can improve it :-) I don't have a real environment to test it now, but you can find some more information here.
I recommend checking out Apache Commons Exec to avoid recreating the wheel. It has some nice features like choosing between synchronous vs. asynchronous execution, as well as a standard solution to spawning a watchdog process that can help in timing out the execution in case it gets stuck.
A helper class like this would do the trick:
public class ProcessWatcher implements Runnable {
private Process p;
private volatile boolean finished = false;
public ProcessWatcher(Process p) {
this.p = p;
new Thread(this).start();
}
public boolean isFinished() {
return finished;
}
public void run() {
try {
p.waitFor();
} catch (Exception e) {}
finished = true;
}
}
You would then implement your loop exactly as you describe:
Process p = Runtime.getRuntime().exec("whatever command");
ProcessWatcher pw = new ProcessWatcher(p);
InputStream output = p.getInputStream();
while(!pw.isFinished()) {
processOutput(output);
if(shouldCancel()) p.destroy();
Thread.sleep(500);
}
Depending upon what conditions would make you want to destroy the process, you might want to do that in a separate thread. Otherwise, you may block while waiting for more program output to process and never really get the option to destroy it.
EDIT: McDowell is 100% right in his comment below, so I've made the finished variable volatile.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With