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

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

Rewriting GShell.runLocal() to use SafeProcess instead of Process. This required overhaul of SafeProcess to correctly deal with interrupts as the build/import process launched in GShell can be cancelled from the CreatePane. SafeProcess now also keeps track of its internal process object and has static logging methods to simplify the changes required when swapping between the GS3 version and GS2 version of SafeProcess. May eventually call DebugStream methods from the log methods for GLI. Will add a DEBUG flag to allow verbose logging.

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