Subversion Repositories DevTools

Rev

Rev 7071 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
6914 dpurdie 1
package com.erggroup.buildtool.ripple;
2
 
3
import java.io.File;
4
import java.sql.SQLException;
5
import java.util.ArrayList;
7048 dpurdie 6
import java.util.Comparator;
6914 dpurdie 7
import java.util.HashMap;
8
import java.util.Iterator;
9
import java.util.LinkedHashSet;
10
import java.util.Vector;
11
 
7033 dpurdie 12
import org.slf4j.Logger;
13
import org.slf4j.LoggerFactory;
6914 dpurdie 14
 
15
import com.erggroup.buildtool.ripple.ReleaseManager.BuildReason;
16
import com.erggroup.buildtool.smtp.CreateUrls;
17
import com.erggroup.buildtool.smtp.Smtpsend;
18
import com.erggroup.buildtool.utilities.MutableInt;
19
import com.erggroup.buildtool.utilities.XmlBuilder;
20
import com.erggroup.buildtool.utilities.utilities;
21
 
22
public class Package
23
{
24
    /**
25
     * name of package, must not contain spaces
26
     * 
27
     * @attribute
28
     */
29
    String mName = new String();
30
 
31
    /**
32
     * package scope
33
     * 
34
     * @attribute
35
     */
36
    String mExtension = new String();
37
 
38
    /**
39
     * instance identifier
40
     * 
41
     * @attribute
42
     */
43
    public String mVersion = new String();
7048 dpurdie 44
 
45
    /**
46
     * If this package is a candidate for building, then this value will be calculated
47
     * and will  be used if the package is selected for building
48
     */
49
    public String mNextVersion = null;
6914 dpurdie 50
 
51
    /**
52
     * Version string as specified by the user. Used with a ripple type of 'F'
53
     */
54
    String mFixedVersion = new String();
55
 
56
    /**
57
     * unique identifier 
58
     *      daemon builds = mName + mExtension
59
     *      escrow builds = mName + mVersion + mExtension
60
     * 
61
     * @attribute
62
     */
63
    public String mAlias = new String();
64
 
65
    /**
66
     * Version Control System Tag
67
     * 
68
     * @attribute
69
     */
70
    String mVcsTag = new String();
71
 
72
    /**
73
     * build standards
74
     * 
75
     * @attribute
76
     */
77
    public Vector<BuildStandard> mBuildStandardCollection = new Vector<BuildStandard>();
78
 
79
    /**
80
     * GBE_MACHTYPE used to build generic packages for this baseline only has
81
     * meaning in the daemon build, not the escrow build accessed by
82
     * BuildStandard::getPlatform, getBuildStandard
83
     * 
84
     * @attribute
85
     */
86
    public static String mGenericMachtype = System.getenv("GBE_MACHTYPE");
87
 
88
    /**
89
     * build dependencies by package alias
90
     * 
91
     * @attribute
92
     */
93
    public Vector<String> mDependencyCollection = new Vector<String>();
94
 
95
    /**
96
     * primary package version key pv_id in database
97
     * 
98
     * @attribute
99
     */
100
    public int mId;
101
 
102
    /**
103
     * indication of the nature of change
104
     * 
105
     * @attribute
106
     */
107
    Package.VersionNumberingStandard mChangeType = new VersionNumberingStandard();
108
 
109
    /**
110
     * determines what field is rippled on a package version whose dependencies
111
     * have changed
112
     * 
113
     * @attribute
114
     */
115
    Package.VersionNumberingStandard mRippleField = new VersionNumberingStandard();
116
 
117
    /**
118
     * interested owners
119
     * 
120
     * @attribute
121
     */
122
    private Vector<String> mBuildFailureEmailCollection = new Vector<String>();
123
 
124
    /**
125
     * when true will trigger unit tests as part of the package build phase in
126
     * daemon mode
127
     * 
128
     * @attribute
129
     */
130
    public boolean mHasAutomatedUnitTests = false;
131
 
132
    /**
133
     * when true, do not ripple this package through packages which are
134
     * dependent upon it in daemon mode
135
     * 
136
     * @attribute
137
     */
138
    public boolean mAdvisoryRipple = false;
139
 
140
    /**
141
     * Pegged packages will:
142
     *   - Not be rippled
143
     *   - Not be test buildable
144
     *   - Dependencies will not be considered
145
     *   Daemon Mode:
146
     *      Are not built in this release
147
     *      Are imported with out consideration as to their dependencies
148
     *      Must exist in dpkg_archive - will not be built if not present
149
     *   Escrow Mode:
150
     *      Have no special consideration
151
     *      The package and its dependencies will be built
152
     *
153
     * Packages imported into the release from an SDK will be treated as if they were
154
     * pegged by the built system.
155
     * 
156
     */
157
    public boolean mIsPegged = false;
158
    public boolean mIsSdk = false;
159
 
160
    /**
161
     * Unbuildable packages will not be candidates for a ripple or a rebuild
162
     * If they do not exist in dpkg_archive, then then will cause an error
163
     */
164
    public boolean mIsBuildable = true;
165
 
166
    /**
167
     * determines the build file the package is built in, or not
7048 dpurdie 168
     * <br>   1 Post Plan: build file - Result of planning
169
     * <br>   2 Post Plan: Future build requirement
170
     * <br>   3 Post Plan: Package has no build requirement
171
     * <br>   0 not yet processed (initial value) 
172
     * <br>  -1 not reproducible
173
     * <br>  -2 escrow: not reproducible on the build platforms configured for this release
174
     * <br>  -3 daemon: do not ripple
175
     * <br>  -4 directly dependent on package versions not in the baseline
176
     * <br>  -5 indirectly dependent on package versions which are not reproducible due to detected fault
177
     * <br>  -6 circular dependency
178
     * <br>  -7 Pegged or SDK Imported package not in dpkg_archive
179
     * <br>  -8 Pegged or SDK Imported package
180
     * <br>  -9 Rejected Daemon Instruction
181
     * <br>  -10 UnBuildable Package not in dpkg_archive
182
     * <br>  -11 Marked as RippleStop
6914 dpurdie 183
     * 
184
     */
185
    int mBuildFile = 0;
186
 
187
    /** Retained reason the package will not be built
188
     *  Save negative values as mBuildFile
189
     * @attribute
190
     */
191
    int mNoBuildReason = 0;
192
 
193
    /**
194
     * build dependencies by package
195
     * Calculated from information in mDependencyCollection
196
     * 
197
     * @attribute
198
     */
199
    Vector<Package> mPackageDependencyCollection = new Vector<Package>();
200
 
201
    /**
202
     * used for escrow build purposes set true when a package has been processed
203
     * 
204
     * @attribute
205
     */
206
    boolean mProcessed = false;
207
 
208
    /**
209
     * Reason that a package is being built
210
     */
211
    public BuildReason mBuildReason = null;
212
 
213
    /**
214
     * set true for WIP package versions only used in daemon mode
215
     * 
216
     * @attribute
217
     */
218
    public boolean mDirectlyPlanned = false;
219
 
220
    /**
221
     * set true when it is determined to be ripple built
222
     * 
223
     * @attribute
224
     */
225
    boolean mIndirectlyPlanned = false;
226
 
227
    /**
228
     * non zero instruction number when it is determined to be ripple built by force
229
     * 
230
     * @attribute
231
     */
7048 dpurdie 232
    public int mForcedRippleInstruction = 0;
6914 dpurdie 233
 
234
    /**
235
     * test build - non zero instruction number when it is determined to be test built
236
     * 
237
     * @attribute
238
     */
7048 dpurdie 239
    public int mTestBuildInstruction = 0;
6914 dpurdie 240
 
241
    /**
242
     * build dependencies by pv_id (-1 or not used for planned dependencies)
243
     * 
244
     * @attribute
245
     */
246
    public Vector<Integer> mDependencyIDCollection = new Vector<Integer>();
247
 
248
    /**
249
     * unique pkg_id in the database used for querying package version existence
250
     * in the database in daemon mode
251
     * 
252
     * @attribute
253
     */
254
    public int mPid;
255
 
256
    /**
257
     * maximum major number supported for determining ripple number
258
     * 
259
     * @attribute
260
     */
261
    int mMajorLimit;
262
 
263
    /**
264
     * maximum minor number supported for determining ripple number
265
     * 
266
     * @attribute
267
     */
268
    int mMinorLimit;
269
 
270
    /**
271
     * maximum patch number supported for determining ripple number
272
     * 
273
     * @attribute
274
     */
275
    int mPatchLimit;
276
 
277
    /**
278
     * maximum build number number supported for determining ripple number
279
     * 
280
     * @attribute
281
     */
282
    int mBuildLimit;
283
 
284
    /**
285
     * Logger
286
     * 
287
     * @attribute
288
     */
7033 dpurdie 289
    private static final Logger mLogger = LoggerFactory.getLogger(Package.class);
6914 dpurdie 290
 
291
    /**
292
     * dpkg archive location - The writable dpkg_archive
293
     * 
294
     * @attribute
295
     */
296
    public static String mGbeDpkg = System.getenv("GBE_DPKG");
297
 
298
    /**
299
     *  A physically close replica of dpkg_archive
300
     *  Is not writable 
301
     */
302
    public static final String mGbeDpkgReplica = System.getenv("GBE_DPKG_REPLICA");
303
 
304
    /**
305
     * Exception message used upon detection an archive does not exist Seems
306
     * this is a rare but transient and recoverable scenario
307
     * 
308
     * @attribute
309
     */
310
    public static final String mRecoverable = "dpkg_archive does not exist, recovery will be attempted";
311
 
312
    /**
313
     * true if the package exists in the package archive (dpkg_archive)
314
     * 
315
     * @attribute
316
     */
317
    private boolean mArchivalExistence = true;
318
 
319
    /**
320
     * when true will trigger source control interaction eg labelling
321
     * 
322
     * @attribute
323
     */
324
    public boolean mRequiresSourceControlInteraction = true;
325
 
326
    /**
327
     * when true has been checked for circular dependency
328
     * 
329
     * @attribute
330
     */
331
    boolean mCheckedCircularDependency = false;
332
 
333
    /**
334
     * when true has circular dependency, or in the process of detecting a circular dependency
335
     * 
336
     * @attribute
337
     */
338
    boolean mHasCircularDependency = false;
339
 
340
    /**
341
     * Bread crumb used to detect circular dependencies
342
     * 0 - Normal state
343
     * 1 - Crumb has been set
344
     * 2 - Crumb is a part of a circular dependency
345
     * 3 - Crumb is the start/end of the loop
346
     */
347
    int mBreadCrumb = 0;
348
 
349
    /**
350
     * Planned packages (WIPS) need calculate a new version number
351
     * This should be based on the Version of the package that the WIP was based upon
352
     */
353
	public String mPrevVersion;
354
 
7048 dpurdie 355
    /** Used to preserve the basic ordering of the package list(s)
356
     *  Only valid for the duration of the comparison sorts
357
     */
358
    int mSeqId;	
359
 
6914 dpurdie 360
	/** Indicates that the package not be ripple built at this time
361
	 *  Sequence is:
362
	 *     User sets to 's'
363
	 *     This buildtool will set to 'w' when a ripple has been detected
364
	 *     User resets to NULL when the ripple can be resumed. This is stored as an 'n'
365
	 * 
366
	 */
367
    public char mRippleStop;
7048 dpurdie 368
 
369
    /**
370
     *  The calculated ripple build plan for this package
371
     *  Will not be calculated for all packages, just those of interest
372
     *  All packages that can (not must) be built when this package is built
373
     *  The list will not include packages that cannot be built at the moment
374
     *  
375
     *  Does not include this package
376
     */
377
    public ArrayList<Package> mRipplePlan = null;
378
 
379
    /** The duration (seconds) of the last build of this package
380
     *  Used to estimate the cost of building the package
381
     *  A WIP packages gets the buildTime of the package its based upon
382
     */
383
    public int mBuildTime;
6914 dpurdie 384
 
7048 dpurdie 385
    /** Calculated time ( seconds) that is may take to build this package and then ripple packages that depend on it
386
     *  both directly and indirectly. Based on mRipplePlan. 
387
     */
388
    public int mRippleTime = 0;
389
 
7070 dpurdie 390
    /** Used in the calculation of a ripple plan
391
     * 
392
     */
393
    public int mRippleOrder = 0;
394
 
7048 dpurdie 395
    /** Set to indicate that the package is NOT a part of the full release set
396
     *  The package is a WIP, TestBuild or a ForcedRipple
397
     *  
398
     *   Used to simplify detection of such packages and to limit processing of these packages
399
     *   May be cleared if the package is added to the full set
400
     */
401
    public boolean mIsNotReleased = false;
7070 dpurdie 402
 
403
    /** Used by some algorithms for various indications
404
     * 
405
     */
406
    public boolean mIsProcessed = false;
7048 dpurdie 407
 
6914 dpurdie 408
    /**
409
     * Constructor
410
     * Used to create planned packages. In these packages the change_type is significant
411
     * 
412
     * @param pkg_id	   Package Name Identifier
413
     * @param pv_id        Package Version Id
414
     * @param pkg_name     Package Name
415
     * @param pkg_version  Package Version
416
     * @param v_ext        Package Suffix
417
     * @param alias        Package Alias
418
     * @param pkg_vcs_tag  Vcs Tag
419
     * @param ripple_field Ripple Field
420
     * @param change_type  Change Type
421
     */
7051 dpurdie 422
    public Package(int pkg_id, int pv_id, String pkg_name, String pkg_version, String v_ext, String alias, String pkg_vcs_tag, char ripple_field, char change_type)
6914 dpurdie 423
    {
424
        mLogger.debug("Package 1: pv_id " + pv_id + " pkg_name " + pkg_name + " v_ext " + v_ext + " alias " + alias
425
                + " pkg_vcs_tag " + pkg_vcs_tag + " change_type " + change_type);
426
        mId = pv_id;
427
        mName = pkg_name;
428
        mPid = pkg_id;
429
        mVersion = "0.0.0000";
430
        mExtension = v_ext;
431
        mAlias = alias;
432
        mVcsTag = pkg_vcs_tag;
433
 
434
        // Remove the package suffix from package_version to create the fixed version number
435
        mFixedVersion = pkg_version;
436
        mFixedVersion = mFixedVersion.substring(0, mFixedVersion.length() - mExtension.length());
437
 
438
        // a ripple_field of 'L' indicates this package has limited version numbering
439
        if (change_type == 'M') {
440
            mChangeType.setMajor(ripple_field == 'L' ? true : false);
441
 
442
        } else if (change_type == 'N') {
443
            mChangeType.setMinor(ripple_field == 'L' ? true : false);
444
 
445
        } else if (change_type == 'P') {
446
            mChangeType.setPatch(ripple_field == 'L' ? true : false);
447
 
448
        } else if (change_type == 'F') {
449
            mChangeType.setFixed();
450
 
451
        } else {
452
            mChangeType.setUnknown();
453
        }
454
    }
455
 
456
    /**
457
     * Constructor
458
     * Used to create existing packages, in these packages the ripple_field is significant
459
     * 
460
     * @param pkg_id	   Package Name Identifier
461
     * @param pv_id        Package Version Id
462
     * @param pkg_name     Package Name
463
     * @param pkg_version  Package Version
464
     * @param v_ext        Package Suffix
465
     * @param alias        Package Alias
466
     * @param pkg_vcs_tag  Vcs Tag
467
     * @param ripple_field Ripple Field
468
     */
7051 dpurdie 469
    public Package(int pkg_id,int pv_id, String pkg_name, String pkg_version, String v_ext, String alias, String pkg_vcs_tag, char ripple_field)
6914 dpurdie 470
    {
471
        mLogger.debug("Package 2: pv_id " + pv_id + " pkg_name " + pkg_name + " pkg_version " + pkg_version + " v_ext "
472
                + v_ext + " alias " + alias + " pkg_vcs_tag " + pkg_vcs_tag + " ripple_field " + ripple_field);
473
        mId = pv_id;
474
        mName = pkg_name;
475
        mPid = pkg_id;
476
        mVersion = pkg_version;
477
        int endindex = mVersion.length() - v_ext.length();
478
 
479
        if (endindex > 0)
480
        {
481
            mVersion = mVersion.substring(0, endindex);
482
        }
483
 
484
        mExtension = v_ext;
485
        mAlias = alias;
486
        mVcsTag = pkg_vcs_tag;
487
 
488
        // setBuild is the default
489
        if (ripple_field == 'M') {
490
            mRippleField.setMajor();
491
 
492
        } else if (ripple_field == 'm') {
493
            mRippleField.setMinor();
494
 
495
        } else if (ripple_field == 'p') {
496
            mRippleField.setPatch();
497
 
498
        } else if (ripple_field == 'L') {
499
            mRippleField.setLimit();
500
        }
501
    }
502
 
503
    /**
504
     * constructor
505
     */
506
    Package()
507
    {
508
        mLogger.debug("Package 3");
509
        mId = 0;
510
        mName = "null";
511
        mExtension = "null";
512
        mAlias = "null";
513
        mVcsTag = "null";
514
    }
515
 
516
    /**
7049 dpurdie 517
     * Constructor for unit test purposes
7051 dpurdie 518
     *  Will invoke applyPV and save the results for the UTF framework
6914 dpurdie 519
     */
520
    public Package(ReleaseManager rm, String version, int majorLimit, int minorLimit, int patchLimit, int buildNumberLimit)
521
    {
522
        mId = -1;
523
        mRippleField.setLimit();
524
        mVersion = version;
525
        mMajorLimit = majorLimit;
526
        mMinorLimit = minorLimit;
527
        mPatchLimit = patchLimit;
528
        mBuildLimit = buildNumberLimit;
529
 
530
        if (version.endsWith(".cots"))
531
        {
532
            mExtension = ".cots";
533
            mVersion = version.substring(0, version.length() - 5);
534
            mChangeType.setMajor(false);
535
            mChangeType.setMinor(false);
536
            mChangeType.setPatch(true);
537
            mRippleField.setBuild();
538
        }
539
 
540
        try
541
        {
7050 dpurdie 542
            mId = applyPV(rm);
6914 dpurdie 543
        } catch (Exception e)
544
        {
545
        }
546
    }
547
 
548
    /**
7051 dpurdie 549
     * Constructor for unit test purposes
550
     * Performs a partial copy of a package - sufficient for test purposes
551
     * @param base      - Base package  
552
     * @param newPvId   - New pvid of the package
553
     */
7070 dpurdie 554
    @SuppressWarnings("unchecked")
7051 dpurdie 555
    public Package(int newPvId, Package base) {
556
 
557
        mId = newPvId;
558
        mPid = base.mPid;
559
        mName = base.mName;
560
        mExtension = base.mExtension;
561
        mVersion = base.mVersion;
562
        mAlias = base.mAlias;
563
        mVcsTag = base.mVcsTag;
564
        mFixedVersion = base.mFixedVersion;
565
        mChangeType = base.mChangeType;
566
        mRippleField = base.mRippleField;
567
        mBuildTime = base.mBuildTime;
568
        mHasAutomatedUnitTests = base.mHasAutomatedUnitTests;
569
        mSeqId = base.mSeqId;
570
 
571
        mBuildFailureEmailCollection = (Vector<String>) base.mBuildFailureEmailCollection.clone();
572
        mPackageDependencyCollection = (Vector<Package>) base.mPackageDependencyCollection.clone();
573
        mDependencyIDCollection = (Vector<Integer>) base.mDependencyIDCollection.clone();
574
        mDependencyCollection = (Vector<String>) base.mDependencyCollection.clone();
575
        mBuildStandardCollection = (Vector<BuildStandard>) base.mBuildStandardCollection.clone();
576
 
577
    }
7076 dpurdie 578
 
579
    /**
580
     * Create a nice package description
581
     * Used only for debugging as it allows eclipse to show details without drilling down
582
     */
583
    public String toString() {
584
        return mId + "-" + mAlias;
585
    }
7051 dpurdie 586
 
587
    /**
6914 dpurdie 588
     * accessor for unit test purposes
589
     */
590
    public int getId()
591
    {
592
        return mId;
593
    }
594
 
595
    /**
596
     * accessor for unit test purposes
597
     */
598
    public String getVersion()
599
    {
600
        return mVersion;
601
    }
602
 
7049 dpurdie 603
    /**
604
     * accessor for unit test purposes
605
     */
606
    public String getNextVersion()
607
    {
608
        return mNextVersion;
609
    }
610
 
6914 dpurdie 611
 
612
    /**
613
     * returns true if mBuildStandardCollection is not empty
614
     */
615
    boolean isReproducible()
616
    {
7048 dpurdie 617
        mLogger.debug("isReproducible on Package {}", mName);
6914 dpurdie 618
        boolean retVal = ! mBuildStandardCollection.isEmpty();
7048 dpurdie 619
        mLogger.info("isReproducible returned {}", retVal);
6914 dpurdie 620
        return retVal;
621
    }
622
 
623
 
624
    /**
625
     * returns true if at least one of its BuildStandards has mGeneric true
626
     */
627
    boolean isGeneric()
628
    {
7048 dpurdie 629
        mLogger.debug("isGeneric on Package {}", mName);
6914 dpurdie 630
        boolean retVal = false;
631
        for (Iterator<BuildStandard> it = mBuildStandardCollection.iterator(); it.hasNext();)
632
        {
633
            BuildStandard buildStandard = it.next();
634
 
635
            if (buildStandard.isGeneric())
636
            {
637
                retVal = true;
638
                break;
639
            }
640
        }
641
 
7048 dpurdie 642
        mLogger.info("isGeneric returned {}", retVal);
6914 dpurdie 643
        return retVal;
644
    }
645
 
646
    /**
647
     * Returns true if at least one of the packages build standards can be 
648
     * built in the named machine class. 
649
     *  
650
     * Used to determine if the package can be built with the current buildset 
651
     * by iteration over all machine classes in the buildset. 
652
     */
653
    boolean canBeBuildby(String machineClass)
654
    {
655
        mLogger.debug("canBeBuildby on Package " + mName);
656
        boolean retVal = false;
657
        for (Iterator<BuildStandard> it = mBuildStandardCollection.iterator(); it.hasNext();)
658
        {
659
            BuildStandard buildStandard = it.next();
660
 
661
            if (buildStandard.isGeneric())
662
            {
663
                retVal = true;
664
                break;
665
            }
666
 
667
            if (buildStandard.mMachClass.equals(machineClass))
668
            {
669
                retVal = true;
670
                break;
671
            }
672
        }
673
 
674
        mLogger.info("canBeBuildby returned " + retVal);
675
        return retVal;
676
    }
677
 
678
    /**
679
     * Compare the build standards of two packages 
680
     * Used only in escrow mode 
681
     * Used to compare a package and its dependents (one by one) 
682
     *  
683
     * Returns true if the parent (this) package  can be built in the same escrow 
684
     * build iteration as the dependent package. 
685
     *  
686
     * This is a complex decision and has a few built in assumptions. 
687
     *  
688
     * If the dependent package is 'generic' then the parent package can be built 
689
     *  
690
     * If the parent package is generic then is can only be built if the dependent is 
691
     * also generic. Otherwise we must assume that the parent package is an agregator 
692
     * package and requires artifacts from the dependent package built by all of its 
693
     * required build machines. 
694
     *  
695
     * If both packages are not generic, then if the build standards of the parent and the 
696
     * dependent are identical then 
697
     *  
698
     *      If we have one build standard, we can build the parent in this iteration as
699
     *      the dependent package has been completely built.
700
     *  
701
     *      If we have more than one build standard ( but they are identical ) then we
702
     *      ASSUME that we can build the parent in this iteration. The assumption is that
703
     *      there is no mixing between build machines. ie: Windows consumer users Windows
704
     *      artifacts and the Linux consumer uses Linux artifacts ...
705
     *  
706
     * If both packages are not generic and the build standards are not identical, then 
707
     * things get hard. The safest solution is to assume the parent cannot be built in this 
708
     * iteration. This is not a bad assumption. 
709
     *  
710
     */
711
    boolean haveSameBuildStandards(Package d)
712
    {
713
        HashMap<String, Boolean> standardSet = new HashMap<String, Boolean>();
714
        boolean isGeneric = false;
715
        boolean isGeneric_d = false;
716
        boolean isIdentical = true;
717
 
718
        // Scan the build standards of the parent package and create a hash map for each machine
719
        // class required by the parent. Also determine if the parent is generic.
720
        for (Iterator<BuildStandard> it = mBuildStandardCollection.iterator(); it.hasNext();)
721
        {
722
            BuildStandard bs = it.next();
723
            standardSet.put(bs.mMachClass, false);
724
 
725
            if (bs.isGeneric())
726
            {
727
                isGeneric = true;
728
                break;
729
            }
730
        }
731
 
732
        // Scan the build standards in the dependent package and remove items from the map
733
        // If it was not in the map then the dependent package builds for platforms that the
734
        // parent package does not - thus the build standards cannot be identical
735
        // Also determine if the dependent is generic.
736
        for (Iterator<BuildStandard> it = d.mBuildStandardCollection.iterator(); it.hasNext();)
737
        {
738
            BuildStandard bs = it.next();
739
             if (bs.isGeneric())
740
             {
741
             isGeneric_d = true;
742
             break;
743
             }
744
 
745
            Boolean value = standardSet.remove(bs.mMachClass);
746
            if (value == null)
747
            {
748
                isIdentical = false;
749
                break;
750
            }
751
        }
752
 
753
        //
754
        // If there are any items left in the map then the parent package builds on machines
755
        // that the dependent does not. The two are not identical.
756
 
757
        if (!standardSet.isEmpty())
758
        {
759
            isIdentical = false;
760
        }
761
 
762
        // If dependent is generic, then it will be build on the first platform in this iteration
763
        // All is good
764
 
765
        if( isGeneric_d)
766
            return true;
767
 
768
        //  If I am generic and the dependent is generic, then I can be built at this time
769
        // 
770
        //  Will not reach here as we have already said that if dependenbt is generic, then all is good
771
        //  if (isGeneric_d && isGeneric)
772
        //  {
773
        //      return true;
774
        //  }
775
 
776
 
777
        //  If I am generic then I must wait for ALL dependent to be built on all platforms
778
        //  Thus I can't be built this round
779
        //
780
        if (isGeneric)
781
            return false;
782
 
783
        //
784
        //  If the two sets of build standards don't have generic, BUT are identical
785
        //  the we can assume that there is no cross breading. and that windows bits build from windows bits
786
        //  and solaris bits build from solaris
787
        //
788
        if (isIdentical)
789
            return true;
790
 
791
        //  The two sets of build standards are not an exact match
792
        //  Cheap solution: Assume that I can't be built this round
793
        //  Possible solution: If I am a subset of the dependent - then I can be built
794
        //                     If I am a superset of the dependent - then ???
795
        return false;
796
 
797
    }
798
 
799
    /**
7048 dpurdie 800
     * Applies the required version number change. Will calculate mNextVersion
7050 dpurdie 801
     * while not changing mVersion.
6914 dpurdie 802
     * 
803
     * @param releaseManager    Release Manager instance to work against
804
     * 
7048 dpurdie 805
     * @return 0 : success
806
     *         1 : cannot work with non standard versioning
807
     *         2 : ripple field limitations prevent a ripple build
808
     *         3 : Invalid Change Type
6914 dpurdie 809
     * @exception Exception
810
     */
7050 dpurdie 811
 
812
    int applyPV(ReleaseManager releaseManager) throws Exception
6914 dpurdie 813
    {
814
        String logInfo = "applyPV," + mName;
815
        mLogger.debug("applyPV on Package " + mName);
7048 dpurdie 816
 
6914 dpurdie 817
        //
7048 dpurdie 818
        //  This method used to actually perform the version change
819
        //  Now it just calculates the potential value
820
        //  Must not alter mVersion as it will be used if the package is not selected to be built,
821
        //  but some of the code assumes that it can be.
822
        //
823
        String originalVersion = mVersion;
824
 
825
        //
6914 dpurdie 826
        // Four scenarios, only applyPV for 3 of them
827
        // mDirectlyPlanned mIndirectlyPlanned mArchivalExistence mForcedRipple
828
        // Action
829
        // WIP/test build exists: true true don't care don't care applyPV
830
        // Package version is out of date: false true true don't care applyPV
831
        // Forced ripple: false true don't care > 0 applyPV
832
        // Package version does not exist: false true false = 0 do not applyPV
833
        //
834
        if (!mDirectlyPlanned && mIndirectlyPlanned && !mArchivalExistence && mForcedRippleInstruction == 0)
835
        {
836
            // the package has an mIndirectlyPlanned flag set true in daemon
837
            // mode because the package does not exist in an archive
838
            // do not apply a different package version
7048 dpurdie 839
            mLogger.info("applyPV. Rebuild Package {}", mName);
6914 dpurdie 840
            mLogger.info("applyPv returned 0");
841
            return 0;
842
        }
843
 
844
        // override - no longer doing a rebuild - version number change from this point on
845
        if (mTestBuildInstruction == 0)
846
        {
847
            mRequiresSourceControlInteraction = true;
848
        }
849
 
850
        //	Force test builds to use a sensible version number
851
        if (mTestBuildInstruction > 0 )
852
        {
853
        	mChangeType.resetData();		// Resolve conflict via build numbers
854
        	mRippleField.setBuild();
855
        	mVersion = "99.99.98999";		// Such that rippling build number will goto 99.99.99000
856
        }
857
 
858
        //
859
        // Detect invalid change type
860
        // Flagged when package instance is created
861
        //
862
        if (mChangeType.mUnknown)
863
        {
7048 dpurdie 864
            mLogger.info("Package Version specified on Package {} New Version: {}", mName, mVersion);
6914 dpurdie 865
            mLogger.info("applyPv returned 3");
866
            return 3;
867
        }
868
 
869
        // If we are not calculating the new package version because the user
870
        // has fixed the version of the package. We are given the new package version.
871
        if (mChangeType.mFixed)
872
        {
873
            // mVersion is already setup
874
 
7048 dpurdie 875
            mNextVersion = mFixedVersion;
876
            mLogger.info("Package Version specified on Package {} New Version: {}", mName,  mVersion);
6914 dpurdie 877
            mLogger.info("applyPv returned 0");
878
            return 0;
879
        }
880
 
881
        // We need to calculate the new version number
882
        //
883
        MutableInt major = new MutableInt(0);
884
        MutableInt minor = new MutableInt(0);
885
        MutableInt patch = new MutableInt(1000);
886
 
887
        // Planned packages have a previous version number to be used as the bases for the
888
        // calculation. Ripples won't.
889
        //
890
        if (mPrevVersion != null)
891
        {
892
        	mVersion = mPrevVersion;
893
        }
894
        String[] field = mVersion.split("\\D");
895
        String nonStandardCotsVersion = "";
896
        logInfo += ", Prev:" + mVersion;
897
 
898
        if (field.length == 3)
899
        {
900
            major.value = Integer.parseInt(field[0]);
901
            minor.value = Integer.parseInt(field[1]);
902
            patch.value = Integer.parseInt(field[2]);
903
        } 
904
        else
905
        {
906
            //
907
            // Can ripple a .cots package under very controlled conditions
908
            // Its ends with a .patchBuild field
909
            // Package is marked as ripple via build number
910
            // Change type of Major and Minor are not allowed
911
            //
7048 dpurdie 912
            if (!mChangeType.mMajor && !mChangeType.mMinor && mRippleField.mBuild && mExtension.compareTo(".cots") == 0 && field.length > 0)
6914 dpurdie 913
            {
914
                // allow and work with (ripple build) versions a.b.c.d....xxxx
915
                // where xxxx.length > 3
916
                String patchStr = field[field.length - 1];
917
                int patchLen = patchStr.length();
918
 
919
                // check patchStr is the last (at least 4) digits
920
                if (patchLen > 3
921
                        && mVersion.substring(mVersion.length() - patchLen, mVersion.length()).compareTo(patchStr) == 0)
922
                {
923
                    patch.value = Integer.parseInt(patchStr);
924
                    nonStandardCotsVersion = mVersion.substring(0, mVersion.length() - patchLen);
925
                }
926
            }
927
 
928
            if (nonStandardCotsVersion.length() == 0)
929
            {
930
                // cannot work with non standard versioning
931
                mLogger.error("applyPV cannot work with non standard versioning");
932
                mLogger.info("applyPv returned 1");
933
                return 1;
934
            }
935
        }
936
 
937
        if (nonStandardCotsVersion.length() == 0 && patch.value < 1000 && field[2].substring(0, 1).compareTo("0") != 0)
938
        {
7048 dpurdie 939
            mLogger.info("applyPV accomodate old style Version of the form 1.0.1");
6914 dpurdie 940
            patch.value = patch.value * 1000;
941
        }
942
 
943
        // mChangeType overrides mRippleField
944
        do
945
        {
946
            if (mChangeType.mMajor)
947
            {
948
                logInfo += ",CT Major";
949
                if (!incrementFieldsAccordingToLimits(4, major, minor, patch))
950
                {
951
                    mLogger.info("applyPv returned 2");
7048 dpurdie 952
                    mVersion = originalVersion;
6914 dpurdie 953
                    return 2;
954
                }
955
            } else if (mChangeType.mMinor)
956
            {
957
                logInfo += ",CT Minor";
958
                if (!incrementFieldsAccordingToLimits(3, major, minor, patch))
959
                {
960
                    mLogger.info("applyPv returned 2");
7048 dpurdie 961
                    mVersion = originalVersion;
6914 dpurdie 962
                    return 2;
963
                }
964
            } else if (mChangeType.mPatch)
965
            {
966
                logInfo += ",CT Patch";
967
                if (!incrementFieldsAccordingToLimits(2, major, minor, patch))
968
                {
969
                    mLogger.info("applyPv returned 2");
7048 dpurdie 970
                    mVersion = originalVersion;
6914 dpurdie 971
                    return 2;
972
                }
973
            } else
974
            {
975
                if (mRippleField.mMajor)
976
                {
977
                    logInfo += ",R Major";
978
                    major.value++;
7048 dpurdie 979
                    mLogger.info("applyPV mRippleField.mMajor {}", major.value);
6914 dpurdie 980
                    minor.value = 0;
981
                    patch.value = 0;
982
                } else if (mRippleField.mMinor)
983
                {
984
                    logInfo += ",R Minor";
985
                    minor.value++;
7048 dpurdie 986
                    mLogger.info("applyPV mRippleField.mMinor {}", minor.value);
6914 dpurdie 987
                    patch.value = 0;
988
                } else if (mRippleField.mPatch)
989
                {
990
                    logInfo += ",R Patch";
991
                    patch.value = ((patch.value / 1000) + 1) * 1000; 
7048 dpurdie 992
                    mLogger.info("applyPV mRippleField.mPatch {}", patch.value);
6914 dpurdie 993
                } else if (mRippleField.mBuild)
994
                {
995
                    logInfo += ", R Build";
996
                    patch.value++;
7048 dpurdie 997
                    mLogger.info("applyPV mRippleField.mBuild {}", patch.value);
6914 dpurdie 998
                } else
999
                {
1000
                    if (!incrementFieldsAccordingToLimits(1, major, minor, patch))
1001
                    {
1002
                        mLogger.info("applyPv returned 2");
7048 dpurdie 1003
                        mVersion = originalVersion;
6914 dpurdie 1004
                        return 2;
1005
                    }
1006
                }
1007
            }
1008
 
1009
            if (nonStandardCotsVersion.length() == 0)
1010
            {
1011
                mVersion = String.valueOf(major.value) + "." + String.valueOf(minor.value) + ".";
1012
            } else
1013
            {
1014
                mVersion = nonStandardCotsVersion;
1015
            }
1016
 
1017
            if (patch.value < 10)
1018
            {
1019
                mVersion += "000";
1020
            } else if (patch.value < 100)
1021
            {
1022
                mVersion += "00";
1023
            } else if (patch.value < 1000)
1024
            {
1025
                mVersion += "0";
1026
            }
1027
 
1028
            mVersion += String.valueOf(patch.value);
7050 dpurdie 1029
        } while (exists(releaseManager));
6914 dpurdie 1030
 
7048 dpurdie 1031
        logInfo += ", Next Version:" + mVersion;
7033 dpurdie 1032
        mLogger.error(logInfo);
6914 dpurdie 1033
        mLogger.info("applyPv returned 0");
7048 dpurdie 1034
        mNextVersion = mVersion;
1035
        mVersion = originalVersion;
6914 dpurdie 1036
        return 0;
1037
    }
1038
 
1039
    /**
1040
     * increments fields according to mRippleField.mLimit if necessary will
1041
     * apply it to the field passed as follows 1 = build 2 = patch 3 = minor
1042
     * other = major returns true on success false on ripple field limitations
1043
     * prevent a ripple build
1044
     */
1045
    private boolean incrementFieldsAccordingToLimits(int field, MutableInt major, MutableInt minor, MutableInt patch)
1046
    {
1047
        boolean retVal = true;
1048
 
1049
        if (!mChangeType.mLimit && !mRippleField.mLimit)
1050
        {
1051
            // simple case
1052
            // no need to take field limits into consideration
1053
            switch (field)
1054
            {
1055
            case 1:
1056
                // unreachable
1057
                // the only scenario involving build number manipulation
1058
                // involves the mRippleField.mLimit being set
1059
                retVal = false;
1060
                break;
1061
            case 2:
1062
                do
1063
                {
1064
                    patch.value++;
1065
                } while ((patch.value / 1000) * 1000 != patch.value);
1066
                mLogger.info("incrementFieldsAccordingToLimits patch " + patch.value);
1067
                break;
1068
            case 3:
1069
                minor.value++;
1070
                mLogger.info("incrementFieldsAccordingToLimits minor " + minor.value);
1071
                patch.value = 0;
1072
                break;
1073
            default:
1074
                major.value++;
1075
                mLogger.info("incrementFieldsAccordingToLimits major " + major.value);
1076
                minor.value = 0;
1077
                patch.value = 0;
1078
            }
1079
        } else
1080
        {
1081
            // take field limits into consideration
1082
            boolean changeOccurred = false;
1083
            boolean incrementField = true;
1084
 
1085
            switch (field)
1086
            {
1087
            case 1:
1088
                if (mBuildLimit != 0)
1089
                {
1090
                    // increment or reset the patch build number
1091
                    int buildNumber = patch.value - (patch.value / 1000) * 1000;
1092
 
1093
                    if (buildNumber < mBuildLimit)
1094
                    {
1095
                        // can increment the patch build number
1096
                        patch.value++;
1097
                        mLogger.info("incrementFieldsAccordingToLimits mRippleField.mLimit build number " + patch.value);
1098
                        changeOccurred = true;
1099
                        incrementField = false;
1100
                    } else
1101
                    {
1102
                        if (mPatchLimit == 0)
1103
                        {
1104
                            // reset the patch number and patch build number
1105
                            patch.value = 0;
1106
                        }
1107
                    }
1108
                }
1109
                // no break by design
1110
            case 2:
1111
                if (mPatchLimit != 0 && incrementField)
1112
                {
1113
                    // increment or reset the patch number
1114
                    if ((patch.value / 1000) < mPatchLimit)
1115
                    {
1116
                        do
1117
                        {
1118
                            patch.value++;
1119
                        } while ((patch.value / 1000) * 1000 != patch.value);
1120
 
1121
                        mLogger.info("incrementFieldsAccordingToLimits mRippleField.mLimit patch " + patch.value);
1122
                        changeOccurred = true;
1123
                        incrementField = false;
1124
                    } else
1125
                    {
1126
                        // reset the patch number and patch build number
1127
                        patch.value = 0;
1128
                    }
1129
                }
1130
                // no break by design
1131
            case 3:
1132
                if (mMinorLimit != 0 && incrementField)
1133
                {
1134
                    // increment or reset the minor number
1135
                    if (minor.value < mMinorLimit)
1136
                    {
1137
                        minor.value++;
1138
                        patch.value = 0;
1139
                        mLogger.info("incrementFieldsAccordingToLimits mRippleField.mLimit minor " + minor.value);
1140
                        changeOccurred = true;
1141
                        incrementField = false;
1142
                    } else
1143
                    {
1144
                        // reset the minor number
1145
                        minor.value = 0;
1146
                    }
1147
                }
1148
                // no break by design
1149
            default:
1150
                if (mMajorLimit != 0 && incrementField)
1151
                {
1152
                    // increment or reset the major number
1153
                    if (major.value < mMajorLimit)
1154
                    {
1155
                        // increment the major number
1156
                        changeOccurred = true;
1157
                        major.value++;
1158
                        minor.value = 0;
1159
                        patch.value = 0;
1160
                        mLogger.info("incrementFieldsAccordingToLimits mRippleField.mLimit major " + major.value);
1161
                    }
1162
                }
1163
            }
1164
 
1165
            if (!changeOccurred)
1166
            {
1167
                // unable to increment a field due to field limitations
1168
                mLogger.error("incrementFieldsAccordingToLimits ripple field limitations prevent a ripple build");
1169
                mLogger.info("incrementFieldsAccordingToLimits returned false");
1170
                retVal = false;
1171
            }
1172
        }
1173
 
1174
        return retVal;
1175
    }
1176
 
1177
    /**
7048 dpurdie 1178
     * Check if a specified Version of the package exists in dpkg_archive or the Release Manager Database
6914 dpurdie 1179
     * 
1180
     * @param releaseManager Release Manager Instance
1181
     * 
1182
     * @return True if the Package Version exists within the Release Manager Database
1183
     * @exception Exception
1184
     */
7050 dpurdie 1185
    private boolean exists(ReleaseManager releaseManager) throws Exception
6914 dpurdie 1186
    {
1187
        mLogger.debug("exists on Package " + mName + " version " + mVersion + " extension " + mExtension);
1188
        boolean retVal = false;
1189
 
7048 dpurdie 1190
        if (!releaseManager.mUseDatabase)
6914 dpurdie 1191
        {
1192
            mLogger.info("exists !releaseManager.mUseDatabase");
1193
        }
1194
        else
1195
        {
1196
            //  Check Package Archive
1197
            retVal = existsInDpkgArchive();
1198
            if (!retVal)
1199
            {
1200
                //  Check Release Manager Database
1201
                retVal = releaseManager.queryPackageVersions(mPid, mVersion + mExtension);
1202
            }
1203
        }
1204
 
1205
        mLogger.info("exists returned " + retVal);
1206
        return retVal;
1207
    }
1208
 
1209
 
1210
    /**
1211
     * Check to see if a package exists in dpkg_archive
1212
     * 
1213
     * @return true if the version exists in dpkg_archive
1214
     * @exception Exception Thrown if dpkg_archive does not exist. The 'cause' of 'mRecoverable' is special and
1215
     *                      will be trapped later to determine if this is a recoverable exception.
1216
     */
1217
    boolean existsInDpkgArchive() throws Exception
1218
    {
1219
        mLogger.debug("existsInDpkgArchive on " + mName);
1220
        boolean retVal = false;
1221
        String name = utilities.catDir(mName, mVersion + mExtension );
1222
 
1223
        //  If a replica exists, then check it first
1224
        //  If we are configured with a replica its because access to the main archive is slow
1225
        //  and we want this check to be fast
1226
        //
1227
        if (mGbeDpkgReplica != null && mGbeDpkgReplica.length() > 0)
1228
        {
1229
            File dpkg = new File(mGbeDpkgReplica);
1230
            if (!dpkg.exists())
1231
            {
7033 dpurdie 1232
                mLogger.error("existsInDpkgArchive. mGbeDpkgReplica not accessable. " + mRecoverable);
6914 dpurdie 1233
                throw new Exception(mRecoverable);
1234
            }
1235
 
1236
            if( utilities.freshFileExists(utilities.catDir(mGbeDpkgReplica, name) ) )
1237
            {
1238
                mLogger.info("existsInDpkgArchive mGbeDpkgReplica");
1239
                retVal = true;
1240
            }
1241
        }
1242
 
1243
        //  Check (possibly remote) dpkg_archive for files existence if it was not found locally
1244
        //
1245
        if ( !retVal )
1246
        {
1247
 
1248
            //  If the package archive does not exist at the moment, then we have a network issue
1249
            //  This is a recoverable error
1250
 
1251
            File dpkg = new File(mGbeDpkg);
1252
            if (!dpkg.exists())
1253
            {
7033 dpurdie 1254
                mLogger.error("existsInDpkgArchive. mGbeDpkg not accessable. " + mRecoverable);
6914 dpurdie 1255
                throw new Exception(mRecoverable);
1256
            }
1257
 
1258
            if( utilities.freshFileExists(utilities.catDir(mGbeDpkg, name) ) )
1259
            {
1260
                mLogger.info("existsInDpkgArchive mGbeDpkg");
1261
                retVal = true;
1262
            }
1263
        }
1264
 
1265
        mArchivalExistence = retVal;
1266
        mLogger.info("existsInDpkgArchive returned " + retVal);
1267
        return retVal;
1268
    }
1269
 
1270
    /**
1271
     * returns true if the required package archives (dpkg_archive) exist
1272
     * attempt to recover from their transient loss
1273
     */
1274
    public static boolean recover()
1275
    {
1276
        mLogger.debug("recover");
1277
        boolean retVal = false;
1278
 
1279
        String Release = mGbeDpkg;
1280
        if (Release != null)
1281
        {
1282
            if ( utilities.freshFileExists(mGbeDpkg) )
1283
            {
1284
                retVal = true;
7033 dpurdie 1285
                mLogger.error("recover: dpkg_archive access has been restored");
6914 dpurdie 1286
            }
1287
        }
1288
 
1289
        mLogger.info("recover returned " + retVal);
1290
        return retVal;
1291
    }
1292
 
1293
    /**
1294
     * Returns a data structure of unique email addresses
1295
     * Uses the Global Email Collection and the packages own failure email collection
1296
     */
1297
    private LinkedHashSet<String>buildEmailList(RippleEngine rippleEngine)
1298
    {
1299
        //  Create a single list of email targets ensuring only one instance of each email address
1300
        //  ie: Remove duplicates, null and empty strings
1301
        LinkedHashSet<String> hs = new LinkedHashSet<String>();
1302
 
1303
        // Global and Project Wide emails
1304
        for (Iterator<String> it = rippleEngine.mMailGlobalCollection.iterator(); it.hasNext();)
1305
        {
1306
            String item = it.next();
1307
            if (item != null && item.length() > 0) 
1308
            {
1309
                hs.add(item);
1310
            }
1311
        }
1312
 
1313
        // Package specific collection
1314
        for (Iterator<String> it = mBuildFailureEmailCollection.iterator(); it.hasNext();)
1315
        {
1316
            String item = it.next();
1317
            if (item != null && item.length() > 0) 
1318
            {
1319
                hs.add(item);
1320
            }
1321
        }
1322
 
1323
        return hs;
1324
    }
1325
 
1326
    /**
1327
     * Add email information in a form suitable for creating an Ant file
1328
     * @param   rippleEngine    - Ripple Engine Instance
1329
     * @param   xml             - An XmlBuilder element to extend
1330
     */
1331
    void emailInfo(RippleEngine rippleEngine, XmlBuilder xml)
1332
    {
1333
 
1334
        //  Create a single list of email targets ensuring only one instance of each email address
1335
        //  ie: Remove duplicates
1336
        LinkedHashSet<String> hs = buildEmailList(rippleEngine);
1337
 
1338
        for (Iterator<String> it = hs.iterator(); it.hasNext();)
1339
        {
1340
            String email = it.next();
1341
            XmlBuilder entry = xml.addNewElement("owner");
1342
            entry.addAttribute("email", email);
1343
        }
1344
    }
1345
 
1346
    /**
1347
     * Returns email information in a form suitable for direct use
1348
     * @param   rippleEngine Current Release Manager context
1349
     * @return  A comma separated list of user names. May return a 'null' String
1350
     */
1351
    String emailInfoNonAntTask(RippleEngine rippleEngine)
1352
    {
1353
        //  Create a single list of email targets ensuring only one instance of each email address
1354
        //  ie: Remove duplicates
1355
        LinkedHashSet<String> hs = buildEmailList(rippleEngine);
1356
 
1357
        String retVal = null;
1358
        for (Iterator<String> it = hs.iterator(); it.hasNext();)
1359
        {
1360
            String email = it.next();
1361
 
1362
            if (retVal == null)
1363
            {
1364
                retVal = new String();
1365
            } else
1366
            {
1367
                retVal += ",";
1368
            }
1369
            retVal += email;
1370
        }
1371
 
1372
        return retVal;
1373
    }
1374
 
1375
    /**
1376
     * Adds email to mBuildFailureEmailCollection.
1377
     * Do not worry about multiple entries. These will be handled when the data is extracted
7048 dpurdie 1378
     * @param   email   - Email address. Null is allowed and will not be added
6914 dpurdie 1379
     */
1380
    public void addEmail(String email)
1381
    {
7048 dpurdie 1382
        if (email != null) 
6914 dpurdie 1383
        {
7048 dpurdie 1384
            mBuildFailureEmailCollection.add(email);
6914 dpurdie 1385
        }
1386
    }
1387
 
1388
    /**
1389
     * End of Processing for a Test Build
1390
     *     - sends email notification
1391
     *     - marks the instruction complete in the database
1392
     */
1393
    public void completeTestBuild(RippleEngine rippleEngine, boolean success) throws SQLException, Exception
1394
    {
1395
        mLogger.debug("completeTestBuild");
1396
 
1397
        if (mTestBuildInstruction == 0)
1398
        {
1399
            mLogger.info("completeTestBuild. Not Build Instruction");
1400
            return;
1401
        }
1402
 
1403
        //  Email Subject
1404
        String subject = (success == true ? "TEST BUILD COMPLETED SUCCESSFULLY" : "TEST BUILD FAILED") 
1405
                + " on package "
1406
                + mAlias;
1407
 
1408
        // Email Body
1409
        String mailBody = "";
1410
        if ( success != true)
1411
        {
1412
            mailBody += "Test build issues are identified in preceding build failure email.<p>"; 
1413
        }
1414
        mailBody += "Release: " + rippleEngine.mBaselineName + "<br>" 
1415
                  + "Package: " + mAlias + "<br>" 
1416
                  + "Rm Ref: " + CreateUrls.generateRmUrl(rippleEngine.getRtagId(), mId) + "<br>" 
1417
                  + "VcsTag: " + mVcsTag + "<br>"
1418
                  + "Build dependencies:<br>";
1419
 
1420
        String indentString = "&nbsp;&nbsp;&nbsp;&nbsp;";
1421
 
1422
        for (Iterator<Package> it3 = mPackageDependencyCollection.iterator(); it3.hasNext();)
1423
        {
1424
            Package depend = it3.next();
1425
 
1426
            String dependsExtension = depend.mExtension;
1427
            String dependsVersion = depend.mVersion;
1428
 
1429
            if (dependsExtension.length() > 0)
1430
            {
1431
                dependsVersion += dependsExtension;
1432
            }
1433
            mailBody += indentString + RippleEngine.quoteString(depend.mName, dependsVersion) + "<br>";
1434
        }
1435
 
1436
        mailBody += "<br>Build standards:<br>";
1437
 
1438
        for (Iterator<BuildStandard> it = mBuildStandardCollection.iterator(); it.hasNext();)
1439
        {
1440
            BuildStandard bs = it.next();
1441
 
1442
            String bsText = bs.getBuildStandardText();
1443
            if (bsText.length() > 0)
1444
            {
1445
                mailBody += indentString + bsText + "<br>";
1446
            }
1447
        }
1448
 
1449
        mailBody += "<p><hr>";
1450
        try
1451
        {
1452
            String target = emailInfoNonAntTask(rippleEngine);
7033 dpurdie 1453
            mLogger.error("completeTestBuildEmail Server: " + rippleEngine.getMailServer());
1454
            mLogger.error("completeTestBuildEmail Sender: " + rippleEngine.getMailSender());
1455
            mLogger.error("completeTestBuildEmail Target: " + target);
6914 dpurdie 1456
 
1457
            Smtpsend.send(rippleEngine.getMailServer(), // mailServer
1458
                    rippleEngine.getMailSender(),       // source
1459
                    target,                         // target
1460
                    rippleEngine.getMailSender(),       // cc
1461
                    null,                           // bcc
1462
                    subject,                        // subject
1463
                    mailBody,                       // body
1464
                    null                            // attachment
1465
            );
1466
        } catch (Exception e)
1467
        {
1468
            mLogger.warn("Email Failure: completeTestBuild:" + e.getMessage());
1469
        }
1470
 
1471
        // Update the Release Manager Database
1472
        rippleEngine.mReleaseManager.markDaemonInstCompleted(mTestBuildInstruction);
7033 dpurdie 1473
        mLogger.error("completeTest. Returning");
6914 dpurdie 1474
    }
1475
 
1476
 
1477
    /**
1478
     * Returns true if the package is a part of a circular dependency
1479
     * 
1480
     * If the package depends on a package with a circular dependency then the function
1481
     * will return false.
1482
     */
1483
    public boolean hasCircularDependency(RippleEngine ripEng)
1484
    {
7048 dpurdie 1485
        mLogger.debug("hasCircularDependency: {}", mAlias);
6914 dpurdie 1486
        boolean retVal = detectCircularDependency(mAlias, ripEng, null);
7048 dpurdie 1487
        mLogger.info("hasCircularDependency returned {} ", retVal);
6914 dpurdie 1488
        return retVal;
1489
    }
1490
 
1491
    /**
1492
     * Returns true is a part of a circular dependency
1493
     * Will examine all the packages sub dependencies and mark those that do have a
1494
     * circular dependency.
1495
     * 
1496
     * This process works by descending the dependency tree and dropping a bread crumb
1497
     * If the bread crumb is seen during the decent, then a circle has been detected and
1498
     * the package (with the bread crumb) will be marked as having a circular dependency
1499
     * 
1500
     *  Assumes that the caller will walk ALL packages and flag those with a circular
1501
     *  dependence AND those that depend on that package.
1502
     * 
1503
     */
1504
    private boolean detectCircularDependency(String alias, RippleEngine ripEng, Package parent)
1505
    {
1506
        mLogger.debug("detectCircularDependency");
1507
        boolean retVal = false;
1508
 
1509
        // if this package has yet to be checked for circular dependency
1510
        if (!mCheckedCircularDependency)
1511
        {
1512
            // Will be set as we drill down through dependencies
1513
            // If we see this marker (bread crumb) then we have a loop
1514
            if (mBreadCrumb != 0)
1515
            {
1516
                mBreadCrumb = 3;
1517
 
1518
                mHasCircularDependency = true;
1519
                if(parent != null && parent.mBreadCrumb != 3 )
1520
                {
1521
                    parent.mBreadCrumb = 2;
1522
                }
1523
            }
1524
            else
1525
            {
1526
                // Mark this package as potentially having a circular dependency
1527
                // Will now drill down and see if we hit a marker
1528
                mBreadCrumb = 1;
1529
 
1530
                // Recurse down the dependencies and sub dependencies
1531
                for (Iterator<String> it2 = mDependencyCollection.iterator(); it2.hasNext();)
1532
                {
1533
                    String dependencyAlias = it2.next();
1534
                    Package dependency = ripEng.findPackage(dependencyAlias);
1535
                    dependency.detectCircularDependency(alias, ripEng, this);
1536
                }
1537
 
1538
                if (mBreadCrumb == 2)
1539
                {
1540
                    mHasCircularDependency = true;
1541
                    if(parent != null && parent.mBreadCrumb != 3 )
1542
                    {
1543
                        parent.mBreadCrumb = 2;
1544
                    }
1545
                }
1546
                mBreadCrumb = 0;
1547
            }
1548
 
1549
            // Flag package as having been examined
1550
            mCheckedCircularDependency = true;
1551
        } 
1552
 
1553
        // return the persisted circular dependency outcome
1554
        retVal = mHasCircularDependency;
7048 dpurdie 1555
        mLogger.info("detectCircularDependency 2 returned {}", retVal);
6914 dpurdie 1556
        return retVal;
1557
    }
7048 dpurdie 1558
 
1559
    /** Set the sequence of all packages in the list
1560
     *  Used so that the Unit Tests function can preserve the basic ordering of the list 
1561
     */
1562
    public static void setSequence(ArrayList<Package> al)
1563
    {
1564
        int seq = 1;
1565
        for (Iterator<Package> it = al.iterator(); it.hasNext(); )
1566
        {
1567
            Package p = it.next();
1568
            p.mSeqId = seq++;
1569
        }
1570
    }
6914 dpurdie 1571
 
7070 dpurdie 1572
    /** Reset the processed flag on a collection of packages
1573
     * 
1574
     */
1575
    public static void resetProcessed (ArrayList<Package> al)
1576
    {
1577
        for (Iterator<Package> it = al.iterator(); it.hasNext(); )
1578
        {
1579
            Package p = it.next();
1580
            p.mIsProcessed = false;
1581
        }
1582
    }
7048 dpurdie 1583
 
7070 dpurdie 1584
    /** Reset the rippleOrder flag on a collection of packages
1585
     * 
1586
     */
1587
    public static void resetRippleOrder (ArrayList<Package> al)
1588
    {
1589
        for (Iterator<Package> it = al.iterator(); it.hasNext(); )
1590
        {
1591
            Package p = it.next();
1592
            p.mRippleOrder = 0;
1593
        }
1594
    }   
1595
 
7048 dpurdie 1596
    /** Comparator for sorting package collections by mSeqId
1597
     *  Used to preserve order for unit testing
1598
     */
1599
    public static final Comparator<Package> SeqComparator = new Comparator<Package>() {
1600
 
1601
        /**
1602
         * Returns -ve: p1 is less than p2
1603
         *           0: p1 = p2
1604
         *         +ve: p1 > p2
1605
         */
1606
        public int compare (Package p1, Package p2) {
1607
                return p1.mSeqId - p2.mSeqId;
1608
        }
7070 dpurdie 1609
    };
1610
 
1611
    /** Comparator for sorting package collections by mRippleTimme and mRippleOrder
1612
     *  Preserve rippleOrder
1613
     *  
1614
     */
1615
    public static final Comparator<Package> PlanComparator = new Comparator<Package>() {
1616
 
1617
        /**
1618
         * Returns -ve: p1 is less than p2
1619
         *           0: p1 = p2
1620
         *         +ve: p1 > p2
1621
         */
1622
        public int compare (Package p1, Package p2) {
1623
            if (p1.mRippleOrder == p2.mRippleOrder)
1624
            {
1625
                return p1.mRippleTime - p2.mRippleTime;
1626
            }
1627
 
1628
            return p1.mRippleOrder - p2.mRippleOrder;
1629
 
1630
        }
1631
    };
1632
 
1633
    /** Comparator for sorting package collections by mRippleTimme (only)
1634
     *  
1635
     */
1636
    public static final Comparator<Package> RippleTimeComparator = new Comparator<Package>() {
1637
 
1638
        /**
1639
         * Returns -ve: p1 is less than p2
1640
         *           0: p1 = p2
1641
         *         +ve: p1 > p2
1642
         */
1643
        public int compare (Package p1, Package p2) {
1644
            return p1.mRippleTime - p2.mRippleTime;
1645
        }
1646
    };
1647
 
7048 dpurdie 1648
 
6914 dpurdie 1649
    /**
1650
     * entity class supporting the ERG version numbering standard:
1651
     * <major>.<minor>.<patch/build> patch/build is at least a 4 digit number
1652
     * whose last 3 digits represent the build
1653
     */
1654
    public class VersionNumberingStandard
1655
    {
1656
        /**
1657
         * in terms of the mChangeType Package field, when true indicates the
1658
         * contract of the package has changed in a non backwardly compatible
1659
         * manner in terms of the mRippleField Package field, when true indicates
1660
         * the major version number will be incremented
1661
         * 
1662
         * @attribute
1663
         */
1664
        private boolean mMajor = false;
1665
 
1666
        /**
1667
         * in terms of the mChangeType Package field, when true indicates the
1668
         * contract of the package has changed in a backwardly compatible manner
1669
         * in terms of the mRippleField Package field, when true indicates the
1670
         * minor version number will be incremented
1671
         * 
1672
         * @attribute
1673
         */
1674
        private boolean mMinor = false;
1675
 
1676
        /**
1677
         * in terms of the mChangeType Package field, when true indicates the
1678
         * contract of the package has not changed, but the package has changed
1679
         * internally in terms of the mRippleField Package field, when true
1680
         * indicates the minor version number will be incremented
1681
         * 
1682
         * @attribute
1683
         */
1684
        private boolean mPatch = false;
1685
 
1686
        /**
1687
         * in terms of the mChangeType Package field, when true indicates the
1688
         * package has not changed, its dependencies potentially have in terms
1689
         * of the mRippleField Package field, when true indicates the build
1690
         * number will be incremented
1691
         * 
1692
         * @attribute
1693
         */
1694
        private boolean mBuild = true;
1695
 
1696
        /**
1697
         * in terms of the mChangeType Package field, when true indicates the
1698
         * major, minor, and patch number will be incremented according to field
1699
         * limits in terms of the mRippleField Package field, when true indicates
1700
         * the major, minor, patch and build number will be incremented
1701
         * according to field limits
1702
         * 
1703
         * @attribute
1704
         */
1705
        private boolean mLimit = false;
1706
 
1707
        /**
1708
         * in terms of the mChangeType Package field, when true indicates the
1709
         * package version number will not be rippled. The user will have fixed
1710
         * the version number. This is only application to WIP packages
1711
         * 
1712
         * @attribute
1713
         */
1714
        private boolean mFixed = false;
1715
 
1716
        /**
1717
         * in terms of the mChangeType Package field, when true indicates the
1718
         * method of rippling a package version number is not known.
1719
         * 
1720
         * @attribute
1721
         */
1722
        private boolean mUnknown = false;
1723
 
1724
        /**
1725
         * constructor
1726
         */
1727
        private VersionNumberingStandard()
1728
        {
1729
            mLogger.debug("VersionNumberingStandard");
1730
        }
1731
 
1732
        /**
1733
         * Reset all values to a known state
1734
         * 
1735
         */
1736
        void resetData()
1737
        {
1738
            mBuild = false;
1739
            mMajor = false;
1740
            mMinor = false;
1741
            mPatch = false;
1742
            mLimit = false;
1743
            mFixed = false;
1744
            mUnknown = false;
1745
        }
1746
 
1747
        /**
1748
         * sets mBuild true, mMajor false, mMinor false, mPatch false, mLimit
1749
         * false
1750
         */
1751
        void setBuild()
1752
        {
1753
            mLogger.debug("setBuild");
1754
            resetData();
1755
            mBuild = true;
1756
        }
1757
 
1758
        /**
1759
         * sets mBuild false, mMajor true, mMinor false, mPatch false, mLimit
1760
         * false
1761
         */
1762
        void setMajor()
1763
        {
1764
            mLogger.debug("setMajor");
1765
            resetData();
1766
            mMajor = true;
1767
        }
1768
 
1769
        /**
1770
         * sets mBuild false, mMajor true, mMinor false, mPatch false, mLimit
1771
         * limit
1772
         */
1773
        void setMajor(boolean limit)
1774
        {
1775
            mLogger.debug("setMajor " + limit);
1776
            resetData();
1777
            mMajor = true;
1778
            mLimit = limit;
1779
        }
1780
 
1781
        /**
1782
         * sets mBuild false, mMajor false, mMinor true, mPatch false, mLimit
1783
         * false
1784
         */
1785
        void setMinor()
1786
        {
1787
            mLogger.debug("setMinor");
1788
            resetData();
1789
            mMinor = true;
1790
        }
1791
 
1792
        /**
1793
         * sets mBuild false, mMajor false, mMinor true, mPatch false, mLimit
1794
         * limit
1795
         */
1796
        void setMinor(boolean limit)
1797
        {
1798
            mLogger.debug("setMinor " + limit);
1799
            resetData();
1800
            mMinor = true;
1801
            mLimit = limit;
1802
        }
1803
 
1804
        /**
1805
         * sets mBuild false, mMajor false, mMinor false, mPatch true, mLimit
1806
         * false
1807
         */
1808
        void setPatch()
1809
        {
1810
            mLogger.debug("setPatch");
1811
            resetData();
1812
            mPatch = true;
1813
        }
1814
 
1815
        /**
1816
         * sets mBuild false, mMajor false, mMinor false, mPatch true, mLimit
1817
         * limit
1818
         */
1819
        void setPatch(boolean limit)
1820
        {
1821
            mLogger.debug("setPatch");
1822
            resetData();
1823
            mPatch = true;
1824
            mLimit = limit;
1825
        }
1826
 
1827
        /**
1828
         * sets mBuild false, mMajor false, mMinor false, mPatch false, mLimit
1829
         * true
1830
         */
1831
        void setLimit()
1832
        {
1833
            mLogger.debug("setPatch");
1834
            resetData();
1835
            mLimit = true;
1836
        }
1837
 
1838
        /**
1839
         * sets parameters to indicate that the change type is Fixed. The
1840
         * version number is set by the user and a ripple will not be calculated
1841
         */
1842
        void setFixed()
1843
        {
1844
            mLogger.debug("setFixed");
1845
            resetData();
1846
            mFixed = true;
1847
        }
1848
 
1849
        /**
1850
         * Sets parameters to indicate that the change type is not known
1851
         * 
1852
         */
1853
        void setUnknown()
1854
        {
1855
            resetData();
1856
            mUnknown = true;
1857
        }
1858
 
1859
    }
1860
 
7070 dpurdie 1861
    /**
1862
     *  Add a package dependency
1863
     *  UTF Use
1864
     *  @param p - The package to add as a dependent
1865
     *  @return - The current package to allow chaining of calls
1866
     */
1867
    public Package addDependency(Package p) {
1868
        mDependencyCollection.add(p.mAlias);
1869
        mDependencyIDCollection.add(p.mId);
1870
        return this;
1871
    }
1872
 
7071 dpurdie 1873
    /** Clear the packages list of dependencies
1874
     *  UTF use
1875
     *  @return - The current package to allow chaining of calls
1876
     */
1877
    public Package resetDependencies() {
1878
        mDependencyCollection.clear();
1879
        mDependencyIDCollection.clear();
1880
        return this;
1881
    }
1882
 
6914 dpurdie 1883
}