package xcat.framework.util;
import java.io.*;
import java.net.*;
import java.util.Date;
import soaprmi.events.*;
/**
* A wrapper around the Runtime exec method.
*
* Can be constructed to capture the streams either
* to a file or to an event channel, or both. The option
* "addressOnly" indicates that only a final event indicating
* the location of the log file should be sent.
*
* Messages are written in xml-style name/value pairs.
* Possible names are:
*
* who instantiated this ProcessManager
* host name where running
* host address where running
* timestamp of event
* name of method executed
* arguments to method
* status of execution
* name of process output stream
* data from process output stream
* try-block where exception was thrown
* stringified exception message
* stringified integer
* hostname:path
*
* @version $Revision: 1.3 $ $Author: arossi
* $ $Date: 2001/12/29 15:15:22 $ (GMT)
* @author Albert L. Rossi [mailto:arossi@indiana.edu]
*/
public class ProcessManager
{
private EventChannel ec;
private File logdir;
private String source;
private String filename;
private String localhost;
private String ipAddress;
private String header;
private boolean addressOnly;
private BufferedWriter lgw;
/**
* First constructor: specify both a file and a channel.
* @param source_: the origin of the call to
* construct this object.
* @param filename_: name of file to write to; this will
* be placed in a subdirectory of the
* current directory called "logs"; if
* $PWD/logs does not exist, it will be
* created.
* @param ec_: event channel reference.
* @param addressOnly_: true = send only one event
* indicating the host and path of
* the log file for the process.
*/
public ProcessManager
(String source_,
String filename_,
EventChannel ec_,
boolean addressOnly_)
{
source = source_;
filename = filename_;
ec = ec_;
addressOnly = addressOnly_;
init();
} // first constructor
/**
* Second constructor: no channel.
* @param source_: the origin of the call to
* construct this object.
* @param filename_: name of file to write to; this will
* be placed in a subdirectory of the
* current directory called "logs"; if
* $PWD/logs does not exist, it will be
* created.
*/
public ProcessManager
(String source_,
String filename_)
{
source = source_;
filename = filename_;
ec = null;
addressOnly = false;
init();
} // second constructor
/**
* Third constructor: no file.
* @param source_: the origin of the call to
* construct this object.
* @param ec_: event channel reference.
*/
public ProcessManager
(String source_,
EventChannel ec_)
{
source = source_;
filename = null;
ec = ec_;
addressOnly = false;
init();
} // third constructor
/**
* Returns xml-style name-value pair string.
*/
private static String nameValuePair
(String name,
String value)
{
StringBuffer sb = new StringBuffer("");
sb.append("");
sb.append(name);
sb.append(" ");
sb.append("");
sb.append(value);
sb.append(" ");
return sb.toString();
} // nameValuePair
/**
* Takes an array of strings and executes them as a single command,
* spins off two threads to monitor the input (stdout) and
* error (stderr) streams of the process, waits for the process
* to exit and returns exit value.
* @param cmdstr: array of command-line style strings comprising
* executable name + arguments.
*/
public int exec (String [] command)
{
int exitval = -1;
Process p = null;
Runtime rt = Runtime.getRuntime();
File dest = null;
String fileAddress = null;
StringBuffer sb = new StringBuffer("");
StringBuffer args = new StringBuffer("");
sb.append(nameValuePair("method", "ProcessManager.exec"));
for (int i = 0; i < command.length; i++)
{
args.append(command[i]);
args.append(" ");
}
sb.append(nameValuePair("args", args.toString()));
final String eheader = sb.toString();
final boolean sendEvent = !addressOnly;
try {
sb.append(nameValuePair("status","executing"));
publish(sb.toString(), sendEvent);
p = rt.exec (command);
} catch (Exception e) {
sb.append(nameValuePair("try-block","rt.exec"));
sb.append(nameValuePair("Exception", e.toString()));
publish(sb.toString(), sendEvent);
p.destroy();
return exitval;
}
final InputStreamReader isr
= new InputStreamReader(p.getInputStream());
final InputStreamReader esr
= new InputStreamReader(p.getErrorStream());
Thread t_input = new Thread()
{
public void run()
{
int bytes = 0;
StringBuffer sb = new StringBuffer("");
char [] buf = new char[256];
while (!Thread.interrupted())
{
try {
bytes = isr.read(buf);
if (bytes > 0) {
sb.setLength(0);
sb.append(eheader);
sb.append(nameValuePair
("stream", "input"));
sb.append(nameValuePair
("output",
new String(buf, 0, bytes)));
publish(sb.toString(), sendEvent);
}
} catch (IOException ignore) {
break;
}
}
}
};
t_input.start();
Thread t_error = new Thread()
{
public void run()
{
int bytes = 0;
StringBuffer sb = new StringBuffer("");
char [] buf = new char[256];
while (!Thread.interrupted())
{
try {
bytes = esr.read(buf);
if (bytes > 0) {
sb.setLength(0);
sb.append(eheader);
sb.append(nameValuePair
("stream", "error"));
sb.append(nameValuePair
("output",
new String(buf, 0, bytes)));
publish(sb.toString(), sendEvent);
}
} catch (IOException ignore) {
break;
}
}
}
};
t_error.start();
try {
sb.setLength(0);
sb.append(eheader);
sb.append(nameValuePair("status", "waiting"));
publish(sb.toString(), sendEvent);
p.waitFor();
} catch (Exception e) {
sb.append(nameValuePair("try-block", "p.waitFor"));
sb.append(nameValuePair("Exception", e.toString()));
p.destroy();
publish(sb.toString(), sendEvent);
}
exitval = p.exitValue();
sb.setLength(0);
sb.append(eheader);
sb.append(nameValuePair("process exit value",
Integer.toString(exitval)));
publish(sb.toString(), sendEvent);
try {
isr.close();
} catch (Exception e) {
sb.setLength(0);
sb.append(eheader);
sb.append(nameValuePair("try-block", "isr.close"));
sb.append(nameValuePair("Exception", e.toString()));
publish(sb.toString(), sendEvent);
}
try {
esr.close();
} catch (Exception e) {
sb.setLength(0);
sb.append(eheader);
sb.append(nameValuePair("try-block", "esr.close"));
sb.append(nameValuePair("Exception", e.toString()));
publish(sb.toString(), sendEvent);
}
try {
t_input.join();
} catch (Exception e) {
sb.setLength(0);
sb.append(eheader);
sb.append(nameValuePair("try-block", "t_input.join"));
sb.append(nameValuePair("Exception", e.toString()));
publish(sb.toString(), sendEvent);
}
try {
t_error.join();
} catch (Exception e) {
sb.setLength(0);
sb.append(eheader);
sb.append(nameValuePair("try-block", "t_error.join"));
sb.append(nameValuePair("Exception", e.toString()));
publish(sb.toString(), sendEvent);
}
// publish address of file
if (filename != null) {
dest = new File(filename);
sb.setLength(0);
sb.append(eheader);
sb.append(nameValuePair
("log file address",
localhost + ":" + dest.getAbsolutePath()));
publish(sb.toString(), true);
}
sb.setLength(0);
sb.append(eheader);
sb.append(nameValuePair("status", "done"));
publish(sb.toString(), sendEvent);
return exitval;
} // exec
/**
* Sets up the publication fields. Header is
* a set of name-value pairs written in xml-style.
*/
private void init ()
{
localhost = "";
ipAddress = "";
lgw = null;
StringBuffer sb = new StringBuffer("");
sb.append(nameValuePair("source", source));
try {
localhost = InetAddress.getLocalHost().getHostName();
sb.append(nameValuePair("localhost", localhost));
} catch (Exception ex) {
System.err.println
("ProcessManager init: could not get localhost: "
+ ex.toString());
}
try {
ipAddress = InetAddress.getLocalHost().getHostAddress();
sb.append(nameValuePair("ipAddress", ipAddress));
} catch (Exception ex) {
System.err.println
("ProcessManager init: could not get ipAddress: "
+ ex.toString());
}
header = sb.toString();
if (filename != null) {
logdir = new File("logs");
if (!logdir.exists()) logdir.mkdir();
else if (!logdir.isDirectory()) {
System.err.println
("ProcessManager init: a file named 'logs' "
+ "exists in this location; cannot make log dir");
return;
}
filename = "logs/" + filename;
try {
lgw = new BufferedWriter (new FileWriter (filename, true));
} catch (Exception e) {
System.err.println
("ProcessManager init: could not open writer for " +
filename + ": " + e);
}
}
} // init
/**
* Synchronized publication of events.
* The boolean indicates whether to send an event or not.
*/
private synchronized void publish
(String msg_, boolean sendEvent)
{
StringBuffer sb = new StringBuffer("");
if (lgw != null)
{
// write to log file
sb.append(header);
sb.append
(nameValuePair
("timestamp",
(new Date
(System.currentTimeMillis())).toString()));
sb.append(msg_);
try {
lgw.write("\n" + sb.toString() + "\n");
lgw.flush();
} catch (Exception e) {
sb.setLength(0);
sb.append(header);
sb.append(nameValuePair
("method", "ProcessManager.publish"));
sb.append(nameValuePair
("try-block", "lgw.write"));
sb.append(nameValuePair
("Exception", e.toString()));
System.err.println (sb.toString());
}
}
if (ec != null && sendEvent)
{
// publish to event channel
sb.setLength(0);
sb.append(header);
sb.append(msg_);
Event event = new Event(sb.toString());
event.setEventType("log");
event.setSource(source);
try {
ec.handleEvent(event);
} catch (Exception e) {
sb = new StringBuffer(header);
sb.append(nameValuePair
("method", "ProcessManager.publish"));
sb.append(nameValuePair
("try-block", "ec.handleEvent"));
sb.append(nameValuePair
("Exception", e.toString()));
System.err.println (sb.toString());
}
}
} // publish
} // ProcessManager