Reputation: 301
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
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
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