Subversion Repositories DevTools

Rev

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

package com.erggroup.buildtool.ripple;

import java.io.File;

import java.sql.SQLException;
import java.util.Iterator;
import java.util.Vector;

import org.apache.log4j.Logger;

import com.erggroup.buildtool.smtp.Smtpsend;

public class Package
{
        /**
         * name of package, must not contain spaces
         * 
         * @attribute
         */
        String mName = new String();

        /**
         * package scope
         * 
         * @attribute
         */
        String mExtension = new String();

        /**
         * instance identifier
         * 
         * @attribute
         */
        String mVersion = new String();

        /**
         * Version string as specified by the user. Used with a ripple type of 'F'
         */
        String mFixedVersion = new String();

        /**
         * unique identifier for daemon builds = mName + mExtension for escrow
         * builds = mName + mVersion + mExtension
         * 
         * @attribute
         */
        String mAlias = new String();

        /**
         * Version Control System Tag
         * 
         * @attribute
         */
        String mVcsTag = new String();

        /**
         * build standards
         * 
         * @attribute
         */
        Vector<BuildStandard> mBuildStandardCollection = new Vector<BuildStandard>();

        /**
         * GBE_MACHTYPE used to build generic packages for this baseline only has
         * meaning in the daemon build, not the escrow build accessed by
         * BuildStandard::getPlatform, getBuildStandard
         * 
         * @attribute
         */
        public static final String mGenericMachtype = System.getenv("GBE_MACHTYPE");

        /**
         * build dependencies by package alias
         * 
         * @attribute
         */
        Vector<String> mDependencyCollection = new Vector<String>();

        /**
         * primary package version key pv_id in database
         * 
         * @attribute
         */
        int mId;

        /**
         * indication of the nature of change
         * 
         * @attribute
         */
        Package.VersionNumberingStandard mChangeType = new VersionNumberingStandard();

        /**
         * determines what field is rippled on a package version whose dependencies
         * have changed
         * 
         * @attribute
         */
        Package.VersionNumberingStandard mRippleField = new VersionNumberingStandard();

        /**
         * interested owners
         * 
         * @attribute
         */
        private Vector<String> mBuildFailureEmailCollection = new Vector<String>();

        /**
         * when true will trigger unit tests as part of the package build phase in
         * daemon mode
         * 
         * @attribute
         */
        boolean mHasAutomatedUnitTests = false;

        /**
         * when true, do not ripple this package through packages which are
         * dependent upon it in daemon mode
         * 
         * @attribute
         */
        boolean mAdvisoryRipple = false;

        /**
         * determines the build file the package is built in, or not 1 Post Plan:
         * buildfile - Result of planning 2 Post Plan: Future build requirement 3
         * Post Plan: Package has no build requiremnt 0 not yet processed (initial
         * value) -1 not reproducible -2 not reproducible on the build platforms
         * configured for this release -3 do not ripple -4 directly dependent on
         * package versions not in the baseline -5 indirectly dependent on package
         * versions which are not reproducible because of -1, -2 (escrow), -3
         * (daemon), -4, -6 -6 circular dependency
         * 
         * @attribute
         */
        int mBuildFile = 0;

        /**
         * build dependencies by package
         * 
         * @attribute
         */
        Vector<Package> mPackageDependencyCollection = new Vector<Package>();

        /**
         * used for escrow build purposes set true when a package has been processed
         * 
         * @attribute
         */
        boolean mProcessed = false;

        /**
         * set true for WIP package versions only used in daemon mode
         * 
         * @attribute
         */
        boolean mDirectlyPlanned = false;

        /**
         * set true when it is determined to be ripple built
         * 
         * @attribute
         */
        boolean mIndirectlyPlanned = false;

        /**
         * non zero instruction number when it is determined to be ripple built by
         * force
         * 
         * @attribute
         */
        int mForcedRippleInstruction = 0;

        /**
         * non zero instruction number when it is determined to be test built
         * 
         * @attribute
         */
        int mTestBuildInstruction = 0;

        /**
         * test build email destination
         * 
         * @attribute
         */
        String mTestBuildEmail;

        /**
         * Version Control System Tag
         * 
         * @attribute
         */
        String mTestBuildVcsTag = new String();

        /**
         * build standards
         * 
         * @attribute
         */
        Vector<BuildStandard> mTestBuildStandardCollection = new Vector<BuildStandard>();

        /**
         * build dependencies by package alias
         * 
         * @attribute
         */
        Vector<String> mTestBuildDependencyCollection = new Vector<String>();

        /**
         * build dependencies by pv_id (-1 or not used for planned dependencies)
         * 
         * @attribute
         */
        Vector<Integer> mDependencyIDCollection = new Vector<Integer>();

        /**
         * unique pkg_id in the database used for querying package version existence
         * in the database in daemon mode
         * 
         * @attribute
         */
        int mPid;

        /**
         * maximum major number supported for determining ripple number
         * 
         * @attribute
         */
        int mMajorLimit;

        /**
         * maximum minor number supported for determining ripple number
         * 
         * @attribute
         */
        int mMinorLimit;

        /**
         * maximum patch number supported for determining ripple number
         * 
         * @attribute
         */
        int mPatchLimit;

        /**
         * maximum build number number supported for determining ripple number
         * 
         * @attribute
         */
        int mBuildLimit;

        /**
         * Logger
         * 
         * @attribute
         */
        private static final Logger mLogger = Logger.getLogger(Package.class);

        /**
         * dpkg archive location
         * 
         * @attribute
         */
        public static final String mGbeDpkg = System.getenv("GBE_DPKG");

        /**
         * Exception message used upon detection an archive does not exist Seems
         * this is a rare but transient and recoverable scenario
         * 
         * @attribute
         */
        public static final String mRecoverable = "dpkg_archive does not exist, recovery will be attempted";

        /**
         * true if the package exists in the package archive (dpkg_archive)
         * 
         * @attribute
         */
        private boolean mArchivalExistence = true;

        /**
         * when true will trigger source control interaction eg labelling
         * 
         * @attribute
         */
        public boolean mRequiresSourceControlInteraction = true;

        /**
         * when true has been checked for circular dependency
         * 
         * @attribute
         */
        boolean mCheckedCircularDependency = false;

        /**
         * when true has circular dependency
         * 
         * @attribute
         */
        boolean mHasCircularDependency = false;

        /**
         * constructor
         */
        Package(int pv_id, String pkg_name, String v_ext, String alias, String pkg_vcs_tag, char change_type,
                        char ripple_field, String pkg_version)
        {
                mLogger.debug("Package 1: pv_id " + pv_id + " pkg_name " + pkg_name + " v_ext " + v_ext + " alias " + alias
                                + " pkg_vcs_tag " + pkg_vcs_tag + " change_type " + change_type);
                mId = pv_id;
                mName = pkg_name;
                mVersion = "0.0.0000";
                mExtension = v_ext;
                mAlias = alias;
                mVcsTag = pkg_vcs_tag;

                // Must not have any extension
                mFixedVersion = pkg_version;
                mFixedVersion = mFixedVersion.substring(0, mFixedVersion.length() - mExtension.length());

                if (change_type == 'M')
                {
                        // a ripple_field of 'L' indicates this package has limited version
                        // numbering
                        mChangeType.setMajor(ripple_field == 'L' ? true : false);
                } else if (change_type == 'N')
                {
                        mChangeType.setMinor(ripple_field == 'L' ? true : false);
                } else if (change_type == 'P')
                {
                        mChangeType.setPatch(ripple_field == 'L' ? true : false);
                } else if (change_type == 'F')
                {
                        mChangeType.setFixed();
                } else
                {
                        mChangeType.setUnknown();
                }
        }

        /**
         * constructor
         */
        Package(int pv_id, String pkg_name, String pkg_version, String v_ext, String alias, String pkg_vcs_tag,
                        char ripple_field)
        {
                mLogger.debug("Package 2: pv_id " + pv_id + " pkg_name " + pkg_name + " pkg_version " + pkg_version + " v_ext "
                                + v_ext + " alias " + alias + " pkg_vcs_tag " + pkg_vcs_tag + " ripple_field " + ripple_field);
                mId = pv_id;
                mName = pkg_name;
                mVersion = pkg_version;
                int endindex = mVersion.length() - v_ext.length();

                if (endindex > 0)
                {
                        mVersion = mVersion.substring(0, endindex);
                }

                mExtension = v_ext;
                mAlias = alias;
                mVcsTag = pkg_vcs_tag;

                // setBuild is the default
                if (ripple_field == 'M')
                {
                        mRippleField.setMajor();
                } else if (ripple_field == 'm')
                {
                        mRippleField.setMinor();
                } else if (ripple_field == 'p')
                {
                        mRippleField.setPatch();
                } else if (ripple_field == 'L')
                {
                        mRippleField.setLimit();
                }
        }

        /**
         * constructor
         */
        Package()
        {
                mLogger.debug("Package 3");
                mId = 0;
                mName = "null";
                mExtension = "null";
                mAlias = "null";
                mVcsTag = "null";
        }

        /**
         * constructor for test build purposes
         */
        Package(String pkg_name, String v_ext, String alias, String pkg_vcs_tag, int testBuildInstruction, String email)
        {
                mLogger.debug("Package 4: pkg_name " + pkg_name + " v_ext " + v_ext + " alias " + alias + " pkg_vcs_tag "
                                + pkg_vcs_tag);
                // don't need pv_id
                mId = -1;
                mName = pkg_name;
                // avoid interaction with real versions
                mVersion = "0.0.0000";
                mExtension = v_ext;
                mAlias = alias;
                mTestBuildInstruction = testBuildInstruction;
                mTestBuildEmail = email;
                mTestBuildVcsTag = pkg_vcs_tag;

        }

        /**
         * constructor for unit test purposes
         */
        public Package(ReleaseManager rm, String version, int majorLimit, int minorLimit, int patchLimit,
                        int buildNumberLimit)
        {
                mId = -1;
                mRippleField.setLimit();
                mVersion = version;
                mMajorLimit = majorLimit;
                mMinorLimit = minorLimit;
                mPatchLimit = patchLimit;
                mBuildLimit = buildNumberLimit;

                if (version.endsWith(".cots"))
                {
                        mExtension = ".cots";
                        mVersion = version.substring(0, version.length() - 5);
                        mChangeType.setMajor(false);
                        mChangeType.setMinor(false);
                        mChangeType.setPatch(true);
                        mRippleField.setBuild();
                }

                try
                {
                        mId = applyPV(rm, 0);
                } catch (Exception e)
                {
                }
        }

        /**
         * accessor for unit test purposes
         */
        public int getId()
        {
                return mId;
        }

        /**
         * accessor for unit test purposes
         */
        public String getVersion()
        {
                return mVersion;
        }

        /**
         * returns true if mBuildStandardCollection is not empty
         */
        boolean isReproducible()
        {
                mLogger.debug("isReproducible on Package " + mName);
                boolean retVal = false;

                if (mBuildStandardCollection.size() > 0)
                {
                        retVal = true;
                }

                mLogger.info("isReproducible returned " + retVal);
                return retVal;
        }

        /**
         * returns true if at least one of its BuildStandards has mWin32 or mGeneric
         * true
         */
        boolean isWin32Built()
        {
                mLogger.debug("isWin32Built on Package " + mName);
                boolean retVal = false;
                for (Iterator<BuildStandard> it = mBuildStandardCollection.iterator(); it.hasNext();)
                {
                        BuildStandard buildStandard = it.next();

                        if (buildStandard.getWin32() || buildStandard.getGeneric())
                        {
                                retVal = true;
                                break;
                        }
                }

                mLogger.info("isWin32Built returned " + retVal);
                return retVal;
        }

        /**
         * returns true if at least one of its BuildStandards has mSolaris or
         * mGeneric true
         */
        boolean isSolarisBuilt()
        {
                mLogger.debug("isSolarisBuilt on Package " + mName);
                boolean retVal = false;
                for (Iterator<BuildStandard> it = mBuildStandardCollection.iterator(); it.hasNext();)
                {
                        BuildStandard buildStandard = it.next();

                        if (buildStandard.getSolaris() || buildStandard.getGeneric())
                        {
                                retVal = true;
                                break;
                        }
                }

                mLogger.info("isSolarisBuilt returned " + retVal);
                return retVal;
        }

        /**
         * returns true if at least one of its BuildStandards has mLinux or mGeneric
         * true
         */
        boolean isLinuxBuilt()
        {
                mLogger.debug("isLinuxBuilt on Package " + mName);
                boolean retVal = false;
                for (Iterator<BuildStandard> it = mBuildStandardCollection.iterator(); it.hasNext();)
                {
                        BuildStandard buildStandard = it.next();

                        if (buildStandard.getLinux() || buildStandard.getGeneric())
                        {
                                retVal = true;
                                break;
                        }
                }

                mLogger.info("isLinuxBuilt returned " + retVal);
                return retVal;
        }

        /**
         * returns true if at least one of its BuildStandards has mGeneric true
         */
        boolean isGeneric()
        {
                mLogger.debug("isGeneric on Package " + mName);
                boolean retVal = false;
                for (Iterator<BuildStandard> it = mBuildStandardCollection.iterator(); it.hasNext();)
                {
                        BuildStandard buildStandard = it.next();

                        if (buildStandard.getGeneric())
                        {
                                retVal = true;
                                break;
                        }
                }

                mLogger.info("isGeneric returned " + retVal);
                return retVal;
        }

        /**
         * applies the required version number change returns 0 on success 1 on
         * cannot work with non standard versioning 2 on ripple field limitations
         * prevent a ripple build 3 on Invalid Change Type
         */
        int applyPV(ReleaseManager releaseManager, int rtag_id) throws Exception
        {
                mLogger.debug("applyPV on Package " + mName);
                //
                // Four scenarios, only applyPV for 3 of them
                // mDirectlyPlanned mIndirectlyPlanned mArchivalExistence mForcedRipple
                // Action
                // WIP/test build exists: true true don't care don't care applyPV
                // Package version is out of date: false true true don't care applyPV
                // Forced ripple: false true don't care > 0 applyPV
                // Package version does not exist: false true false = 0 do not applyPV
                //
                if (!mDirectlyPlanned && mIndirectlyPlanned && !mArchivalExistence && mForcedRippleInstruction == 0)
                {
                        // the package has an mIndirectlyPlanned flag set true in daemon
                        // mode because the package does not exist in an archive
                        // do not apply a different package version
                        mLogger.info("applyPV !mDirectlyPlanned && mIndirectlyPlanned && !mArchivalExistence && zero mForcedRippleInstruction on Package "
                                        + mName);
                        releaseManager.claimVersion(mPid, mVersion + mExtension, rtag_id);
                        mLogger.info("applyPv returned 0");
                        return 0;
                }

                // override - no longer doing a rebuild - version number change from
                // this point on
                if (mTestBuildInstruction == 0)
                {
                        mRequiresSourceControlInteraction = true;
                }

                //
                // Detect invalid change type
                // Flagged when package instance is created
                //
                if (mChangeType.mUnknown)
                {
                        mLogger.info("Package Vesrion specified on Package " + mName + "New Version:" + mVersion);
                        mLogger.info("applyPv returned 3");
                        return 3;
                }

                // If we are not calculating the new package version because the user
                // has fixed the version
                // of the package. We are given the new package version.
                if (mChangeType.mFixed)
                {
                        // mVersion is already setup

                        mVersion = mFixedVersion;
                        mLogger.info("Package Vesrion specified on Package " + mName + "New Version:" + mVersion);
                        releaseManager.claimVersion(mPid, mVersion + mExtension, rtag_id);
                        mLogger.info("applyPv returned 0");
                        return 0;
                }

                // We need to calculate the new version number
                //
                MutableInt major = new MutableInt();
                major.value = 0;
                MutableInt minor = new MutableInt();
                minor.value = 0;
                MutableInt patch = new MutableInt();
                patch.value = 1000;

                String field[] = mVersion.split("\\D");
                String nonStandardCotsVersion = "";

                if (field.length == 3)
                {
                        major.value = Integer.parseInt(field[0]);
                        minor.value = Integer.parseInt(field[1]);
                        patch.value = Integer.parseInt(field[2]);
                } else
                {
                        //
                        // Can ripple a .cots package under very controlled conditions
                        // Its ends with a .patchBuild field
                        // Package is marked as ripple via build number
                        // Change type of Major and Minor are not allowed
                        //
                        if (!mChangeType.mMajor && !mChangeType.mMinor && mRippleField.mBuild && mExtension.compareTo(".cots") == 0
                                        && field.length > 0)
                        {
                                // DEVI 52782
                                // allow and work with (ripple build) versions a.b.c.d....xxxx
                                // where xxxx.length > 3
                                String patchStr = field[field.length - 1];
                                int patchLen = patchStr.length();

                                // check patchStr is the last (at least 4) digits
                                if (patchLen > 3
                                                && mVersion.substring(mVersion.length() - patchLen, mVersion.length()).compareTo(patchStr) == 0)
                                {
                                        patch.value = Integer.parseInt(patchStr);
                                        nonStandardCotsVersion = mVersion.substring(0, mVersion.length() - patchLen);
                                }
                        }

                        if (nonStandardCotsVersion.length() == 0)
                        {
                                // cannot work with non standard versioning
                                mLogger.error("applyPV cannot work with non standard versioning");
                                mLogger.info("applyPv returned 1");
                                return 1;
                        }
                }

                if (nonStandardCotsVersion.length() == 0 && patch.value < 1000 && field[2].substring(0, 1).compareTo("0") != 0)
                {
                        mLogger.info("applyPV accomodate old style mVersion of the form 1.0.1");
                        patch.value = patch.value * 1000;
                }

                // mChangeType overrides mRippleField
                do
                {
                        if (mChangeType.mMajor)
                        {
                                if (!incrementFieldsAccordingToLimits(4, major, minor, patch))
                                {
                                        mLogger.info("applyPv returned 2");
                                        return 2;
                                }
                        } else if (mChangeType.mMinor)
                        {
                                if (!incrementFieldsAccordingToLimits(3, major, minor, patch))
                                {
                                        mLogger.info("applyPv returned 2");
                                        return 2;
                                }
                        } else if (mChangeType.mPatch)
                        {
                                if (!incrementFieldsAccordingToLimits(2, major, minor, patch))
                                {
                                        mLogger.info("applyPv returned 2");
                                        return 2;
                                }
                        } else
                        {
                                if (mRippleField.mMajor)
                                {
                                        major.value++;
                                        mLogger.info("applyPV mRippleField.mMajor " + major.value);
                                        minor.value = 0;
                                        patch.value = 0;
                                } else if (mRippleField.mMinor)
                                {
                                        minor.value++;
                                        mLogger.info("applyPV mRippleField.mMinor " + minor.value);
                                        patch.value = 0;
                                } else if (mRippleField.mPatch)
                                {
                                        do
                                        {
                                                patch.value++;
                                        } while ((patch.value / 1000) * 1000 != patch.value);
                                        mLogger.info("applyPV mRippleField.mPatch " + patch.value);
                                } else if (mRippleField.mBuild)
                                {
                                        patch.value++;
                                        mLogger.info("applyPV mRippleField.mBuild " + patch.value);
                                } else
                                {
                                        if (!incrementFieldsAccordingToLimits(1, major, minor, patch))
                                        {
                                                mLogger.info("applyPv returned 2");
                                                return 2;
                                        }
                                }
                        }

                        if (nonStandardCotsVersion.length() == 0)
                        {
                                mVersion = String.valueOf(major.value) + "." + String.valueOf(minor.value) + ".";
                        } else
                        {
                                mVersion = nonStandardCotsVersion;
                        }

                        if (patch.value < 10)
                        {
                                mVersion += "000";
                        } else if (patch.value < 100)
                        {
                                mVersion += "00";
                        } else if (patch.value < 1000)
                        {
                                mVersion += "0";
                        }

                        mVersion += String.valueOf(patch.value);
                } while (exists(releaseManager, rtag_id));

                releaseManager.claimVersion(mPid, mVersion + mExtension, rtag_id);
                mLogger.info("applyPv returned 0");
                return 0;
        }

        /**
         * increments fields according to mRippleField.mLimit if necessary will
         * apply it to the field passed as follows 1 = build 2 = patch 3 = minor
         * other = major returns true on success false on ripple field limitations
         * prevent a ripple build
         */
        private boolean incrementFieldsAccordingToLimits(int field, MutableInt major, MutableInt minor, MutableInt patch)
        {
                boolean retVal = true;

                if (!mChangeType.mLimit && !mRippleField.mLimit)
                {
                        // simple case
                        // no need to take field limits into consideration
                        switch (field)
                        {
                        case 1:
                                // unreachable
                                // the only scenario involving build number manipulation
                                // involves the mRippleField.mLimit being set
                                retVal = false;
                                break;
                        case 2:
                                do
                                {
                                        patch.value++;
                                } while ((patch.value / 1000) * 1000 != patch.value);
                                mLogger.info("incrementFieldsAccordingToLimits patch " + patch.value);
                                break;
                        case 3:
                                minor.value++;
                                mLogger.info("incrementFieldsAccordingToLimits minor " + minor.value);
                                patch.value = 0;
                                break;
                        default:
                                major.value++;
                                mLogger.info("incrementFieldsAccordingToLimits major " + major.value);
                                minor.value = 0;
                                patch.value = 0;
                        }
                } else
                {
                        // take field limits into consideration
                        boolean changeOccurred = false;
                        boolean incrementField = true;

                        switch (field)
                        {
                        case 1:
                                if (mBuildLimit != 0)
                                {
                                        // increment or reset the patch build number
                                        int buildNumber = patch.value - (patch.value / 1000) * 1000;

                                        if (buildNumber < mBuildLimit)
                                        {
                                                // can increment the patch build number
                                                patch.value++;
                                                mLogger.info("incrementFieldsAccordingToLimits mRippleField.mLimit build number " + patch.value);
                                                changeOccurred = true;
                                                incrementField = false;
                                        } else
                                        {
                                                if (mPatchLimit == 0)
                                                {
                                                        // reset the patch number and patch build number
                                                        patch.value = 0;
                                                }
                                        }
                                }
                                // no break by design
                        case 2:
                                if (mPatchLimit != 0 && incrementField)
                                {
                                        // increment or reset the patch number
                                        if ((patch.value / 1000) < mPatchLimit)
                                        {
                                                do
                                                {
                                                        patch.value++;
                                                } while ((patch.value / 1000) * 1000 != patch.value);

                                                mLogger.info("incrementFieldsAccordingToLimits mRippleField.mLimit patch " + patch.value);
                                                changeOccurred = true;
                                                incrementField = false;
                                        } else
                                        {
                                                // reset the patch number and patch build number
                                                patch.value = 0;
                                        }
                                }
                                // no break by design
                        case 3:
                                if (mMinorLimit != 0 && incrementField)
                                {
                                        // increment or reset the minor number
                                        if (minor.value < mMinorLimit)
                                        {
                                                minor.value++;
                                                patch.value = 0;
                                                mLogger.info("incrementFieldsAccordingToLimits mRippleField.mLimit minor " + minor.value);
                                                changeOccurred = true;
                                                incrementField = false;
                                        } else
                                        {
                                                // reset the minor number
                                                minor.value = 0;
                                        }
                                }
                                // no break by design
                        default:
                                if (mMajorLimit != 0 && incrementField)
                                {
                                        // increment or reset the major number
                                        if (major.value < mMajorLimit)
                                        {
                                                // increment the major number
                                                changeOccurred = true;
                                                major.value++;
                                                minor.value = 0;
                                                patch.value = 0;
                                                mLogger.info("incrementFieldsAccordingToLimits mRippleField.mLimit major " + major.value);
                                        }
                                }
                        }

                        if (!changeOccurred)
                        {
                                // unable to increment a field due to field limitations
                                mLogger.error("incrementFieldsAccordingToLimits ripple field limitations prevent a ripple build");
                                mLogger.info("incrementFieldsAccordingToLimits returned false");
                                retVal = false;
                        }
                }

                return retVal;
        }

        /**
         * returns true if the version exists in the dpkg_archive or release manager
         * database claims the version in the release manager database
         */
        private boolean exists(ReleaseManager releaseManager, int rtag_id) throws Exception
        {
                mLogger.debug("exists 1 on Package " + mName + " version " + mVersion + " extension " + mExtension);
                boolean retVal = false;

                if (!ReleaseManager.mUseDatabase)
                {
                        mLogger.info("exists 1 !releaseManager.mUseDatabase");
                } else
                {
                        retVal = exists();

                        if (!retVal)
                        {
                                String pkg_version = new String(mVersion);

                                if (mExtension.length() > 0)
                                {
                                        pkg_version += mExtension;
                                }

                                retVal = releaseManager.queryPackageVersions(mPid, pkg_version);
                        }
                }

                mLogger.info("exists 1 returned " + retVal);
                return retVal;
        }

        /**
         * returns true if the required package archives (dpkg_archive) exist
         * attempt to recover from their transient loss
         */
        public static boolean recover()
        {
                mLogger.debug("recover");
                boolean retVal = false;

                String Release = mGbeDpkg;
                if (Release != null)
                {
                        File dpkg = new File(mGbeDpkg);

                        if (dpkg.exists())
                        {
                                retVal = true;
                                mLogger.fatal("recover: dpkg_archive access has been restored");
                        }
                }

                mLogger.info("recover returned " + retVal);
                return retVal;
        }

        /**
         * returns true if the version exists in a package archive (dpkg_archive)
         */
        boolean exists() throws Exception
        {
                mLogger.debug("exists 2 on Package " + mName);
                boolean retVal = false;

                String Release = mGbeDpkg;
                if (Release == null)
                {
                        mLogger.fatal("exists 2 Release == null");
                        throw new Exception("exists 2 Release == null");
                }

                File dpkg = new File(mGbeDpkg);
                if (!dpkg.exists())
                {
                        mLogger.fatal("exists 2 " + mRecoverable);
                        throw new Exception(mRecoverable);
                }

                String fs = System.getProperty("file.separator");
                String name = new String(Release);
                name += fs + mName + fs + mVersion + mExtension;
                File release = new File(name);

                if (release.exists())
                {
                        mLogger.info("exists 2 release.exists()");
                        retVal = true;
                }

                mArchivalExistence = retVal;
                mLogger.info("exists 2 returned " + retVal);
                return retVal;
        }

        /**
         * returns email information
         */
        String emailInfo(String lf)
        {
                String retVal = new String();

                for (Iterator<String> it = mBuildFailureEmailCollection.iterator(); it.hasNext();)
                {
                        String email = it.next();
                        retVal += "  <owner email=\"" + email + "\"/>" + lf;
                }

                return retVal;
        }

        /**
         * returns email information
         */
        String emailInfoNonAntTask()
        {
                String retVal = null;

                for (Iterator<String> it = mBuildFailureEmailCollection.iterator(); it.hasNext();)
                {
                        String email = it.next();

                        if (retVal == null)
                        {
                                retVal = new String();
                        } else
                        {
                                retVal += ",";
                        }
                        retVal += email;
                }

                return retVal;
        }

        /**
         * adds email to mBuildFailureEmailCollection if unique
         */
        void addEmail(String email)
        {
                boolean alreadyExists = false;

                for (Iterator<String> it = mBuildFailureEmailCollection.iterator(); it.hasNext();)
                {
                        String existingEmail = it.next();

                        if (existingEmail.compareTo(email) == 0)
                        {
                                alreadyExists = true;
                                break;
                        }
                }

                if (!alreadyExists)
                {
                        mBuildFailureEmailCollection.add(email);
                }
        }

        /**
         * accessor method
         */
        void setEmail()
        {
                mBuildFailureEmailCollection.clear();
                addEmail(mTestBuildEmail);
        }

        /**
         * accessor method
         */
        void setDependencyCollection()
        {
                // does not worry about mPackageDendencyCollection by design
                mDependencyCollection.clear();

                for (Iterator<String> it = mTestBuildDependencyCollection.iterator(); it.hasNext();)
                {
                        String dependency = it.next();
                        mDependencyCollection.add(dependency);
                }
        }

        /**
         * accessor method
         */
        void setBuildStandardCollection()
        {
                mBuildStandardCollection.clear();

                for (Iterator<BuildStandard> it = mTestBuildStandardCollection.iterator(); it.hasNext();)
                {
                        BuildStandard buildStandard = it.next();
                        mBuildStandardCollection.add(buildStandard);
                }
        }

        /**
         * sends email notification and marks the instruction complete in the
         * database
         */
        public void completeTestBuild(String mailServer, String mailSender, ReleaseManager releaseManager, String release,
                        boolean success) throws SQLException, Exception
        {
                mLogger.debug("completeTestBuild");

                if (mTestBuildInstruction == 0)
                {
                        mLogger.fatal("completeTestBuild. Not Build Instruction");
                        return;
                }
                String subject = (success == true ? "TEST BUILD COMPLETED SUCCESSFULLY" : "TEST BUILD FAILED") + " on package "
                                + mAlias;
                String mailBody = "Test build issues are identified in preceding build failure email.<p>" + "Release: "
                                + release + "<br>" + "Package: " + mAlias + "<br>" + "VcsTag: " + mVcsTag + "<br>"
                                + "Build dependencies:<br>";
                String indentString = "&nbsp;&nbsp;&nbsp;&nbsp;";

                for (Iterator<Package> it3 = mPackageDependencyCollection.iterator(); it3.hasNext();)
                {
                        Package depend = it3.next();

                        String dependsExtension = depend.mExtension;
                        String dependsVersion = depend.mVersion;

                        if (dependsExtension.length() > 0)
                        {
                                dependsVersion += dependsExtension;
                        }
                        mailBody += indentString + "\'" + depend.mName + "\',\'" + dependsVersion + "\' <br>";
                }

                mailBody += "<br>Build standards:<br>";

                for (Iterator<BuildStandard> it = mBuildStandardCollection.iterator(); it.hasNext();)
                {
                        BuildStandard bs = it.next();

                        String platform = bs.getPlatform(!ReleaseManager.mUseDatabase, false);

                        if (platform.length() > 0)
                        {
                                mailBody += indentString + platform + ", ";
                        }

                        String standard = bs.getBuildStandard(!ReleaseManager.mUseDatabase, false);

                        if (standard.length() > 0)
                        {
                                mailBody += standard + "<br>";
                        }
                }

                mailBody += "<p><hr>";
                try
                {
                        String target = emailInfoNonAntTask();
                        mLogger.fatal("completeTestBuildEmail Server: " + mailServer);
                        mLogger.fatal("completeTestBuildEmail Sender: " + mailSender);
                        mLogger.fatal("completeTestBuildEmail Target: " + target);

                        Smtpsend.send(mailServer,       // mailServer
                                        mailSender,             // source
                                        target,                         // target
                                        mailSender,             // cc
                                        null,                           // bcc
                                        subject,                        // subject
                                        mailBody,                       // body
                                        null                            // attachment
                        );
                } catch (Exception e)
                {
                        mLogger.warn("Email Failure: completeTestBuild:" + e.getMessage());
                }

                releaseManager.markDaemonInstCompletedConnect(mTestBuildInstruction);
                mLogger.fatal("completeTest. Returning");
        }

        /**
         * returns true if the package has a circular dependency
         */
        public boolean hasCircularDependency(RippleEngine ripEng)
        {
                mLogger.debug("hasCircularDependency");
                boolean retVal = detectCircularDependency(mAlias, ripEng);
                mLogger.info("hasCircularDependency returned " + retVal);
                return retVal;
        }

        /**
         * returns true if the package has a circular dependency
         */
        public boolean detectCircularDependency(String alias, RippleEngine ripEng)
        {
                mLogger.debug("detectCircularDependency");
                boolean retVal = false;

                // if this package has yet to be checked for circular dependency
                if (!mCheckedCircularDependency)
                {
                        for (Iterator<String> it2 = mDependencyCollection.iterator(); it2.hasNext();)
                        {
                                String dependencyAlias = it2.next();

                                // check its direct dependencies for an alias match
                                if (alias.compareTo(dependencyAlias) == 0)
                                {
                                        retVal = true;
                                        break;
                                }

                                Package dependency = ripEng.findPackage(dependencyAlias);

                                // check its indirect dependencies for an alias match
                                if (dependency.detectCircularDependency(alias, ripEng))
                                {
                                        retVal = true;
                                        break;
                                }
                        }
                        // mark the check complete
                        mCheckedCircularDependency = true;
                        // persist the circular dependency outcome
                        mHasCircularDependency = retVal;
                } else
                {
                        // return the persisted circular dependency outcome
                        retVal = mHasCircularDependency;
                }
                mLogger.info("detectCircularDependency 2 returned " + retVal);
                return retVal;
        }

        /**
         * entity class supporting the ERG version numbering standard:
         * <major>.<minor>.<patch/build> patch/build is at least a 4 digit number
         * whose last 3 digits represent the build
         */
        public class VersionNumberingStandard
        {
                /**
                 * in terms of the mChangeType Package field, when true indicates the
                 * contract of the package has changed in a non backwardly compatible
                 * manner in terms of the mRippleField Package field, when true indicates
                 * the major version number will be incremented
                 * 
                 * @attribute
                 */
                private boolean mMajor = false;

                /**
                 * in terms of the mChangeType Package field, when true indicates the
                 * contract of the package has changed in a backwardly compatible manner
                 * in terms of the mRippleField Package field, when true indicates the
                 * minor version number will be incremented
                 * 
                 * @attribute
                 */
                private boolean mMinor = false;

                /**
                 * in terms of the mChangeType Package field, when true indicates the
                 * contract of the package has not changed, but the package has changed
                 * internally in terms of the mRippleField Package field, when true
                 * indicates the minor version number will be incremented
                 * 
                 * @attribute
                 */
                private boolean mPatch = false;

                /**
                 * in terms of the mChangeType Package field, when true indicates the
                 * package has not changed, its dependencies potentially have in terms
                 * of the mRippleField Package field, when true indicates the build
                 * number will be incremented
                 * 
                 * @attribute
                 */
                private boolean mBuild = true;

                /**
                 * in terms of the mChangeType Package field, when true indicates the
                 * major, minor, and patch number will be incremented according to field
                 * limits in terms of the mRippleField Package field, when true indicates
                 * the major, minor, patch and build number will be incremented
                 * according to field limits
                 * 
                 * @attribute
                 */
                private boolean mLimit = false;

                /**
                 * in terms of the mChangeType Package field, when true indicates the
                 * package version number will not be rippled. The user will have fixed
                 * the version number. This is only application to WIP packages
                 * 
                 * @attribute
                 */
                private boolean mFixed = false;

                /**
                 * in terms of the mChangeType Package field, when true indicates the
                 * method of rippling a package version number is not known.
                 * 
                 * @attribute
                 */
                private boolean mUnknown = false;

                /**
                 * constructor
                 */
                private VersionNumberingStandard()
                {
                        mLogger.debug("VersionNumberingStandard");
                }

                /**
                 * Reset all values to a known state
                 * 
                 */
                void resetData()
                {
                        mBuild = false;
                        mMajor = false;
                        mMinor = false;
                        mPatch = false;
                        mLimit = false;
                        mFixed = false;
                        mUnknown = false;
                }

                /**
                 * sets mBuild true, mMajor false, mMinor false, mPatch false, mLimit
                 * false
                 */
                void setBuild()
                {
                        mLogger.debug("setBuild");
                        resetData();
                        mBuild = true;
                }

                /**
                 * sets mBuild false, mMajor true, mMinor false, mPatch false, mLimit
                 * false
                 */
                void setMajor()
                {
                        mLogger.debug("setMajor");
                        resetData();
                        mMajor = true;
                }

                /**
                 * sets mBuild false, mMajor true, mMinor false, mPatch false, mLimit
                 * limit
                 */
                void setMajor(boolean limit)
                {
                        mLogger.debug("setMajor " + limit);
                        resetData();
                        mMajor = true;
                        mLimit = limit;
                }

                /**
                 * sets mBuild false, mMajor false, mMinor true, mPatch false, mLimit
                 * false
                 */
                void setMinor()
                {
                        mLogger.debug("setMinor");
                        resetData();
                        mMinor = true;
                }

                /**
                 * sets mBuild false, mMajor false, mMinor true, mPatch false, mLimit
                 * limit
                 */
                void setMinor(boolean limit)
                {
                        mLogger.debug("setMinor " + limit);
                        resetData();
                        mMinor = true;
                        mLimit = limit;
                }

                /**
                 * sets mBuild false, mMajor false, mMinor false, mPatch true, mLimit
                 * false
                 */
                void setPatch()
                {
                        mLogger.debug("setPatch");
                        resetData();
                        mPatch = true;
                }

                /**
                 * sets mBuild false, mMajor false, mMinor false, mPatch true, mLimit
                 * limit
                 */
                void setPatch(boolean limit)
                {
                        mLogger.debug("setPatch");
                        resetData();
                        mPatch = true;
                        mLimit = limit;
                }

                /**
                 * sets mBuild false, mMajor false, mMinor false, mPatch false, mLimit
                 * true
                 */
                void setLimit()
                {
                        mLogger.debug("setPatch");
                        resetData();
                        mLimit = true;
                }

                /**
                 * sets parameters to indicate that the change type is Fixed. The
                 * version number is set by the user and a ripple will not be calculated
                 */
                void setFixed()
                {
                        mLogger.debug("setFixed");
                        resetData();
                        mFixed = true;
                }

                /**
                 * Sets parameters to indicate that the change type is not known
                 * 
                 */
                void setUnknown()
                {
                        resetData();
                        mUnknown = true;
                }

        }

}