Subversion Repositories DevTools

Rev

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