Subversion Repositories DevTools

Rev

Rev 7033 | Blame | Compare with Previous | Last modification | View Log | RSS feed

package com.erggroup.buildtool.daemon;

import com.erggroup.buildtool.abt.BuildToolException;
import com.erggroup.buildtool.daemon.BuildThread;
import com.erggroup.buildtool.ripple.Package;
import com.erggroup.buildtool.ripple.ReleaseManager;
import com.erggroup.buildtool.ripple.RippleEngine;
import com.erggroup.buildtool.ripple.RunLevel.BuildState;
import com.erggroup.buildtool.utilities.MutableDate;

import java.sql.SQLException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Slave Thread sub component
 */
public class SlaveThread extends BuildThread
{

    /**
     * Logger
     * 
     * @attribute
     */
    private static final Logger mLogger = LoggerFactory.getLogger(SlaveThread.class);
    
    /** Holds the buildFile content
     * 
     */
    protected String mBuildFileContent = null;

    /**constructor
     * @param rtagId          - Identifies the Release
     * @param rconId          - Identifies the Controlling entry
     * @param releaseManager  - A ReleaseManager entry to use
     * @param unitTest        - Unit Test data 
     */
    public SlaveThread(int rtagId, int rconId, ReleaseManager releaseManager, String unitTest)
    {
        super(rtagId, rconId, releaseManager, unitTest);
        mLogger.warn("SlaveThread rtag_id " + rtagId + " rcon_id " + rconId);
    }

    /**
     * implements the sequence diagrams consume build files, allowed to proceed,
     * check environment
     */
    public void run()
    {
        Integer id = Integer.valueOf(mRtagId);
        setName(id.toString());
        mLogger.warn("run");
        boolean exit = false;
        mRippleEngine = new RippleEngine(mReleaseManager, mRtagId, true);

        while (!exit)
        {
            try
            {
                mLogger.error("run calling sleepCheck");
                mPhase.setPhase("sleepCheck");
                sleepCheck();
                
                mLogger.error("run calling rippleEngine.collectMetaData");
                mPhase.setPhase("collectMetaData");
                mRippleEngine.collectMetaData();

                if (Thread.currentThread().isInterrupted())
                {
                    mLogger.warn("run is interrupted");
                    // unit test technique
                    throw new ExitException();
                }

                if (mUnitTest.compareTo("unit test spawn thread") == 0)
                {
                    throw new Exception();
                }

                // allowed to proceed
                if (mUnitTest.compareTo("unit test consume build files") != 0)
                {
                    mLogger.error("run calling allowedToProceed");
                    mPhase.setPhase("allowedToProceed");
                    allowedToProceed(false);
                    mLogger.info("run allowedToProceed returned");
                }

                // Indicate that the Slave is WAITING - for a job
                mPhase.setPhase("Waiting for work");
                mLogger.error("run changing run level to WAITING for rcon_id " + mRconId);


                // Wait for Slave to be set as Active
                while (!BuildDaemon.mShutDown)
                {
                    // Periodically indicate, to Release Manager, that the slave is still alive
                    // A warning will be raised after 10 minutes, so it must be less than that
                    mRunLevel.persist(mReleaseManager, mRconId, BuildState.DB_WAITING);
                    
                    //
                    //  Wait to be notified that a build request is present
                    //  Have a timeout just to be sure to be sure
                    //
                    if (mUnitTest.length() == 0) {
                        synchronized (mActiveBuildMonitor) {
                            try {
                                mActiveBuildMonitor.wait(120 * 1000L);
                            }
                            catch ( InterruptedException e  ) {
                                Thread.currentThread().interrupt();
                                mLogger.error("InterruptedException in Wait for Active Build");
                            }
                        }
                    }
                    
                    //
                    //  Determine if the build cycle can proceed
                    //      May be a shutdown request
                    //      May be no longer configured
                    //      May be stopped
                    //      May be paused
                    //      May be indefinite pause
                    //  Will throw exception if we shouldn't continue on this cycle
                    //  
                    mLogger.info("run allowedToContinue");
                    allowedToContinue(false);

                    // Detect build Request
                    //  A non-null buildfile published in the database
                    mLogger.info("run calling mReleaseManager.queryBuildFile");
                    mBuildFileContent =  mReleaseManager.queryBuildFile(mRconId);
                    if (mBuildFileContent != null )
                    {
                        // Build request detected
                        mRunLevel.persist(mReleaseManager, mRconId, BuildState.DB_ACTIVE);
                        break;
                    }
                   
                }

                //
                //  Build Request detected
                //      Buildfile has been extracted from the database, but not yet saved
                //
                if (BuildDaemon.mShutDown)
                {
                    mLogger.warn("Slave run ShutDown requested");
                    throw new ExitException();
                }

                mLogger.info("run consumed build files");
                if (mUnitTest.compareTo("unit test consume build files") == 0)
                {
                    throw new ExitException();
                }

                // set CURRENT_BUILD_FILES to null
                //  Indicates that we have taken the file and will prevent multiple builds
                mLogger.info("run calling mReleaseManager.clearBuildFile");
                mPhase.setPhase("clearBuildFile");
                mReleaseManager.clearBuildFile(mRconId);

                // check environment
                mLogger.warn("run checkEnvironment");
                mPhase.setPhase("checkEnvironment");
                checkEnvironment();
                mLogger.info("run checkEnvironment returned");

                mLastBuildWasBenign = true;
                if (mBuildFileContent.compareTo(mDummyBuildFileContent) != 0)
                {
                    mLastBuildWasBenign = false;
                    
                    // Start of a build cycle. Set new log file to capture
                    // entire build log
                    mPhase.setPhase("flagStartBuildCycle");
                    flagStartBuildCycle();
                    mReporting.resetData();
                    
                    mLogger.error("Collect fresh MetaData");
                    mPhase.setPhase("collectMetaData");
                    mRippleEngine.collectMetaData();
                    
                    // Save the build file
                    mLogger.error("Save buildfile");
                    mPhase.setPhase("Save buildfile");
                    saveBuildFile(mBuildFileContent);

                    // deliver change to product baseline
                    mLogger.error("run calling setViewUp");
                    mPhase.setPhase("Setup View for build");
                    deliverChange("AbtSetUp", true, true);

                    mLogger.error("run calling deliverChange - the actual build");
                    mPhase.setPhase("deliverChange for Build");
                    deliverChange(null, true, false);
                    
                    mLogger.error("run calling deliverChange on AbtTearDown");
                    mPhase.setPhase("AbtTearDown");
                    deliverChange("AbtTearDown", false, true);

                    if (mReporting.buildFailureLogFile != null)
                    {
                        mLogger.error("run calling excludeFromBuild");
                        mPhase.setPhase("excludeFromBuild");
                        mReleaseManager.excludeFromBuild(false, 
                                                         mReporting.packageVersionId,
                                                         mReporting.packageVersion, 
                                                         mRtagId, 
                                                         null, 
                                                         null, 
                                                         mReporting.buildFailureLogFile,
                                                         false, mReporting.isaTestBuild);
                    }

                    mLogger.info("run deliverChange returned");
                    mPhase.setPhase("End of Build Cycle");
                }
                mReleaseManager.clearCurrentPackageBeingBuilt(mRconId);
            } 
            //---------------------------------------------------------------
            //    Build loop exception handling (only)
            //
            catch (ShouldNotContinueExpection e)
            {
                mLogger.error("ShouldNotContinueExpection:" + e.getMessage());
            }
            catch (SQLException e)
            {
                // oracle connection issues
                //  Request a prolonged sleep at the start of the build loop
                mLogger.error("run oracle connection issues");
                mException = true;
            } 
            catch (ExitException e)
            {
                mLogger.warn("run ExitException");
                exit = true;
            } 
            catch (InterruptedException e)
            {
                mLogger.warn("run InterruptedException");
                Thread.currentThread().interrupt();
            }
            catch (BuildSystemException e) {
                // BuildSystem exception - a (possibly) correctable error
                // Flag as recoverable
                //    - Can't create XML build file
                //    - 
                // 
                mLogger.error("run BuildSystemException");
                mPhase.setPhase("BuildSystemException indefinitePause");
                notifyIndefinitePause(e.getMessage());
                mReleaseManager.indefinitePause(false);
                mException = true;
                mRecoverable = true;
                
            }
            catch( BuildToolException e)
            {
                // Buildtool exception - an uncorrectable error
                //    - Can't parse XML build file
                //    - 
                // 
                mLogger.error("run BuildToolException");
                mPhase.setPhase("BuildToolException indefinitePause");
                notifyIndefinitePause(e.getMessage());
                mReleaseManager.indefinitePause(false);
                mException = true;
            }
            catch (Exception e)
            {
                //  Uncaptured exception
                //  These are not good. Current mechanism to handle this condition is:
                //      1) Email build system administrator
                //      2) Pause the build system
                //  
                mLogger.error("run indefinitePause " + e.toString());
                
                // DEVI 51366 force sleep at beginning of while loop
                mException = true;
                
                String cause = e.getMessage();
                if ( cause == null || cause.compareTo("null") == 0 )
                {
                  cause = "Internal Error: " + e.toString();
                }

                try
                {
                    // notify first
                    // many reasons for indefinite pause, including database related, so do database last
                    mRecoverable = false;

                    if (cause.compareTo(Package.mRecoverable) == 0)
                    {
                        mRecoverable = true;
                    }

                    mPhase.setPhase("Notify indefinitePause");
                    notifyIndefinitePause(cause);
                    mReleaseManager.indefinitePause(mRecoverable);
                }
                catch( Exception f )
                {
                    mLogger.error("run indefinitePause failed");
                }
            }
        }
        try {
            mReleaseManager.clearCurrentPackageBeingBuilt(mRconId);
        } catch (Exception e) {
            mLogger.error("Exception in clearCurrentPackageBeingBuilt:" + e.getMessage());
        }
        mPhase.setPhase("Exit Thread");
    }

    /** Determine if we are allowed to continue in this build cycle
     *      May be no longer configured
     *      May be stopped
     *      May be paused
     *      May be indefinite pause
     *  Will throw exceptions if we shouldn't continue on this cycle
     *  Does not hang around waiting for condition to clear - thats done elsewhere
     *  
     * @param master - is a master machine making this call
     * @throws ExitException 
     * @throws ShouldNotContinueExpection 
     * @throws SQLException 
     */
    private void allowedToContinue(boolean master) throws ExitException, ShouldNotContinueExpection, SQLException, Exception {
        
        if (BuildDaemon.mShutDown)
        {
            mLogger.warn("allowedToContinue. ShutDown requested");
            throw new ExitException();
        }
        
        try
        {
            mLogger.error("allowedToContinue calling mReleaseManager.connect");                      
            mReleaseManager.connect();
            
            //  Ensure that the machine is a part of the current build set
            mLogger.error("allowedToContinue calling mReleaseManager.queryReleaseConfig");                      
            if ( !mReleaseManager.queryReleaseConfig(mRtagId, mRconId, BuildDaemon.mHostname, getMode(), mStartTime) )
            {
                mLogger.warn("allowedToContinue queryReleaseConfig failed");
                mReleaseManager.disconnect();
                throw new ExitException();
            }
            
            
            //  Check for scheduled downtime or indefinite pause
            MutableDate resumeTime = new MutableDate();
            mLogger.error("allowedToContinue calling mReleaseManager.queryRunLevelSchedule");                      
            if ( !mReleaseManager.queryRunLevelSchedule(resumeTime) )
            {
              mLogger.info("allowedToContinue scheduled downtime");
              mReleaseManager.disconnect();
              throw new ShouldNotContinueExpection("Scheduled downtime");
            }
            
            //
            //  If commanded to pause, then wait around until the command has been removed.
            //
            mLogger.error("allowedToContinue calling mReleaseManager.queryDirectedRunLevel");                      
            if ( !mReleaseManager.queryDirectedRunLevel(mRconId) )
            {
                mLogger.error("allowedToContinue Pause Detected");
                throw new ShouldNotContinueExpection("Pause Detected");
            }
          
        }
        finally
        {
          // this block is executed regardless of what happens in the try block
          // even if an exception is thrown
          // ensure disconnect
          mLogger.error("allowedToContinue calling mReleaseManager.disconnect");                      
          mReleaseManager.disconnect();
        }

        // One last test for a shut down request
        if (BuildDaemon.mShutDown)
        {
            mLogger.warn("allowedToContinue. ShutDown requested");
            throw new ExitException();
        }
        
    }

    /**
     * returns 'S'
     */
    protected char getMode()
    {
        mLogger.debug("getMode");
        return 'S';
    }
}