Ignore:
Timestamp:
2017-04-07T17:47:14+12:00 (7 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.
File:
1 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}
Note: See TracChangeset for help on using the changeset viewer.