Changeset 31620 for main/trunk/gli

Show
Ignore:
Timestamp:
20.04.2017 21:32:08 (3 years ago)
Author:
ak19
Message:

Porting recent changes from GS3 SafeProcess? to GLI SafeProcess?

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • main/trunk/gli/src/org/greenstone/gatherer/util/SafeProcess.java

    r31594 r31620  
    9090    } 
    9191 
    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 { 
    10293    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)); 
    134118            prcs = rt.exec(this.command_args, this.envp, this.dir); 
    135             } 
    136119        } 
    137120        } 
    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 
    169134            // kick off the stream gobblers 
    170135            inputGobbler.start(); 
     
    175140            this.exitValue = prcs.waitFor(); // can throw an InterruptedException if process did not terminate 
    176141 
    177             ///System.err.println("Process exitValue: " + exitValue); 
     142            ///logger.info("Process exitValue: " + exitValue); 
     143        ///System.err.println("Process exitValue: " + exitValue);  
    178144 
    179145        // From the comments of  
     
    194160        // Set process to null so we don't forcibly terminate it below with process.destroy() 
    195161        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); 
    196259        
    197260    } catch(IOException ioe) { 
     
    253316    } 
    254317     
    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 ********************// 
    257417// Static inner classes can be instantiated without having to instantiate an object of the outer class first 
    258418 
     
    279439} 
    280440 
     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) **********// 
    281450 
    282451// http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html?page=2 
     
    289458    private boolean split_newlines = false; 
    290459    private CustomProcessHandler customHandler = null; 
     460    private LineByLineHandler lineByLineHandler = null; 
    291461 
    292462    public InputStreamGobbler(InputStream is) 
     
    306476    this.is = is; 
    307477    this.customHandler = customHandler; 
     478    } 
     479 
     480    public void setLineByLineHandler(LineByLineHandler lblHandler) { 
     481    this.lineByLineHandler = lblHandler; 
    308482    } 
    309483 
     
    318492 
    319493        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."); 
    321496            break; // will go to finally block 
    322497        } 
     
    327502            outputstr.append(Utility.NEWLINE); // "\n" is system dependent (Win must be "\r\n") 
    328503        } 
    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        } 
    335517    } finally { 
    336518        SafeProcess.closeResource(br); 
     
    433615 
    434616 
    435 //**************** Copied from GLI's Utility.java ****************** 
     617//**************** Useful static methods. Copied from GLI's Utility.java ****************** 
    436618    // For safely closing streams/handles/resources.  
    437619    // For examples of use look in the Input- and OutputStreamGobbler classes.