You really have to go the way of creating a thread for each thread that you want to control. If your use case allows you to combine both stdout and stderr of the process in question, you only need one thread, otherwise two are needed.
It took me some time to understand this in one of our projects, where I have to start an external process, release it and do something with it, at the same time look for errors and terminate the process, as well as to stop it when the application user java cancels the operation.
I created a fairly simple class to encapsulate the observing part, whose run () method looks something like this:
public void run() { BufferedReader tStreamReader = null; try { while (externalCommand == null && !shouldHalt) { logger.warning("ExtProcMonitor(" + (watchStdErr ? "err" : "out") + ") Sleeping until external command is found"); Thread.sleep(500); } if (externalCommand == null) { return; } tStreamReader = new BufferedReader(new InputStreamReader(watchStdErr ? externalCommand.getErrorStream() : externalCommand.getInputStream())); String tLine; while ((tLine = tStreamReader.readLine()) != null) { logger.severe(tLine); if (filter != null) { if (filter.matches(tLine)) { informFilterListeners(tLine); return; } } } } catch (IOException e) { logger.logExceptionMessage(e, "IOException stderr"); } catch (InterruptedException e) { logger.logExceptionMessage(e, "InterruptedException waiting for external process"); } finally { if (tStreamReader != null) { try { tStreamReader.close(); } catch (IOException e) {
On the caller, it looks like this:
Thread tExtMonitorThread = new Thread(new Runnable() { public void run() { try { while (externalCommand == null) { getLogger().warning("Monitor: Sleeping until external command is found"); Thread.sleep(500); if (isStopRequested()) { getLogger() .warning("Terminating external process on user request"); if (externalCommand != null) { externalCommand.destroy(); } return; } } int tReturnCode = externalCommand.waitFor(); getLogger().warning("External command exited with code " + tReturnCode); } catch (InterruptedException e) { getLogger().logExceptionMessage(e, "Interrupted while waiting for external command to exit"); } } }, "ExtCommandWaiter"); ExternalProcessOutputHandlerThread tExtErrThread = new ExternalProcessOutputHandlerThread("ExtCommandStdErr", getLogger(), true); ExternalProcessOutputHandlerThread tExtOutThread = new ExternalProcessOutputHandlerThread("ExtCommandStdOut", getLogger(), true); tExtMonitorThread.start(); tExtOutThread.start(); tExtErrThread.start(); tExtErrThread.setFilter(new FilterFunctor() { public boolean matches(Object o) { String tLine = (String)o; return tLine.indexOf("Error") > -1; } }); FilterListener tListener = new FilterListener() { private boolean abortFlag = false; public boolean shouldAbort() { return abortFlag; } public void matched(String aLine) { abortFlag = abortFlag || (aLine.indexOf("Error") > -1); } }; tExtErrThread.addFilterListener(tListener); externalCommand = new ProcessBuilder(aCommand).start(); tExtErrThread.setProcess(externalCommand); try { tExtMonitorThread.join(); tExtErrThread.join(); tExtOutThread.join(); } catch (InterruptedException e) {
Unfortunately, I don't need to use a standalone runnable example, but maybe this helps. If I had to do this again, I would once again look at using the Thread.interrupt () method instead of the self-starting stop flag (mind to declare it volatile!), But I leave it at a different time. :)