Changeset 31620
- Timestamp:
- 2017-04-20T21:32:08+12:00 (7 years ago)
- Location:
- main/trunk
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
main/trunk/gli/src/org/greenstone/gatherer/util/SafeProcess.java
r31594 r31620 90 90 } 91 91 92 //***************** Copied from gli's gui/FormatConversionDialog.java *************// 93 94 public int runProcess() { 95 return runProcess(null, null, null); // use default processing of all 3 of the process' iostreams 96 } 97 98 public int runProcess(CustomProcessHandler procInHandler, 99 CustomProcessHandler procOutHandler, 100 CustomProcessHandler procErrHandler) 101 { 92 private Process doRuntimeExec() throws IOException { 102 93 Process prcs = null; 103 SafeProcess.OutputStreamGobbler inputGobbler = null; 104 SafeProcess.InputStreamGobbler errorGobbler = null; 105 SafeProcess.InputStreamGobbler outputGobbler = null; 106 107 try { 108 109 Runtime rt = Runtime.getRuntime(); 110 111 112 if(this.command != null) { 113 prcs = rt.exec(this.command); 114 } 115 else { // at least command_args must be set now 116 117 // http://stackoverflow.com/questions/5283444/convert-array-of-strings-into-a-string-in-java 118 System.err.println("SafeProcess running: " + Arrays.toString(command_args)); 119 ///logger.info("SafeProcess running: " + Arrays.toString(command_args)); 120 121 if(this.envp == null) { 122 prcs = rt.exec(this.command_args); 123 } else { // launch process using cmd str with env params 124 125 if(this.dir == null) { 126 ///logger.info("\twith: " + Arrays.toString(this.envp)); 127 ///System.err.println("\twith: " + Arrays.toString(this.envp)); 128 prcs = rt.exec(this.command_args, this.envp); 129 } else { 130 ///logger.info("\tfrom directory: " + this.dir); 131 ///logger.info("\twith: " + Arrays.toString(this.envp)); 132 ///System.err.println("\tfrom directory: " + this.dir); 133 ///System.err.println("\twith: " + Arrays.toString(this.envp)); 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)); 134 118 prcs = rt.exec(this.command_args, this.envp, this.dir); 135 }136 119 } 137 120 } 138 139 // Create the streamgobblers and set any specified handlers on them 140 141 // PROC INPUT STREAM 142 if(procInHandler == null) { 143 // send inputStr to process. The following constructor can handle inputStr being null 144 inputGobbler = // WriterToProcessInputStream 145 new SafeProcess.OutputStreamGobbler(prcs.getOutputStream(), this.inputStr); 146 } else { // user will do custom handling of process' InputStream 147 inputGobbler = new SafeProcess.OutputStreamGobbler(prcs.getOutputStream(), procInHandler); 148 } 149 150 // PROC ERR STREAM to monitor for any error messages or expected output in the process' stderr 151 if(procErrHandler == null) { 152 errorGobbler // ReaderFromProcessOutputStream 153 = new SafeProcess.InputStreamGobbler(prcs.getErrorStream(), splitStdErrorNewLines); 154 } else { 155 errorGobbler 156 = new SafeProcess.InputStreamGobbler(prcs.getErrorStream(), procErrHandler); 157 } 158 159 // PROC OUT STREAM to monitor for the expected std output line(s) 160 if(procOutHandler == null) { 161 outputGobbler 162 = new SafeProcess.InputStreamGobbler(prcs.getInputStream(), splitStdOutputNewLines); 163 } else { 164 outputGobbler 165 = new SafeProcess.InputStreamGobbler(prcs.getInputStream(), procOutHandler); 166 } 167 168 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 169 134 // kick off the stream gobblers 170 135 inputGobbler.start(); … … 175 140 this.exitValue = prcs.waitFor(); // can throw an InterruptedException if process did not terminate 176 141 177 ///System.err.println("Process exitValue: " + exitValue); 142 ///logger.info("Process exitValue: " + exitValue); 143 ///System.err.println("Process exitValue: " + exitValue); 178 144 179 145 // From the comments of … … 194 160 // Set process to null so we don't forcibly terminate it below with process.destroy() 195 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); 196 259 197 260 } catch(IOException ioe) { … … 253 316 } 254 317 255 256 //**************** Inner class definitions (stream gobblers copied from GLI) **********// 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 ********************// 257 417 // Static inner classes can be instantiated without having to instantiate an object of the outer class first 258 418 … … 279 439 } 280 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 444 public 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) **********// 281 450 282 451 // http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html?page=2 … … 289 458 private boolean split_newlines = false; 290 459 private CustomProcessHandler customHandler = null; 460 private LineByLineHandler lineByLineHandler = null; 291 461 292 462 public InputStreamGobbler(InputStream is) … … 306 476 this.is = is; 307 477 this.customHandler = customHandler; 478 } 479 480 public void setLineByLineHandler(LineByLineHandler lblHandler) { 481 this.lineByLineHandler = lblHandler; 308 482 } 309 483 … … 318 492 319 493 if(this.isInterrupted()) { // should we not instead check if SafeProcess thread was interrupted? 320 System.err.println("Got interrupted when reading lines from process err/out stream."); 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."); 321 496 break; // will go to finally block 322 497 } … … 327 502 outputstr.append(Utility.NEWLINE); // "\n" is system dependent (Win must be "\r\n") 328 503 } 329 } 330 } catch (IOException ioe) { 331 //logger.error("Exception when reading from a process' stdout/stderr stream: ", ioe); 332 System.err.println("Exception when reading from a process' stdout/stderr stream: "); 333 ioe.printStackTrace(); 334 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 } 335 517 } finally { 336 518 SafeProcess.closeResource(br); … … 433 615 434 616 435 //**************** Copied from GLI's Utility.java ******************617 //**************** Useful static methods. Copied from GLI's Utility.java ****************** 436 618 // For safely closing streams/handles/resources. 437 619 // For examples of use look in the Input- and OutputStreamGobbler classes. -
main/trunk/greenstone3/src/java/org/greenstone/util/SafeProcess.java
r31616 r31620 100 100 101 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 102 logger.info("SafeProcess running: " + Arrays.toString(command_args)); 103 //System.err.println("SafeProcess running: " + Arrays.toString(command_args)); 104 104 105 105 if(this.envp == null) { … … 141 141 142 142 ///logger.info("Process exitValue: " + exitValue); 143 ///System.err.println("Process exitValue: " + exitValue); 143 144 144 145 // From the comments of … … 492 493 if(this.isInterrupted()) { // should we not instead check if SafeProcess thread was interrupted? 493 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."); 494 496 break; // will go to finally block 495 497 }
Note:
See TracChangeset
for help on using the changeset viewer.