2

While running an external script, I want to read the ErrorStream and OutputStream of this script both simultaneously and separately and then process them further. Therefore, I start a Thread for one of the streams. Unfortunately, the Process doesn't seem to waitFor the Thread to be terminated, but return after the non-threaded stream has no further input.

In a nutshell, here is what I am doing:

ProcessBuilder pb = new ProcessBuilder(script);  
final Process p = pb.start();  

new Thread(new Runnable() {  
  public void run() {
    BufferedReader br = new BufferedReader(new InputStreamReader(p.getErrorStream()));
    ...read lines and process them...
  }
}).start();

BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
...read lines and process them...

int exitValue = p.waitFor();
p.getOutputStream().close();
return exitValue;

Is there any possibility to waitFor the Thread to be terminated?

stan
  • 95
  • 2
  • 8
  • Maybe just use [Commons Exec](http://commons.apache.org/proper/commons-exec//) - no use reinventing the wheel. – Boris the Spider Mar 18 '13 at 17:01
  • 1
    Would be helpful if you posted your real code. Two things, you never start your input stream reader thread; and you should not close the processes output stream before it is complete. – Perception Mar 18 '13 at 17:03
  • @Perception : Sorry for the incomplete and thus defective code. I have corrected it above. – stan Mar 26 '13 at 10:42

2 Answers2

1

Here's general code for doing what you want to do. In this case there is both input and output: I am piping someFile into the process and piping the output to System.out. Files.copy() and ByteStreams.copy() are just Guava convenience methods to hook up an InputStream to an OutputStream. We then wait for the command to finish.

final Process pr = Runtime.getRuntime().exec(cmd);

new Thread() {
    public void run() {
        try (OutputStream stdin = pr.getOutputStream()) {
            Files.copy(someFile, stdin);
        } 
        catch (IOException e) { e.printStackTrace(); }
    }
}.start();

new Thread() {
    public void run() {
        try (InputStream stdout = pr.getInputStream()) {
            ByteStreams.copy(stdout, System.out);
        } 
        catch (IOException e) { e.printStackTrace(); }  
    }
}.start();              

int exitVal = pr.waitFor();
if( exitVal == 0 )
    System.out.println("Command succeeded!");
else    
    System.out.println("Exited with error code " + exitVal);

A more verbose version if you are running prior to Java 7 with the try-with-resources block:

final Process pr = Runtime.getRuntime().exec(cmd);

new Thread() {
    public void run() {
        OutputStream stdin = null;
        try {
            Files.copy(someFile, stdin = pr.getOutputStream());
        } 
        catch (IOException e) { e.printStackTrace(); }
        finally {
            if( stdin != null ) {
                try { stdin.close(); } 
                catch (IOException e) { e.printStackTrace(); }
            }
        }               
    }
}.start();

new Thread() {
    public void run() {
        InputStream stdout = null;
        try {
            ByteStreams.copy(stdout = pr.getInputStream(), System.out);
        } 
        catch (IOException e) { e.printStackTrace(); }
        finally {
            if( stdout != null ) {
                try { stdout.close(); } 
                catch (IOException e) { e.printStackTrace(); }
            }
        }               
    }
}.start();              

int exitVal = pr.waitFor();
if( exitVal == 0 )
    System.out.println("Command succeeded!");
else    
    System.out.println("Exited with error code " + exitVal);
Andrew Mao
  • 35,740
  • 23
  • 143
  • 224
  • Thank you for your answer. Unfortunately, using 'Commons Exec' doesn't work for me. There is no `waitFor` the threaded stream, as well. But I could solve the problem using Stephan's solution above. – stan Mar 26 '13 at 10:55
  • Not sure where you see Commons Exec in my answer. The `Process` and its `waitFor()` method are available in standard Java. – Andrew Mao Mar 26 '13 at 15:59
  • Very useful answer (first code) but I had to use "IOUtils" instead of "ByteStreams" (second thread). – Léa Massiot Nov 27 '14 at 15:33
  • Hello Andrew, do you think it is required to close the process stream (variable stdout) ? Once the process exits , the streams would be closed automatically, isnt it? Not sure, so asking you. – RuntimeException Nov 13 '15 at 16:19
  • You don't have to if this is the only code running in the process, but it's generally good practice to because if you do this along with some other code, it will cause a memory leak. – Andrew Mao Nov 13 '15 at 17:32
1

You can use Thread.join(...) to wait for a Thread to finish. Note that the call throws InterruptedException if the current thread receives an interrupt before the thread you are waiting for finishes.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216