Ignore:
Timestamp:
2017-04-20T19:38:44+12:00 (7 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.

File:
1 edited

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.
Note: See TracChangeset for help on using the changeset viewer.