001: package org.objectweb.celtix.common.commands;
002:
003: import java.io.*;
004: import java.util.StringTokenizer;
005: import java.util.logging.Level;
006: import java.util.logging.Logger;
007:
008: import org.objectweb.celtix.common.i18n.Message;
009:
010: public class ForkedCommand extends Thread {
011: public static final String EXE_SUFFIX;
012: public static final int DEFAULT_TIMEOUT = 0;
013: private static final Logger LOG = Logger
014: .getLogger(ForkedCommand.class.getName());
015: private String[] arguments;
016: private String[] environment;
017: private PrintStream outputStream;
018: private PrintStream errorStream;
019: private java.lang.Process proc;
020: private boolean completed;
021: private boolean killed;
022: private boolean joinErrOut = true;
023:
024: static {
025: if (System.getProperty("os.name").startsWith("Windows")) {
026: EXE_SUFFIX = ".exe";
027: } else {
028: EXE_SUFFIX = "";
029: }
030: }
031:
032: public ForkedCommand() {
033: }
034:
035: public ForkedCommand(String[] args) {
036: arguments = args;
037: }
038:
039: protected void setArgs(String[] args) {
040: arguments = args;
041: }
042:
043: public void setEnvironment(String[] env) {
044: environment = env;
045: }
046:
047: public String toString() {
048: if (null == arguments) {
049: return null;
050: }
051: StringBuffer buf = new StringBuffer();
052: for (int i = 0; i < arguments.length; i++) {
053: if (i > 0) {
054: buf.append(" ");
055: }
056: boolean quotesNeeded = false;
057: if (arguments[i] != null) {
058: StringTokenizer st = new StringTokenizer(arguments[i]);
059: quotesNeeded = st.countTokens() > 1;
060: }
061: if (quotesNeeded) {
062: buf.append("\"");
063: }
064: buf.append(arguments[i]);
065: if (quotesNeeded) {
066: buf.append("\"");
067: }
068: }
069: return buf.length() > 0 ? buf.toString() : "";
070: }
071:
072: /**
073: * Determines if the threads collecting the forked process' stdout/stderr
074: * should be joined.
075: *
076: * @param flag boolean indicating if threads should be joined
077: */
078: public void joinErrOut(boolean flag) {
079: this .joinErrOut = flag;
080: }
081:
082: public int execute() {
083: return execute(DEFAULT_TIMEOUT);
084: }
085:
086: /**
087: * Executes the process. If the process has not completed after the
088: * specified amount of seconds, it is killed.
089: *
090: * @param timeout the timeout in seconds
091: * @throws ForkedCommandException if process execution fails for some reason
092: * or if the timeout has expired and the process was killed
093: */
094: public int execute(int timeout) {
095: if (null == arguments || arguments.length == 0) {
096: throw new ForkedCommandException(new Message(
097: "NO_ARGUMENTS_EXC", LOG));
098: }
099: if (LOG.isLoggable(Level.FINE)) {
100: LOG.fine("Executing command: " + this );
101: }
102: try {
103: Runtime rt = Runtime.getRuntime();
104: if (environment == null) {
105: proc = rt.exec(arguments);
106: } else {
107: StringBuffer msg = null;
108: if (LOG.isLoggable(Level.FINE)) {
109: msg = new StringBuffer();
110: msg.append("Process environment: ");
111:
112: for (int i = 0; i < environment.length; i++) {
113: msg.append(environment[i]);
114: msg.append(" ");
115: }
116: LOG.fine(msg.toString());
117: }
118:
119: proc = rt.exec(arguments, environment);
120: }
121: } catch (IOException ex) {
122: throw new ForkedCommandException(new Message("EXECUTE_EXC",
123: LOG, this ), ex);
124: }
125:
126: // catch process stderr/stdout
127: ForkedCommandStreamHandler cmdOut = new ForkedCommandStreamHandler(
128: proc.getInputStream(),
129: outputStream == null ? System.out : outputStream);
130: ForkedCommandStreamHandler cmdErr = new ForkedCommandStreamHandler(
131: proc.getErrorStream(), errorStream == null ? System.err
132: : errorStream);
133: cmdErr.start();
134: cmdOut.start();
135:
136: // now wait for the process on our own thread
137: start();
138:
139: // kill process after timeout
140: try {
141: if (timeout > 0) {
142: if (LOG.isLoggable(Level.FINE)) {
143: LOG.fine("Waiting " + timeout
144: + " seconds for process to complete");
145: }
146: join(timeout * 1000);
147: } else {
148: if (LOG.isLoggable(Level.FINE)) {
149: LOG.fine("Waiting for process to complete");
150: }
151: join();
152: }
153: } catch (InterruptedException ex) {
154: ex.printStackTrace();
155: } finally {
156: if (completed) {
157: if (LOG.isLoggable(Level.FINE)) {
158: LOG.fine("Process completed in time");
159: }
160: } else {
161: proc.destroy();
162: killed = true;
163: LOG.fine("Process timed out and was killed");
164: }
165:
166: // wait for the streams threads to finish if necessary
167: if (joinErrOut) {
168: if (LOG.isLoggable(Level.FINE)) {
169: LOG
170: .info("Waiting a further 10 seconds for process "
171: + " stdout/stderr streams to be flushed");
172: }
173: try {
174: cmdErr.join(10 * 1000);
175: cmdOut.join(10 * 1000);
176: } catch (InterruptedException ex) {
177: // silently ignore
178: }
179: }
180: }
181:
182: if (killed) {
183: throw new ForkedCommandException(new Message("TIMEOUT_EXC",
184: LOG, timeout));
185: }
186: int exitVal = proc.exitValue();
187: if (LOG.isLoggable(Level.FINE)) {
188: LOG.fine("Process exited with value: " + exitVal);
189: }
190:
191: return exitVal;
192: }
193:
194: /**
195: * Implements the run method for the thread on which the process is
196: * executed.
197: */
198: public void run() {
199: try {
200: proc.waitFor();
201: completed = true;
202: } catch (InterruptedException ex) {
203: // ignore this one
204: ex.printStackTrace();
205: }
206: }
207:
208: public void setOutputStream(PrintStream os) {
209: outputStream = os;
210: }
211:
212: public void setErrorStream(PrintStream es) {
213: errorStream = es;
214: }
215: }
216:
217: class ForkedCommandStreamHandler extends Thread {
218:
219: private final InputStream is;
220: private final PrintStream ps;
221:
222: ForkedCommandStreamHandler(InputStream i, PrintStream p) {
223: is = i;
224: ps = p;
225: }
226:
227: public void run() {
228: try {
229: InputStreamReader isr = new InputStreamReader(is);
230: BufferedReader br = new BufferedReader(isr);
231: String line = br.readLine();
232: while (line != null) {
233: ps.println(line);
234: line = br.readLine();
235: }
236: } catch (IOException ioe) {
237: // ignore
238: }
239: }
240: }
|