KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > enterprise > util > ProcessExecutor


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the License). You may not use this file except in
5  * compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * https://glassfish.dev.java.net/public/CDDLv1.0.html or
9  * glassfish/bootstrap/legal/CDDLv1.0.txt.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * Header Notice in each file and include the License file
15  * at glassfish/bootstrap/legal/CDDLv1.0.txt.
16  * If applicable, add the following below the CDDL Header,
17  * with the fields enclosed by brackets [] replaced by
18  * you own identifying information:
19  * "Portions Copyrighted [year] [name of copyright owner]"
20  *
21  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
22  */

23
24 package com.sun.enterprise.util;
25
26 //JDK imports
27
import java.io.File JavaDoc;
28 import java.io.IOException JavaDoc;
29 import java.io.InputStream JavaDoc;
30 import java.io.BufferedInputStream JavaDoc;
31 import java.io.FileInputStream JavaDoc;
32 import java.io.OutputStream JavaDoc;
33 import java.io.BufferedOutputStream JavaDoc;
34 import java.io.FileOutputStream JavaDoc;
35 import java.io.PrintWriter JavaDoc;
36 import java.io.BufferedWriter JavaDoc;
37 import java.io.OutputStreamWriter JavaDoc;
38 import java.io.BufferedReader JavaDoc;
39 import java.io.InputStreamReader JavaDoc;
40 import java.io.Reader JavaDoc;
41 import java.io.FileReader JavaDoc;
42 import java.io.RandomAccessFile JavaDoc;
43 import java.util.ArrayList JavaDoc;
44 import java.lang.IllegalArgumentException JavaDoc;
45 /**
46     @author Kedar
47     @version 1.0
48 */

49
50 public class ProcessExecutor
51 {
52     public static final long kDefaultTimeoutMillis = 600000;
53     public static final long kSleepTime = 2000;
54     private static final long DEFAULT_TIMEOUT_SEC = 600;
55     private static final String JavaDoc NEWLINE = System.getProperty("line.separator");
56     private long mTimeoutMilliseconds = 0;
57     protected String JavaDoc[] mCmdStrings = null;
58     protected File JavaDoc mOutFile = null;
59     protected File JavaDoc mErrFile = null;
60     private OutputStream JavaDoc mOutStream = null;
61     private OutputStream JavaDoc mErrStream = null;
62
63     private File JavaDoc mWorkingDir = null; //working directory
64
private String JavaDoc[] mEnv = null; //environment
65
private String JavaDoc[] mInputLines = null; // strings to set in process's InputStream (like from redirection)
66
private int mExitValue = -1;
67     private Process JavaDoc mSubProcess=null; // used to get handle to child process for ProcessManager funtionality
68
private boolean mVerboseMode = false;
69     private boolean retainExecutionLogs = false;
70     private String JavaDoc lastExecutionOutputString = null;
71     private String JavaDoc lastExecutionErrorString = null;
72     private static boolean bDebug=false;
73
74     /**
75         Creates new ProcessExecutor
76     */

77     public ProcessExecutor(String JavaDoc[] cmd)
78     {
79         this(cmd, DEFAULT_TIMEOUT_SEC, null);
80     }
81     
82     /**
83         Creates new ProcessExecutor
84     */

85     public ProcessExecutor(String JavaDoc[] cmd, String JavaDoc[] inputLines)
86     {
87         this(cmd, DEFAULT_TIMEOUT_SEC, inputLines);
88     }
89
90     /**
91         Creates new ProcessExecutor
92     */

93         public ProcessExecutor(String JavaDoc[] cmd, long timeoutSeconds)
94     {
95         this(cmd, timeoutSeconds, null);
96         }
97     
98         public ProcessExecutor(String JavaDoc[] cmd, long timeoutSeconds, String JavaDoc[] inputLines)
99         {
100             this(cmd, timeoutSeconds, inputLines, null, null);
101         }
102         
103     /**
104         Creates a new <code> ProcessExecutor </code> that executes the given
105         command.
106         @param cmd String that has command name and its command line arguments
107         @param timeoutSeconds long integer timeout to be applied in seconds. After this time
108         if the process to execute does not end, it will be destroyed.
109     */

110     
111         public ProcessExecutor(String JavaDoc[] cmd, long timeoutSeconds, String JavaDoc[] inputLines,
112             String JavaDoc[] env, File JavaDoc workingDir)
113     {
114             mCmdStrings = cmd;
115             mInputLines = inputLines;
116             mEnv = env;
117             mWorkingDir = workingDir;
118             char fwdSlashChar = '/';
119             char backSlashChar = '\\';
120             
121             if (System.getProperty("Debug") != null) {
122                 // turn on debug, this option was added to help developers
123
// debug the their code
124
bDebug=true;
125             }
126             
127             for(int i=0; i<mCmdStrings.length; i++)
128             {
129                 if (OS.isUnix())
130                 {
131                     mCmdStrings[i] = mCmdStrings[i].replace(backSlashChar, fwdSlashChar);
132                 }
133                 else
134                 {
135                     mCmdStrings[i] = mCmdStrings[i].replace(fwdSlashChar, backSlashChar);
136                 }
137             }
138             mTimeoutMilliseconds = (long) timeoutSeconds * 1000;
139         }
140     
141     /** This is the setting after the fact that an instance of ProcessExecutor
142      * is created. This is to be used in case the output and error of the last
143      * execute call has to be retained for latter analysis.
144      * @param s boolean representing whether to retain, true means the buffers
145      * will be retained, false otherwise.
146      */

147     public void setExecutionRetentionFlag(final boolean s) {
148         this.retainExecutionLogs = s;
149     }
150     public boolean getExecutionRetentionFlag() {
151         return ( this.retainExecutionLogs );
152     }
153     /** Returns the last 4096 bytes in the error stream of last execution as a String, if
154      * the ProcessExecutor was configured properly. It may return null if the
155      * retentionFlag is set to false.
156      */

157     public String JavaDoc getLastExecutionError() {
158         return ( this.lastExecutionErrorString );
159     }
160     /** Returns the last 4096 bytes in the output stream of last execution as a String, if
161      * the ProcessExecutor was configured properly. It may return null if the
162      * retentionFlag is set to false.
163      */

164     public String JavaDoc getLastExecutionOutput() {
165         return ( this.lastExecutionOutputString );
166     }
167     private void init() throws ExecException
168     {
169       try{
170         mOutFile = File.createTempFile("stdout", null);
171         mOutFile.deleteOnExit();
172         mErrFile = File.createTempFile("stderr", null);
173         mErrFile.deleteOnExit();
174       }
175       catch (IllegalArgumentException JavaDoc iae){
176         deleteTempFiles();
177         throw new ExecException("Internal error (util.ProcessExecutor.init()): "+iae.getMessage());
178       }
179       catch (IOException JavaDoc ioe){
180         deleteTempFiles();
181         throw new ExecException(cannotCreateTempFiles());
182       }
183     }
184
185     private final static String JavaDoc cannotCreateTempFiles(){
186     return "Could not create temporary files - check "
187     + System.getProperty("java.io.tmpdir")
188     + " to see if its writeable and not-full";
189     }
190
191
192     private void deleteTempFiles(){
193         if (mOutStream != null) {
194             try {
195                 mOutStream.flush();
196                 mOutStream.close();
197             } catch (IOException JavaDoc ioe) {
198                 // Ignore
199
}
200         }
201         
202         if (mErrStream != null) {
203             try {
204                 mErrStream.flush();
205                 mErrStream.close();
206             } catch (IOException JavaDoc ioe) {
207                 // Ignore
208
}
209         }
210     if (mOutFile != null) mOutFile.delete();
211     if (mErrFile != null) mErrFile.delete();
212     }
213   
214     public void execute() throws ExecException
215     {
216         execute(false);
217     }
218
219     
220     /*
221         Executes the command. Redirects the standard output and error streams
222         safely to files. This makes the subprocess NOT block or wait on
223         buffers getting flushed. This is done in a threaded manner.
224         Note that the subprocess will be killed if it does not end in given
225         timeout.
226      
227         @throws ExecException if anything goes wrong in subprocess, or subprocess
228         terminates abruptly.
229     */

230     
231     public String JavaDoc[] execute(boolean bReturnOutputLines) throws ExecException
232     {
233         return execute(bReturnOutputLines, true);
234     }
235     
236     
237     /**
238      * Allows a subclass to control the error message returned when a non-zero exit code is
239      * returned from a failed execution
240      * @return
241      */

242     protected String JavaDoc getExceptionMessage()
243     {
244         /* read the error message from error file */
245         String JavaDoc errorMessage = getFileBuffer(mErrFile);
246         if (errorMessage.length() == 0) {
247             errorMessage = "The Process Output: " + getLatestOutput(mOutFile);
248         }
249         return "abnormal subprocess termination: Detailed Message:" + errorMessage;
250     }
251     
252     /*
253         Executes the command. Redirects the standard output and error streams
254         safely to files. This makes the subprocess NOT block or wait on
255         buffers getting flushed. This is done in a threaded manner.
256         Note that the subprocess will be killed if it does not end in given
257         timeout.
258      
259         @throws ExecException if anything goes wrong in subprocess, or subprocess
260         terminates abruptly.
261     */

262     
263     public String JavaDoc[] execute(boolean bReturnOutputLines, boolean bStartUpTimeLimit) throws ExecException
264         {
265             init();
266             InputStream JavaDoc inputStream = null;
267             try
268             {
269                 
270                 if (bDebug) {
271                     System.out.println("\n**** Executing command:");
272                     for(int ii=0; ii < mCmdStrings.length; ii++) {
273                         System.out.println(mCmdStrings[ii]);
274                     }
275                 }
276                 
277                 mSubProcess = Runtime.getRuntime().exec(mCmdStrings, mEnv, mWorkingDir);
278                 if(mInputLines != null)
279                     addInputLinesToProcessInput(mSubProcess);
280                 if(!bReturnOutputLines)
281                     mOutStream = redirectProcessOutput(mSubProcess);
282                 else
283                     inputStream = mSubProcess.getInputStream(); //attach to input stream for later reading
284
mErrStream = redirectProcessError(mSubProcess);
285                 
286                 // see if process should startup in a limited ammount of time
287
// processes used by ProcessManager don't return
288
if (bStartUpTimeLimit) {
289                     long timeBefore = System.currentTimeMillis();
290                     boolean timeoutReached = false;
291                     boolean isSubProcessFinished = false;
292                     boolean shouldBeDone = false;
293                     while (! shouldBeDone)
294                     {
295                         sleep(kSleepTime);
296                         long timeAfter = System.currentTimeMillis();
297                         timeoutReached = (timeAfter - timeBefore) >= mTimeoutMilliseconds;
298                         try
299                         {
300                             mExitValue = mSubProcess.exitValue();
301                             isSubProcessFinished = true;
302                         }
303                         catch(IllegalThreadStateException JavaDoc itse)
304                         {
305                             isSubProcessFinished = false;
306                             //ignore exception
307
}
308                         shouldBeDone = timeoutReached || isSubProcessFinished;
309                     }
310                     if (!isSubProcessFinished)
311                     {
312                         mSubProcess.destroy();
313                         mExitValue = -255;
314                         throw new ExecException("Subprocess timed out after "+mTimeoutMilliseconds +"mS");
315                     }
316                     else
317                     {
318                         mExitValue = mSubProcess.exitValue();
319                         if (debug()) {
320                             System.out.println("Subprocess command line = " + a2s(mCmdStrings));
321                             System.out.println("Subprocess exit value = " + mExitValue);
322                         }
323                         if (mExitValue != 0)
324                         {
325                             mExitValue = mSubProcess.exitValue();
326                             if (mExitValue != 0)
327                             {
328                                 throw new ExecException(getExceptionMessage());
329                             }
330                         }
331                     }
332                 }
333             }
334             catch(SecurityException JavaDoc se)
335             {
336                 throw new ExecException(se.getMessage());
337             }
338             catch(IOException JavaDoc ioe)
339             {
340                 throw new ExecException(ioe.getMessage());
341             }
342             finally {
343
344                 // retain buffers before deleting them
345
retainBuffers();
346
347                 // only delete files if the time is limited
348
// for processes that don't return, the temp files will remain
349
if (bStartUpTimeLimit) {
350                     deleteTempFiles();
351                 }
352             }
353
354             if(bReturnOutputLines) {
355                 return getInputStrings(inputStream);
356             } else {
357                 return null;
358             }
359             
360         }
361
362     /**
363      * Get the exit value of the process executed. If this method is called
364      * before process execution is complete (i.e. before execute() method has
365      * returned, it will return -1. If sub process is terminated at timeout,
366      * the method will return -255
367      */

368     public int getProcessExitValue() {
369         return mExitValue;
370     }
371
372     private void addInputLinesToProcessInput(Process JavaDoc subProcess) throws ExecException {
373     if(mInputLines==null)
374             return;
375         
376         PrintWriter JavaDoc out = null;
377         try
378         {
379             out = new PrintWriter JavaDoc(new BufferedWriter JavaDoc(
380                 new OutputStreamWriter JavaDoc(subProcess.getOutputStream())));
381
382             for(int i=0; i<mInputLines.length; i++)
383             {
384                 if(bDebug) {
385                     System.out.println("InputLine ->" + mInputLines[i] + "<-");
386                 }
387                 out.println(mInputLines[i]);
388             }
389             out.flush();
390         }
391         catch (Exception JavaDoc e)
392         {
393             throw new ExecException(e.getMessage());
394         }
395         finally
396         {
397             try
398             {
399                 out.close();
400             }
401             catch (Throwable JavaDoc t)
402             {
403         }
404         }
405     }
406
407     private String JavaDoc[] getInputStrings(InputStream JavaDoc inputStream) throws ExecException
408     {
409         if(inputStream==null)
410             return null;
411         BufferedReader JavaDoc in = null;
412         ArrayList JavaDoc list = new ArrayList JavaDoc();
413         String JavaDoc str;
414         try
415         {
416             in = new BufferedReader JavaDoc( new InputStreamReader JavaDoc(inputStream));
417             while((str=in.readLine())!=null)
418                 list.add(str);
419             if(list.size()<1)
420                 return null;
421             return (String JavaDoc[])list.toArray(new String JavaDoc[list.size()]);
422             
423         }
424         catch (Exception JavaDoc e)
425         {
426             throw new ExecException(e.getMessage());
427         }
428         finally
429         {
430             try
431             {
432                 in.close();
433             }
434             catch (Throwable JavaDoc t)
435             {
436             }
437         }
438     }
439
440     private OutputStream JavaDoc redirectProcessOutput(Process JavaDoc subProcess) throws ExecException
441     {
442         OutputStream JavaDoc out = null;
443                 try
444         {
445             InputStream JavaDoc in = subProcess.getInputStream();
446             // Redirect stderr for verbose mode
447
if(mVerboseMode) {
448                 // send output to stderr
449
out=System.err;
450             } else {
451                 // send to temp file
452
out=new FileOutputStream JavaDoc(mOutFile);
453             }
454             
455             new FlusherThread(in, out).start();
456         }
457         catch (Exception JavaDoc e)
458         {
459             throw new ExecException(e.getMessage());
460         }
461                 return out;
462     }
463     
464     private OutputStream JavaDoc redirectProcessError(Process JavaDoc subProcess) throws ExecException
465     {
466                 OutputStream JavaDoc out = null;
467         try
468         {
469             InputStream JavaDoc in = subProcess.getErrorStream();
470             // Redirect stderr for verbose mode
471
if(mVerboseMode) {
472                 // send output to stderr
473
out=System.err;
474             } else {
475                 // send to temp file
476
out=new FileOutputStream JavaDoc(mErrFile);
477             }
478             new FlusherThread(in, out).start();
479         }
480         catch (Exception JavaDoc e)
481         {
482             throw new ExecException(e.getMessage());
483         }
484                 return out;
485     }
486
487     public void setVerbose(boolean verbose) {
488         mVerboseMode=verbose;
489     }
490     
491     private void sleep (long millis)
492     {
493         try
494         {
495             Thread.sleep(millis);
496         }
497         catch(InterruptedException JavaDoc ie)
498         {
499             //ignore exception
500
}
501     }
502     
503     /** Returns the contents of a file as a String. It never returns a null. If
504      * the file is empty, an empty string is returned.
505      * @param file the file to read
506     */

507     protected String JavaDoc getFileBuffer(File JavaDoc file) {
508         final StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
509         BufferedReader JavaDoc reader = null;
510         try {
511             reader = new BufferedReader JavaDoc(new FileReader JavaDoc(file));
512             String JavaDoc line = null;
513             while ((line = reader.readLine()) != null) {
514                 sb.append(line);
515                 sb.append(NEWLINE);
516             }
517         }
518         catch(Exception JavaDoc e) {
519             //squelch the exception
520
}
521         finally {
522             try {
523                 reader.close();
524             }
525             catch(Exception JavaDoc e) {}
526         }
527         return ( sb.toString() );
528     }
529     protected String JavaDoc getLatestOutput(final File JavaDoc f) {
530         return ( new RAFileReader(f).readLastBytesAsString() );
531     }
532     
533     public void retainBuffers() {
534         if (this.retainExecutionLogs) {
535             this.lastExecutionErrorString = this.getLatestOutput(this.mErrFile);
536             this.lastExecutionOutputString = this.getLatestOutput(this.mOutFile);
537         }
538     }
539     
540     private boolean debug() {
541         final String JavaDoc td = System.getProperty("java.io.tmpdir");
542         final String JavaDoc n = "as_debug_process_executor"; // a debug hook
543
final File JavaDoc f = new File JavaDoc(td, n);
544         return ( f.exists() );
545     }
546     private String JavaDoc a2s(String JavaDoc[] a) {
547         final StringBuffer JavaDoc s = new StringBuffer JavaDoc();
548         if (a != null) {
549             for (int i = 0 ; i < a.length ; i++) {
550                 s.append(a[i]);
551                 s.append(" ");
552             }
553         }
554         return ( s.toString() );
555     }
556     private static class RAFileReader {
557         final File JavaDoc file;
558         final int LAST_BYTES = 4096;
559         final String JavaDoc RMODE = "r"; //read
560

561         RAFileReader(final File JavaDoc file) {
562             this.file= file;
563         }
564
565         String JavaDoc readLastBytesAsString() {
566             final int n = getNumberOfBytes(LAST_BYTES);
567             final StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
568             final long ln = file.length(); //if SecurityManager is not present, this is safe.
569
if (ln == 0)
570                 return ( sb.toString() ); //nothing to read, file may not exist, is protected, is a directory etc.
571
assert (n <= ln) : ("Asked to read number of bytes more than size of file");
572             final long s = ln - n;
573             return ( readWithoutCheck(s) );
574         }
575
576         private String JavaDoc readWithoutCheck(final long seekPos) {
577             final StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
578             RandomAccessFile JavaDoc rf = null;
579             long ln = 0L;
580             int lines = 0;
581             try {
582                 rf = new RandomAccessFile JavaDoc(file, RMODE);
583                 ln = rf.length();
584                 rf.seek(seekPos);
585                 String JavaDoc tmp = rf.readLine();
586                 while (tmp != null) {
587                     lines++;
588                     sb.append(tmp);
589                     //sb.append(Character.LINE_SEPARATOR);
590
sb.append('\n'); // adding a newline character is going to add one extra byte
591
tmp = rf.readLine();
592                 }
593             }
594             catch (Exception JavaDoc e) {
595                 //e.printStackTrace(); //ignore
596
}
597             finally {
598                 try {
599                     if (rf != null)
600                         rf.close();
601                 }
602                 catch(Exception JavaDoc e) {}//ignore;
603
}
604             //System.out.println("ln-seekPos = " + (ln - seekPos) );
605
//System.out.println("bytes = " + sb.toString().getBytes().length);
606
//System.out.println("lines = " + lines);
607
//assert ((ln - seekPos) == (sb.toString().getBytes().length + lines)) : "Wrong number of bytes read";
608
return ( sb.toString() );
609         }
610
611         private int getNumberOfBytes(final int max) {
612             final long ln = file.length();
613             return ( max >= ln ? (int)ln : max );
614         }
615     }
616
617     // used for ProcessManager to watchdog subProcess
618
public Process JavaDoc getSubProcess() {
619         return mSubProcess;
620     }
621
622     public static void main(String JavaDoc args[])
623     {
624         testProcessError();
625     }
626     
627     /* This method tests the condition of process throwing an error.
628      * On Unixlike systems this throws an error in error file. On non-unixlike
629      * Systems it will throw IOException for CreateProcess, which is desired */

630     private static void testProcessError() {
631         ProcessExecutor executor = new ProcessExecutor(
632             new String JavaDoc[]{"/usr/bin/ls", "-wrongPARAMS123"});
633         try {
634             executor.execute();
635         }
636         catch (ExecException ee) {
637             System.out.println(ee.getMessage());
638         }
639     }
640 }
641
642
643     /**
644      * inner class to flush runtime.exec process so it doesn't hang
645      */

646     class FlusherThread extends Thread JavaDoc {
647         InputStream JavaDoc mInStream = null;
648         OutputStream JavaDoc mOutStream = null;
649
650         public static final int kSize = 1024;
651
652         FlusherThread(InputStream JavaDoc in, OutputStream JavaDoc out) {
653             mInStream = in;
654             mOutStream = out;
655         }
656
657         public void run() {
658         // check for null stream
659
if (mInStream == null) return;
660
661         // transfer bytes from input to output stream
662
try {
663             int byteCnt=0;
664             byte[] buffer=new byte[4096];
665             while ((byteCnt=mInStream.read(buffer)) != -1) {
666                 if (mOutStream != null && byteCnt > 0) {
667                 mOutStream.write(buffer, 0, byteCnt);
668                 mOutStream.flush();
669             }
670             yield();
671             }
672         } catch (IOException JavaDoc e) {
673             // ignore
674
} finally {
675             try {
676                 mOutStream.close();
677             } catch (IOException JavaDoc ioe) {
678             // ignore
679
}
680         }
681     }
682 }
683
Popular Tags