/*
* $Id: ProcessMonitor.java,v 1.1 2007-02-27 12:45:29 eugen Exp $
*
* Copyright (c) 2003 Brockmann Consult GmbH. All right reserved.
* http://www.brockmann-consult.de
*/
package com.bc.process.util;
import java.io.IOException;
import java.io.InputStream;
/**
* This class is currently being reorganized: Implementation moves from the "old"
* ProcessObserver to three observers (2*ProcessStreamObserver, ProcessExitObserver)
* The default implementation uses only the new Observers to signal events. As
* this class is already used compatibility with ProcessObserver is maintained - the
* default implementation for the new Observers delegates to ProcessObserver - guessing
* that all current users of this class create objects of this type initialize it
* using ProcessObserver.
*/
public class ProcessMonitor {
/**
* @deprecated use constructor without ProcessObserver and set one of three possible observers using set*Observer()
*/
public ProcessMonitor(Process process, ProcessObserver observer) {
this(process, observer, false);
}
/**
* @deprecated use constructor without ProcessObserver and set one of three possible observers using set*Observer()
*/
public ProcessMonitor(Process process, ProcessObserver observer, boolean blocking) {
this.process = process;
this.blocking = blocking;
this.processExited = false;
this.monitorActive = false;
stdoutObserver = new StdoutObserverAdapter(observer);
stderrObserver = new StderrObserverAdapter(observer);
exitObserver = new ExitObserverAdapter(observer);
}
public ProcessMonitor(Process process) {
this.process = process;
this.blocking = false;
this.monitorActive = false;
this.processExited = false;
// Default behaviour is empty - must be overridden by set*Observer()
stdoutObserver = new ProcessStreamObserver.DefaultEmptyProcessStreamObserver();
stderrObserver = new ProcessStreamObserver.DefaultEmptyProcessStreamObserver();
exitObserver = new ProcessExitObserver.DefaultEmptyProcessExitObserver();
}
/**
* Sets the standard output observer.<br>
* <code>null</code> is an illegal value.<br>
* Changing of the standard output observer only allowet before the observing has been started.
*
* @param observer
* @throws IllegalArgumentException if the given observer is <code>null</code>
* @throws IllegalStateException if the observer wants to be changed after starting.
*/
public void setStdoutObserver(ProcessStreamObserver observer) {
if (observer == null) {
throw new IllegalArgumentException("The observer must not be null");
}
if (monitorActive) {
throw new IllegalStateException("The method was called after the Monitoring has beeing started.");
}
stdoutObserver = observer;
}
/**
* Sets the standard error observer.<br>
* <code>null</code> is an illegal value.<br>
* Changing of the standard error observer only allowet before the observing has been started.
*
* @param observer
* @throws IllegalArgumentException if the given observer is <code>null</code>
* @throws IllegalStateException if the observer wants to be changed after starting.
*/
public void setStderrObserver(ProcessStreamObserver observer) {
if (observer == null) {
throw new IllegalArgumentException("The observer must not be null");
}
if (monitorActive) {
throw new IllegalStateException("The method was called after the Monitoring has beeing started.");
}
stderrObserver = observer;
}
/**
* Sets the exit observer.<br>
* <code>null</code> is an illegal value.<br>
* Changing of the exit observer only allowet before the observing has been started.
*
* @param observer
* @throws IllegalArgumentException if the given observer is <code>null</code>
* @throws IllegalStateException if the observer wants to be changed after starting.
*/
public void setExitObserver(ProcessExitObserver observer) {
if (observer == null) {
throw new IllegalArgumentException("The observer must not be null");
}
if (monitorActive) {
throw new IllegalStateException("The method was called after the Monitoring has beeing started.");
}
exitObserver = observer;
}
/**
* Sets the blocking value. If the blocking is set to <code>true</code> the current thread was
* waiting until the process has exitted.
* Setting of the blocking value only allowed before the moitorig was started.
*
* @param blocking
* @throws IllegalStateException if the blocking value wants to be set after starting.
*/
public void setBlocking(boolean blocking) {
if (monitorActive) {
throw new IllegalStateException("The method was called after the Monitoring has beeing started.");
}
this.blocking = blocking;
}
public void start() {
if (monitorActive) {
throw new IllegalStateException("monitor is already active");
}
monitorActive = true;
final Thread sendOut = new Thread("stdoutObserverThread") {
public void run() {
observeStream(process.getInputStream(), stdoutObserver);
}
};
sendOut.start();
final Thread sendErr = new Thread("stderrObserverThread") {
public void run() {
observeStream(process.getErrorStream(), stderrObserver);
}
};
sendErr.start();
if (blocking) {
waitForExit();
} else {
final Thread sendExit = new Thread("exitObserverThread") {
public void run() {
waitForExit();
}
};
sendExit.start();
}
}
public void stop() {
monitorActive = false;
}
public boolean isMonitorActive() {
return monitorActive;
}
public boolean isProcessExited() {
return processExited;
}
///////////////////////////////////////////////////////////////////////////
/////// END OF PUBLIC
///////////////////////////////////////////////////////////////////////////
private static final boolean DEBUG = false;
private final Process process;
private ProcessStreamObserver stdoutObserver;
private ProcessStreamObserver stderrObserver;
private ProcessExitObserver exitObserver;
private boolean blocking = false;
private boolean processExited;
private boolean monitorActive;
private void observeStream(InputStream is, ProcessStreamObserver observer) {
final StringBuffer lineBuffer = new StringBuffer();
try {
while (true) {
if (processExited) {
debugReadCharacterData("break 1");
// break;
}
if (!monitorActive) {
debugReadCharacterData("break 2");
break;
}
if (is.available() == 0) {
fireOutput(observer, lineBuffer.toString());
lineBuffer.setLength(0);
}
int c = is.read();
if (c >= 0) {
lineBuffer.append((char) c);
if (c == '\n') {
fireOutput(observer, lineBuffer.toString());
lineBuffer.setLength(0);
}
} else {
debugReadCharacterData("break 3");
break;
}
}
is.close();
} catch (IOException e) {
debugReadCharacterData("IOException: " + e.getMessage());
}
fireOutput(observer, lineBuffer.toString());
}
private void fireOutput(ProcessStreamObserver observer, String characterData) {
if (characterData.length() > 0)
observer.processWroteToStream(characterData);
}
private void debugReadCharacterData(String m) {
if (DEBUG) {
System.out.println(process.toString() + ": readCharacterData: " + m);
}
}
private void waitForExit() {
try {
process.waitFor();
} catch (InterruptedException ignore) {
}
processExited = true;
exitObserver.processExited(process.exitValue());
}
/**
* Adapter class to delegate events to ProcessObserver, used internally as standard
* implementation for stdoutObserver
*/
@SuppressWarnings("deprecation")
private class StdoutObserverAdapter implements ProcessStreamObserver {
private ProcessObserver observer;
public StdoutObserverAdapter(ProcessObserver observer) {
this.observer = observer;
}
public void processWroteToStream(String characterData) {
observer.processWroteToStdout(characterData);
}
}
/**
* Adapter class to delegate events to ProcessObserver, used internally as standard
* implementation for stderrObserver
*/
@SuppressWarnings("deprecation")
private class StderrObserverAdapter implements ProcessStreamObserver {
private ProcessObserver observer;
public StderrObserverAdapter(ProcessObserver observer) {
this.observer = observer;
}
public void processWroteToStream(String characterData) {
observer.processWroteToStderr(characterData);
}
}
/**
* Adapter class to delegate events to ProcessObserver, used internally as standard
* implementation for exitObserver
*/
@SuppressWarnings("deprecation")
private class ExitObserverAdapter implements ProcessExitObserver {
private ProcessObserver observer;
public ExitObserverAdapter(ProcessObserver observer) {
this.observer = observer;
}
public void processExited(int exitValue) {
observer.processExited(exitValue);
}
}
}