Steven Solomon
Steven Solomon

Reputation: 301

Java Runtime.exec() -- Is it possible to synchronize stream output with wait()/notify()?

I have implemented a simple external process executor in Java using Runtime.exec(). I have implemented a "stream gobbler" class that consumes process output from the process's output stream and error stream. My code waits for the external process to complete using Process.waitFor(), then I print the process return code.

The problem I'm experiencing is that the process ends before the "stream gobblers" have finished printing, so in my Java output (System.out), the returned exit code appears in the middle of the process output/error lines.

My question is, Is it possible to synchronize the output on System.out using wait()/notify() instead of polling the alive status of each StreamGobbler in a while loop?

Here is the code:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class RuntimeExecExample
{
    private enum StreamType
    {
        OUTPUT,
        ERROR
    }

    public class MyStreamGobbler extends Thread
    {
        private InputStream is;
        StreamType type;

        public MyStreamGobbler(InputStream is, StreamType type)
        {
            this.is = is;
            this.type = type;
        }

        public void run()
        {
            try
            {
                BufferedReader br = new BufferedReader(new InputStreamReader(is));
                String line=null;
                while ( (line = br.readLine()) != null)
                {
                    System.out.println(type.toString() + ">" + line);
                }
            } 
            catch (IOException ioe)
            {
                ioe.printStackTrace();
            }
        }
    }

    public void executeCommand(String args[])
    {
        try
        {
            Runtime rt = Runtime.getRuntime();

            String commandLine = "";
            for(String cmdItem : args)
            {
                commandLine += " " + cmdItem;
            }
            System.out.println("Exec-ing " + commandLine);

            Process proc = rt.exec(args);
            // any error message?
            MyStreamGobbler errorGobbler = new MyStreamGobbler(proc.getErrorStream(), StreamType.ERROR);
            // any output?
            MyStreamGobbler outputGobbler = new MyStreamGobbler(proc.getInputStream(), StreamType.OUTPUT);
            // kick them off
            errorGobbler.start();
            outputGobbler.start();
            // any error???
            int exitVal = proc.waitFor();

            /*
             * Wait for both to finish before printing exit value
             * IS THERE A WAY TO DO THIS WITH wait()/notify() ??
             */
            while(errorGobbler.isAlive())
                ;
            while(outputGobbler.isAlive())
                ;

            System.out.println("ExitValue: " + exitVal);
        }
        catch (Throwable t)
        {
            t.printStackTrace();
        }

    }

    public static void main(String args[])
    {

        if(args.length < 1)
        {
            System.out.println("USAGE: java RuntimeExecExample <cmd> [<arg1> <arg2> ..]");
            System.exit(1);
        }
        else
        {
            RuntimeExecExample runtimeExecExample = new RuntimeExecExample();
            runtimeExecExample.executeCommand(args);
        }
    }

}

Upvotes: 3

Views: 1986

Answers (2)

Jool
Jool

Reputation: 1785

In your main thread, as well as doing a waitFor() the sub-process to finish, also do errorGobbler.join() and outputGobbler.join(), then you won't need the while(xGobbler.isAlive()).

Upvotes: 2

Isaac
Isaac

Reputation: 16736

First, you should call join() on any thread that you start.

Second, System.out is buffered, so that might explain it. Try calling System.out.flush() before reading the contents.

A way to know whether it's a buffering issue would be to temporarily change your invoked process to write into System.err instead of System.out. System.err is not buffered.

Upvotes: 1

Related Questions