I have two Java threads that need to talk to each other and I'm not sure I'm going about this the right way.
The basic is this: I have on thread, the "runner" which will start off a long-running process by making a call to the command line using the the Apache exec library. Another thread, the "monitor", will periodically monitor and report on the state of the system.
Part of this involves returning the lines that the runner process got from stdout. Currently, the runner and monitor run as separate threads. The monitor has a reference to the runner and can access the output collected (so far) by calling the runner's "get stdout" method.
This seems to be working fine, but the whole setup just doesn't seem very good to me. Partly, I'm worried this implementation isn't thread-safe, though I'm not even sure how to test that. It's been quite a while since I've done multithreaded programming, so I'm wondering if there's a better/cleaner way to do what I'm trying to do. The code looks something like this:
public class Runner implements Callable<String>
{
private ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
private ByteArrayOutputStream errorStream = new ByteArrayOutputStream();
public String getStdOutLines()
{
return new String(outputStream.toByteArray());
}
public String getStdErrLines()
{
return new String(errorStream.toByteArray());
}
@Override
public String call() throws Exception
{
/*
Use apache exec to begin long-running process.
stdout and stderr are written to two local members (outputStream and errorStream).
This is done by the apache exec library.
*/
PumpStreamHandler streamHandler = new PumpStreamHandler(this.outputStream, this.errorStream);
DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
executor.setStreamHandler(streamHandler);
CommandLine cli = //Details not important here.
executor.execute(cli, resultHandler);
resultHandler.waitFor();
String resultsString = generateFinalResultString(outputStream, errorStream); //implementation details of this not important here.
return resultsString; /*some string calculated after the process has completed.*/
}
}
public class Monitor implements Runnable
{
private Runner runner;
public void setRunner(Runner r)
{
this.runner = r;
}
@Override
public void run()
{
try
{
while (!Thread.currentThread().interrupted())
{
//Call the functions on the other thread to get the stdout and stderr
commandStdOut = this.runner.getStdOutLines();
commandStdErr = this.runner.getStdErrLines();
doSendMonitoringMessage(commandStdOut, commandStdErr);
Thread.sleep(SOME_DELAY);
}
}
catch(InterruptedException e)
{
System.out.println("Monitor is shutting down");
}
}
}
public class ExecuteAllThis
{
public void doTheStuff()
{
System.out.println("begining...");
Runner runner = new Runner();
Monitor monitor = new Monitor();
monitor.setRunner(runner);
ExecutorService xservice = Executors.newFixedThreadpool(2);
xservice.execute(monitor);
Future<String> runnerFuture = xservice.submit(runner);
String result = runnerFuture.get(0);
System.out.println("result is: "+result);
}
EDIT: I've added some detail about how I'm calling the long-running process which writes to the stdout and stderr strings, since that is more relevant than I initially realized.