Changeset 31616

Show
Ignore:
Timestamp:
20.04.2017 19:38:44 (2 years ago)
Author:
ak19
Message:

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.

Files:
1 modified

Legend:

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

    r31615 r31616  
    124124    } 
    125125 
    126  
    127     // no reading from or writing to Process' iostreams, just exec process and wait for it to return 
    128     public int runBasicProcess() { 
    129     Process prcs = null; 
    130     try { 
    131         prcs = doRuntimeExec(); 
    132         this.exitValue = prcs.waitFor(); 
    133  
    134          
    135     } catch(IOException ioe) { 
    136         if(exceptionHandler != null) { 
    137         exceptionHandler.gotException(ioe); 
    138         } else { 
    139         logger.error("IOException: " + ioe.getMessage(), ioe); 
    140         //System.err.println("IOException " + ioe.getMessage()); 
    141         //ioe.printStackTrace(); 
    142         } 
    143     } catch(InterruptedException ie) { 
    144  
    145         if(exceptionHandler != null) { 
    146         exceptionHandler.gotException(ie); 
    147         } else { 
    148         logger.error("Process InterruptedException: " + ie.getMessage(), ie); 
    149         //System.err.println("Process InterruptedException " + ie.getMessage()); 
    150         ///ie.printStackTrace(); // an interrupt here is not an error, it can be a cancel action 
    151         } 
    152          
    153         Thread.currentThread().interrupt(); 
    154     } finally {  
    155  
    156         if( prcs != null ) { 
    157         prcs.destroy(); // see runProcess() below 
    158         } 
    159     } 
    160     return this.exitValue; 
    161     } 
    162  
    163     public int runProcess() { 
    164     return runProcess(null, null, null); // use default processing of all 3 of the process' iostreams 
    165     } 
    166  
    167 //***************** Copied from gli's gui/FormatConversionDialog.java *************// 
    168  
    169     public int runProcess(CustomProcessHandler procInHandler, 
    170                CustomProcessHandler procOutHandler, 
    171                CustomProcessHandler procErrHandler) 
    172     { 
    173     Process prcs = null; 
    174     SafeProcess.OutputStreamGobbler inputGobbler = null; 
    175     SafeProcess.InputStreamGobbler errorGobbler = null; 
    176     SafeProcess.InputStreamGobbler outputGobbler = null; 
    177  
    178     try { 
    179         prcs = doRuntimeExec(); 
    180          
    181  
    182         // Create the streamgobblers and set any specified handlers on them 
    183  
    184         // PROC INPUT STREAM 
    185         if(procInHandler == null) { 
    186         // send inputStr to process. The following constructor can handle inputStr being null 
    187         inputGobbler = // WriterToProcessInputStream 
    188             new SafeProcess.OutputStreamGobbler(prcs.getOutputStream(), this.inputStr); 
    189         } else { // user will do custom handling of process' InputStream  
    190         inputGobbler = new SafeProcess.OutputStreamGobbler(prcs.getOutputStream(), procInHandler); 
    191         } 
    192  
    193         // PROC ERR STREAM to monitor for any error messages or expected output in the process' stderr 
    194         if(procErrHandler == null) { 
    195         errorGobbler // ReaderFromProcessOutputStream 
    196             = new SafeProcess.InputStreamGobbler(prcs.getErrorStream(), splitStdErrorNewLines);      
    197         } else { 
    198         errorGobbler 
    199             = new SafeProcess.InputStreamGobbler(prcs.getErrorStream(), procErrHandler); 
    200         } 
    201  
    202             // PROC OUT STREAM to monitor for the expected std output line(s) 
    203         if(procOutHandler == null) { 
    204         outputGobbler 
    205             = new SafeProcess.InputStreamGobbler(prcs.getInputStream(), splitStdOutputNewLines); 
    206         } else { 
    207         outputGobbler 
    208             = new SafeProcess.InputStreamGobbler(prcs.getInputStream(), procOutHandler); 
    209         } 
    210  
     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    { 
    211133 
    212134            // kick off the stream gobblers 
     
    237159        // Set process to null so we don't forcibly terminate it below with process.destroy() 
    238160        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); 
    239258        
    240259    } catch(IOException ioe) { 
     
    296315    } 
    297316     
    298  
    299 //**************** Inner class definitions (stream gobblers copied from GLI) **********// 
     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 ********************// 
    300416// Static inner classes can be instantiated without having to instantiate an object of the outer class first 
    301417 
     
    322438} 
    323439 
     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) **********// 
    324449 
    325450// http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html?page=2 
     
    332457    private boolean split_newlines = false; 
    333458    private CustomProcessHandler customHandler = null; 
     459    private LineByLineHandler lineByLineHandler = null; 
    334460 
    335461    public InputStreamGobbler(InputStream is) 
     
    349475    this.is = is; 
    350476    this.customHandler = customHandler; 
     477    } 
     478 
     479    public void setLineByLineHandler(LineByLineHandler lblHandler) { 
     480    this.lineByLineHandler = lblHandler; 
    351481    } 
    352482 
     
    370500            outputstr.append(Misc.NEWLINE); // "\n" is system dependent (Win must be "\r\n") 
    371501        } 
    372         } 
    373     } catch (IOException ioe) {      
    374         logger.error("Exception when reading from a process' stdout/stderr stream: ", ioe); 
    375         //System.err.println("Exception when reading from a process' stdout/stderr stream: "); 
    376         //ioe.printStackTrace();   
    377          
     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        } 
    378515    } finally { 
    379516        SafeProcess.closeResource(br); 
     
    476613 
    477614 
    478 //**************** Copied from GLI's Utility.java ****************** 
     615//**************** Useful static methods. Copied from GLI's Utility.java ****************** 
    479616    // For safely closing streams/handles/resources.  
    480617    // For examples of use look in the Input- and OutputStreamGobbler classes.