Subversion Repositories DevTools

Rev

Rev 7155 | Rev 7176 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
6914 dpurdie 1
package com.erggroup.buildtool.ripple;
2
 
7033 dpurdie 3
import org.slf4j.Logger;
4
import org.slf4j.LoggerFactory;
6914 dpurdie 5
 
6
import com.erggroup.buildtool.smtp.CreateUrls;
7
import com.erggroup.buildtool.smtp.Smtpsend;
8
 
9
import java.sql.SQLException;
10
import java.util.ArrayList;
11
import java.util.Iterator;
12
 
13
/**entity class holding build exclusion data
7082 dpurdie 14
 * 
15
 * There are three types of exclusion
7164 dpurdie 16
 * Package Errors. Errors detected during planning. They may go away on the next plan
7082 dpurdie 17
 *      These are 'hard' errors
18
 *          Have a rootCase string
19
 *          Have a null rootId
20
 *          
21
 * Build Errors
22
 *      These are persistent errors
23
 *          Have a null rootCause
24
 *          Have a null rootId
25
 *          Have a non-null root_file
26
 *          
27
 *  Indirect errors
28
 *      These are packages that depend on excluded packages
7164 dpurdie 29
 *          Will have a non-null rootId
30
 *          Will be recalculated on each planning cycle          
7082 dpurdie 31
 * 
6914 dpurdie 32
 */
33
public class BuildExclusion
34
{
35
  /**Logger
36
   * @attribute
37
   */
7033 dpurdie 38
  private static final Logger mLogger = LoggerFactory.getLogger(BuildExclusion.class);
6914 dpurdie 39
 
40
  /**The pvid of the package being excluded
41
   */
7082 dpurdie 42
  public int mId;
6914 dpurdie 43
 
44
  /**root identifier is the PVID of the package that is causing the exclusion
7082 dpurdie 45
   * A NULL (-1) value indicates that this entry(package) is the root cause of the of an exclusion
46
   * A Value of -2 indicates that cause is a RippleStop
6914 dpurdie 47
   */
48
  private int mRootId;
49
 
50
  /**root cause
51
   * If not NULL, then this is the reason this package is being excluded
52
   */
53
  private String mRootCause;
54
 
55
  /**test build instruction
7082 dpurdie 56
   * Associated package is a test build
6914 dpurdie 57
   */
58
  private int mTestBuildInstruction;
59
 
7082 dpurdie 60
  /** Indicates that the error associated with this package has been seen again
61
   *  Set if we discover 'Package Error'
62
   *  
63
   *  Used to determine if a 'Package Error' should be removed
64
   *    
6914 dpurdie 65
   */
66
  private boolean mProcessed = false;
7032 dpurdie 67
 
68
  /** Indicates that the entry was imported from the database
69
   *  Used to determine entries that are no-longer needed and can be removed from the database
70
   *  Really only applied to indirect exclusions where the reason for the exclusion no longer exists
71
   *  
72
   */
73
  private boolean mImported = false;
6914 dpurdie 74
 
75
  /**constructor
7032 dpurdie 76
   *    @param  identifier - pvid of the package being excluded
77
   *    @param  rootIdentifier  - pvid of the root package causing the exclusion. May be null
78
   *    @param  rootCause - Short (< 50 Char) string explaining the cause
79
   *    @param  testBuildInstruction - Indicates a text build instruction
6914 dpurdie 80
   */
81
  public BuildExclusion(int identifier, int rootIdentifier, String rootCause, int testBuildInstruction )
82
  {
83
    mLogger.debug("BuildExclusion");
84
    mId = identifier;
85
    mRootId = dealWithNullRootPvId(identifier, rootIdentifier);
86
    mRootCause = rootCause;
87
    mTestBuildInstruction = testBuildInstruction;
88
  }
89
 
90
  /**sets mProcessed true
91
   */
7032 dpurdie 92
  void setProcessed()
6914 dpurdie 93
  {
7033 dpurdie 94
    mLogger.debug("process {}", mId);
6914 dpurdie 95
    mProcessed = true;
96
  }
97
 
98
  /** @returns true if the item has been marked as processed. 
7032 dpurdie 99
   * Appears to be an indication that the entry has been superseded.
6914 dpurdie 100
   */
101
  boolean isProcessed()
102
  {
7082 dpurdie 103
    mLogger.debug("isProcessed returned {}", mProcessed);
6914 dpurdie 104
    return mProcessed;
105
  }
7032 dpurdie 106
 
107
  /** Flag as an imported entry
108
   *  Used to detect indirect exclusions that are no longer valid
109
   */
110
  void setImported()
111
  {
112
      mImported = true;
113
  }
6914 dpurdie 114
 
7032 dpurdie 115
  /** Test the state of the imported flag
116
   * 
117
   */
118
  boolean isImported()
119
  {
120
      return mImported;
121
  }
122
 
6914 dpurdie 123
  /**
7082 dpurdie 124
   * Determine the entry type
125
   * Have three types - see class comments above
126
   */
127
  boolean isAPackageError()
128
  {
129
      return (mRootCause != null && mRootId < 0);
130
  }
131
 
132
  boolean isABuildError()
133
  {
134
      return (mRootCause == null && mRootId < 0);
135
  }
136
 
137
  boolean isAIndirectError()
138
  {
139
      return (mRootId >= 0);
140
  }
141
 
142
  /**
143
   * @return true if this item is the root case, and is not simply a by product of some other cause.
144
   * This is the same as !isAIndirectError() 
145
   */
146
  boolean isARootCause()
147
  {
148
    boolean retVal = false;
149
 
150
    if ( mRootId == -1 )
151
    {
152
      retVal = true;
153
    }
154
 
155
    mLogger.info("isARootCause {} returned {}",mId, retVal);
156
    return retVal;
157
  }
158
 
159
  /**
6914 dpurdie 160
   * Compare this item with parameters
161
   * @param identifier          - Related Package identifier
162
   * @param rootIdentifier      - Package identifier of root cause
163
   * @param  rootCause          - Root cause string. May be null
164
   * @return true if all attributes match
165
   */
166
  boolean compare( int identifier, int rootIdentifier, String rootCause)
167
  {
7033 dpurdie 168
    mLogger.debug("compare {}, {}, {}, {}", mId, identifier, rootIdentifier,  rootCause);
6914 dpurdie 169
    boolean retVal = false;
170
    rootIdentifier = dealWithNullRootPvId(identifier, rootIdentifier);
171
 
172
    if ( mRootCause == null )
173
    {
174
      if ( mId == identifier && mRootId == rootIdentifier && rootCause == null )
175
      {
176
        retVal = true;
177
      }
178
    }
179
    else
180
    {
181
      if ( mId == identifier && mRootId == rootIdentifier && mRootCause.compareTo(rootCause) == 0 )
182
      {
183
        retVal = true;
184
      }
185
    }
186
 
7033 dpurdie 187
    mLogger.info("compare returned {}", retVal);
6914 dpurdie 188
    return retVal;
189
  }
190
 
7082 dpurdie 191
  /**   Does this entry relate to the package with a specified mId
192
   * 
193
   * @param identifier  - Package Identifier (mId) to match
6914 dpurdie 194
   * @return true if mId attribute matches
195
   */
196
  boolean compare( int identifier )
197
  {
7033 dpurdie 198
    mLogger.debug("compare {},{}", mId,  identifier);
6914 dpurdie 199
    boolean retVal = false;
200
 
201
    if ( mId == identifier )
202
    {
203
      retVal = true;
204
    }
205
 
7033 dpurdie 206
    mLogger.info("compare returned {}", retVal);
6914 dpurdie 207
    return retVal;
208
  }
209
 
210
  /**runs exclude from build
211
   * Assumes that a connection to RM has been established
212
   * 
213
   * @param rm          Release Manager instance
7033 dpurdie 214
   * @param rtagId      Rtag Id we are working against
6914 dpurdie 215
   */
7033 dpurdie 216
  void excludeFromBuild( ReleaseManager rm, int rtagId ) throws SQLException, Exception
6914 dpurdie 217
  {
7033 dpurdie 218
    mLogger.debug("excludeFromBuild {}", mId);
6914 dpurdie 219
 
220
    // a null version and log file is passed to oracle
221
    // the planned version is only needed to remove a planned version from the planned version table
222
    // the ripple engine does not get this far ie it excludes pvs before claiming a version
223
    // this is the one instance where an existing build failure must be superseded in the database
224
    rm.excludeFromBuild(true, 
225
                        mId, 
226
                        null, 
7033 dpurdie 227
                        rtagId, 
6914 dpurdie 228
    		            mRootId == -1 ? null : String.valueOf(mRootId), 
229
    		            mRootCause, 
230
    		            null, 
231
    		            true, (mTestBuildInstruction > 0) );
232
  }
233
 
234
  /**runs include to build
235
   * Include a previously excluded package-version back into the build set
236
   * 
237
   * @param rm          Release Manager instance
7033 dpurdie 238
   * @param rtagId      Rtag Id we are working against
6914 dpurdie 239
   */
7033 dpurdie 240
  void includeToBuild( ReleaseManager rm, int rtagId ) throws SQLException, Exception
6914 dpurdie 241
  {
7033 dpurdie 242
    mLogger.debug("includeToBuild {}", mId);
243
    rm.includeToBuild(mId, rtagId);
6914 dpurdie 244
  }
245
 
246
  /**
247
   * Match this items mRootId against the id's provided in a collection
248
   * ie: Determine if any items in the collection are the root cause of this items
249
   * ie: Used to determine if an entry is for an indirectly excluded package where
250
   *     the root cause of the exclusion has been removed.
251
   *     
252
   * @param buildExclusionCollection - Collection to be processed
253
   * @return false: Indirectly excluded package whose root cause no longer exists
254
   */
255
  boolean isRelevant(ArrayList<BuildExclusion> buildExclusionCollection)
256
  {
7033 dpurdie 257
    mLogger.debug("isRelevant {}", mId);
6914 dpurdie 258
    boolean retVal = false;
259
 
260
    if ( mRootId == -1 )     {
7082 dpurdie 261
        // This is a Build or Package Error
262
        retVal = true;
6914 dpurdie 263
 
264
    } else if(mRootId == -2 ) {
265
        // Excluded due to Ripple Stop
266
        // Will be recalculated so its not relevant
267
        retVal = false;
268
 
269
    } else {
7032 dpurdie 270
 
7082 dpurdie 271
        //
272
        //  Must be an indirect exclusion
273
        //  Scan to see if the rootCause is still present. It may have been removed by the user
274
 
7032 dpurdie 275
      retVal = false;
6914 dpurdie 276
      for (Iterator<BuildExclusion> it = buildExclusionCollection.iterator(); it.hasNext(); )
277
      {
278
        BuildExclusion buildExclusion = it.next();
279
 
7082 dpurdie 280
        if ( buildExclusion.mRootId == mRootId )
6914 dpurdie 281
        {
282
          retVal = true;
283
          break;
284
        }
285
      }
286
    }
287
 
7033 dpurdie 288
    mLogger.info("isRelevant {} returned {}", mId, retVal);
6914 dpurdie 289
    return retVal;
290
  }
291
 
7082 dpurdie 292
 
6914 dpurdie 293
 
294
  /**
295
   * Send an email notifying users about a build excluded package
296
   * It is user friendly, in that it does not trigger a storm of emails because a low level package 
297
   * has a build issue. It limits the emails to the low level package
298
   * 
299
   * i.e. only send email if the build exclusion has a null root pv id
300
   * and a non null root cause
301
   * 
302
   * @param    rippleEngine        - Ripple Engine Instance
303
   * @param    packageCollection   - Collection to process
304
   */
305
    public void email(RippleEngine rippleEngine, ArrayList<Package> packageCollection) throws SQLException, Exception
306
    {
7033 dpurdie 307
      mLogger.debug("email {}", mId);
6914 dpurdie 308
 
309
      //
310
      //    Only process entries that are direct failures of ripple engine detected failure
311
      //    Do not process entries that are indirectly excluded as this will cause an email storm
312
      //    Direct build failure: 
313
      //        Have no RootId and have a rootCause
7164 dpurdie 314
      //        Special handling for rippleStop - do not send emails
6914 dpurdie 315
      //
7164 dpurdie 316
      if ( isAPackageError() && mRootId != -2 )
6914 dpurdie 317
      {
318
        //  Locate the associated package entry
319
        Package pkg= rippleEngine.mReleaseManager.findPackage(mId, packageCollection);
320
 
321
        if ( pkg != ReleaseManager.NULL_PACKAGE )
322
        {
7155 dpurdie 323
            // Generate a nice subject line
324
            String subject;
325
            if (pkg.mTestBuildInstruction > 0) {
326
                subject = "TEST BUILD FAILED on package " + pkg.mAlias;
327
            } else {
328
                subject = "BUILD FAILURE on package " + pkg.mAlias;
329
            }
330
 
6914 dpurdie 331
          // Is there anyone to send an email to
332
          String owners = pkg.emailInfoNonAntTask(rippleEngine);
333
 
334
          if ( owners != null )
335
          {
336
 
337
            String body =
338
            "Release: " + rippleEngine.mBaselineName + "<p>" +
339
            "Package: " + pkg.mName + "<p>" + 
340
            "Cause: "   + mRootCause + "<p>"+
341
            "RmRef: "   + CreateUrls.generateRmUrl(rippleEngine.getRtagId(), pkg.mId) +"<p>";
342
 
343
            try
344
            {
345
              Smtpsend.send(
7032 dpurdie 346
              rippleEngine.getMailServer(),             // mailServer
347
              rippleEngine.getMailSender(),             // source
6914 dpurdie 348
              owners,                                   // target
349
              null,                                     // cc
350
              null,                                     // bcc
7155 dpurdie 351
              subject,                                  // subject
6914 dpurdie 352
              body,                                     // body
353
              null                                      // attachment
354
              );
355
            }
356
            catch( Exception e )
357
            {
7033 dpurdie 358
                mLogger.info("email send exception. {}", e);
6914 dpurdie 359
            }
360
          }
361
 
7155 dpurdie 362
          //    Handle test builds here
363
          if (pkg.mTestBuildInstruction > 0 )
364
          {
365
 
6914 dpurdie 366
          // Having sent the build failure email, complete a test build if applicable.
367
          // This ensures the test build instruction is not processed indefinitely
368
          // as there is no notion of excluding test builds
7155 dpurdie 369
 
370
          // Update the Release Manager Database
371
          rippleEngine.mReleaseManager.markDaemonInstCompleted(pkg.mTestBuildInstruction);
372
 
373
          }
6914 dpurdie 374
        }
375
      }
376
    }
377
 
378
    /**
379
     * Hides how a rootPvId is treated
380
     * Only use rootPvId if not equal to the pvid
381
     * 
382
     * If the provided rootPvId matches the pvId, then the rootPvId will be set to null (-1)
383
     * This is to drive a direct build exclusion in the release manager
384
     * 
385
     * @param   pvid        - id
386
     * @param   rootPvId    - rootPvid
387
     * 
388
     * @returns rootPvId unless pvId matches= rootPvId, when it returns a null (-1)
389
    */
390
    private int dealWithNullRootPvId( int pvId, int rootPvId )
391
    {
392
      int retVal = rootPvId;
393
 
394
      if ( pvId == rootPvId )
395
      {
396
        retVal = -1;    // -1 == null entry
397
      }
398
 
399
      return retVal;
400
    }
401
 
402
    /**
403
     * Generate a single text line of info
7082 dpurdie 404
     * Used within the UTF to display diagnostic info
6914 dpurdie 405
     * @return String form of the state of the entry
406
     */
7082 dpurdie 407
    public String toString()
6914 dpurdie 408
    {
409
        String rv = "";
410
        rv += "pvid=" + mId + ",RootId=" + mRootId + ",Processed=" + mProcessed + ",TestBuild=" + mTestBuildInstruction + ",RootCause=" + mRootCause;
7032 dpurdie 411
        if ( mImported ) {
412
            rv += ",Imported=" + mImported;
413
        }
6914 dpurdie 414
        return rv;
415
    }
416
}