KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > fenyo > gnetwatch > actions > ExternalCommand


1
2 /*
3  * GNetWatch
4  * Copyright 2006, 2007 Alexandre Fenyo
5  * gnetwatch@fenyo.net
6  *
7  * This file is part of GNetWatch.
8  *
9  * GNetWatch is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * GNetWatch is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with GNetWatch; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22  */

23
24 package net.fenyo.gnetwatch.actions;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28
29 import java.io.*;
30
31 /**
32  * Instances of this class launch and manage processes outside of the JVM.
33  * @author Alexandre Fenyo
34  * @version $Id: ExternalCommand.java,v 1.14 2007/03/03 00:38:20 fenyo Exp $
35  */

36
37 public class ExternalCommand {
38   private static Log log = LogFactory.getLog(ExternalCommand.class);
39
40   private boolean merge = false;
41
42   final private String JavaDoc [] cmdLine;
43   final private String JavaDoc directory;
44
45   final private StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
46
47   private Process JavaDoc process = null;
48   private BufferedReader reader = null;
49   private BufferedReader errReader = null;
50
51   /**
52    * Creates an ExternalCommand instance and saves the command line.
53    * any thread.
54    * @param cmdLine command line.
55    * @param directory path to the current directory for the script to be launched.
56    */

57   public ExternalCommand(final String JavaDoc [] cmdLine, final String JavaDoc directory) {
58     this.cmdLine = cmdLine;
59     this.directory = directory;
60   }
61
62   /**
63    * Creates an ExternalCommand instance and saves the command line.
64    * any thread.
65    * @param cmdLine command line.
66    */

67   // any thread
68
public ExternalCommand(final String JavaDoc [] cmdLine) {
69     this.cmdLine = cmdLine;
70     this.directory = System.getProperty("java.io.tmpdir");
71   }
72
73   /**
74    * Creates an ExternalCommand instance and saves the command line.
75    * any thread.
76    * @param cmdLine command line.
77    * @param merge merge standard output and standard error.
78    */

79   // any thread
80
public ExternalCommand(final String JavaDoc [] cmdLine, final boolean merge) {
81     this.merge = merge;
82     this.cmdLine = cmdLine;
83     this.directory = System.getProperty("java.io.tmpdir");
84   }
85
86   /**
87    * Reads a line from the process output.
88    * @param r reader.
89    * @return line read.
90    * @throws IOException IO exception.
91    * @throws InterruptedException exception.
92    */

93   // data read is lost when interrupted
94
// returns null if EOF
95
// major feature: it never blocks the current thread while reading a stream
96
// On peut améliorer les perfs en gardant dans sb ce qui est lu et donc en lisant plusieurs caractères à la fois
97
// et en ne retournant que jusqu'au retour chariot.
98
// this private method must be called from synchronized methods
99
// any thread
100
private String JavaDoc readLine(Reader r) throws IOException, InterruptedException JavaDoc {
101     sb.setLength(0);
102     while (!Thread.currentThread().isInterrupted()) {
103       if (r.ready()) {
104         final int ret = r.read();
105         if (ret == -1) return sb.length() != 0 ? sb.toString() : null;
106         if (ret == '\n') return sb.toString();
107         sb.append((char) ret);
108       } else {
109         try {
110           process.exitValue();
111           return sb.length() != 0 ? sb.toString() : null;
112         } catch (final IllegalThreadStateException JavaDoc ex) {}
113         Thread.sleep(100);
114       }
115     }
116     log.info("readLine(): was interrupted");
117     throw new InterruptedException JavaDoc("readLine()");
118   }
119
120   /**
121    * Reads the whole output.
122    * @return whole output.
123    * @throws InterruptedException exception.
124    */

125   // return null if IOException (EOF for instance)
126
// any thread
127
public synchronized String JavaDoc runStdoutStderr() throws InterruptedException JavaDoc {
128     String JavaDoc retval = null;
129     try {
130       fork();
131       retval = readStdoutStderr();
132     } catch (final IOException ex) {}
133     try {
134       end();
135     } catch (final IOException ex) {}
136     return retval;
137   }
138
139   /**
140    * Reads the whole standard output.
141    * @return whole standard output.
142    * @throws InterruptedException interrupted.
143    */

144   // return null if IOException (EOF for instance)
145
// any thread
146
public synchronized String JavaDoc runStdout() throws InterruptedException JavaDoc {
147     String JavaDoc retval = null;
148     try {
149       fork();
150       retval = readStdout();
151     } catch (final IOException ex) {}
152     try {
153       end();
154     } catch (final IOException ex) {}
155     return retval;
156   }
157
158   /**
159    * Displays command line arguments.
160    * any thread.
161    * @param none.
162    * @return void.
163    */

164   public synchronized void logArgs() {
165     for (int x = 0; x < cmdLine.length; x++)
166       log.debug("arg " + x + ": " + cmdLine[x]);
167   }
168
169   /**
170    * Launches a process but do not wait for its completion.
171    * any thread.
172    * @param none.
173    * @return void.
174    * @throws IOException i/o exception <bold>before</bold> reading the process output stream.
175    */

176   public synchronized void fork() throws IOException {
177     final ProcessBuilder JavaDoc pb = new ProcessBuilder JavaDoc(cmdLine);
178     pb.directory(new File(directory));
179     pb.redirectErrorStream(merge);
180     process = pb.start();
181
182     if (process == null) throw new IOException("null process");
183
184     reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
185     errReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
186   }
187
188   /**
189    * Merges stdout and stderr.
190    * Waits for the end of the process output stream.
191    * Note that this method can return before the process completion.
192    * any thread.
193    * @param none.
194    * @return void.
195    * @throws IOException i/o exception <bold>while</bold> reading the process output stream.
196    */

197   public synchronized String JavaDoc readStdoutStderr() throws IOException, InterruptedException JavaDoc {
198     StringBuffer JavaDoc output = new StringBuffer JavaDoc();
199     String JavaDoc str;
200     while ((str = readLine(reader)) != null) {
201       output.append(str);
202       output.append("\n");
203     }
204
205     while ((str = readLine(errReader)) != null) {
206       output.append(str);
207       output.append("\n");
208     }
209
210     // We can not return a StringBuffer since it could be modified by readOutput() running
211
// in another thread, even if we synchronize each method of ExternalCommand.
212
return output.toString();
213   }
214
215   /**
216    * Reads stdout.
217    * Waits for the end of the process output stream.
218    * Note that this method can return before the process completion.
219    * any thread.
220    * @param none.
221    * @return void.
222    * @throws IOException i/o exception <bold>while</bold> reading the process output stream.
223    */

224   public synchronized String JavaDoc readStdout() throws IOException, InterruptedException JavaDoc{
225     StringBuffer JavaDoc output = new StringBuffer JavaDoc();
226     String JavaDoc str;
227
228     while ((str = readLine(reader)) != null) {
229       output.append(str);
230       output.append("\n");
231     }
232
233     // We can not return a StringBuffer since it could be modified by readOutput() running
234
// in another thread, even if we synchronize each method of ExternalCommand.
235
return output.toString();
236   }
237
238   /**
239    * Reads stderr.
240    * Waits for the closure of the process output stream.
241    * Note that this method can return before the process completion.
242    * any thread.
243    * @param none.
244    * @return void.
245    * @throws IOException i/o exception <bold>while</bold> reading the process output stream.
246    */

247   public synchronized String JavaDoc readStderr() throws IOException, InterruptedException JavaDoc {
248     StringBuffer JavaDoc output = new StringBuffer JavaDoc();
249     String JavaDoc str;
250
251     while ((str = readLine(errReader)) != null) {
252       output.append(str);
253       output.append("\n");
254     }
255
256     // We can not return a StringBuffer since it could be modified by readOutput() running
257
// in another thread, even if we synchronize each method of ExternalCommand.
258
return output.toString();
259   }
260
261   /**
262    * Reads one line of the stdout.
263    * any thread.
264    * @param none.
265    * @return void.
266    * @throws IOException i/o exception <bold>while</bold> reading the process output stream.
267    */

268   public synchronized String JavaDoc readLineStdout() throws IOException, InterruptedException JavaDoc {
269     return readLine(reader);
270   }
271
272   /**
273    * Reads one line of the stderr.
274    * any thread.
275    * @param none.
276    * @return void.
277    * @throws IOException i/o exception <bold>while</bold> reading the process output stream.
278    */

279   public synchronized String JavaDoc readLineStderr() throws IOException, InterruptedException JavaDoc {
280     return readLine(errReader);
281   }
282
283   /**
284    * Make sure the underlying file descriptors are closed, to avoid maintening
285    * unused resources in an application server JVM for instance.
286    * any thread.
287    * @param none.
288    * @return void.
289    * @throws IOException error while closing streams.
290    */

291   public void end() throws IOException {
292     // may be closing the readers is sufficient to get these streams closed
293
// we want the underlying file descriptors being close to avoid maintening
294
// unused resources in an application server JVM, so we explicitely close
295
// those streams.
296
if (process != null) {
297       kill();
298       synchronized(this) {
299         if (process.getInputStream() != null) process.getInputStream().close();
300         if (process.getOutputStream() != null) process.getOutputStream().close();
301         if (process.getErrorStream() != null) process.getErrorStream().close();
302       }
303     }
304   }
305
306   /**
307    * Kills the process.
308    * We do not reset process to null since it can be used to get exit code.
309    * any thread.
310    * @param none.
311    * @return void.
312    */

313   private void kill() {
314     if (process != null) {
315       boolean terminated = false;
316       process.destroy();
317       while (terminated == false)
318         try {
319           process.waitFor();
320           terminated = true;
321         } catch (final InterruptedException JavaDoc ex) {
322           log.warn("Exception", ex);
323         }
324     }
325   }
326 }
327
Popular Tags