Changeset 31640

Show
Ignore:
Timestamp:
01.05.2017 20:26:27 (2 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.

Files:
1 modified

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