Rev 7186 | Blame | Compare with Previous | Last modification | View Log | RSS feed
package com.erggroup.buildtool.ripple;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import com.erggroup.buildtool.smtp.CreateUrls;import com.erggroup.buildtool.smtp.Smtpsend;import java.sql.SQLException;import java.util.ArrayList;import java.util.Iterator;/**entity class holding build exclusion data** There are three types of exclusion* Package Errors. Errors detected during planning. They may go away on the next plan* These are 'hard' errors* Have a rootCase string* Have a null rootId** Build Errors* These are persistent errors* Have a null rootCause* Have a null rootId* Have a non-null root_file** Indirect errors* These are packages that depend on excluded packages* Will have a non-null rootId* Will be recalculated on each planning cycle**/public class BuildExclusion{/**Logger* @attribute*/private static final Logger mLogger = LoggerFactory.getLogger(BuildExclusion.class);/**The pvid of the package being excluded*/public int mId;/**root identifier is the PVID of the package that is causing the exclusion* A NULL (-1) value indicates that this entry(package) is the root cause of the of an exclusion* A Value of -2 indicates that cause is a RippleStop*/private int mRootId;/**root cause* If not NULL, then this is the reason this package is being excluded*/private String mRootCause;/**test build instruction* Associated package is a test build*/private int mTestBuildInstruction;/** Indicates that the error associated with this package has been seen again* Set if we discover 'Package Error'** Used to determine if a 'Package Error' should be removed**/private boolean mProcessed = false;/** Indicates that the entry was imported from the database* Used to determine entries that are no-longer needed and can be removed from the database* Really only applied to indirect exclusions where the reason for the exclusion no longer exists**/private boolean mImported = false;/**constructor* @param identifier - pvid of the package being excluded* @param rootIdentifier - pvid of the root package causing the exclusion. May be -1* @param rootCause - Short (< 50 Char) string explaining the cause* @param testBuildInstruction - Indicates a text build instruction*/public BuildExclusion(int identifier, int rootIdentifier, String rootCause, int testBuildInstruction ){mLogger.debug("BuildExclusion");mId = identifier;mRootId = dealWithNullRootPvId(identifier, rootIdentifier);mRootCause = rootCause;mTestBuildInstruction = testBuildInstruction;}/**sets mProcessed true*/void setProcessed(){mLogger.debug("process {}", mId);mProcessed = true;}/** @returns true if the item has been marked as processed.* Appears to be an indication that the entry has been superseded.*/boolean isProcessed(){mLogger.debug("isProcessed returned {}", mProcessed);return mProcessed;}/** Flag as an imported entry* Used to detect indirect exclusions that are no longer valid*/void setImported(){mImported = true;}/** Test the state of the imported flag**/boolean isImported(){return mImported;}/*** Determine the entry type* Have three types - see class comments above*/boolean isAPackageError(){return (mRootCause != null && mRootId < 0);}boolean isABuildError(){return (mRootCause == null && mRootId < 0);}boolean isAIndirectError(){return (mRootId >= 0);}/*** @return true if this item is the root case, and is not simply a by product of some other cause.* This is the same as !isAIndirectError()*/boolean isARootCause(){return (mRootId < 0);}/*** Compare this item with parameters* @param identifier - Related Package identifier* @param rootIdentifier - Package identifier of root cause* @param rootCause - Root cause string. May be null* @return true if all attributes match*/boolean compare( int identifier, int rootIdentifier, String rootCause){mLogger.debug("compare {}, {}, {}, {}", mId, identifier, rootIdentifier, rootCause);boolean retVal = false;rootIdentifier = dealWithNullRootPvId(identifier, rootIdentifier);if ( mRootCause == null ){if ( mId == identifier && mRootId == rootIdentifier && rootCause == null ){retVal = true;}}else{if ( mId == identifier && mRootId == rootIdentifier && mRootCause.compareTo(rootCause) == 0 ){retVal = true;}}mLogger.debug("compare returned {}", retVal);return retVal;}/** Does this entry relate to the package with a specified mId** @param identifier - Package Identifier (mId) to match* @return true if mId attribute matches*/boolean compare( int identifier ){mLogger.debug("compare {},{}", mId, identifier);boolean retVal = false;if ( mId == identifier ){retVal = true;}mLogger.debug("compare returned {}", retVal);return retVal;}/**runs exclude from build* Assumes that a connection to RM has been established** @param rm Release Manager instance* @param rtagId Rtag Id we are working against*/void excludeFromBuild( ReleaseManager rm, int rtagId ) throws SQLException, Exception{mLogger.debug("excludeFromBuild {}", mId);// a null version and log file is passed to oracle// the planned version is only needed to remove a planned version from the planned version table// the ripple engine does not get this far ie it excludes pvs before claiming a version// this is the one instance where an existing build failure must be superseded in the databaserm.excludeFromBuild(true,mId,null,rtagId,mRootId == -1 ? null : String.valueOf(mRootId),mRootCause,null,true, (mTestBuildInstruction > 0) );}/**runs include to build* Include a previously excluded package-version back into the build set** @param rm Release Manager instance* @param rtagId Rtag Id we are working against*/void includeToBuild( ReleaseManager rm, int rtagId ) throws SQLException, Exception{mLogger.debug("includeToBuild {}", mId);rm.includeToBuild(mId, rtagId);}/*** Match this items mRootId against the id's provided in a collection* ie: Determine if any items in the collection are the root cause of this items* ie: Used to determine if an entry is for an indirectly excluded package where* the root cause of the exclusion has been removed.** @param buildExclusionCollection - Collection to be processed* @return false: Indirectly excluded package whose root cause no longer exists*/boolean isRelevant(ArrayList<BuildExclusion> buildExclusionCollection){mLogger.debug("isRelevant {}", mId);boolean retVal = false;if ( mRootId == -1 ) {// This is a Build or Package ErrorretVal = true;} else if(mRootId == -2 ) {// Excluded due to Ripple Stop// Will be recalculated so its not relevantretVal = false;} else {//// Must be an indirect exclusion// Scan to see if the rootCause is still present. It may have been removed by the userretVal = false;for (Iterator<BuildExclusion> it = buildExclusionCollection.iterator(); it.hasNext(); ){BuildExclusion be = it.next();if ( be.isARootCause() && be.mRootId == mRootId ){retVal = true;break;}}}mLogger.info("isRelevant {} returned {}", mId, retVal);return retVal;}/*** Send an email notifying users about a build excluded package* It is user friendly, in that it does not trigger a storm of emails because a low level package* has a build issue. It limits the emails to the low level package** i.e. only send email if the build exclusion has a null root pv id* and a non null root cause** @param rippleEngine - Ripple Engine Instance* @param packageCollection - Collection to process*/public void email(RippleEngine rippleEngine, PackageCollection packageCollection) throws SQLException, Exception{mLogger.debug("email {}", mId);//// Only process entries that are direct failures of ripple engine detected failure// Do not process entries that are indirectly excluded as this will cause an email storm// Direct build failure:// Have no RootId and have a rootCause// Special handling for rippleStop - do not send emails//if ( isAPackageError() && mRootId != -2 ){// Locate the associated package entryPackage pkg= packageCollection.contains(mId);if ( pkg != null ){// Generate a nice subject lineString subject;if (pkg.mTestBuildInstruction > 0) {subject = "TEST BUILD FAILED on package " + pkg.mAlias;} else {subject = "BUILD FAILURE on package " + pkg.mAlias;}// Is there anyone to send an email toString owners = pkg.emailInfoNonAntTask(rippleEngine);if ( owners != null ){String body ="Release: " + rippleEngine.mBaselineName + "<p>" +"Package: " + pkg.mName + "<p>" +"Cause: " + mRootCause + "<p>"+"RmRef: " + CreateUrls.generateRmUrl(rippleEngine.getRtagId(), pkg.mId) +"<p>";try{Smtpsend.send(rippleEngine.getMailServer(), // mailServerrippleEngine.getMailSender(), // sourceowners, // targetnull, // ccnull, // bccsubject, // subjectbody, // bodynull // attachment);}catch( Exception e ){mLogger.info("email send exception. {}", e);}}// Handle test builds hereif (pkg.mTestBuildInstruction > 0 ){// Having sent the build failure email, complete a test build if applicable.// This ensures the test build instruction is not processed indefinitely// as there is no notion of excluding test builds// Update the Release Manager DatabaserippleEngine.mReleaseManager.markDaemonInstCompleted(pkg.mTestBuildInstruction);}}}}/*** Hides how a rootPvId is treated* Only use rootPvId if not equal to the pvid** If the provided rootPvId matches the pvId, then the rootPvId will be set to null (-1)* This is to drive a direct build exclusion in the release manager** @param pvid - id* @param rootPvId - rootPvid** @returns rootPvId unless pvId matches= rootPvId, when it returns a null (-1)*/private int dealWithNullRootPvId( int pvId, int rootPvId ){int retVal = rootPvId;if ( pvId == rootPvId ){retVal = -1; // -1 == null entry}return retVal;}/*** Generate a single text line of info* Used within the UTF to display diagnostic info* @return String form of the state of the entry*/public String toString(){String rv = "";rv += "pvid=" + mId + ",RootId=" + mRootId + ",Processed=" + mProcessed + ",TestBuild=" + mTestBuildInstruction + ",RootCause=" + mRootCause;if ( mImported ) {rv += ",Imported=" + mImported;}return rv;}}