source: main/trunk/greenstone3/src/java/org/greenstone/util/SafeProcess.java@ 31631

Last change on this file since 31631 was 31631, checked in by ak19, 7 years ago

Further changes to SafeProcess to turn some interfaces to abstract classes. Corresponding changes to GS2PerlConstructor which makes use of this.

File size: 25.7 KB
Line 
1package org.greenstone.util;
2
3import java.io.BufferedReader;
4import java.io.BufferedWriter;
5import java.io.Closeable;
6import java.io.File;
7import java.io.InputStream;
8import java.io.InputStreamReader;
9import java.io.IOException;
10import java.io.OutputStream;
11import java.io.OutputStreamWriter;
12
13import java.util.Arrays;
14
15import org.apache.log4j.*;
16
17// Use this class to run a Java Process. It follows the good and safe practices at
18// http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html?page=2
19// to avoid blocking problems that can arise from a Process' input and output streams.
20
21public class SafeProcess {
22
23 public static final int STDERR = 0;
24 public static final int STDOUT = 1;
25 public static final int STDIN = 2;
26
27 static Logger logger = Logger.getLogger(org.greenstone.util.SafeProcess.class.getName());
28
29 // input to SafeProcess and initialising it
30 private String command = null;
31 private String[] command_args = null;
32 private String[] envp = null;
33 private File dir = null;
34 private String inputStr = null;
35
36 // output from running SafeProcess.runProcess()
37 private String outputStr = "";
38 private String errorStr = "";
39 private int exitValue = -1;
40
41 // allow callers to process exceptions of the main process thread if they want
42 private ExceptionHandler exceptionHandler = null;
43
44 // whether std/err output should be split at new lines
45 private boolean splitStdOutputNewLines = false;
46 private boolean splitStdErrorNewLines = false;
47
48 // call one of these constructors
49
50 // cmd args version
51 public SafeProcess(String[] cmd_args)
52 {
53 command_args = cmd_args;
54 }
55
56 // cmd string version
57 public SafeProcess(String cmdStr)
58 {
59 command = cmdStr;
60 }
61
62 // cmd args with env version, launchDir can be null.
63 public SafeProcess(String[] cmd_args, String[] envparams, File launchDir)
64 {
65 command_args = cmd_args;
66 envp = envparams;
67 dir = launchDir;
68 }
69
70 // The important methods:
71 // to get the output from std err and std out streams after the process has been run
72 public String getStdOutput() { return outputStr; }
73 public String getStdError() { return errorStr; }
74 public int getExitValue() { return exitValue; }
75
76
77 // set any string to send as input to the process spawned by SafeProcess
78 public void setInputString(String sendStr) {
79 inputStr = sendStr;
80 }
81
82 // register a SafeProcess ExceptionHandler whose gotException() method will
83 // get called for each exception encountered
84 public void setExceptionHandler(ExceptionHandler exception_handler) {
85 exceptionHandler = exception_handler;
86 }
87
88 // set if you want the std output or err output to have \n at each newline read from the stream
89 public void setSplitStdOutputNewLines(boolean split) {
90 splitStdOutputNewLines = split;
91 }
92 public void setSplitStdErrorNewLines(boolean split) {
93 splitStdErrorNewLines = split;
94 }
95
96 private Process doRuntimeExec() throws IOException {
97 Process prcs = null;
98 Runtime rt = Runtime.getRuntime();
99
100 if(this.command != null) {
101 prcs = rt.exec(this.command);
102 }
103 else { // at least command_args must be set now
104
105 // http://stackoverflow.com/questions/5283444/convert-array-of-strings-into-a-string-in-java
106 logger.info("SafeProcess running: " + Arrays.toString(command_args));
107 //System.err.println("SafeProcess running: " + Arrays.toString(command_args));
108
109 if(this.envp == null) {
110 prcs = rt.exec(this.command_args);
111 } else { // launch process using cmd str with env params
112
113 if(this.dir == null) {
114 ///logger.info("\twith: " + Arrays.toString(this.envp));
115 ///System.err.println("\twith: " + Arrays.toString(this.envp));
116 prcs = rt.exec(this.command_args, this.envp);
117 } else {
118 ///logger.info("\tfrom directory: " + this.dir);
119 ///logger.info("\twith: " + Arrays.toString(this.envp));
120 ///System.err.println("\tfrom directory: " + this.dir);
121 ///System.err.println("\twith: " + Arrays.toString(this.envp));
122 prcs = rt.exec(this.command_args, this.envp, this.dir);
123 }
124 }
125 }
126
127 return prcs;
128 }
129
130 // Copied from gli's gui/FormatConversionDialog.java
131 private int waitForWithStreams(Process prcs,
132 SafeProcess.OutputStreamGobbler inputGobbler,
133 SafeProcess.InputStreamGobbler outputGobbler,
134 SafeProcess.InputStreamGobbler errorGobbler)
135 throws IOException, InterruptedException
136 {
137
138 // kick off the stream gobblers
139 inputGobbler.start();
140 errorGobbler.start();
141 outputGobbler.start();
142
143 // any error???
144 this.exitValue = prcs.waitFor(); // can throw an InterruptedException if process did not terminate
145
146 ///logger.info("Process exitValue: " + exitValue);
147 ///System.err.println("Process exitValue: " + exitValue);
148
149 // From the comments of
150 // http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html?page=2
151 // To avoid running into nondeterministic failures to get the process output
152 // if there's no waiting for the threads, call join() on each Thread (StreamGobbler) object.
153 // From Thread API: join() "Waits for this thread (the thread join() is invoked on) to die."
154 outputGobbler.join();
155 errorGobbler.join();
156 inputGobbler.join();
157
158
159 // set the variables the code that created a SafeProcess object may want to inspect
160 this.outputStr = outputGobbler.getOutput();
161 this.errorStr = errorGobbler.getOutput();
162
163 // Since we didn't have an exception, process should have terminated now (waitFor blocks until then)
164 // Set process to null so we don't forcibly terminate it below with process.destroy()
165 prcs = null;
166
167 return this.exitValue;
168 }
169
170
171 // Run a very basic process: with no reading from or writing to the Process' iostreams,
172 // this just execs the process and waits for it to return
173 public int runBasicProcess() {
174 Process prcs = null;
175 try {
176 // 1. create the process
177 prcs = doRuntimeExec();
178 // 2. basic waitFor the process to finish
179 this.exitValue = prcs.waitFor();
180
181
182 } catch(IOException ioe) {
183 if(exceptionHandler != null) {
184 exceptionHandler.gotException(ioe);
185 } else {
186 logger.error("IOException: " + ioe.getMessage(), ioe);
187 //System.err.println("IOException " + ioe.getMessage());
188 //ioe.printStackTrace();
189 }
190 } catch(InterruptedException ie) {
191
192 if(exceptionHandler != null) {
193 exceptionHandler.gotException(ie);
194 } else {
195 logger.error("Process InterruptedException: " + ie.getMessage(), ie);
196 //System.err.println("Process InterruptedException " + ie.getMessage());
197 ///ie.printStackTrace(); // an interrupt here is not an error, it can be a cancel action
198 }
199
200 Thread.currentThread().interrupt();
201 } finally {
202
203 if( prcs != null ) {
204 prcs.destroy(); // see runProcess() below
205 }
206 }
207 return this.exitValue;
208 }
209
210 // Runs a process with default stream processing. Returns the exitValue
211 public int runProcess() {
212 return runProcess(null, null, null); // use default processing of all 3 of the process' iostreams
213 }
214
215 // Run a process with custom stream processing (any custom handlers passed in that are null
216 // will use the default stream processing).
217 // Returns the exitValue from running the Process
218 public int runProcess(CustomProcessHandler procInHandler,
219 CustomProcessHandler procOutHandler,
220 CustomProcessHandler procErrHandler)
221 {
222 Process prcs = null;
223 SafeProcess.OutputStreamGobbler inputGobbler = null;
224 SafeProcess.InputStreamGobbler errorGobbler = null;
225 SafeProcess.InputStreamGobbler outputGobbler = null;
226
227 try {
228 // 1. get the Process object
229 prcs = doRuntimeExec();
230
231
232 // 2. create the streamgobblers and set any specified handlers on them
233
234 // PROC INPUT STREAM
235 if(procInHandler == null) {
236 // send inputStr to process. The following constructor can handle inputStr being null
237 inputGobbler = // WriterToProcessInputStream
238 new SafeProcess.OutputStreamGobbler(prcs.getOutputStream(), this.inputStr);
239 } else { // user will do custom handling of process' InputStream
240 inputGobbler = new SafeProcess.OutputStreamGobbler(prcs.getOutputStream(), procInHandler);
241 }
242
243 // PROC ERR STREAM to monitor for any error messages or expected output in the process' stderr
244 if(procErrHandler == null) {
245 errorGobbler // ReaderFromProcessOutputStream
246 = new SafeProcess.InputStreamGobbler(prcs.getErrorStream(), splitStdErrorNewLines);
247 } else {
248 errorGobbler
249 = new SafeProcess.InputStreamGobbler(prcs.getErrorStream(), procErrHandler);
250 }
251
252 // PROC OUT STREAM to monitor for the expected std output line(s)
253 if(procOutHandler == null) {
254 outputGobbler
255 = new SafeProcess.InputStreamGobbler(prcs.getInputStream(), splitStdOutputNewLines);
256 } else {
257 outputGobbler
258 = new SafeProcess.InputStreamGobbler(prcs.getInputStream(), procOutHandler);
259 }
260
261
262 // 3. kick off the stream gobblers
263 this.exitValue = waitForWithStreams(prcs, inputGobbler, outputGobbler, errorGobbler);
264
265 } catch(IOException ioe) {
266 if(exceptionHandler != null) {
267 exceptionHandler.gotException(ioe);
268 } else {
269 logger.error("IOexception: " + ioe.getMessage(), ioe);
270 //System.err.println("IOexception " + ioe.getMessage());
271 //ioe.printStackTrace();
272 }
273 } catch(InterruptedException ie) {
274
275 if(exceptionHandler != null) {
276 exceptionHandler.gotException(ie);
277 } else {
278 logger.error("Process InterruptedException: " + ie.getMessage(), ie);
279 //System.err.println("Process InterruptedException " + ie.getMessage());
280 ///ie.printStackTrace(); // an interrupt here is not an error, it can be a cancel action
281 }
282
283 // propagate interrupts to worker threads here?
284 // unless the interrupt emanated from any of them in any join()...
285 // Only if the thread SafeProcess runs in was interrupted
286 // do we propagate the interrupt to the worker threads.
287 // http://stackoverflow.com/questions/2126997/who-is-calling-the-java-thread-interrupt-method-if-im-not
288 // "I know that in JCiP it is mentioned that you should never interrupt threads you do not own"
289 // But SafeProcess owns the worker threads, so it have every right to interrupt them
290 // Also read http://stackoverflow.com/questions/13623445/future-cancel-method-is-not-working?noredirect=1&lq=1
291 if(Thread.currentThread().isInterrupted()) {
292 inputGobbler.interrupt();
293 errorGobbler.interrupt();
294 outputGobbler.interrupt();
295 }
296
297 // On catchingInterruptedException, re-interrupt the thread.
298 // This is just how InterruptedExceptions tend to be handled
299 // See also http://stackoverflow.com/questions/4906799/why-invoke-thread-currentthread-interrupt-when-catch-any-interruptexception
300 // and https://praveer09.github.io/technology/2015/12/06/understanding-thread-interruption-in-java/
301
302 // http://stackoverflow.com/questions/3976344/handling-interruptedexception-in-java
303 // http://stackoverflow.com/questions/4906799/why-invoke-thread-currentthread-interrupt-when-catch-any-interruptexception
304 // "Only code that implements a thread's interruption policy may swallow an interruption request. General-purpose task and library code should never swallow interruption requests."
305 // Does that mean that since this code implements this thread's interruption policy, it's ok
306 // to swallow the interrupt this time and not let it propagate by commenting out the next line?
307 Thread.currentThread().interrupt(); // re-interrupt the thread - which thread? Infinite loop?
308 } finally {
309
310 // Moved into here from GS2PerlConstructor which said
311 // "I need to somehow kill the child process. Unfortunately Thread.stop() and Process.destroy() both fail to do this. But now, thankx to the magic of Michaels 'close the stream suggestion', it works fine."
312 // http://steveliles.github.io/invoking_processes_from_java.html
313 // http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html?page=2
314 // http://mark.koli.ch/leaky-pipes-remember-to-close-your-streams-when-using-javas-runtimegetruntimeexec
315 if( prcs != null ) {
316 prcs.destroy();
317 }
318 }
319
320 return this.exitValue;
321 }
322
323 public int runProcess(LineByLineHandler outLineByLineHandler, LineByLineHandler errLineByLineHandler)
324 {
325 Process prcs = null;
326 SafeProcess.OutputStreamGobbler inputGobbler = null;
327 SafeProcess.InputStreamGobbler errorGobbler = null;
328 SafeProcess.InputStreamGobbler outputGobbler = null;
329
330 try {
331 // 1. get the Process object
332 prcs = doRuntimeExec();
333
334
335 // 2. create the streamgobblers and set any specified handlers on them
336
337 // PROC INPUT STREAM
338 // send inputStr to process. The following constructor can handle inputStr being null
339 inputGobbler = // WriterToProcessInputStream
340 new SafeProcess.OutputStreamGobbler(prcs.getOutputStream(), this.inputStr);
341
342 // PROC ERR STREAM to monitor for any error messages or expected output in the process' stderr
343 errorGobbler // ReaderFromProcessOutputStream
344 = new SafeProcess.InputStreamGobbler(prcs.getErrorStream(), splitStdErrorNewLines);
345 // PROC OUT STREAM to monitor for the expected std output line(s)
346 outputGobbler
347 = new SafeProcess.InputStreamGobbler(prcs.getInputStream(), splitStdOutputNewLines);
348
349
350 // 3. register line by line handlers, if any were set, for the process stderr and stdout streams
351 if(outLineByLineHandler != null) {
352 outputGobbler.setLineByLineHandler(outLineByLineHandler);
353 }
354 if(errLineByLineHandler != null) {
355 errorGobbler.setLineByLineHandler(errLineByLineHandler);
356 }
357
358
359 // 4. kick off the stream gobblers
360 this.exitValue = waitForWithStreams(prcs, inputGobbler, outputGobbler, errorGobbler);
361
362 } catch(IOException ioe) {
363 if(exceptionHandler != null) {
364 exceptionHandler.gotException(ioe);
365 } else {
366 logger.error("IOexception: " + ioe.getMessage(), ioe);
367 //System.err.println("IOexception " + ioe.getMessage());
368 //ioe.printStackTrace();
369 }
370 } catch(InterruptedException ie) {
371
372 if(exceptionHandler != null) {
373 exceptionHandler.gotException(ie);
374 } else {
375 logger.error("Process InterruptedException: " + ie.getMessage(), ie);
376 //System.err.println("Process InterruptedException " + ie.getMessage());
377 ///ie.printStackTrace(); // an interrupt here is not an error, it can be a cancel action
378 }
379
380 // propagate interrupts to worker threads here?
381 // unless the interrupt emanated from any of them in any join()...
382 // Only if the thread SafeProcess runs in was interrupted
383 // do we propagate the interrupt to the worker threads.
384 // http://stackoverflow.com/questions/2126997/who-is-calling-the-java-thread-interrupt-method-if-im-not
385 // "I know that in JCiP it is mentioned that you should never interrupt threads you do not own"
386 // But SafeProcess owns the worker threads, so it have every right to interrupt them
387 // Also read http://stackoverflow.com/questions/13623445/future-cancel-method-is-not-working?noredirect=1&lq=1
388 if(Thread.currentThread().isInterrupted()) {
389 inputGobbler.interrupt();
390 errorGobbler.interrupt();
391 outputGobbler.interrupt();
392 }
393
394 // On catchingInterruptedException, re-interrupt the thread.
395 // This is just how InterruptedExceptions tend to be handled
396 // See also http://stackoverflow.com/questions/4906799/why-invoke-thread-currentthread-interrupt-when-catch-any-interruptexception
397 // and https://praveer09.github.io/technology/2015/12/06/understanding-thread-interruption-in-java/
398
399 // http://stackoverflow.com/questions/3976344/handling-interruptedexception-in-java
400 // http://stackoverflow.com/questions/4906799/why-invoke-thread-currentthread-interrupt-when-catch-any-interruptexception
401 // "Only code that implements a thread's interruption policy may swallow an interruption request. General-purpose task and library code should never swallow interruption requests."
402 // Does that mean that since this code implements this thread's interruption policy, it's ok
403 // to swallow the interrupt this time and not let it propagate by commenting out the next line?
404 Thread.currentThread().interrupt(); // re-interrupt the thread - which thread? Infinite loop?
405 } finally {
406
407 // Moved into here from GS2PerlConstructor which said
408 // "I need to somehow kill the child process. Unfortunately Thread.stop() and Process.destroy() both fail to do this. But now, thankx to the magic of Michaels 'close the stream suggestion', it works fine."
409 // http://steveliles.github.io/invoking_processes_from_java.html
410 // http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html?page=2
411 // http://mark.koli.ch/leaky-pipes-remember-to-close-your-streams-when-using-javas-runtimegetruntimeexec
412 if( prcs != null ) {
413 prcs.destroy();
414 }
415 }
416
417 return this.exitValue;
418 }
419
420
421//******************** Inner class and interface definitions ********************//
422// Static inner classes can be instantiated without having to instantiate an object of the outer class first
423
424// Can have public static interfaces too,
425// see http://stackoverflow.com/questions/71625/why-would-a-static-nested-interface-be-used-in-java
426// Implementors need to take care that the implementations are thread safe
427// http://stackoverflow.com/questions/14520814/why-synchronized-method-is-not-included-in-interface
428public static interface ExceptionHandler {
429
430 // when implementing ExceptionHandler.gotException(), if it manipulates anything that's
431 // not threadsafe, declare gotException() as a synchronized method to ensure thread safety
432 public void gotException(Exception e); // can't declare as synchronized in interface method declaration
433}
434
435// Write your own run() body for any StreamGobbler. You need to create an instance of a class
436// extending CustomProcessHandler for EACH IOSTREAM of the process that you want to handle.
437// Do not create a single CustomProcessHandler instance and reuse it for all three streams,
438// i.e. don't call SafeProcess' runProcess(x, x, x); It should be runProcess(x, y, z).
439// Make sure your implementation is threadsafe if you're sharing immutable objects between the threaded streams
440// example implementation is in the GS2PerlConstructor.SynchronizedProcessHandler class.
441// CustomProcessHandler is made an abstract class instead of an interface to force classes that want
442// to use a CustomProcessHandler to create a separate class that extends CustomProcessHandler, rather than
443// that the classes that wish to use it "implementing" the CustomProcessHandler interface itself: the
444// CustomProcessHandler.run() method may then be called in the major thread from which the Process is being
445// executed, rather than from the individual threads that deal with each iostream of the Process.
446public static abstract class CustomProcessHandler {
447
448 protected final int source;
449
450 protected CustomProcessHandler(int src) {
451 this.source = src; // STDERR or STDOUT or STDIN
452 }
453
454 public abstract void run(Closeable stream); //InputStream or OutputStream
455}
456
457// When using the default stream processing to read from a process' stdout or stderr stream, you can
458// create a class extending LineByLineHandler for the process' err stream and one for its output stream
459// to do something on a line by line basis, such as sending the line to a log
460public static abstract class LineByLineHandler {
461 protected final int source;
462
463 protected LineByLineHandler(int src) {
464 this.source = src; // STDERR or STDOUT
465 }
466
467 public abstract void gotLine(String line); // first non-null line
468 public abstract void gotException(Exception e); // for when an exception occurs instead of getting a line
469}
470
471//**************** StreamGobbler Inner class definitions (stream gobblers copied from GLI) **********//
472
473// http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html?page=2
474// This class is used in FormatConversionDialog to properly read from the stdout and stderr
475// streams of a Process, Process.getInputStream() and Process.getErrorSream()
476public static class InputStreamGobbler extends Thread
477{
478 private InputStream is = null;
479 private StringBuffer outputstr = new StringBuffer();
480 private boolean split_newlines = false;
481 private CustomProcessHandler customHandler = null;
482 private LineByLineHandler lineByLineHandler = null;
483
484 public InputStreamGobbler(InputStream is)
485 {
486 this.is = is;
487 this.split_newlines = false;
488 }
489
490 public InputStreamGobbler(InputStream is, boolean split_newlines)
491 {
492 this.is = is;
493 this.split_newlines = split_newlines;
494 }
495
496 public InputStreamGobbler(InputStream is, CustomProcessHandler customHandler)
497 {
498 this.is = is;
499 this.customHandler = customHandler;
500 }
501
502 public void setLineByLineHandler(LineByLineHandler lblHandler) {
503 this.lineByLineHandler = lblHandler;
504 }
505
506 // default run() behaviour
507 public void runDefault()
508 {
509 BufferedReader br = null;
510 try {
511 br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
512 String line=null;
513 while ( (line = br.readLine()) != null ) {
514
515 if(this.isInterrupted()) { // should we not instead check if SafeProcess thread was interrupted?
516 logger.info("Got interrupted when reading lines from process err/out stream.");
517 //System.err.println("InputStreamGobbler.runDefault() Got interrupted when reading lines from process err/out stream.");
518 break; // will go to finally block
519 }
520
521 //System.out.println("@@@ GOT LINE: " + line);
522 outputstr.append(line);
523 if(split_newlines) {
524 outputstr.append(Misc.NEWLINE); // "\n" is system dependent (Win must be "\r\n")
525 }
526
527 if(lineByLineHandler != null) { // let handler deal with newlines
528 lineByLineHandler.gotLine(line);
529 }
530 }
531 } catch (IOException ioe) {
532 if(lineByLineHandler != null) {
533 lineByLineHandler.gotException(ioe);
534 } else {
535 logger.error("Exception when reading from a process' stdout/stderr stream: ", ioe);
536 //System.err.println("Exception when reading from a process' stdout/stderr stream: ");
537 //ioe.printStackTrace();
538 }
539 } finally {
540 SafeProcess.closeResource(br);
541 }
542 }
543
544 public void runCustom() {
545 this.customHandler.run(is);
546 }
547
548 public void run() {
549 if(this.customHandler == null) {
550 runDefault();
551 } else {
552 runCustom();
553 }
554 }
555
556 public String getOutput() {
557 return outputstr.toString(); // implicit toString() call anyway. //return outputstr;
558 }
559} // end static inner class InnerStreamGobbler
560
561
562// http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html?page=2
563// This class is used in FormatConversionDialog to properly write to the inputstream of a Process
564// Process.getOutputStream()
565public static class OutputStreamGobbler extends Thread
566{
567 private OutputStream os = null;
568 private String inputstr = "";
569 private CustomProcessHandler customHandler = null;
570
571 public OutputStreamGobbler(OutputStream os) {
572 this.os = os;
573 }
574
575 public OutputStreamGobbler(OutputStream os, String inputstr)
576 {
577 this.os = os;
578 this.inputstr = inputstr;
579 }
580
581 public OutputStreamGobbler(OutputStream os, CustomProcessHandler customHandler) {
582 this.os = os;
583 this.customHandler = customHandler;
584 }
585
586 // default run() behaviour
587 public void runDefault() {
588
589 if (inputstr == null) {
590 return;
591 }
592
593 BufferedWriter osw = null;
594 try {
595 osw = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
596 //System.out.println("@@@ SENDING LINE: " + inputstr);
597 osw.write(inputstr, 0, inputstr.length());
598 osw.newLine();//osw.write("\n");
599 osw.flush();
600
601 // Don't explicitly send EOF when using StreamGobblers as below,
602 // as the EOF char is echoed to output.
603 // Flushing the write handle and/or closing the resource seems
604 // to already send EOF silently.
605
606 /*if(Utility.isWindows()) {
607 osw.write("\032"); // octal for Ctrl-Z, EOF on Windows
608 } else { // EOF on Linux/Mac is Ctrl-D
609 osw.write("\004"); // octal for Ctrl-D, see http://www.unix-manuals.com/refs/misc/ascii-table.html
610 }
611 osw.flush();
612 */
613 } catch (IOException ioe) {
614 logger.error("Exception writing to SafeProcess' inputstream: ", ioe);
615 //System.err.println("Exception writing to SafeProcess' inputstream: ");
616 //ioe.printStackTrace();
617 } finally {
618 SafeProcess.closeResource(osw);
619 }
620 }
621
622 // call the user's custom handler for the run() method
623 public void runCustom() {
624 this.customHandler.run(os);
625 }
626
627 public void run()
628 {
629 if(this.customHandler == null) {
630 runDefault();
631 } else {
632 runCustom();
633 }
634
635 }
636} // end static inner class OutputStreamGobbler
637
638
639//**************** Useful static methods. Copied from GLI's Utility.java ******************
640 // For safely closing streams/handles/resources.
641 // For examples of use look in the Input- and OutputStreamGobbler classes.
642 // http://docs.oracle.com/javase/tutorial/essential/exceptions/finally.html
643 // http://stackoverflow.com/questions/481446/throws-exception-in-finally-blocks
644 public static boolean closeResource(Closeable resourceHandle) {
645 boolean success = false;
646 try {
647 if(resourceHandle != null) {
648 resourceHandle.close();
649 resourceHandle = null;
650 success = true;
651 }
652 } catch(Exception e) {
653 logger.error("Exception closing resource: ", e);
654 //System.err.println("Exception closing resource: " + e.getMessage());
655 //e.printStackTrace();
656 resourceHandle = null;
657 success = false;
658 } finally {
659 return success;
660 }
661 }
662
663 public static boolean closeProcess(Process prcs) {
664 boolean success = true;
665 if( prcs != null ) {
666 success = success && closeResource(prcs.getErrorStream());
667 success = success && closeResource(prcs.getOutputStream());
668 success = success && closeResource(prcs.getInputStream());
669 prcs.destroy();
670 }
671 return success;
672 }
673
674} // end class SafeProcess
Note: See TracBrowser for help on using the repository browser.