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

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

Porting recent changes from GS3 SafeProcess to GLI SafeProcess

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