Ignore:
Timestamp:
2017-05-01T20:26:27+12:00 (7 years ago)
Author:
ak19
Message:

Porting across changes made to GLI's SafeProcess: 1. correct interrupt handling for when a process is cancelled; 2. Process object is now a member var; 3. Better way of swapping btw logging with logger in GS3 code versus in GLI which can use System.err or DebugStream; 4. Static helper methods including logging methods, and processRunning method ported from GLI's GShell.

File:
1 edited

Legend:

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

    r31631 r31640  
    1010import java.io.OutputStream;
    1111import java.io.OutputStreamWriter;
    12 
    1312import java.util.Arrays;
    1413
    1514import org.apache.log4j.*;
     15
     16//import org.greenstone.gatherer.DebugStream;
    1617
    1718// Use this class to run a Java Process. It follows the good and safe practices at
     
    2021
    2122public class SafeProcess {
    22    
     23    //public static int DEBUG = 0;
     24
    2325    public static final int STDERR = 0;
    2426    public static final int STDOUT = 1;
    25     public static final int STDIN = 2;   
     27    public static final int STDIN = 2;
     28
     29    // charset for reading process stderr and stdout streams
     30    //public static final String UTF8 = "UTF-8";   
    2631
    2732    static Logger logger = Logger.getLogger(org.greenstone.util.SafeProcess.class.getName());
     
    3338    private File dir = null;
    3439    private String inputStr = null;
     40    private Process process = null;
    3541
    3642    // output from running SafeProcess.runProcess()
     
    3844    private String errorStr = "";
    3945    private int exitValue = -1;
     46    //private String charset = null;
    4047
    4148    // allow callers to process exceptions of the main process thread if they want
     
    7481    public int getExitValue() { return exitValue; }
    7582
    76    
     83    //public void setStreamCharSet(String charset) { this.charset = charset; }
     84
    7785    // set any string to send as input to the process spawned by SafeProcess
    7886    public void setInputString(String sendStr) {
     
    99107   
    100108    if(this.command != null) {
     109        log("SafeProcess running: " + command);
    101110        prcs = rt.exec(this.command);
    102111    }
     
    104113       
    105114        // http://stackoverflow.com/questions/5283444/convert-array-of-strings-into-a-string-in-java
    106         logger.info("SafeProcess running: " + Arrays.toString(command_args));
    107         //System.err.println("SafeProcess running: " + Arrays.toString(command_args));
     115        log("SafeProcess running: " + Arrays.toString(command_args));
    108116       
    109117        if(this.envp == null) {
     
    112120       
    113121        if(this.dir == null) {
    114             ///logger.info("\twith: " + Arrays.toString(this.envp));
    115             ///System.err.println("\twith: " + Arrays.toString(this.envp));
     122            //log("\twith: " + Arrays.toString(this.envp));
    116123            prcs = rt.exec(this.command_args, this.envp);
    117124        } else {
    118             ///logger.info("\tfrom directory: " + this.dir);
    119             ///logger.info("\twith: " + Arrays.toString(this.envp));
    120             ///System.err.println("\tfrom directory: " + this.dir);
    121             ///System.err.println("\twith: " + Arrays.toString(this.envp));
    122             prcs = rt.exec(this.command_args, this.envp, this.dir);
     125            //log("\tfrom directory: " + this.dir);
     126            //log("\twith: " + Arrays.toString(this.envp));         
     127            prcs = rt.exec(this.command_args, this.envp, this.dir);
    123128        }
    124129        }
     
    129134
    130135    // Copied from gli's gui/FormatConversionDialog.java
    131     private int waitForWithStreams(Process prcs,
    132                    SafeProcess.OutputStreamGobbler inputGobbler,
     136    private int waitForWithStreams(SafeProcess.OutputStreamGobbler inputGobbler,
    133137                   SafeProcess.InputStreamGobbler outputGobbler,
    134138                   SafeProcess.InputStreamGobbler errorGobbler)
     
    136140    {
    137141
    138             // kick off the stream gobblers
    139             inputGobbler.start();
    140             errorGobbler.start();
    141             outputGobbler.start();
    142 
    143             // any error???
    144             this.exitValue = prcs.waitFor(); // can throw an InterruptedException if process did not terminate
    145 
    146             ///logger.info("Process exitValue: " + exitValue);
    147         ///System.err.println("Process exitValue: " + exitValue);
    148 
     142   
     143    // kick off the stream gobblers
     144    inputGobbler.start();
     145    errorGobbler.start();
     146    outputGobbler.start();
     147   
     148    // any error???
     149    try {
     150        this.exitValue = process.waitFor(); // can throw an InterruptedException if process did not terminate
     151    } catch(InterruptedException ie) {
     152        log("*** Process interrupted (InterruptedException). Expected to be a Cancel operation.");
     153            // don't print stacktrace: an interrupt here is not an error, it's expected to be a cancel action
     154        if(exceptionHandler != null) {     
     155        exceptionHandler.gotException(ie);
     156        }           
     157       
     158        // propagate interrupts to worker threads here
     159        // unless the interrupt emanated from any of them in any join(),
     160        // which will be caught by caller's catch on InterruptedException.
     161        // Only if the thread that SafeProcess runs in was interrupted
     162        // should we propagate the interrupt to the worker threads.
     163        // http://stackoverflow.com/questions/2126997/who-is-calling-the-java-thread-interrupt-method-if-im-not
     164        // "I know that in JCiP it is mentioned that you should never interrupt threads you do not own"
     165        // But SafeProcess owns the worker threads, so it has every right to interrupt them
     166        // Also read http://stackoverflow.com/questions/13623445/future-cancel-method-is-not-working?noredirect=1&lq=1
     167       
     168        // http://stackoverflow.com/questions/3976344/handling-interruptedexception-in-java
     169        // http://stackoverflow.com/questions/4906799/why-invoke-thread-currentthread-interrupt-when-catch-any-interruptexception
     170        // "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."
     171        // Does that mean that since this code implements this thread's interruption policy, it's ok
     172        // to swallow the interrupt this time and not let it propagate by commenting out the next line?
     173        //Thread.currentThread().interrupt(); // re-interrupt the thread
     174
     175        inputGobbler.interrupt();
     176        errorGobbler.interrupt();
     177        outputGobbler.interrupt();
     178
     179        // even after the interrupts, we want to proceed to calling join() on all the worker threads
     180        // in order to wait for each of them to die before attempting to destroy the process if it
     181        // still hasn't terminated after all that.
     182    } finally {     
     183       
     184        //log("Process exitValue: " + exitValue);
     185       
    149186        // From the comments of
    150187        // http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html?page=2
     
    152189        // if there's no waiting for the threads, call join() on each Thread (StreamGobbler) object.
    153190        // From Thread API: join() "Waits for this thread (the thread join() is invoked on) to die."
     191       
     192        // Wait for each of the threads to die, before attempting to destroy the process
     193        // Any of these can throw InterruptedExceptions too
     194        // and will be processed by the calling function's catch on InterruptedException.
     195        // However, no one besides us will interrupting these threads I think...
     196        // and we won't be throwing the InterruptedException from within the threads...
     197        // So if any streamgobbler.join() call throws an InterruptedException, that would be unexpected
     198
    154199        outputGobbler.join();
    155200        errorGobbler.join();
    156         inputGobbler.join(); 
    157        
    158 
    159         // set the variables the code that created a SafeProcess object may want to inspect
     201        inputGobbler.join();
     202       
     203       
     204        // set the variables that the code which created a SafeProcess object may want to inspect
    160205        this.outputStr = outputGobbler.getOutput();
    161206        this.errorStr = errorGobbler.getOutput();
     
    163208        // Since we didn't have an exception, process should have terminated now (waitFor blocks until then)
    164209        // Set process to null so we don't forcibly terminate it below with process.destroy()
    165         prcs = null;
    166 
    167         return this.exitValue;
    168     }
    169 
     210        this.process = null;       
     211    }
     212
     213    // Don't return from finally, it's considered an abrupt completion and exceptions are lost, see
     214    // http://stackoverflow.com/questions/18205493/can-we-use-return-in-finally-block
     215    return this.exitValue;
     216    }
     217
     218
     219    public synchronized boolean processRunning() {
     220    if(process == null) return false;
     221    return SafeProcess.processRunning(this.process);
     222    }
    170223
    171224    // Run a very basic process: with no reading from or writing to the Process' iostreams,
    172225    // this just execs the process and waits for it to return
    173226    public int runBasicProcess() {
    174     Process prcs = null;
    175227    try {
    176228        // 1. create the process
    177         prcs = doRuntimeExec();
     229        process = doRuntimeExec();
    178230        // 2. basic waitFor the process to finish
    179         this.exitValue = prcs.waitFor();
     231        this.exitValue = process.waitFor();
    180232
    181233       
     
    184236        exceptionHandler.gotException(ioe);
    185237        } else {
    186         logger.error("IOException: " + ioe.getMessage(), ioe);
    187         //System.err.println("IOException " + ioe.getMessage());
    188         //ioe.printStackTrace();
     238        log("IOException: " + ioe.getMessage(), ioe);
    189239        }
    190240    } catch(InterruptedException ie) {
     
    192242        if(exceptionHandler != null) {
    193243        exceptionHandler.gotException(ie);
    194         } else {
    195         logger.error("Process InterruptedException: " + ie.getMessage(), ie);
    196         //System.err.println("Process InterruptedException " + ie.getMessage());
    197         ///ie.printStackTrace(); // an interrupt here is not an error, it can be a cancel action
     244        } else { // Unexpected InterruptedException, so printstacktrace
     245        log("Process InterruptedException: " + ie.getMessage(), ie);
    198246        }
    199247       
     
    201249    } finally {
    202250
    203         if( prcs != null ) {
    204         prcs.destroy(); // see runProcess() below
     251        if( process != null ) {
     252        process.destroy(); // see runProcess() below
    205253        }
    206254    }
     
    220268               CustomProcessHandler procErrHandler)
    221269    {
    222     Process prcs = null;
    223270    SafeProcess.OutputStreamGobbler inputGobbler = null;
    224271    SafeProcess.InputStreamGobbler errorGobbler = null;
     
    227274    try {
    228275        // 1. get the Process object
    229         prcs = doRuntimeExec();
     276        process = doRuntimeExec();
    230277       
    231278
     
    236283        // send inputStr to process. The following constructor can handle inputStr being null
    237284        inputGobbler = // WriterToProcessInputStream
    238             new SafeProcess.OutputStreamGobbler(prcs.getOutputStream(), this.inputStr);
     285            new SafeProcess.OutputStreamGobbler(process.getOutputStream(), this.inputStr);
    239286        } else { // user will do custom handling of process' InputStream
    240         inputGobbler = new SafeProcess.OutputStreamGobbler(prcs.getOutputStream(), procInHandler);
     287        inputGobbler = new SafeProcess.OutputStreamGobbler(process.getOutputStream(), procInHandler);
    241288        }
    242289
     
    244291        if(procErrHandler == null) {
    245292        errorGobbler // ReaderFromProcessOutputStream
    246             = new SafeProcess.InputStreamGobbler(prcs.getErrorStream(), splitStdErrorNewLines);
     293            = new SafeProcess.InputStreamGobbler(process.getErrorStream(), splitStdErrorNewLines);
    247294        } else {
    248295        errorGobbler
    249             = new SafeProcess.InputStreamGobbler(prcs.getErrorStream(), procErrHandler);
     296            = new SafeProcess.InputStreamGobbler(process.getErrorStream(), procErrHandler);
    250297        }
    251298
     
    253300        if(procOutHandler == null) {
    254301        outputGobbler
    255             = new SafeProcess.InputStreamGobbler(prcs.getInputStream(), splitStdOutputNewLines);
     302            = new SafeProcess.InputStreamGobbler(process.getInputStream(), splitStdOutputNewLines);
    256303        } else {
    257304        outputGobbler
    258             = new SafeProcess.InputStreamGobbler(prcs.getInputStream(), procOutHandler);
     305            = new SafeProcess.InputStreamGobbler(process.getInputStream(), procOutHandler);
    259306        }
    260307
    261308
    262309            // 3. kick off the stream gobblers
    263         this.exitValue = waitForWithStreams(prcs, inputGobbler, outputGobbler, errorGobbler);
     310        this.exitValue = waitForWithStreams(inputGobbler, outputGobbler, errorGobbler);
    264311       
    265312    } catch(IOException ioe) {
     
    267314        exceptionHandler.gotException(ioe);
    268315        } else {
    269         logger.error("IOexception: " + ioe.getMessage(), ioe);
    270         //System.err.println("IOexception " + ioe.getMessage());
    271         //ioe.printStackTrace();
    272         }
    273     } catch(InterruptedException ie) {
    274 
     316        log("IOexception: " + ioe.getMessage(), ioe);
     317        }
     318    } catch(InterruptedException ie) { // caused during any of the gobblers.join() calls, this is unexpected so print stack trace
     319       
    275320        if(exceptionHandler != null) {
    276321        exceptionHandler.gotException(ie);
     322        log("@@@@ Unexpected InterruptedException when waiting for process stream gobblers to die");
    277323        } else {
    278         logger.error("Process InterruptedException: " + ie.getMessage(), ie);
    279         //System.err.println("Process InterruptedException " + ie.getMessage());
    280         ///ie.printStackTrace(); // an interrupt here is not an error, it can be a cancel action
    281         }
    282 
    283         // propagate interrupts to worker threads here?
    284         // unless the interrupt emanated from any of them in any join()...
    285         // Only if the thread SafeProcess runs in was interrupted
    286         // do we propagate the interrupt to the worker threads.
    287         // http://stackoverflow.com/questions/2126997/who-is-calling-the-java-thread-interrupt-method-if-im-not
    288         // "I know that in JCiP it is mentioned that you should never interrupt threads you do not own"
    289         // But SafeProcess owns the worker threads, so it have every right to interrupt them
    290         // Also read http://stackoverflow.com/questions/13623445/future-cancel-method-is-not-working?noredirect=1&lq=1
    291         if(Thread.currentThread().isInterrupted()) {
    292         inputGobbler.interrupt();
    293         errorGobbler.interrupt();
    294         outputGobbler.interrupt();     
    295         }
    296 
    297         // On catchingInterruptedException, re-interrupt the thread.
    298         // This is just how InterruptedExceptions tend to be handled
    299         // See also http://stackoverflow.com/questions/4906799/why-invoke-thread-currentthread-interrupt-when-catch-any-interruptexception
    300         // and https://praveer09.github.io/technology/2015/12/06/understanding-thread-interruption-in-java/
    301 
    302         // http://stackoverflow.com/questions/3976344/handling-interruptedexception-in-java
    303         // http://stackoverflow.com/questions/4906799/why-invoke-thread-currentthread-interrupt-when-catch-any-interruptexception
    304         // "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."
    305         // Does that mean that since this code implements this thread's interruption policy, it's ok
    306         // to swallow the interrupt this time and not let it propagate by commenting out the next line?
    307         Thread.currentThread().interrupt(); // re-interrupt the thread - which thread? Infinite loop?
     324        log("*** Unexpected InterruptException when waiting for process stream gobblers to die:" + ie.getMessage(), ie);
     325        }
     326
     327        // see comments in other runProcess()
     328        Thread.currentThread().interrupt();
     329   
    308330    } finally {
    309 
    310         // Moved into here from GS2PerlConstructor which said
    311         // "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."
    312         // http://steveliles.github.io/invoking_processes_from_java.html
    313         // http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html?page=2
    314         // http://mark.koli.ch/leaky-pipes-remember-to-close-your-streams-when-using-javas-runtimegetruntimeexec       
    315         if( prcs != null ) {       
    316         prcs.destroy();
    317         }
     331        //String cmd = (this.command == null) ? Arrays.toString(this.command_args) : this.command;
     332        //log("*** In finally of SafeProcess.runProcess(3 params): " + cmd);
     333
     334        if( process != null ) {
     335        log("*** Going to call process.destroy 2");
     336        process.destroy();
     337        process = null;
     338        log("*** Have called process.destroy 2");
     339        }
     340       
    318341    }
    319342   
     
    323346    public int runProcess(LineByLineHandler outLineByLineHandler, LineByLineHandler errLineByLineHandler)
    324347    {
    325     Process prcs = null;
    326348    SafeProcess.OutputStreamGobbler inputGobbler = null;
    327349    SafeProcess.InputStreamGobbler errorGobbler = null;
     
    330352    try {
    331353        // 1. get the Process object
    332         prcs = doRuntimeExec();
     354        process = doRuntimeExec();
    333355       
    334356
     
    338360        // send inputStr to process. The following constructor can handle inputStr being null
    339361        inputGobbler = // WriterToProcessInputStream
    340         new SafeProcess.OutputStreamGobbler(prcs.getOutputStream(), this.inputStr);
     362        new SafeProcess.OutputStreamGobbler(process.getOutputStream(), this.inputStr);
    341363
    342364        // PROC ERR STREAM to monitor for any error messages or expected output in the process' stderr   
    343365        errorGobbler // ReaderFromProcessOutputStream
    344             = new SafeProcess.InputStreamGobbler(prcs.getErrorStream(), splitStdErrorNewLines);     
     366            = new SafeProcess.InputStreamGobbler(process.getErrorStream(), splitStdErrorNewLines);     
    345367            // PROC OUT STREAM to monitor for the expected std output line(s)
    346368        outputGobbler
    347         = new SafeProcess.InputStreamGobbler(prcs.getInputStream(), splitStdOutputNewLines);
     369        = new SafeProcess.InputStreamGobbler(process.getInputStream(), splitStdOutputNewLines);
    348370
    349371
     
    358380
    359381            // 4. kick off the stream gobblers
    360         this.exitValue = waitForWithStreams(prcs, inputGobbler, outputGobbler, errorGobbler);
     382        this.exitValue = waitForWithStreams(inputGobbler, outputGobbler, errorGobbler);
    361383       
    362384    } catch(IOException ioe) {
     
    364386        exceptionHandler.gotException(ioe);
    365387        } else {
    366         logger.error("IOexception: " + ioe.getMessage(), ioe);
    367         //System.err.println("IOexception " + ioe.getMessage());
    368         //ioe.printStackTrace();
    369         }
    370     } catch(InterruptedException ie) {
    371 
     388        log("IOexception: " + ioe.getMessage(), ioe);       
     389        }
     390    } catch(InterruptedException ie) { // caused during any of the gobblers.join() calls, this is unexpected so log it
     391       
    372392        if(exceptionHandler != null) {
    373393        exceptionHandler.gotException(ie);
     394        log("@@@@ Unexpected InterruptedException when waiting for process stream gobblers to die");
    374395        } else {
    375         logger.error("Process InterruptedException: " + ie.getMessage(), ie);
    376         //System.err.println("Process InterruptedException " + ie.getMessage());
    377         ///ie.printStackTrace(); // an interrupt here is not an error, it can be a cancel action
    378         }
    379 
    380         // propagate interrupts to worker threads here?
    381         // unless the interrupt emanated from any of them in any join()...
    382         // Only if the thread SafeProcess runs in was interrupted
    383         // do we propagate the interrupt to the worker threads.
    384         // http://stackoverflow.com/questions/2126997/who-is-calling-the-java-thread-interrupt-method-if-im-not
    385         // "I know that in JCiP it is mentioned that you should never interrupt threads you do not own"
    386         // But SafeProcess owns the worker threads, so it have every right to interrupt them
    387         // Also read http://stackoverflow.com/questions/13623445/future-cancel-method-is-not-working?noredirect=1&lq=1
    388         if(Thread.currentThread().isInterrupted()) {
    389         inputGobbler.interrupt();
    390         errorGobbler.interrupt();
    391         outputGobbler.interrupt();     
    392         }
    393 
    394         // On catchingInterruptedException, re-interrupt the thread.
     396        log("*** Unexpected InterruptException when waiting for process stream gobblers to die: " + ie.getMessage(), ie);
     397        }
     398        // We're not causing any interruptions that may occur when trying to stop the worker threads
     399        // So resort to default behaviour in this catch?
     400        // "On catching InterruptedException, re-interrupt the thread."
    395401        // This is just how InterruptedExceptions tend to be handled
    396402        // See also http://stackoverflow.com/questions/4906799/why-invoke-thread-currentthread-interrupt-when-catch-any-interruptexception
    397403        // and https://praveer09.github.io/technology/2015/12/06/understanding-thread-interruption-in-java/
    398 
    399         // http://stackoverflow.com/questions/3976344/handling-interruptedexception-in-java
    400         // http://stackoverflow.com/questions/4906799/why-invoke-thread-currentthread-interrupt-when-catch-any-interruptexception
    401         // "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."
    402         // Does that mean that since this code implements this thread's interruption policy, it's ok
    403         // to swallow the interrupt this time and not let it propagate by commenting out the next line?
    404404        Thread.currentThread().interrupt(); // re-interrupt the thread - which thread? Infinite loop?
     405   
    405406    } finally {
    406407
    407         // Moved into here from GS2PerlConstructor which said
    408         // "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."
     408        // Moved into here from GS2PerlConstructor and GShell.runLocal() which said
     409        // "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 (no it doesn't!)"
    409410        // http://steveliles.github.io/invoking_processes_from_java.html
    410411        // http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html?page=2
    411412        // http://mark.koli.ch/leaky-pipes-remember-to-close-your-streams-when-using-javas-runtimegetruntimeexec       
    412         if( prcs != null ) {       
    413         prcs.destroy();
     413       
     414        //String cmd = (this.command == null) ? Arrays.toString(this.command_args) : this.command;
     415        //log("*** In finally of SafeProcess.runProcess(2 params): " + cmd);
     416
     417        if( process != null ) {
     418        log("*** Going to call process.destroy 1");
     419        process.destroy();
     420        process = null;
     421        log("*** Have called process.destroy 1");
    414422        }
    415423    }
     
    451459    this.source = src; // STDERR or STDOUT or STDIN
    452460    }
     461
     462    public String getThreadNamePrefix() {
     463    return SafeProcess.streamToString(this.source);
     464    }
    453465   
    454466    public abstract void run(Closeable stream); //InputStream or OutputStream
     
    465477    }
    466478
     479    public String getThreadNamePrefix() {
     480    return SafeProcess.streamToString(this.source);
     481    }
     482
    467483    public abstract void gotLine(String line); // first non-null line
    468484    public abstract void gotException(Exception e); // for when an exception occurs instead of getting a line
    469485}
     486
    470487
    471488//**************** StreamGobbler Inner class definitions (stream gobblers copied from GLI) **********//
     
    482499    private LineByLineHandler lineByLineHandler = null;
    483500
     501    protected InputStreamGobbler() {
     502    super("InputStreamGobbler");
     503    }
     504
    484505    public InputStreamGobbler(InputStream is)
    485506    {
     507    this(); // sets thread name
    486508    this.is = is;
    487509    this.split_newlines = false;
     
    490512    public InputStreamGobbler(InputStream is, boolean split_newlines)
    491513    {
     514    this(); // sets thread name
    492515    this.is = is;
    493516    this.split_newlines = split_newlines;
     
    496519    public InputStreamGobbler(InputStream is, CustomProcessHandler customHandler)
    497520    {
     521    this(); // thread name
    498522    this.is = is;
    499523    this.customHandler = customHandler;
     524    this.adjustThreadName(customHandler.getThreadNamePrefix());
     525    }
     526
     527   
     528    private void adjustThreadName(String prefix) { 
     529    this.setName(prefix + this.getName());
    500530    }
    501531
    502532    public void setLineByLineHandler(LineByLineHandler lblHandler) {
    503533    this.lineByLineHandler = lblHandler;
     534    this.adjustThreadName(lblHandler.getThreadNamePrefix());
    504535    }
    505536
     
    511542        br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
    512543        String line=null;
    513         while ( (line = br.readLine()) != null ) {
    514 
    515         if(this.isInterrupted()) { // should we not instead check if SafeProcess thread was interrupted?
    516             logger.info("Got interrupted when reading lines from process err/out stream.");
    517             //System.err.println("InputStreamGobbler.runDefault() Got interrupted when reading lines from process err/out stream.");
    518             break; // will go to finally block
    519         }
    520 
    521         //System.out.println("@@@ GOT LINE: " + line);
     544        while ( !this.isInterrupted() && (line = br.readLine()) != null ) {
     545
     546        //log("@@@ GOT LINE: " + line);
    522547        outputstr.append(line);
    523548        if(split_newlines) {
     
    529554        }
    530555        }
     556
    531557    } catch (IOException ioe) {
    532558        if(lineByLineHandler != null) {
    533559        lineByLineHandler.gotException(ioe);
    534560        } else {
    535         logger.error("Exception when reading from a process' stdout/stderr stream: ", ioe);
    536         //System.err.println("Exception when reading from a process' stdout/stderr stream: ");
    537         //ioe.printStackTrace(); 
     561        log("Exception when reading process stream with " + this.getName() + ": ", ioe);
    538562        }
    539563    } finally {
     564        if(this.isInterrupted()) {
     565        log("@@@ Successfully interrupted " + this.getName() + ".");
     566        }
    540567        SafeProcess.closeResource(br);
    541568    }
     
    569596    private CustomProcessHandler customHandler = null;
    570597
     598    protected OutputStreamGobbler() {
     599    super("stdinOutputStreamGobbler"); // thread name
     600    }
     601
    571602    public OutputStreamGobbler(OutputStream os) {
     603    this(); // set thread name
    572604    this.os = os;
    573605    }
     
    575607    public OutputStreamGobbler(OutputStream os, String inputstr)
    576608    {
     609    this(); // set thread name
    577610    this.os = os;
    578611    this.inputstr = inputstr;
     
    580613
    581614    public OutputStreamGobbler(OutputStream os, CustomProcessHandler customHandler) {
     615    this(); // set thread name
    582616    this.os = os;
    583617    this.customHandler = customHandler;
     
    589623    if (inputstr == null) {
    590624        return;
     625    }
     626
     627    // also quit if the process was interrupted before we could send anything to its stdin
     628    if(this.isInterrupted()) {
     629        log(this.getName() + " thread was interrupted.");
     630        return;
    591631    }
    592632   
     
    612652        */
    613653    } catch (IOException ioe) {
    614         logger.error("Exception writing to SafeProcess' inputstream: ", ioe);
    615         //System.err.println("Exception writing to SafeProcess' inputstream: ");
    616         //ioe.printStackTrace();   
     654        log("Exception writing to SafeProcess' inputstream: ", ioe);
    617655    } finally {
    618656        SafeProcess.closeResource(osw);
     
    632670        runCustom();
    633671    }
    634 
    635672    }   
    636673} // end static inner class OutputStreamGobbler
    637674
     675//**************** Static methods **************//
     676
     677
     678    // logger and DebugStream print commands are synchronized, therefore thread safe.
     679    public static void log(String msg) {
     680    logger.info(msg);
     681
     682    //System.err.println(msg);
     683
     684    //DebugStream.println(msg);
     685    }
     686
     687    public static void log(String msg, Exception e) { // Print stack trace on the exception
     688    logger.error(msg, e);
     689
     690    //System.err.println(msg);
     691    //e.printStackTrace();
     692
     693    //DebugStream.println(msg);
     694    //DebugStream.printStackTrace(e);
     695    }
     696
     697    public static void log(Exception e) {
     698    logger.error(e);
     699
     700    //e.printStackTrace();
     701
     702    //DebugStream.printStackTrace(e);
     703    }
     704
     705    public static void log(String msg, Exception e, boolean printStackTrace) {
     706    if(printStackTrace) {
     707        log(msg, e);
     708    } else {
     709        log(msg);
     710    }
     711    }
     712
     713    public static String streamToString(int src) {
     714    String stream;
     715    switch(src) {
     716    case STDERR:
     717        stream = "stderr";
     718        break;
     719    case STDOUT:
     720        stream = "stdout";
     721        break;
     722    default:
     723        stream = "stdin";
     724    }
     725    return stream;
     726    }
    638727
    639728//**************** Useful static methods. Copied from GLI's Utility.java ******************
     
    651740        }
    652741    } catch(Exception e) {
    653         logger.error("Exception closing resource: ", e);
    654         //System.err.println("Exception closing resource: " + e.getMessage());
    655         //e.printStackTrace();
     742        log("Exception closing resource: " + e.getMessage(), e);
    656743        resourceHandle = null;
    657744        success = false;
     
    672759    }
    673760
     761// Moved from GShell.java
     762    /** Determine if the given process is still executing. It does this by attempting to throw an exception - not the most efficient way, but the only one as far as I know
     763     * @param process the Process to test
     764     * @return true if it is still executing, false otherwise
     765     */
     766    static public boolean processRunning(Process process) {
     767    boolean process_running = false;
     768
     769    try {
     770        process.exitValue(); // This will throw an exception if the process hasn't ended yet.
     771    }
     772    catch(IllegalThreadStateException itse) {
     773        process_running = true;
     774    }
     775    catch(Exception exception) {
     776        log(exception); // DebugStream.printStackTrace(exception);
     777    }
     778    return process_running;
     779    }
     780
    674781} // end class SafeProcess
Note: See TracChangeset for help on using the changeset viewer.