KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > oddjob > jobs > ExecJob


1 package org.oddjob.jobs;
2
3
4 import java.io.BufferedInputStream JavaDoc;
5 import java.io.File JavaDoc;
6 import java.io.IOException JavaDoc;
7 import java.io.InputStream JavaDoc;
8 import java.io.ObjectInputStream JavaDoc;
9 import java.io.ObjectOutputStream JavaDoc;
10 import java.io.OutputStream JavaDoc;
11 import java.util.Vector JavaDoc;
12
13 import org.oddjob.Stoppable;
14 import org.oddjob.framework.SimpleJob;
15 import org.oddjob.logging.ConsoleArchive;
16 import org.oddjob.logging.ConsoleArchiveImpl;
17 import org.oddjob.logging.LogArchive;
18 import org.oddjob.logging.LogLevel;
19 import org.oddjob.logging.LoggingOutputStream;
20 import org.oddjob.util.IO;
21 import org.oddjob.util.OddjobConfigException;
22
23 /**
24  * @oddjob.description Execute an external program. This job will
25  * flag complete if the return state of the external program is 0,
26  * otherwise it will flag not complete.
27  * <p>
28  * Processes may behave differently on different operating systems - for
29  * instance stop doesn't alway kill the process. Please see
30  * <a HREF="http://bugs.sun.com/bugdatabase/view_bug.do;:YfiG?bug_id=4109888">
31  * http://bugs.sun.com/bugdatabase/view_bug.do;:YfiG?bug_id=4109888</a>
32  * for addtional information.
33  *
34  * @oddjob.example
35  *
36  * <pre>
37  * &lt;exec name="Batch Example"&gt;
38  * &lt;args&gt;
39  * &lt;value value="cmd"/&gt;
40  * &lt;value value="/C"/&gt;
41  * &lt;value value="${oddjob.dir}\bin\greeting.bat"/&gt;
42  * &lt;value value="Hello"/&gt;
43  * &lt;/args&gt;
44  * &lt;/exec&gt;
45  * </pre>
46  *
47  * @oddjob.example
48  *
49  * The example has the existing environement with an additional environment variable.
50  *
51  * <pre>
52  * &lt;exec name="Example With Environment"
53  * command="foo.exe"&gt;
54  * &lt;environment&gt;
55  * &lt;environment command="set" /&gt;
56  * &lt;value name="ODDJOB_FILE" value="myfile.txt" /&gt;
57  * &lt;/environment&gt;
58  * &lt;/exec&gt;
59  * </pre>
60  *
61  * @oddjob.example
62  *
63  * Using the output of one process as the input of another.
64  *
65  * <pre>
66  * &lt;sequential&gt;
67  * &lt;exec name='One' id='one' command='cmd /c echo hello'&gt;
68  * &lt;stdout&gt;
69  * &lt;buffer/&gt;
70  * &lt;/stdout&gt;
71  * &lt;/exec&gt;
72  * &lt;exec name='Two' id='two' command='c:/cygwin/bin/cat'
73  * stdin='${one.stdout}'/&gt;
74  * &lt;/sequential&gt;
75  *
76  * @author Rob Gordon.
77  */

78 public class ExecJob extends SimpleJob implements Stoppable, ConsoleArchive {
79
80     private transient ConsoleArchiveImpl consoleArchiver;
81
82     /**
83      * Default Constructor.
84      */

85     public ExecJob() {
86         completeConstruction();
87     }
88     
89     /**
90      * Complete construction.
91      */

92     private void completeConstruction() {
93         consoleArchiver = new ConsoleArchiveImpl();
94     }
95     
96     /**
97      * @oddjob.property
98      * @oddjob.description The working directory.
99      * @oddjob.required No
100      */

101     private File JavaDoc dir;
102     
103     /**
104      * @oddjob.property
105      * @oddjob.description The command to run.
106      * @oddjob.required yes, unless arg elements are
107      * used instead.
108      */

109     private String JavaDoc command;
110     
111     /**
112      * @oddjob.property
113      * @oddjob.description A string list of arguments.
114      * @oddjob.required No.
115      */

116     private String JavaDoc[] args;
117     
118     /**
119      * @oddjob.property env
120      * @oddjob.description An environment variable to be
121      * set before the program is executed. This is a
122      * {@link org.oddjob.values.types.MapType} like property.
123      * @oddjob.required No.
124      */

125     private final Vector JavaDoc envs = new Vector JavaDoc();
126
127     /**
128      * @oddjob.property
129      * @oddjob.description An input stream which will
130      * act as stdin for the process.
131      * @oddjob.required No.
132      */

133     private InputStream JavaDoc stdin;
134     
135     /**
136      * @oddjob.property
137      * @oddjob.description An output to where stdout
138      * for the process will be written.
139      * @oddjob.required No.
140      */

141     private OutputStream JavaDoc stdout;
142     
143     /**
144      * @oddjob.property
145      * @oddjob.description An output to where stderr
146      * of the proces will be written.
147      * @oddjob.required No.
148      */

149     private OutputStream JavaDoc stderr;
150     
151     /**
152      * The process.
153      */

154     private volatile Process JavaDoc proc;
155     
156     private volatile Thread JavaDoc thread;
157     
158     /**
159      * Add an argument.
160      *
161      * @param arg The argument.
162      */

163     public void setArgs(String JavaDoc[] args) {
164         this.args = args;
165     }
166
167     /**
168      * Set the command to run.
169      *
170      * @param command The command.
171      */

172     public void setCommand(String JavaDoc command) {
173         this.command = command;
174     }
175     
176     /**
177      * Get the command.
178      *
179      * @return The command.
180      */

181     public String JavaDoc getCommand() {
182         return command;
183     }
184     
185     /**
186      * Set the working directory.
187      *
188      * @param dir The working directory.
189      */

190     public void setDir(File JavaDoc dir) {
191         this.dir = dir;
192     }
193
194     /**
195      * Add an environment variable.
196      *
197      * @param nvp The name/value pair variable.
198      */

199     public void setEnvironment(String JavaDoc name, String JavaDoc value) {
200         this.envs.add(name + "=" + value);
201     }
202
203     /**
204      * Set the input stream stdin for the process will
205      * be read from.
206      *
207      * @param stdin An InputStream.
208      */

209     public void setStdin(InputStream JavaDoc stdin) {
210         this.stdin = stdin;
211     }
212
213     /**
214      * Get the input stream for stdin. This will be null unless one has
215      * been provided.
216      *
217      * @return An InputStream or null.
218      */

219     public InputStream JavaDoc getStdin() {
220         return stdin;
221     }
222     
223     /**
224      * Set the output stream stdout from the process will
225      * be directed to.
226      *
227      * @param stdout The output stream.
228      */

229     public void setStdout(OutputStream JavaDoc stdout) {
230         this.stdout = stdout;
231     }
232
233     /**
234      * Get the output stream for stdout. This will be null unless one has
235      * been provided.
236      *
237      * @return An OutputStream or null.
238      */

239     public OutputStream JavaDoc getStdout() {
240         return stdout;
241     }
242     
243     /**
244      * Set the output stream stderr from the process will
245      * be directed to.
246      *
247      * @param stderr The error stream.
248      */

249     public void setStderr(OutputStream JavaDoc stderr) {
250         this.stderr = stderr;
251     }
252     
253     /**
254      * Get the output stream for stderr. This will be null unless one has
255      * been provided.
256      *
257      * @return An OutputStream or null.
258      */

259     public OutputStream JavaDoc getStderr() {
260         return stderr;
261     }
262     
263     /*
264      * (non-Javadoc)
265      * @see org.oddjob.jobs.AbstractJob#execute()
266      */

267     protected int execute() throws Exception JavaDoc {
268         String JavaDoc envp[] = (String JavaDoc[])envs.toArray(new String JavaDoc[0]);
269         
270         if (envp.length == 0) {
271             envp = null;
272         }
273         
274         Runtime JavaDoc rt = Runtime.getRuntime();
275         if (args == null || args.length == 0) {
276             if (command == null) {
277                 throw new OddjobConfigException("No command given.");
278             }
279             proc = rt.exec(command, envp, dir);
280         }
281         else {
282             proc = rt.exec(args, envp, dir);
283         }
284
285         final InputStream JavaDoc es = proc.getErrorStream();
286         final InputStream JavaDoc is = proc.getInputStream();
287
288         Thread JavaDoc outT = new Thread JavaDoc(new Runnable JavaDoc() {
289             public void run() {
290                 try {
291                     BufferedInputStream JavaDoc bis = new BufferedInputStream JavaDoc(is);
292                     OutputStream JavaDoc os = new LoggingOutputStream(stdout, LogLevel.INFO,
293                             consoleArchiver.consoleLog());
294                     IO.copy(bis, os);
295                     os.close();
296                 } catch (IOException JavaDoc e) {
297                     logger().error("Failed copying stream.", e);
298                 }
299             }
300         });
301
302         Thread JavaDoc errT = new Thread JavaDoc(new Runnable JavaDoc() {
303
304             public void run() {
305                 try {
306                     BufferedInputStream JavaDoc bis = new BufferedInputStream JavaDoc(es);
307                     OutputStream JavaDoc os = new LoggingOutputStream(stderr, LogLevel.ERROR,
308                             consoleArchiver.consoleLog());
309                     IO.copy(bis, os);
310                     os.close();
311                 } catch (IOException JavaDoc e) {
312                     logger().error("Failed copying stream.", e);
313                 }
314             }
315         });
316
317         outT.start();
318         errT.start();
319
320         // copy input.
321
if (stdin != null) {
322             OutputStream JavaDoc os = proc.getOutputStream();
323             IO.copy(stdin, os);
324             os.close();
325         }
326
327         thread = Thread.currentThread();
328         try {
329             proc.waitFor();
330
331             outT.join();
332             errT.join();
333                     
334             if (proc == null) {
335                 // manually stopped
336
return 1;
337             }
338             else {
339                 return proc.exitValue();
340             }
341         }
342         finally {
343             thread = null;
344             synchronized (this) {
345                 // wake up the stop wait.
346
notifyAll();
347             }
348         }
349     }
350
351     /*
352      * (non-Javadoc)
353      * @see org.oddjob.framework.BaseComponent#onStop()
354      */

355     public void onStop() {
356         if (proc != null) {
357             proc.destroy();
358             proc = null;
359         }
360         for (int i = 0; i < 3 && thread != null; ++i) {
361             synchronized (this) {
362                 try {
363                     logger().debug("Waiting for process to die.");
364                     wait(1000);
365                 }
366                 catch (InterruptedException JavaDoc e) {
367                     return;
368                 }
369             }
370         }
371         if (thread != null) {
372             logger().warn("Process failed to die - needs to be manually killed.");
373             thread.interrupt();
374         }
375     }
376
377     /**
378      * @return Returns the dir.
379      */

380     public File JavaDoc getDir() {
381         return dir;
382     }
383     
384     
385     public LogArchive consoleLog() {
386         return consoleArchiver.consoleLog();
387     }
388     
389     /*
390      * Custome serialization.
391      */

392     private void writeObject(ObjectOutputStream JavaDoc s)
393     throws IOException JavaDoc {
394         s.defaultWriteObject();
395     }
396     
397     /*
398      * Custome serialization.
399      */

400     private void readObject(ObjectInputStream JavaDoc s)
401     throws IOException JavaDoc, ClassNotFoundException JavaDoc {
402         s.defaultReadObject();
403         completeConstruction();
404     }
405
406 }
407
Popular Tags