Changeset 31695 for main

Show
Ignore:
Timestamp:
22.05.2017 16:43:13 (3 years ago)
Author:
ak19
Message:

Bringing GS3 src code's SafeProcess? up to speed with GLI version, with some addiitonal minor changes to be included in the GLI version hereafter. Tested on Linux.

Location:
main/trunk/greenstone3/src/java/org/greenstone
Files:
2 modified

Legend:

Unmodified
Added
Removed
  • main/trunk/greenstone3/src/java/org/greenstone/gsdl3/build/GS2PerlConstructor.java

    r31669 r31695  
    504504    // We don't include the cancel check here, as superclass CollectionConstructor.stopAction(), which set 
    505505    // this.cancel to true, never got called anywhere. 
    506     // But I think a proper cancel of our perl process launched by this GS2PerlConstructor Thread object 
    507     // and of the worker threads it launches, could be implemented with interrupts. See: 
     506    // But a proper cancel of our perl process launched by this GS2PerlConstructor Thread object 
     507    // and of the worker threads it launches is now implemented in SafeProcess.cancelRunningProcess() 
     508    // with interrupts. See: 
    508509    // http://stackoverflow.com/questions/6859681/better-way-to-signal-other-thread-to-stop 
    509510    // https://docs.oracle.com/javase/tutorial/essential/concurrency/interrupt.html 
    510511    // https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#interrupted() 
    511512    // https://praveer09.github.io/technology/2015/12/06/understanding-thread-interruption-in-java/ 
    512     // The code that calls GS2PerlConstructor.stopAction() should also call GSPerlConstructor.interrupt() 
    513     // Then in SafeProcess.runProcess(), I think the waitFor() will throw an InterruptedException() 
    514     // This can be caught and interrupt() called on SafeProcess' workerthreads,  
    515     // Any workerthreads' run() methods that block (IO, loops) can test this.isInterrupted()  
    516     // and can break out of any loops and release resources in finally. 
    517     // Back in SafeProcess.runProcess, the InterruptedException catch block will be followed by finally 
    518     // that will clear up any further resources and destroy the process forcibly if it hadn't been ended. 
     513    // If cancel is to be implemented for GS2PerlConstructor, then the code that calls 
     514    // GS2PerlConstructor.stopAction() should also call or result in a call to the SafeProcess instance's 
     515    // cancelRunningProcess() method. 
     516    // For a simple example of the use of SafeProcess' cancel feature, see GLI's GShell. For a more 
     517    // complicated example, see GLI's DownloadJob.java, which implements SafeProcess.MainHandler to do 
     518    // additional work when a process is cancelled while still running (and therefore has to be prematurely 
     519    // terminated) a.o.t. a process that's cancelled when it's already terminated naturally. 
    519520 
    520521    } catch(IOException e) { 
  • main/trunk/greenstone3/src/java/org/greenstone/util/SafeProcess.java

    r31665 r31695  
    1010import java.io.OutputStream; 
    1111import java.io.OutputStreamWriter; 
     12import java.net.Socket; 
    1213import java.util.Arrays; 
    1314import java.util.Scanner; 
    1415import java.util.Stack; 
     16import javax.swing.SwingUtilities; 
     17 
    1518 
    1619import com.sun.jna.*; 
     
    1922 
    2023import java.lang.reflect.Field; 
    21 //import java.lang.reflect.Method; 
    2224 
    2325import org.apache.log4j.*; 
     
    3941    public static final int STDIN = 2; 
    4042    // can't make this variable final and init in a static block, because it needs to use other SafeProcess static methods which rely on this in turn: 
    41     public static String WIN_KILL_CMD;  
    42          
     43    public static String WIN_KILL_CMD; 
     44 
     45    /** 
     46     * Boolean interruptible is used to mark any sections of blocking code that should not be interrupted 
     47     * with an InterruptedExceptions. At present only the cancelRunningProcess() attempts to do such a thing 
     48     * and avoids doing so when interruptible is false. 
     49     * Note that interruptible is also used as a lock, so remember to synchronize on it when using it! 
     50    */ 
     51    public Boolean interruptible = Boolean.TRUE;  
    4352     
    4453    // charset for reading process stderr and stdout streams 
     
    5564    private Process process = null; 
    5665    private boolean forciblyTerminateProcess = false; 
     66 
     67    /** a ref to the thread in which the Process is being executed (the thread wherein Runtime.exec() is called) */ 
     68    private Thread theProcessThread = null; 
    5769     
    5870    // output from running SafeProcess.runProcess() 
     
    6476    // allow callers to process exceptions of the main process thread if they want 
    6577    private ExceptionHandler exceptionHandler = null; 
     78    /** allow callers to implement hooks that get called during the main phases of the internal 
     79     * process' life cycle, such as before and after process.destroy() gets called 
     80    */ 
     81    private MainProcessHandler mainHandler = null; 
    6682 
    6783    // whether std/err output should be split at new lines 
     
    110126    } 
    111127 
     128    /** to set a handler that will handle the main (SafeProcess) thread, 
     129     * implementing the hooks that will get called during the internal process' life cycle, 
     130     * such as before and after process.destroy() is called */ 
     131    public void setMainHandler(MainProcessHandler handler) { 
     132    this.mainHandler = handler; 
     133    } 
     134 
    112135    // set if you want the std output or err output to have \n at each newline read from the stream 
    113136    public void setSplitStdOutputNewLines(boolean split) { 
     
    118141    } 
    119142 
     143     
     144    /* 
     145    public boolean canInterrupt() { 
     146    boolean canInterrupt; 
     147    synchronized(interruptible) { 
     148        canInterrupt = interruptible.booleanValue(); 
     149    } 
     150    return canInterrupt; 
     151    } 
     152    */ 
     153 
     154    /** 
     155     * Call this method when you want to prematurely and safely terminate any process 
     156     * that SafeProcess may be running. 
     157     * You may want to implement the SafeProcess.MainHandler interface to write code 
     158     * for any hooks that will get called during the process' life cycle. 
     159     * @return false if process has already terminated or if it was already terminating 
     160     * when cancel was called. In such cases no interrupt is sent. Returns boolean sentInterrupt. 
     161     */ 
     162    public synchronized boolean cancelRunningProcess() { 
     163    // on interrupt: 
     164    // - forciblyTerminate will be changed to true if the interrupt came in when the process was 
     165    // still running (and so before the process' streams were being joined) 
     166    // - and forciblyTerminate will still remain false if the interrupt happens when the process' 
     167    // streams are being/about to be joined (hence after the process naturally terminated). 
     168    // So we don't touch the value of this.forciblyTerminate here. 
     169    // The value of forciblyTerminate determines whether Process.destroy() and its associated before 
     170    // and after handlers are called or not: we don't bother destroying the process if it came to 
     171    // a natural end. 
     172 
     173    // no process to interrupt, so we're done 
     174    if(this.process == null) { 
     175        log("@@@ No Java process to interrupt."); 
     176        return false; 
     177    } 
     178     
     179    boolean sentInterrupt = false; 
     180 
     181    // can't interrupt when SafeProcess is joining (cleanly terminating) worker threads 
     182    // have to wait until afterward      
     183    if (interruptible) { 
     184        // either way, we can now interrupt the thread - if we have one (we should) 
     185        if(this.theProcessThread != null) { // we're told which thread should be interrupted 
     186        this.theProcessThread.interrupt(); 
     187        log("@@@ Successfully sent interrupt to process."); 
     188        sentInterrupt = true; 
     189        } 
     190    } 
     191    else { // wait for join()s to finish. 
     192        // During and after joining(), there's no need to interrupt any more anyway: no calls 
     193        // subsequent to joins() block, so everything thereafter is insensitive to InterruptedExceptions. 
     194 
     195        if(SwingUtilities.isEventDispatchThread()) { 
     196        log("#### Event Dispatch thread, returning"); 
     197        return false; 
     198        } 
     199 
     200        while(!interruptible) { 
     201 
     202        log("######### Waiting for process to become interruptible..."); 
     203         
     204        // https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html 
     205        // wait will release lock on this object, and regain it when loop condition interruptible is true 
     206        try { 
     207            this.wait(); // can't interrupt when SafeProcess is joining (cleanly terminating) worker threads, so wait 
     208        } catch(Exception e) { 
     209            log("@@@ Interrupted exception while waiting for SafeProcess' worker threads to finish joining on cancelling process"); 
     210        } 
     211        } 
     212 
     213        // now the process is sure to have ended as the worker threads would have been joined 
     214    } 
     215 
     216    return sentInterrupt; 
     217    } 
     218 
     219     
    120220    // In future, think of changing the method doRuntimeExec() over to using ProcessBuilder 
    121221    // instead of Runtime.exec(). ProcessBuilder seems to have been introduced from Java 5. 
    122222    // https://docs.oracle.com/javase/7/docs/api/java/lang/ProcessBuilder.html 
    123     // https://zeroturnaround.com/rebellabs/how-to-deal-with-subprocesses-in-java/ 
     223    // See also https://zeroturnaround.com/rebellabs/how-to-deal-with-subprocesses-in-java/ 
    124224    // which suggests using Apache Common Exec to launch processes and says what will be forthcoming in Java 9 
    125225     
     
    159259    } 
    160260 
     261    this.theProcessThread = Thread.currentThread(); // store a ref to the thread wherein the Process is being run 
    161262    return prcs; 
    162263    } 
     
    173274    outputGobbler.start(); 
    174275     
    175     // any error??? 
    176276    try { 
    177         this.exitValue = process.waitFor(); // can throw an InterruptedException if process did not terminate 
     277        this.exitValue = process.waitFor(); // can throw an InterruptedException if process was cancelled/prematurely terminated 
    178278    } catch(InterruptedException ie) { 
    179279        log("*** Process interrupted (InterruptedException). Expected to be a Cancel operation."); 
     
    185285        // propagate interrupts to worker threads here 
    186286        // unless the interrupt emanated from any of them in any join(), 
    187         // which will be caught by caller's catch on InterruptedException. 
     287        // which will be caught by the calling method's own catch on InterruptedException. 
    188288        // Only if the thread that SafeProcess runs in was interrupted 
    189289        // should we propagate the interrupt to the worker threads. 
     
    208308        // to die off. Don't set process=null until after we've forcibly terminated it if needs be. 
    209309        this.forciblyTerminateProcess = true;  
    210          
     310         
    211311        // even after the interrupts, we want to proceed to calling join() on all the worker threads 
    212312        // in order to wait for each of them to die before attempting to destroy the process if it 
     
    215315         
    216316        //log("Process exitValue: " + exitValue); 
     317        ///log("@@@@ Before join phase. Forcibly terminating: " + this.forciblyTerminateProcess); 
    217318         
    218319        // From the comments of  
     
    225326        // Any of these can throw InterruptedExceptions too 
    226327        // and will be processed by the calling function's catch on InterruptedException. 
    227         // However, no one besides us will interrupting these threads I think... 
    228         // and we won't be throwing the InterruptedException from within the threads... 
    229         // So if any streamgobbler.join() call throws an InterruptedException, that would be unexpected 
    230  
    231         outputGobbler.join();  
     328 
     329 
     330        // Thread.joins() below are blocking calls, same as Process.waitFor(), and a cancel action could 
     331        // send an interrupt during any Join: the InterruptedException ensuing will then break out of the 
     332        // joins() section. We don't want that to happen: by the time the joins() start happening, the 
     333        // actual process has finished in some way (naturally terminated or interrupted), and nothing 
     334        // should interrupt the joins() (nor ideally any potential p.destroy after that). 
     335        // So we mark the join() section as an un-interruptible section, and make anyone who's seeking 
     336        // to interrupt just then first wait for this Thread (in which SafeProcess runs) to become 
     337        // interruptible again. Thos actually assumes anything interruptible can still happen thereafter 
     338        // when in reality, none of the subsequent actions after the joins() block. So they nothing 
     339        // thereafter, which is the cleanup phase, will actually respond to an InterruptedException. 
     340 
     341         
     342        if(this.mainHandler != null) { 
     343        // this method can unset forcible termination flag 
     344        // if the process had already naturally terminated by this stage: 
     345        this.forciblyTerminateProcess = mainHandler.beforeWaitingForStreamsToEnd(this.forciblyTerminateProcess); 
     346        } 
     347 
     348        ///log("@@@@ After beforeJoin Handler. Forcibly terminating: " + this.forciblyTerminateProcess); 
     349 
     350        // Anyone could interrupt/cancel during waitFor() above, 
     351        // but no one should interrupt while the worker threads come to a clean close, 
     352        // so make anyone wanting to cancel the process at this stage wait() 
     353        // until we're done with the join()s: 
     354        synchronized(interruptible) { 
     355        interruptible = Boolean.FALSE; 
     356        } 
     357        //Thread.sleep(5000); // Uncomment to test this uninterruptible section, also comment out block checking for 
     358            // EventDispatchThread in cancelRunningProcess() and 2 calls to progress.enableCancelJob() in DownloadJob.java 
     359        outputGobbler.join(); 
    232360        errorGobbler.join(); 
    233         inputGobbler.join(); 
    234          
    235          
     361        inputGobbler.join();  
     362 
     363        synchronized(interruptible) { 
     364        interruptible = Boolean.TRUE; 
     365        } 
     366 
     367        ///log("@@@@ Join phase done..."); 
     368 
     369        // notify any of those waiting to interrupt this thread, that they may feel free to do so again 
     370        // https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html 
     371        synchronized(this) { 
     372        this.notify(); 
     373        } 
     374 
    236375        // set the variables that the code which created a SafeProcess object may want to inspect 
    237376        this.outputStr = outputGobbler.getOutput(); 
    238         this.errorStr = errorGobbler.getOutput();        
     377        this.errorStr = errorGobbler.getOutput(); 
     378         
     379        // call the after join()s hook 
     380        if(this.mainHandler != null) { 
     381        this.forciblyTerminateProcess = mainHandler.afterStreamsEnded(this.forciblyTerminateProcess); 
     382        } 
    239383    } 
    240384 
     
    260404    try { 
    261405        this.forciblyTerminateProcess = true; 
    262          
     406         
    263407        // 1. create the process 
    264408        process = doRuntimeExec(); 
     
    283427         
    284428        Thread.currentThread().interrupt(); 
    285     } finally {  
    286  
    287         if( this.forciblyTerminateProcess ) { 
    288         destroyProcess(process); // see runProcess() below       
    289         } 
    290         process = null; 
    291         this.forciblyTerminateProcess = false; // reset 
     429    } finally { 
     430         
     431        cleanUp("SafeProcess.runBasicProcess"); 
    292432    } 
    293433    return this.exitValue; 
     
    347487 
    348488 
    349             // 3. kick off the stream gobblers 
     489        // 3. kick off the stream gobblers 
    350490        this.exitValue = waitForWithStreams(inputGobbler, outputGobbler, errorGobbler); 
    351491        
     
    365505        log("@@@@ Unexpected InterruptedException when waiting for process stream gobblers to die"); 
    366506        } else { 
    367         log("*** Unexpected InterruptException when waiting for process stream gobblers to die:" + ie.getMessage(), ie); 
     507        log("*** Unexpected InterruptException when waiting for process stream gobblers to die: " + ie.getMessage(), ie); 
    368508        } 
    369509 
     
    371511        Thread.currentThread().interrupt(); 
    372512     
    373     } finally {  
    374         //String cmd = (this.command == null) ? Arrays.toString(this.command_args) : this.command; 
    375         //log("*** In finally of SafeProcess.runProcess(3 params): " + cmd); 
    376  
    377         if( this.forciblyTerminateProcess ) { 
    378         log("*** Going to call process.destroy 2"); 
    379         destroyProcess(process);                 
    380         log("*** Have called process.destroy 2"); 
    381         } 
    382         process = null; 
    383         this.forciblyTerminateProcess = false; // reset 
     513    } finally { 
     514         
     515        cleanUp("SafeProcess.runProcess(3 params)"); 
    384516    } 
    385517     
     
    424556 
    425557 
    426         // 4. kick off the stream gobblers 
     558        // 3. kick off the stream gobblers 
    427559        this.exitValue = waitForWithStreams(inputGobbler, outputGobbler, errorGobbler); 
    428560        
     
    452584        Thread.currentThread().interrupt(); // re-interrupt the thread - which thread? Infinite loop? 
    453585     
    454     } finally {  
    455  
    456         // Moved into here from GS2PerlConstructor and GShell.runLocal() which said 
    457         // "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!)" 
    458         // http://steveliles.github.io/invoking_processes_from_java.html 
    459         // http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html?page=2 
    460         // http://mark.koli.ch/leaky-pipes-remember-to-close-your-streams-when-using-javas-runtimegetruntimeexec         
    461          
    462         //String cmd = (this.command == null) ? Arrays.toString(this.command_args) : this.command; 
    463         //log("*** In finally of SafeProcess.runProcess(2 params): " + cmd); 
    464  
    465         if( this.forciblyTerminateProcess ) { 
    466         log("*** Going to call process.destroy 1"); 
    467         destroyProcess(process);     
    468         log("*** Have called process.destroy 1"); 
    469         } 
    470         process = null; 
    471         this.forciblyTerminateProcess = false; //reset       
     586    } finally { 
     587         
     588        cleanUp("SafeProcess.runProcess(2 params)");     
    472589    } 
    473590     
    474591    return this.exitValue; 
     592    } 
     593 
     594    private void cleanUp(String callingMethod) { 
     595     
     596    // Moved into here from GS2PerlConstructor and GShell.runLocal() which said 
     597    // "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!)" 
     598    // http://steveliles.github.io/invoking_processes_from_java.html 
     599    // http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html?page=2 
     600    // http://mark.koli.ch/leaky-pipes-remember-to-close-your-streams-when-using-javas-runtimegetruntimeexec         
     601 
     602    //String cmd = (this.command == null) ? Arrays.toString(this.command_args) : this.command; 
     603    //log("*** In finally of " + callingMethod + ": " + cmd); 
     604 
     605    // if we're forcibly terminating the process, call the before- and afterDestroy hooks 
     606    // besides actually destroying the process 
     607    if( this.forciblyTerminateProcess ) { 
     608        log("*** Going to call process.destroy from " + callingMethod); 
     609 
     610        if(mainHandler != null) mainHandler.beforeProcessDestroy(); 
     611        SafeProcess.destroyProcess(process); // see runProcess(2 args/3 args) 
     612        if(mainHandler != null) mainHandler.afterProcessDestroy(); 
     613 
     614        log("*** Have called process.destroy from " + callingMethod); 
     615    }    
     616 
     617    process = null; 
     618    this.theProcessThread = null; // let the process thread ref go too 
     619    boolean wasForciblyTerminated = this.forciblyTerminateProcess; 
     620    this.forciblyTerminateProcess = false; // reset 
     621     
     622    if(mainHandler != null) mainHandler.doneCleanup(wasForciblyTerminated); 
    475623    } 
    476624 
     
    608756// e.g. full-import.pl may be terminated with p.destroy(), but it launches import.pl which is left running until it naturally terminates. 
    609757static void destroyProcess(Process p) { 
     758    log("### in SafeProcess.destroyProcess(Process p)"); 
     759 
    610760    // If it isn't windows, process.destroy() terminates any child processes too 
    611761    if(!Misc.isWindows()) { 
     
    626776    // so we can use it to find the pids of any subprocesses it launched in order to terminate those too. 
    627777     
    628     long processID = SafeProcess.getProcessID(p);        
    629     log("Attempting to terminate sub processes of Windows process with pid " + processID); 
    630     terminateSubProcessesRecursively(processID, p); 
     778    long processID = SafeProcess.getProcessID(p); 
     779    if(processID == -1) { // the process doesn't exist or no longer exists (terminated naturally?) 
     780    p.destroy(); // minimum step, do this anyway, at worst there's no process and this won't have any effect 
     781    } else { 
     782    log("Attempting to terminate sub processes of Windows process with pid " + processID); 
     783    terminateSubProcessesRecursively(processID, p); 
     784    } 
    631785     
    632786} 
     
    706860private static String getWinProcessKillCmd(Long processID) { 
    707861    // check if we first need to init WIN_KILL_CMD. We do this only once, but can't do it in a static codeblock 
    708      
     862    // because of a cyclical dependency regarding this during static initialization 
     863 
    709864    if(WIN_KILL_CMD == null) {       
    710865    if(SafeProcess.isAvailable("wmic")) { 
     
    734889// where is not part of winbin. where is a system command on windows, but only since 2003, https://ss64.com/nt/where.html 
    735890// There is no `where` on Linux/Mac, must use which for them. 
    736 // On windows, "which tskill" fails but "which" succeeds on taskkill|wmic|browser names. 
     891// On windows, "which tskill" fails (and "where tskill" works), but "which" succeeds on taskkill|wmic|browser names. 
    737892public static boolean isAvailable(String program) {      
    738893    try { 
     
    741896    SafeProcess prcs = new SafeProcess("which " + program);      
    742897    prcs.runProcess(); 
    743     String output = prcs.getStdOutput().trim();  
     898    String output = prcs.getStdOutput().trim(); 
    744899    ///System.err.println("*** 'which " + program + "' returned: |" + output + "|"); 
    745900    if(output.equals("")) { 
     
    749904        return false; 
    750905    } 
    751     //System.err.println("*** 'which " + program + "' returned: " + output); 
    752906    return true; 
    753907    } catch (Exception exc) { 
     
    768922public static interface ExceptionHandler { 
    769923 
    770     // when implementing ExceptionHandler.gotException(), if it manipulates anything that's 
    771     // not threadsafe, declare gotException() as a synchronized method to ensure thread safety 
    772     public void gotException(Exception e); // can't declare as synchronized in interface method declaration 
     924    /** 
     925     * Called whenever an exception occurs during the execution of the main thread of SafeProcess 
     926     * (the thread in which the Process is run). 
     927     * Since this method can't be declared as synchronized in this interface method declaration, 
     928     * when implementing ExceptionHandler.gotException(), if it manipulates anything that's 
     929     * not threadsafe, declare gotException() as a synchronized method to ensure thread safety 
     930     */ 
     931    public void gotException(Exception e); 
     932} 
     933 
     934/** On interrupting (cancelling) a process,  
     935 * if the class that uses SafeProcess wants to do special handling  
     936 * either before and after join() is called on all the worker threads, 
     937 * or, only on forcible termination, before and after process.destroy() is to be called, 
     938 * then that class can implement this MainProcessHandler interface 
     939 */ 
     940public static interface MainProcessHandler { 
     941    /**  
     942     * Called before the streamgobbler join()s. 
     943     * If not overriding, the default implementation should be: 
     944     * public boolean beforeWaitingForStreamsToEnd(boolean forciblyTerminating) { return forciblyTerminating; } 
     945     * When overriding: 
     946     * @param forciblyTerminating is true if currently it's been decided that the process needs to be 
     947     * forcibly terminated. Return false if you don't want it to be. For a basic implementation, 
     948     * return the parameter. 
     949     * @return true if the process is still running and therefore still needs to be destroyed, or if 
     950     * you can't determine whether it's still running or not. Process.destroy() will then be called. 
     951     * @return false if the process has already naturally terminated by this stage. Process.destroy() 
     952     * won't be called, and neither will the before- and after- processDestroy methods of this class. 
     953    */ 
     954    public boolean beforeWaitingForStreamsToEnd(boolean forciblyTerminating); 
     955    /**  
     956     * Called after the streamgobbler join()s have finished. 
     957     * If not overriding, the default implementation should be: 
     958     * public boolean afterStreamsEnded(boolean forciblyTerminating) { return forciblyTerminating; } 
     959     * When overriding: 
     960     * @param forciblyTerminating is true if currently it's been decided that the process needs to be 
     961     * forcibly terminated. Return false if you don't want it to be. For a basic implementation, 
     962     * return the parameter (usual case). 
     963     * @return true if the process is still running and therefore still needs to be destroyed, or if 
     964     * can't determine whether it's still running or not. Process.destroy() will then be called. 
     965     * @return false if the process has already naturally terminated by this stage. Process.destroy() 
     966     * won't be called, and neither will the before- and after- processDestroy methods of this class. 
     967    */ 
     968    public boolean afterStreamsEnded(boolean forciblyTerminating); 
     969    /** 
     970     * called after join()s and before process.destroy()/destroyProcess(Process), iff forciblyTerminating 
     971     */ 
     972    public void beforeProcessDestroy();  
     973    /** 
     974     * Called after process.destroy()/destroyProcess(Process), iff forciblyTerminating 
     975     */ 
     976    public void afterProcessDestroy(); 
     977 
     978    /**  
     979     * Always called after process ended: whether it got destroyed or not 
     980     */ 
     981    public void doneCleanup(boolean wasForciblyTerminated); 
    773982} 
    774983 
     
    10141223    logger.info(msg); 
    10151224 
    1016     System.err.println(msg); 
     1225    //System.err.println(msg); 
    10171226 
    10181227    //DebugStream.println(msg); 
     
    10231232    logger.error(msg, e); 
    10241233 
    1025     System.err.println(msg); 
    1026     e.printStackTrace(); 
     1234    //System.err.println(msg); 
     1235    //e.printStackTrace(); 
    10271236 
    10281237    //DebugStream.println(msg); 
     
    10341243    logger.error(e); 
    10351244 
    1036     e.printStackTrace(); 
     1245    //e.printStackTrace(); 
    10371246 
    10381247    //DebugStream.printStackTrace(e); 
     
    10841293    } 
    10851294 
     1295    // in Java 6, Sockets don't yet implement Closeable 
     1296    public static boolean closeSocket(Socket resourceHandle) { 
     1297    boolean success = false; 
     1298    try { 
     1299        if(resourceHandle != null) { 
     1300        resourceHandle.close(); 
     1301        resourceHandle = null; 
     1302        success = true; 
     1303        } 
     1304    } catch(Exception e) { 
     1305        log("Exception closing resource: " + e.getMessage(), e); 
     1306        resourceHandle = null; 
     1307        success = false; 
     1308    } finally { 
     1309        return success; 
     1310    } 
     1311    } 
     1312 
    10861313    public static boolean closeProcess(Process prcs) { 
    10871314    boolean success = true;