Changeset 31591 for main/trunk


Ignore:
Timestamp:
04/07/17 17:47:14 (4 years ago)
Author:
ak19
Message:
  1. SafeProcess.CustomProcessHandler replaces SafeProcess.LineByLineHandler to allow GS developers more control over the handling of a Process' iostreams and more transparency to the developer about what happens. But SafeProcess provides default behaviours. 2. Some informative msgs in the GS2Construct method that marks the flat DB for when the oid entry does not occur in the DB.
Location:
main/trunk/greenstone3/src/java/org/greenstone
Files:
3 edited

Legend:

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

    r31590 r31591  
    1818import java.io.InputStreamReader;
    1919import java.io.File;
     20import java.io.InputStream;
    2021import java.io.IOException;
    2122import java.util.ArrayList;
     
    334335        // http://www.cgi101.com/class/ch3/text.html
    335336        // setenv QUERY_STRING and REQUEST_METHOD = GET.
     337        // Run the perl command as a simple process: no logging to the collection's build log
    336338        if (runPerlCommand(command_str, envvars, new File(cgi_directory)))
    337339                   //new File(GlobalProperties.getGSDL3Home() + File.separator + "WEB-INF" + File.separator + "cgi")))
     
    372374    }
    373375
    374     /** returns true if completed correctly, false otherwise */
    375     protected boolean runPerlCommand(String[] command) {
    376     return runPerlCommand(command, null, null);
    377     }
    378 
    379        
    380     protected boolean runPerlCommand(String[] command, String[] envvars, File dir)
    381     {
    382     boolean success = true;
    383    
     376    protected SafeProcess createPerlProcess(String[] command, String[] envvars, File dir) {
    384377    int sepIndex = this.gsdl3home.lastIndexOf(File.separator);
    385378    String srcHome = this.gsdl3home.substring(0, sepIndex);
     
    402395        args.add(a + "=" + System.getenv(a));
    403396    }
     397
     398    SafeProcess perlProcess
     399        = new SafeProcess(command, args.toArray(new String[args.size()]), dir); //  dir can be null
     400   
     401    return perlProcess;
     402    }
     403
     404    // ModifyMetadata operations call runSimplePerlCommand which produces no output in build log
     405    protected boolean runSimplePerlCommand(String[] command) { 
     406    return runSimplePerlCommand(command, null, null);
     407    }
     408   
     409    protected boolean runSimplePerlCommand(String[] command, String[] envvars, File dir) {
     410    boolean success = false;   
    404411   
    405412    String command_str = "";
     
    410417    sendMessage(new ConstructionEvent(this, GSStatus.INFO, "command = " + command_str));
    411418   
    412     //logger.info("### Running command = " + command_str);
     419    logger.info("### Running simple command = " + command_str);
    413420
    414421    // This is where we create and run our perl process safely
    415     SafeProcess perlProcess
    416         = new SafeProcess(command, args.toArray(new String[args.size()]), dir); //  dir can be null
    417    
     422    SafeProcess perlProcess = createPerlProcess(command, envvars, dir); //  dir can be null
     423   
     424    perlProcess.setExceptionHandler(this);
     425
     426    sendProcessBegun(new ConstructionEvent(this, GSStatus.ACCEPTED, "starting"));
     427
     428    int exitVal = perlProcess.runProcess(); // uses default processing of the perl process' iostreams provided by SafeProcess
     429
     430    if (exitVal == 0) {
     431        success = true;
     432        sendProcessStatus(new ConstructionEvent(this, GSStatus.CONTINUING, "Success"));
     433    } else {
     434        sendProcessStatus(new ConstructionEvent(this, GSStatus.ERROR, "Failure"));     
     435        success = false; // explicit
     436    }
     437   
     438    return success;
     439    }
     440
     441
     442    /** returns true if completed correctly, false otherwise
     443     * Building operations call runPerlCommand which sends build output to collect/log/build_log.#*.txt
     444     */
     445    protected boolean runPerlCommand(String[] command) {
     446    return runPerlCommand(command, null, null);
     447    }
     448
     449       
     450    protected boolean runPerlCommand(String[] command, String[] envvars, File dir)
     451    {
     452    boolean success = true;
     453   
     454    String command_str = "";
     455    for (int i = 0; i < command.length; i++) {
     456        command_str = command_str + command[i] + " ";
     457    }
     458   
     459    sendMessage(new ConstructionEvent(this, GSStatus.INFO, "command = " + command_str));
     460   
     461    logger.info("### Running logged command = " + command_str);
     462
     463    // This is where we create and run our perl process safely
     464    SafeProcess perlProcess = createPerlProcess(command, envvars, dir); //  dir can be null
    418465   
    419466    sendProcessBegun(new ConstructionEvent(this, GSStatus.ACCEPTED, "starting"));
     
    428475    BufferedWriter bw = null;
    429476    try {
    430         bw = new BufferedWriter(new FileWriter(GSFile.collectDir(this.site_home) + File.separator + this.collection_name + File.separator + "log" + File.separator + "build_log." + (System.currentTimeMillis()) + ".txt"));       
     477
     478        bw = new BufferedWriter(new FileWriter(new File(logDir, "build_log." + (System.currentTimeMillis()) + ".txt")));
    431479   
    432480        bw.write("Document Editor Build \n");       
     
    434482       
    435483        // handle each incoming line from stdout and stderr streams, and any exceptions that occur then
    436         SynchronizedProcessLineByLineHandler outLineByLineHandler
    437         = new SynchronizedProcessLineByLineHandler(bw, SynchronizedProcessLineByLineHandler.STDOUT);
    438         SynchronizedProcessLineByLineHandler errLineByLineHandler
    439         = new SynchronizedProcessLineByLineHandler(bw, SynchronizedProcessLineByLineHandler.STDERR);
    440         perlProcess.setStdOutLineByLineHandler(outLineByLineHandler);
    441         perlProcess.setStdErrLineByLineHandler(errLineByLineHandler);
     484        SafeProcess.CustomProcessHandler processOutHandler
     485        = new SynchronizedProcessHandler(bw, SynchronizedProcessHandler.STDOUT);
     486        SafeProcess.CustomProcessHandler processErrHandler
     487        = new SynchronizedProcessHandler(bw, SynchronizedProcessHandler.STDERR);
    442488       
    443489        // GS2PerlConstructor will do further handling of exceptions that may occur during the perl
     
    450496        // std in of java, as before.
    451497
    452         perlProcess.runProcess();
     498        perlProcess.runProcess(null, processOutHandler, processErrHandler); // use default procIn handling
    453499       
    454500    // The original runPerlCommand() code had an ineffective check for whether the cmd had been cancelled
     
    657703    // Called when an exception happens during the running of our perl process. However,
    658704    // exceptions when reading from our perl process' stderr and stdout streams are handled by
    659     // SynchronizedProcessLineByLineHandler.gotException() below.
     705    // SynchronizedProcessHandler.gotException() below, since they happen in separate threads
     706    // from this one (the ine from which the perl process is run).
    660707    public synchronized void gotException(Exception e) {
    661708
     
    664711    e.printStackTrace();
    665712    sendProcessStatus(new ConstructionEvent(this,GSStatus.ERROR,
    666                         "Exception occurred " + e.toString())); // atomic
     713                        "Exception occurred " + e.toString()));
    667714    }
    668715   
     716    // Each instance of this class is run in its own thread by class SafeProcess.InputGobbler.
    669717    // This class deals with each incoming line from the perl process' stderr or stdout streams. One
    670     // instance of this class for each stream. However, since multiple instances of this LineByLineHandler
    671     // could be (and in fact, are) writing to the same file in their own threads, the writer object needs
    672     // to be made threadsafe.
     718    // instance of this class for each stream. However, since multiple instances of this CustomProcessHandler
     719    // could be (and in fact, are) writing to the same file in their own threads, several objects, not just
     720    // the bufferedwriter object, needed to be made threadsafe.
    673721    // This class also handles exceptions during the running of the perl process.
    674722    // The runPerlCommand code originally would do a sendProcessStatus on each exception, so we ensure
    675     // we do that here too, to continue original behaviour.
    676     protected class SynchronizedProcessLineByLineHandler implements SafeProcess.LineByLineHandler
     723    // we do that here too, to continue original behaviour. These calls are also synchronized to make their
     724    // use of the EventListeners threadsafe.
     725    protected class SynchronizedProcessHandler implements SafeProcess.CustomProcessHandler
    677726    {
    678727    public static final int STDERR = 0;
     
    683732       
    684733
    685     public SynchronizedProcessLineByLineHandler(BufferedWriter bw, int src) {
    686         this.bwHandle = bw;
     734    public SynchronizedProcessHandler(BufferedWriter bw, int src) {
     735        this.bwHandle = bw; // caller will close bw, since many more than one
     736                            // SynchronizedProcessHandlers are using it
    687737        this.source = src; // STDERR or STDOUT
    688738    }
    689739
    690     public synchronized void gotLine(String line) {
    691         //if(this.source == STDERR) {
    692         ///System.err.println("ERROR: " + line);
    693         //} else {
    694         ///System.err.println("OUT: " + line);
    695         //}
     740    public void run(Closeable inputStream) {
     741        InputStream is = (InputStream) inputStream;
     742
     743        BufferedReader br = null;
     744        try {
     745        br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
     746        String line=null;
     747        while ( (line = br.readLine()) != null ) {
     748           
     749            if(Thread.currentThread().isInterrupted()) { // should we not instead check if SafeProcess thread was interrupted?
     750            System.err.println("Got interrupted when reading lines from process err/out stream.");
     751            break; // will go to finally block
     752            }
     753           
     754///         System.out.println("@@@ GOT LINE: " + line);
     755///         GS2PerlConstructor.logger.info("@@@ GOT LINE: " + line + " STDOUT=1: " + source);
     756
     757            //if(this.source == STDERR) {
     758            ///System.err.println("ERROR: " + line);
     759            //} else {
     760            ///System.err.println("OUT: " + line);
     761            //}
     762           
     763
     764            this.gotLine(line); // synchronized
     765           
     766            /*
     767            try {
     768            synchronized(bwHandle) { // get a lock on the writer handle, then write
     769               
     770                bwHandle.write(line + "\n");
     771            }
     772            } catch(IOException ioe) {
     773            String msg = (source == STDERR) ? "stderr" : "stdout";
     774            msg = "Exception when writing out a line read from perl process' " + msg + " stream.";
     775            GS2PerlConstructor.logger.error(msg, ioe);
     776            }
     777           
     778            // this next method is thread safe since only synchronized methods are invoked.
     779            // and only immutable (final) vars are used.
     780            // NO, What about the listeners???
     781            sendProcessStatus(new ConstructionEvent(GS2PerlConstructor.this, GSStatus.CONTINUING, line));           
     782            */
     783        }
     784        } catch (IOException ioe) { // problem with reading in from process with BufferedReader br
     785
     786        String msg = (source == STDERR) ? "stderr" : "stdout";
     787        msg = "Got exception when processing the perl process' " + msg + " stream.";
     788        GS2PerlConstructor.logger.error(msg, ioe);
     789        // now do what the original runPerlCommand() code always did:
     790        ioe.printStackTrace();
     791        logException(ioe); // synchronized
     792
     793        } catch (Exception e) { // problem with BufferedWriter bwHandle on processing each line
     794        e.printStackTrace();
     795        logException(e); // synchronized
     796        } finally {
     797        SafeProcess.closeResource(br);
     798        }
     799    }
     800
     801    // trying to keep synchronized methods as short as possible
     802    private synchronized void logException(Exception e) {
     803        sendProcessStatus(new ConstructionEvent(this, GSStatus.ERROR, "Exception occurred " + e.toString()));
     804    }
     805
     806    // trying to keep synchronized methods as short as possible
     807    private synchronized void gotLine(String line) throws Exception {
    696808
    697809        // BufferedWriter writes may not be atomic
     
    699811        // Choosing to put try-catch outside of sync block, since it's okay to give up lock on exception
    700812        // http://stackoverflow.com/questions/14944551/it-is-better-to-have-a-synchronized-block-inside-a-try-block-or-a-try-block-insi
    701         // "All methods on Logger are multi-thread safe", see
    702         // http://stackoverflow.com/questions/14211629/java-util-logger-write-synchronization
    703        
    704         try {       
    705         bwHandle.write(line + "\n");
     813        try {                       
     814        bwHandle.write(line + "\n");   
     815       
     816///     System.out.println("@@@ WROTE LINE: " + line);
     817///     GS2PerlConstructor.logger.info("@@@ WROTE LINE: " + line);
     818
     819        // this next method is thread safe since only synchronized methods are invoked.
     820        // and only immutable (final) vars are used.
     821        sendProcessStatus(new ConstructionEvent(GS2PerlConstructor.this, GSStatus.CONTINUING, line));
     822       
     823        } catch(IOException ioe) { // can't throw Exceptions, but are forced to handle Exceptions here
     824        // since our method definition doesn't specify a throws list.
     825        // "All methods on Logger are multi-thread safe", see
     826        // http://stackoverflow.com/questions/14211629/java-util-logger-write-synchronization
    706827       
    707         } catch(IOException ioe) {
    708828        String msg = (source == STDERR) ? "stderr" : "stdout";
    709         msg = "Exception when writing out a line read from perl process' " + msg + " stream.";
    710         GS2PerlConstructor.logger.error(msg, ioe);
    711         }
    712        
    713         // this next method is thread safe since only synchronized methods are invoked.
    714         // and only immutable (final) vars are used.
    715         sendProcessStatus(new ConstructionEvent(GS2PerlConstructor.this, GSStatus.CONTINUING, line));
    716     }
    717 
    718     // This is called when we get an exception during the processing of a perl's
    719     // input-, err- or output stream
    720     public synchronized void gotException(Exception e) {
    721         String msg = (source == STDERR) ? "stderr" : "stdout";
    722         msg = "Got exception when processing the perl process' " + msg + " stream.";
    723 
    724         // now do what the original runPerlCommand() code always did:
    725         e.printStackTrace();
    726         sendProcessStatus(new ConstructionEvent(this, GSStatus.ERROR, "Exception occurred " + e.toString())); // atomic
    727     }
    728 
    729     } // end inner class SynchronizedProcessLineByLineHandler
     829        msg = "IOException when writing out a line read from perl process' " + msg + " stream.";
     830        msg += "\nGot line: " + line + "\n";
     831        throw new Exception(msg, ioe);
     832        }           
     833    }
     834
     835    } // end inner class SynchronizedProcessHandler
    730836
    731837}
  • main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/GS2Construct.java

    r31546 r31591  
    11871187        // now we know we have an archives folder
    11881188        String old_value = coll_db.getValue(oid);
    1189         String new_value = old_value.replace("<index-status>B", "<index-status>" + mark);
     1189        String new_value = "<index-status>" + mark;
     1190        if(old_value == null) {
     1191            logger.error("### null old_value in flat DB for oid " + oid);
     1192        } else {
     1193            new_value = old_value.replace("<index-status>B", "<index-status>" + mark);
     1194            logger.info("### Replacing db entry for oid " + oid + " which has old_value " + old_value);
     1195            logger.info("### with new value " + new_value);
     1196                   
     1197        }
    11901198        // Close database for reading
    11911199        coll_db.closeDatabase();
     1200
    11921201        if (!coll_db.openDatabase(coll_db_file, SimpleCollectionDatabase.WRITE))
    1193         {
     1202            {
    11941203            logger.error("Could not open collection archives database. Somebody already using this database!");
    11951204            return;
    1196         }
     1205            }
     1206       
    11971207        coll_db.setValue(oid, new_value);
    1198         coll_db.closeDatabase();
    11991208       
     1209        coll_db.closeDatabase();       
    12001210    }
    12011211}
  • main/trunk/greenstone3/src/java/org/greenstone/util/SafeProcess.java

    r31588 r31591  
    3535    private int exitValue = -1;
    3636
    37     // user can write custom LineByLineHandler to deal with stdout lines as they come out one line at a time
    38     // and stderr lines as they come out one at a time
    39     private LineByLineHandler errLineByLineHandler = null;
    40     private LineByLineHandler outLineByLineHandler = null;
     37    // allow callers to process exceptions of the main process thread if they want
    4138    private ExceptionHandler exceptionHandler = null;
    4239
     
    7976    }
    8077
    81     // register a handler whose gotLine() method will get called as each line is read from the process' stdout
    82     public void setStdOutLineByLineHandler(LineByLineHandler out_lbl_handler) {
    83     outLineByLineHandler = out_lbl_handler;
    84     }
    85 
    86     // register a handler whose gotLine() method will get called as each line is read from the process' stderr
    87     public void setStdErrLineByLineHandler(LineByLineHandler err_lbl_handler) {
    88     errLineByLineHandler = err_lbl_handler;
    89     }
    90 
    9178    // register a SafeProcess ExceptionHandler whose gotException() method will
    9279    // get called for each exception encountered
     
    10491
    10592//***************** Copied from gli's gui/FormatConversionDialog.java *************//
     93
    10694    public int runProcess() {
    107 
     95    return runProcess(null, null, null); // use default processing of all 3 of the process' iostreams
     96    }
     97
     98    public int runProcess(CustomProcessHandler procInHandler,
     99               CustomProcessHandler procOutHandler,
     100               CustomProcessHandler procErrHandler)
     101    {
    108102    Process prcs = null;
    109103    SafeProcess.OutputStreamGobbler inputGobbler = null;
     
    112106
    113107    try {       
     108       
    114109        Runtime rt = Runtime.getRuntime();     
     110       
     111       
    115112        if(this.command != null) {
    116113        prcs = rt.exec(this.command);
     
    119116
    120117        // http://stackoverflow.com/questions/5283444/convert-array-of-strings-into-a-string-in-java
    121         ///logger.info("SafeProcess running: " + Arrays.toString(command_args));
     118        logger.info("SafeProcess running: " + Arrays.toString(command_args));
    122119
    123120        if(this.envp == null) {
     
    136133        }
    137134
    138         // send inputStr to process. The following constructor can handle inputStr being null
    139         inputGobbler = // WriterToProcessInputStream
    140         new SafeProcess.OutputStreamGobbler(prcs.getOutputStream(), this.inputStr);
    141        
    142         // monitor for any error messages
    143             errorGobbler // ReaderFromProcessOutputStream
    144         = new SafeProcess.InputStreamGobbler(prcs.getErrorStream(), splitStdErrorNewLines);
    145 
    146             // monitor for the expected std output line(s)
    147             outputGobbler
    148         = new SafeProcess.InputStreamGobbler(prcs.getInputStream(), splitStdOutputNewLines);
    149                    
    150         // register line by line handlers, if any were set, for the process stderr and stdout streams
    151         if(this.outLineByLineHandler != null) {
    152         outputGobbler.setLineByLineHandler(this.outLineByLineHandler);
    153         }
    154         if(this.errLineByLineHandler != null) {
    155         errorGobbler.setLineByLineHandler(this.errLineByLineHandler);
    156         }
    157         if(this.exceptionHandler != null) {
    158         inputGobbler.setExceptionHandler(this.exceptionHandler);
    159         }       
     135        logger.info("### Before creating ProcessInGobbler");
     136
     137        // Create the streamgobblers and set any specified handlers on them
     138
     139        // PROC INPUT STREAM
     140        if(procInHandler == null) {
     141        // send inputStr to process. The following constructor can handle inputStr being null
     142        inputGobbler = // WriterToProcessInputStream
     143            new SafeProcess.OutputStreamGobbler(prcs.getOutputStream(), this.inputStr);
     144        } else { // user will do custom handling of process' InputStream
     145        inputGobbler = new SafeProcess.OutputStreamGobbler(prcs.getOutputStream(), procInHandler);
     146        }
     147
     148        logger.info("### Before creating ProcessErrGobbler");
     149       
     150        // PROC ERR STREAM to monitor for any error messages or expected output in the process' stderr
     151        if(procErrHandler == null) {
     152        errorGobbler // ReaderFromProcessOutputStream
     153            = new SafeProcess.InputStreamGobbler(prcs.getErrorStream(), splitStdErrorNewLines);     
     154        } else {
     155        errorGobbler
     156            = new SafeProcess.InputStreamGobbler(prcs.getErrorStream(), procErrHandler);
     157        }
     158
     159        logger.info("### Before creating ProcessOutGobbler");
     160
     161            // PROC OUT STREAM to monitor for the expected std output line(s)
     162        if(procOutHandler == null) {
     163        outputGobbler
     164            = new SafeProcess.InputStreamGobbler(prcs.getInputStream(), splitStdOutputNewLines);
     165        } else {
     166        outputGobbler
     167            = new SafeProcess.InputStreamGobbler(prcs.getInputStream(), procOutHandler);
     168        }
     169
     170       
     171        logger.info("### Before streamgobblers.start()");
    160172
    161173            // kick off the stream gobblers
     
    163175            errorGobbler.start();
    164176            outputGobbler.start();
     177
     178        logger.info("### After streamgobblers.start() - before waitFor");
    165179                                   
    166180            // any error???
    167             this.exitValue = prcs.waitFor(); // can throw an InterruptedException if process did not terminate             
     181            this.exitValue = prcs.waitFor(); // can throw an InterruptedException if process did not terminate
     182
     183            logger.info("Process exitValue: " + exitValue);
     184
     185        logger.info("### Before streamgobblers.join()");
     186
    168187        // From the comments of
    169188        // http://www.javaworld.com/article/2071275/core-java/when-runtime-exec---won-t.html?page=2
     
    175194        inputGobbler.join();
    176195       
     196        logger.info("### After streamgobblers.join()");
     197
    177198        // set the variables the code that created a SafeProcess object may want to inspect
    178199        this.outputStr = outputGobbler.getOutput();
    179200        this.errorStr = errorGobbler.getOutput();
    180 
     201       
    181202        // Since we didn't have an exception, process should have terminated now (waitFor blocks until then)
    182203        // Set process to null so we don't forcibly terminate it below with process.destroy()
     
    192213        }
    193214    } catch(InterruptedException ie) {
     215
    194216        if(exceptionHandler != null) {
    195217        exceptionHandler.gotException(ie);
     
    197219        logger.error("Process InterruptedException: " + ie.getMessage(), ie);
    198220        //System.err.println("Process InterruptedException " + ie.getMessage());
    199         //ie.printStackTrace(); // an interrupt here is not an error, it can be a cancel action
    200         }
    201 
     221        ///ie.printStackTrace(); // an interrupt here is not an error, it can be a cancel action
     222        }
    202223
    203224        // propagate interrupts to worker threads here?
     
    237258        }
    238259    }
    239 
     260   
    240261    return this.exitValue;
    241262    }
     
    257278}
    258279
    259 // When reading from a process' stdout or stderr stream, you can create a LineByLineHandler
    260 // to do something on a line by line basis, such as sending the line to a log
    261 public static interface LineByLineHandler {
    262     public void gotLine(String line);
    263     public void gotException(Exception e); // for when an exception occurs instead of getting a line
     280// write your own run() body for any StreamGobbler
     281// Make sure your implementation is threadsafe if you're sharing immutable objects between the threaded streams
     282// example implementation is in the GS2PerlConstructor.SynchronizedProcessHandler class.
     283public static interface CustomProcessHandler {
     284    public void run(Closeable stream); //InputStream or OutputStream
    264285}
    265286
     
    270291public static class InputStreamGobbler extends Thread
    271292{
    272     InputStream is = null;
    273     StringBuffer outputstr = new StringBuffer();
    274     boolean split_newlines = false;
    275     LineByLineHandler lineByLineHandler = null;
    276    
     293    private InputStream is = null;
     294    private StringBuffer outputstr = new StringBuffer();
     295    private boolean split_newlines = false;
     296    private CustomProcessHandler customHandler = null;
     297
    277298    public InputStreamGobbler(InputStream is)
    278299    {
    279300    this.is = is;
    280     split_newlines = false;
     301    this.split_newlines = false;
    281302    }
    282303   
     
    287308    }
    288309   
    289     public void setLineByLineHandler(LineByLineHandler lblHandler) {
    290     lineByLineHandler = lblHandler;
    291     }
    292 
    293 
    294     public void run()
     310    public InputStreamGobbler(InputStream is, CustomProcessHandler customHandler)
     311    {
     312    this.is = is;
     313    this.customHandler = customHandler;
     314    }
     315
     316    // default run() behaviour
     317    public void runDefault()
    295318    {
    296319    BufferedReader br = null;
     
    307330        //System.out.println("@@@ GOT LINE: " + line);
    308331        outputstr.append(line);
    309        
    310332        if(split_newlines) {
    311333            outputstr.append(Misc.NEWLINE); // "\n" is system dependent (Win must be "\r\n")
    312334        }
    313 
    314         if(lineByLineHandler != null) { // let handler deal with newlines
    315             lineByLineHandler.gotLine(line);
    316         }       
    317         }
    318     } catch (IOException ioe) {
    319         if(lineByLineHandler != null) {
    320         lineByLineHandler.gotException(ioe);
    321         } else {
    322         logger.error("Exception when reading from a process' stdout/stderr stream: ", ioe);
    323         //ioe.printStackTrace();
    324         }
     335        }
     336    } catch (IOException ioe) {     
     337        logger.error("Exception when reading from a process' stdout/stderr stream: ", ioe);
     338        //System.err.println("Exception when reading from a process' stdout/stderr stream: ");
     339        //ioe.printStackTrace(); 
    325340       
    326341    } finally {
     
    329344    }
    330345   
     346    public void runCustom() {
     347    this.customHandler.run(is);
     348    }
     349   
     350    public void run() {
     351    if(this.customHandler == null) {
     352        runDefault();
     353    } else {
     354        runCustom();
     355    }
     356    }
     357
    331358    public String getOutput() {
    332359    return outputstr.toString(); // implicit toString() call anyway. //return outputstr;
     
    340367public static class OutputStreamGobbler extends Thread
    341368{
    342     OutputStream os = null;
    343     String inputstr = "";
    344     ExceptionHandler exceptionHandler = null;
     369    private OutputStream os = null;
     370    private String inputstr = "";
     371    private CustomProcessHandler customHandler = null;
    345372
    346373    public OutputStreamGobbler(OutputStream os) {
     
    353380    this.inputstr = inputstr;
    354381    }
    355    
    356     public void setExceptionHandler(ExceptionHandler eHandler) {
    357     exceptionHandler = eHandler;
    358     }
    359 
    360     public void run()
    361     {   
     382
     383    public OutputStreamGobbler(OutputStream os, CustomProcessHandler customHandler) {
     384    this.os = os;
     385    this.customHandler = customHandler;
     386    }
     387
     388    // default run() behaviour
     389    public void runDefault() {
     390   
    362391    if (inputstr == null) {
    363392        return;
     
    365394   
    366395    BufferedWriter osw = null;
    367     try {
     396    try {
    368397        osw = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
    369398        //System.out.println("@@@ SENDING LINE: " + inputstr);
     
    385414        */
    386415    } catch (IOException ioe) {
    387         if (this.exceptionHandler != null) {
    388         this.exceptionHandler.gotException(ioe);
    389         } else {
    390         logger.error("Exception writing to SafeProcess' inputstream: ", ioe);       
    391         //ioe.printStackTrace();
    392         }
    393        
     416        logger.error("Exception writing to SafeProcess' inputstream: ", ioe);
     417        //System.err.println("Exception writing to SafeProcess' inputstream: ");
     418        //ioe.printStackTrace();   
    394419    } finally {
    395420        SafeProcess.closeResource(osw);
    396421    }
     422    }
     423
     424    // call the user's custom handler for the run() method
     425    public void runCustom() {
     426    this.customHandler.run(os);
     427    }
     428
     429    public void run()
     430    {
     431    if(this.customHandler == null) {
     432        runDefault();
     433    } else {
     434        runCustom();
     435    }
     436
    397437    }   
    398438} // end static inner class OutputStreamGobbler
Note: See TracChangeset for help on using the changeset viewer.