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

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

Putting the LineByLineHandlers back in case it is useful (especially with an eye on GLI). Things still work in GS3 runtime: doc editing and user comments tested on Linux.

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