Search code examples
javaprocessbuilder

ProcessBuilder: Forwarding stdout and stderr of started processes without blocking the main thread


I'm building a process in Java using ProcessBuilder as follows:

ProcessBuilder pb = new ProcessBuilder()
        .command("somecommand", "arg1", "arg2")
        .redirectErrorStream(true);
Process p = pb.start();

InputStream stdOut = p.getInputStream();

Now my problem is the following: I would like to capture whatever is going through stdout and/or stderr of that process and redirect it to System.out asynchronously. I want the process and its output redirection to run in the background. So far, the only way I've found to do this is to manually spawn a new thread that will continuously read from stdOut and then call the appropriate write() method of System.out.

new Thread(new Runnable(){
    public void run(){
        byte[] buffer = new byte[8192];
        int len = -1;
        while((len = stdOut.read(buffer)) > 0){
            System.out.write(buffer, 0, len);
        }
    }
}).start();

While that approach kind of works, it feels a bit dirty. And on top of that, it gives me one more thread to manage and terminate correctly. Is there any better way to do this?


Solution

  • For Java 7 and later, see Evgeniy Dorofeev's answer.

    For Java 6 and earlier, create and use a StreamGobbler:

    StreamGobbler errorGobbler = 
      new StreamGobbler(p.getErrorStream(), "ERROR");
    
    // any output?
    StreamGobbler outputGobbler = 
      new StreamGobbler(p.getInputStream(), "OUTPUT");
    
    // start gobblers
    outputGobbler.start();
    errorGobbler.start();
    

    ...

    private class StreamGobbler extends Thread {
        InputStream is;
        String type;
    
        private StreamGobbler(InputStream is, String type) {
            this.is = is;
            this.type = type;
        }
    
        @Override
        public void run() {
            try {
                InputStreamReader isr = new InputStreamReader(is);
                BufferedReader br = new BufferedReader(isr);
                String line = null;
                while ((line = br.readLine()) != null)
                    System.out.println(type + "> " + line);
            }
            catch (IOException ioe) {
                ioe.printStackTrace();
            }
        }
    }