1 /***
2 * Redistribution and use of this software and associated documentation
3 * ("Software"), with or without modification, are permitted provided
4 * that the following conditions are met:
5 *
6 * 1. Redistributions of source code must retain copyright
7 * statements and notices. Redistributions must also contain a
8 * copy of this document.
9 *
10 * 2. Redistributions in binary form must reproduce the
11 * above copyright notice, this list of conditions and the
12 * following disclaimer in the documentation and/or other
13 * materials provided with the distribution.
14 *
15 * 3. The name "Exolab" must not be used to endorse or promote
16 * products derived from this Software without prior written
17 * permission of Exoffice Technologies. For written permission,
18 * please contact info@exolab.org.
19 *
20 * 4. Products derived from this Software may not be called "Exolab"
21 * nor may "Exolab" appear in their names without prior written
22 * permission of Exoffice Technologies. Exolab is a registered
23 * trademark of Exoffice Technologies.
24 *
25 * 5. Due credit should be given to the Exolab Project
26 * (http://www.exolab.org/).
27 *
28 * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS
29 * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
32 * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39 * OF THE POSSIBILITY OF SUCH DAMAGE.
40 *
41 * Copyright 2001-2004 (C) Exoffice Technologies Inc. All Rights Reserved.
42 *
43 * $Id: Executor.java,v 1.1 2004/11/26 01:51:10 tanderson Exp $
44 */
45 package org.exolab.jms.net.jvm;
46
47 import java.io.BufferedReader;
48 import java.io.InputStream;
49 import java.io.InputStreamReader;
50 import java.io.IOException;
51 import java.io.OutputStream;
52 import java.io.PrintStream;
53
54 import org.apache.commons.logging.Log;
55 import org.apache.commons.logging.LogFactory;
56
57
58 /***
59 * This class enables commands to be executed, with the output being
60 * captured, or echoed to System.out and System.err
61 *
62 * @version $Revision: 1.1 $ $Date: 2004/11/26 01:51:10 $
63 * @author <a href="mailto:tma@netspace.net.au">Tim Anderson</a>
64 */
65 public class Executor {
66
67 /***
68 * The command to execute
69 */
70 private final String _command;
71
72 /***
73 * The stream to direct standard out to
74 */
75 private final OutputStream _out;
76
77 /***
78 * The stream to direct standard err to
79 */
80 private final OutputStream _err;
81
82 /***
83 * The thread for _out
84 */
85 private Thread _outThread;
86
87 /***
88 * The thread for _err
89 */
90 private Thread _errThread;
91
92 /***
93 * The executing process
94 */
95 private volatile Process _process;
96
97 /***
98 * The logger
99 */
100 private static final Log _log = LogFactory.getLog(Executor.class);
101
102
103 /***
104 * Constructor to execute a command, with output going to System.out and
105 * System.err
106 *
107 * @param command the command to execute
108 */
109 public Executor(String command) {
110 this(command, System.out, System.err);
111 }
112
113 /***
114 * Constructor to execute a command, with all output going to a stream
115 *
116 * @param command the command to execute
117 * @param log the stream to log output to
118 */
119 public Executor(String command, OutputStream log) {
120 this(command, log, log);
121 }
122
123 /***
124 * Constructor to execute a command with standard output and error output
125 * going to two separate streams
126 *
127 * @param command the command to execute
128 * @param out the stream to direct standard output to
129 * @param err the stream to direct standard error to
130 */
131 public Executor(String command, OutputStream out, OutputStream err) {
132 if (command == null) {
133 throw new IllegalArgumentException("Argument 'command' is null");
134 }
135 if (out == null) {
136 throw new IllegalArgumentException("Argument 'out' is null");
137 }
138 if (err == null) {
139 throw new IllegalArgumentException("Argument 'err' is null");
140 }
141 _command = command;
142 _out = out;
143 _err = err;
144 }
145
146 /***
147 * Start the command
148 *
149 * @throws IOException if the command cannot be started
150 */
151 public void start() throws IOException {
152 _log.debug("Starting " + _command);
153 _process = Runtime.getRuntime().exec(_command);
154 Reader out = new Reader(_process.getInputStream(), _out);
155 Reader err = new Reader(_process.getErrorStream(), _err);
156 _outThread = new Thread(out, "StdOut<" + _command + ">");
157 _errThread = new Thread(err, "StdErr<" + _command + ">");
158 _outThread.start();
159 _errThread.start();
160
161 }
162
163 /***
164 * Wait for the command to finish
165 *
166 * @return the command exit status
167 */
168 public int waitFor() {
169 boolean done = false;
170 int status = 1;
171
172 while (!done) {
173 try {
174 status = _process.waitFor();
175 done = true;
176 } catch (InterruptedException ignore) {
177
178 }
179 }
180
181 while (true) {
182 try {
183 _outThread.join();
184 break;
185 } catch (InterruptedException ignore) {
186
187 }
188 }
189
190 while (true) {
191 try {
192 _errThread.join();
193 break;
194 } catch (InterruptedException ignore) {
195
196 }
197 }
198
199 _outThread = null;
200 _errThread = null;
201 _process = null;
202
203 return status;
204 }
205
206 /***
207 * Execute the command and wait for it to complete
208 *
209 * @return the command exit status
210 * @throws IOException if the command cannot be executed
211 */
212 public int run() throws IOException {
213 start();
214 return waitFor();
215 }
216
217 /***
218 * Stop the process
219 */
220 public void stop() {
221 Process process = _process;
222 if (process != null) {
223 _log.debug("Stopping " + _command);
224 process.destroy();
225 }
226 }
227
228 /***
229 * Helper class that reads from one stream and writes to another, in a
230 * separate thread.
231 */
232 class Reader implements Runnable {
233
234 /***
235 * The thread executing the reader instance
236 */
237 private volatile Thread _thread = null;
238
239 /***
240 * The input stream
241 */
242 private BufferedReader _input = null;
243
244 /***
245 * The output stream
246 */
247 private PrintStream _output = null;
248
249 /***
250 * The exception, if an exception was generated while performing I/O
251 */
252 private Exception _exception = null;
253
254 /***
255 * Construct a reader, reading from the input stream and writing to
256 * the output stream
257 *
258 * @param input the input stream
259 * @param output the output stream
260 */
261 public Reader(InputStream input, OutputStream output) {
262 _input = new BufferedReader(new InputStreamReader(input));
263 _output = new PrintStream(output);
264 }
265
266 /***
267 * Run the reader
268 */
269 public void run() {
270 _thread = Thread.currentThread();
271 String line;
272 try {
273 while (_thread != null && (line = _input.readLine()) != null) {
274 _output.println(line);
275 }
276 } catch (IOException exception) {
277
278 _exception = exception;
279 _thread = null;
280 }
281 _input = null;
282 _output = null;
283 }
284
285 /***
286 * Stop the reader
287 */
288 public void stop() {
289 if (_thread != null) {
290 Thread interrupter = _thread;
291 _thread = null;
292 interrupter.interrupt();
293 }
294 }
295
296 /***
297 * Returns the exception if one was generated. If {@link #stop} was
298 * invoked then an exception is likely to have been generated.
299 */
300 public Exception getException() {
301 return _exception;
302 }
303 }
304
305 }