Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Run external program from Java, read output, allow interruption

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?

like image 224
JW. Avatar asked Mar 18 '09 19:03

JW.


3 Answers

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.

like image 92
Paulo Guedes Avatar answered Nov 01 '22 09:11

Paulo Guedes


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.

like image 8
AWhitford Avatar answered Nov 01 '22 08:11

AWhitford


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.

like image 5
Marty Lamb Avatar answered Nov 01 '22 08:11

Marty Lamb