Changeset 31620


Ignore:
Timestamp:
2017-04-20T21:32:08+12:00 (7 years ago)
Author:
ak19
Message:

Porting recent changes from GS3 SafeProcess to GLI SafeProcess

Location:
main/trunk
Files:
2 edited

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.
  • main/trunk/greenstone3/src/java/org/greenstone/util/SafeProcess.java

    r31616 r31620  
    100100       
    101101        // http://stackoverflow.com/questions/5283444/convert-array-of-strings-into-a-string-in-java
    102         ///System.err.println("SafeProcess running: " + Arrays.toString(command_args));
    103102        logger.info("SafeProcess running: " + Arrays.toString(command_args));
     103        //System.err.println("SafeProcess running: " + Arrays.toString(command_args));
    104104       
    105105        if(this.envp == null) {
     
    141141
    142142            ///logger.info("Process exitValue: " + exitValue);
     143        ///System.err.println("Process exitValue: " + exitValue);
    143144
    144145        // From the comments of
     
    492493        if(this.isInterrupted()) { // should we not instead check if SafeProcess thread was interrupted?
    493494            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.");
    494496            break; // will go to finally block
    495497        }
Note: See TracChangeset for help on using the changeset viewer.