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

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