package de.skuzzle.polly.process; /** * A ProcessWatcher waits until a certain process exited and then invokes a callback * method. This allows asynchronous observation of a process created by a * {@link ProcessExecutor}. Use {@link ProcessExecutor#setProcessWatcher(ProcessWatcher)} * to assign a watcher to the process being created. * * Additionally, a ProcessWatcher can be assigned a timeout in milliseconds. If so, the * callback will be invoked if the time runs out or the observed process exited - whatever * happened earlier. * * Note that observation and timeout check both require separate threads. * * <pre> * ProcessExecutor pe = ProcessExecutor.getOsInstance(false); * pe.setProcessWatcher(new ProcessWatcher(60000) { * public void processExit(ProcessWrapper proc, int exitType) { * if (exitType == ProcessWatcher.EXIT_TYPE_TIMEOUT) { * System.out.println("Timeout while waiting for " + proc); * } * }}).start(); * </pre> * * @author Simon */ public abstract class ProcessWatcher extends Thread { /** * Exit type if a timeout occurred. */ public final static int EXIT_TYPE_TIMEOUT = -2; /** * Exit type if an error occurred. */ public final static int EXIT_TYPE_ERROR = -1; /** * Exit type if the process terminated. */ public final static int EXIT_TYPE_SUCCESS = 0; private ProcessWrapper proc; private int timeout; private Thread timeoutWatcher; private boolean timeouted; /** * Creates a new ProcessWatcher with no timeout. */ public ProcessWatcher() { this(-1); } /** * Creates a new ProcessWatcher with a given timeout. * * @param timeout The timeout in milliseconds when waiting for the observed process. * Values < 0 will be ignored (= no timeout will be set). */ public ProcessWatcher(int timeout) { this.timeout = timeout; this.setDaemon(true); } /** * Sets the process being watched by this watcher. This is done by the * {@link ProcessExecutor}. * * @param proc The process to watch. */ void setProc(ProcessWrapper proc) { this.proc = proc; } /** * Starts observation for the Process and additionally starts a thread which checks * this thread for timeouts. If timeouted, this thread is interrupted to stop * observation. */ @Override public synchronized void start() { super.start(); if (this.timeout > 0) { this.timeoutWatcher = new Thread() { public void run() { try { Thread.sleep(ProcessWatcher.this.timeout); ProcessWatcher.this.timeouted = true; ProcessWatcher.this.interrupt(); } catch (InterruptedException ignore) { // ignore } }; }; this.timeoutWatcher.start(); } } /** * The callback method that is invoked when the observed process terminates or * a timeout occured. * * @param proc The wrapper of the process being watched. * @param exitType The event type which caused this callback being called. The value * is either of {@link #EXIT_TYPE_SUCCESS} (if process terminates properly), * {@link #EXIT_TYPE_TIMEOUT} (if a timeout occurred before termination of the * process) or {@link #EXIT_TYPE_ERROR} (if an error occurred while waiting for * the process to terminate). */ public abstract void processExit(ProcessWrapper proc, int exitType); @Override public void run() { try { this.proc.getProcess().waitFor(); if (this.timeoutWatcher != null) { this.timeoutWatcher.interrupt(); } this.processExit(this.proc, EXIT_TYPE_SUCCESS); } catch (InterruptedException e) { if (this.timeouted) { this.processExit(this.proc, EXIT_TYPE_TIMEOUT); return; } else if (this.timeoutWatcher != null) { this.timeoutWatcher.interrupt(); } this.processExit(this.proc, EXIT_TYPE_ERROR); } } }