source: main/trunk/gli/src/org/greenstone/gatherer/util/SafeProcess.java@ 31630

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

Further changes to SafeProcess to turn some interfaces to abstract classes.

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