/** Copyright (C) SAS Institute All rights reserved.
** General Public License: http://www.opensource.org/licenses/gpl-license.php
**/
package org.safs.tools.consoles;
import java.io.*;
import java.lang.Process;
//import org.safs.Log;
import org.safs.tools.CaseInsensitiveFile;
/**
* Continuously monitors the wrapped process out and err streams routing them to the local
* process System.out and System.err via {@link #debug(String)} so they will not block.
* <p>
* By default, all System.out and System.err will go thru the debug() method.
* Users can change this behavior by overriding the debug() method and also by turning off
* the output of either or both of the streams via {@link #setShowOutStream(boolean)} and
* {@link #setShowErrStream(boolean)}.
* <p>
* The class also attempts to see "unhandled exceptions" and store them in the exceptions Vector.
* <p>
* Normal usage might be something like below:
* <pre>
* Process process = runtime.exec(procstr);
* GenericProcessConsole console = new GenericProcessConsole(process);
* Thread athread = new Thread(console);
* athread.start();
* //we can wait until process is finished
* try{ athread.join();}catch(InterruptedException x){;}
* console.shutdown();//precaution
* </pre>
* This class contains no extended SAFS dependencies and can be readily packaged and distributed
* for non-SAFS installations.
*/
public class GenericProcessConsole implements Runnable{
protected Process process;
protected BufferedReader out;
protected BufferedReader err;
protected BufferedWriter in;
protected java.util.Vector exceptions = new java.util.Vector();
boolean shutdown = false;
/**
* set false to stop a copy of output stream data going to the debug sink.
* Default setting is true. */
protected boolean showOutStream = true;
/** set false to stop a copy of error stream data going to the debug sink.
* Default setting is true. */
protected boolean showErrStream = true;
/** "OUT: "*/
public static final String OUT_PREFIX = "OUT: ";
/** "ERR: "*/
public static final String ERR_PREFIX = "ERR: ";
/**
* Default (and only) public constructor for GenericProcessConsole.
* The class instance does NOT start its own separate runnable Thread.
*/
public GenericProcessConsole(Process process) {
super();
_initialize(process);
}
protected void _initialize(Process process){
this.process = process;
try{
if(showOutStream) debug("GenericProcessConsole initializing...");
out = new BufferedReader(new InputStreamReader(process.getInputStream()));
err = new BufferedReader(new InputStreamReader(process.getErrorStream()));
in = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
if(showOutStream) debug("GenericProcessConsole initialization complete.");
}
catch(Exception x){
debug("GenericProcessConsole initialization error:"+ x.getMessage());
}
}
public GenericProcessConsole(Process process, boolean debug2Console) {
super();
setShowOutStream(debug2Console);
setShowErrStream(debug2Console);
_initialize(process);
}
/**
* Writes to System.out .
* Subclasses should override to log to alternate sinks.
* @param message
*/
protected void debug(String message){
System.out.println(message);
}
/** set true to have the output stream copied to the active debug sink */
public void setShowOutStream(boolean showOut){ showOutStream = showOut;}
/** set true to have the error stream copied to the active debug sink */
public void setShowErrStream(boolean showErr){ showErrStream = showErr;}
/**
* Call to set the shutdown flag to true to stop the running console thread.
*/
public void shutdown(){ shutdown = true;}
/**
* @return Vector storing any Exceptions read from Process out and err streams.
*/
public java.util.Vector getExceptions(){
return new java.util.Vector(exceptions);
}
/**
* @return the count of Exceptions in out or err streams, or 0.
*/
public int getExceptionsCount(){
return exceptions.size();
}
/**
* Continuously monitors the process out and err streams routing them to the local
* process System.out and System.err. Also attempts to see "unhandled exceptions" and
* store them in the exceptions Vector. Will run indefinitely until the process
* exits on its own or the shutdown method is called.
* @see #getExceptions()
* @see #shutdown()
*/
public void run(){
boolean outdata = true;
boolean errdata = true;
String linedata = null;
int exitValue = -1;
if(showOutStream) debug("GenericProcessConsole activated for process "+ process);
try{
do{
outdata = out.ready();
if (outdata) {
linedata = "";
linedata = out.readLine();
if(showOutStream) debug(linedata);
if ((linedata.length()>0) &&
(linedata.toLowerCase().startsWith("unhandled exception"))){
exceptions.add(OUT_PREFIX + linedata);
}
}
errdata = err.ready();
if (errdata) {
linedata = "";
linedata = err.readLine();
if(showErrStream) debug(linedata);
if ((linedata.length()>0) &&
(linedata.toLowerCase().startsWith("unhandled exception"))){
exceptions.add(ERR_PREFIX + linedata);
}
}
try{
exitValue = process.exitValue();
shutdown = true;
}catch(IllegalThreadStateException x){
// process not yet finished
}
if ((!outdata)&&(!errdata)&&(!shutdown)) Thread.sleep(250);
}while(! shutdown);
if(showOutStream) debug("GenericProcessConsole shutdown for process "+ process);
}
catch(Exception x){
debug("GenericProcessConsole thread loop error:"+ x.getMessage());
}
}
}