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