Subversion Repositories DevTools

Rev

Rev 7048 | Rev 7050 | 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;
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
	 */
7048 dpurdie 367
 
6914 dpurdie 368
    public char mRippleStop;
7048 dpurdie 369
 
370
    /**
371
     *  The calculated ripple build plan for this package
372
     *  Will not be calculated for all packages, just those of interest
373
     *  All packages that can (not must) be built when this package is built
374
     *  The list will not include packages that cannot be built at the moment
375
     *  
376
     *  Does not include this package
377
     */
378
    public ArrayList<Package> mRipplePlan = null;
379
 
380
    /** The duration (seconds) of the last build of this package
381
     *  Used to estimate the cost of building the package
382
     *  A WIP packages gets the buildTime of the package its based upon
383
     */
384
    public int mBuildTime;
6914 dpurdie 385
 
7048 dpurdie 386
    /** Calculated time ( seconds) that is may take to build this package and then ripple packages that depend on it
387
     *  both directly and indirectly. Based on mRipplePlan. 
388
     */
389
    public int mRippleTime = 0;
390
 
391
    /** Set to indicate that the package is NOT a part of the full release set
392
     *  The package is a WIP, TestBuild or a ForcedRipple
393
     *  
394
     *   Used to simplify detection of such packages and to limit processing of these packages
395
     *   May be cleared if the package is added to the full set
396
     */
397
    public boolean mIsNotReleased = false;
398
 
6914 dpurdie 399
    /**
400
     * Constructor
401
     * Used to create planned packages. In these packages the change_type is significant
402
     * 
403
     * @param pkg_id	   Package Name Identifier
404
     * @param pv_id        Package Version Id
405
     * @param pkg_name     Package Name
406
     * @param pkg_version  Package Version
407
     * @param v_ext        Package Suffix
408
     * @param alias        Package Alias
409
     * @param pkg_vcs_tag  Vcs Tag
410
     * @param ripple_field Ripple Field
411
     * @param change_type  Change Type
412
     */
413
    public Package(int pkg_id, int pv_id, String pkg_name, String pkg_version, String v_ext, String alias, String pkg_vcs_tag,
414
            char ripple_field, char change_type)
415
    {
416
        mLogger.debug("Package 1: pv_id " + pv_id + " pkg_name " + pkg_name + " v_ext " + v_ext + " alias " + alias
417
                + " pkg_vcs_tag " + pkg_vcs_tag + " change_type " + change_type);
418
        mId = pv_id;
419
        mName = pkg_name;
420
        mPid = pkg_id;
421
        mVersion = "0.0.0000";
422
        mExtension = v_ext;
423
        mAlias = alias;
424
        mVcsTag = pkg_vcs_tag;
425
 
426
        // Remove the package suffix from package_version to create the fixed version number
427
        mFixedVersion = pkg_version;
428
        mFixedVersion = mFixedVersion.substring(0, mFixedVersion.length() - mExtension.length());
429
 
430
        // a ripple_field of 'L' indicates this package has limited version numbering
431
        if (change_type == 'M') {
432
            mChangeType.setMajor(ripple_field == 'L' ? true : false);
433
 
434
        } else if (change_type == 'N') {
435
            mChangeType.setMinor(ripple_field == 'L' ? true : false);
436
 
437
        } else if (change_type == 'P') {
438
            mChangeType.setPatch(ripple_field == 'L' ? true : false);
439
 
440
        } else if (change_type == 'F') {
441
            mChangeType.setFixed();
442
 
443
        } else {
444
            mChangeType.setUnknown();
445
        }
446
    }
447
 
448
    /**
449
     * Constructor
450
     * Used to create existing packages, in these packages the ripple_field is significant
451
     * 
452
     * @param pkg_id	   Package Name Identifier
453
     * @param pv_id        Package Version Id
454
     * @param pkg_name     Package Name
455
     * @param pkg_version  Package Version
456
     * @param v_ext        Package Suffix
457
     * @param alias        Package Alias
458
     * @param pkg_vcs_tag  Vcs Tag
459
     * @param ripple_field Ripple Field
460
     */
461
    public Package(int pkg_id,int pv_id, String pkg_name, String pkg_version, String v_ext, String alias, String pkg_vcs_tag,
462
            char ripple_field)
463
    {
464
        mLogger.debug("Package 2: pv_id " + pv_id + " pkg_name " + pkg_name + " pkg_version " + pkg_version + " v_ext "
465
                + v_ext + " alias " + alias + " pkg_vcs_tag " + pkg_vcs_tag + " ripple_field " + ripple_field);
466
        mId = pv_id;
467
        mName = pkg_name;
468
        mPid = pkg_id;
469
        mVersion = pkg_version;
470
        int endindex = mVersion.length() - v_ext.length();
471
 
472
        if (endindex > 0)
473
        {
474
            mVersion = mVersion.substring(0, endindex);
475
        }
476
 
477
        mExtension = v_ext;
478
        mAlias = alias;
479
        mVcsTag = pkg_vcs_tag;
480
 
481
        // setBuild is the default
482
        if (ripple_field == 'M') {
483
            mRippleField.setMajor();
484
 
485
        } else if (ripple_field == 'm') {
486
            mRippleField.setMinor();
487
 
488
        } else if (ripple_field == 'p') {
489
            mRippleField.setPatch();
490
 
491
        } else if (ripple_field == 'L') {
492
            mRippleField.setLimit();
493
        }
494
    }
495
 
496
    /**
497
     * constructor
498
     */
499
    Package()
500
    {
501
        mLogger.debug("Package 3");
502
        mId = 0;
503
        mName = "null";
504
        mExtension = "null";
505
        mAlias = "null";
506
        mVcsTag = "null";
507
    }
508
 
509
    /**
7049 dpurdie 510
     * Constructor for unit test purposes
511
     *  Will invoke applyPv and save the results for the UTF framework
6914 dpurdie 512
     */
513
    public Package(ReleaseManager rm, String version, int majorLimit, int minorLimit, int patchLimit, int buildNumberLimit)
514
    {
515
        mId = -1;
516
        mRippleField.setLimit();
517
        mVersion = version;
518
        mMajorLimit = majorLimit;
519
        mMinorLimit = minorLimit;
520
        mPatchLimit = patchLimit;
521
        mBuildLimit = buildNumberLimit;
522
 
523
        if (version.endsWith(".cots"))
524
        {
525
            mExtension = ".cots";
526
            mVersion = version.substring(0, version.length() - 5);
527
            mChangeType.setMajor(false);
528
            mChangeType.setMinor(false);
529
            mChangeType.setPatch(true);
530
            mRippleField.setBuild();
531
        }
532
 
533
        try
534
        {
535
            mId = applyPV(rm, 0);
536
        } catch (Exception e)
537
        {
538
        }
539
    }
540
 
541
    /**
542
     * accessor for unit test purposes
543
     */
544
    public int getId()
545
    {
546
        return mId;
547
    }
548
 
549
    /**
550
     * accessor for unit test purposes
551
     */
552
    public String getVersion()
553
    {
554
        return mVersion;
555
    }
556
 
7049 dpurdie 557
    /**
558
     * accessor for unit test purposes
559
     */
560
    public String getNextVersion()
561
    {
562
        return mNextVersion;
563
    }
564
 
6914 dpurdie 565
 
566
    /**
567
     * returns true if mBuildStandardCollection is not empty
568
     */
569
    boolean isReproducible()
570
    {
7048 dpurdie 571
        mLogger.debug("isReproducible on Package {}", mName);
6914 dpurdie 572
        boolean retVal = ! mBuildStandardCollection.isEmpty();
7048 dpurdie 573
        mLogger.info("isReproducible returned {}", retVal);
6914 dpurdie 574
        return retVal;
575
    }
576
 
577
 
578
    /**
579
     * returns true if at least one of its BuildStandards has mGeneric true
580
     */
581
    boolean isGeneric()
582
    {
7048 dpurdie 583
        mLogger.debug("isGeneric on Package {}", mName);
6914 dpurdie 584
        boolean retVal = false;
585
        for (Iterator<BuildStandard> it = mBuildStandardCollection.iterator(); it.hasNext();)
586
        {
587
            BuildStandard buildStandard = it.next();
588
 
589
            if (buildStandard.isGeneric())
590
            {
591
                retVal = true;
592
                break;
593
            }
594
        }
595
 
7048 dpurdie 596
        mLogger.info("isGeneric returned {}", retVal);
6914 dpurdie 597
        return retVal;
598
    }
599
 
600
    /**
601
     * Returns true if at least one of the packages build standards can be 
602
     * built in the named machine class. 
603
     *  
604
     * Used to determine if the package can be built with the current buildset 
605
     * by iteration over all machine classes in the buildset. 
606
     */
607
    boolean canBeBuildby(String machineClass)
608
    {
609
        mLogger.debug("canBeBuildby on Package " + mName);
610
        boolean retVal = false;
611
        for (Iterator<BuildStandard> it = mBuildStandardCollection.iterator(); it.hasNext();)
612
        {
613
            BuildStandard buildStandard = it.next();
614
 
615
            if (buildStandard.isGeneric())
616
            {
617
                retVal = true;
618
                break;
619
            }
620
 
621
            if (buildStandard.mMachClass.equals(machineClass))
622
            {
623
                retVal = true;
624
                break;
625
            }
626
        }
627
 
628
        mLogger.info("canBeBuildby returned " + retVal);
629
        return retVal;
630
    }
631
 
632
    /**
633
     * Compare the build standards of two packages 
634
     * Used only in escrow mode 
635
     * Used to compare a package and its dependents (one by one) 
636
     *  
637
     * Returns true if the parent (this) package  can be built in the same escrow 
638
     * build iteration as the dependent package. 
639
     *  
640
     * This is a complex decision and has a few built in assumptions. 
641
     *  
642
     * If the dependent package is 'generic' then the parent package can be built 
643
     *  
644
     * If the parent package is generic then is can only be built if the dependent is 
645
     * also generic. Otherwise we must assume that the parent package is an agregator 
646
     * package and requires artifacts from the dependent package built by all of its 
647
     * required build machines. 
648
     *  
649
     * If both packages are not generic, then if the build standards of the parent and the 
650
     * dependent are identical then 
651
     *  
652
     *      If we have one build standard, we can build the parent in this iteration as
653
     *      the dependent package has been completely built.
654
     *  
655
     *      If we have more than one build standard ( but they are identical ) then we
656
     *      ASSUME that we can build the parent in this iteration. The assumption is that
657
     *      there is no mixing between build machines. ie: Windows consumer users Windows
658
     *      artifacts and the Linux consumer uses Linux artifacts ...
659
     *  
660
     * If both packages are not generic and the build standards are not identical, then 
661
     * things get hard. The safest solution is to assume the parent cannot be built in this 
662
     * iteration. This is not a bad assumption. 
663
     *  
664
     */
665
    boolean haveSameBuildStandards(Package d)
666
    {
667
        HashMap<String, Boolean> standardSet = new HashMap<String, Boolean>();
668
        boolean isGeneric = false;
669
        boolean isGeneric_d = false;
670
        boolean isIdentical = true;
671
 
672
        // Scan the build standards of the parent package and create a hash map for each machine
673
        // class required by the parent. Also determine if the parent is generic.
674
        for (Iterator<BuildStandard> it = mBuildStandardCollection.iterator(); it.hasNext();)
675
        {
676
            BuildStandard bs = it.next();
677
            standardSet.put(bs.mMachClass, false);
678
 
679
            if (bs.isGeneric())
680
            {
681
                isGeneric = true;
682
                break;
683
            }
684
        }
685
 
686
        // Scan the build standards in the dependent package and remove items from the map
687
        // If it was not in the map then the dependent package builds for platforms that the
688
        // parent package does not - thus the build standards cannot be identical
689
        // Also determine if the dependent is generic.
690
        for (Iterator<BuildStandard> it = d.mBuildStandardCollection.iterator(); it.hasNext();)
691
        {
692
            BuildStandard bs = it.next();
693
             if (bs.isGeneric())
694
             {
695
             isGeneric_d = true;
696
             break;
697
             }
698
 
699
            Boolean value = standardSet.remove(bs.mMachClass);
700
            if (value == null)
701
            {
702
                isIdentical = false;
703
                break;
704
            }
705
        }
706
 
707
        //
708
        // If there are any items left in the map then the parent package builds on machines
709
        // that the dependent does not. The two are not identical.
710
 
711
        if (!standardSet.isEmpty())
712
        {
713
            isIdentical = false;
714
        }
715
 
716
        // If dependent is generic, then it will be build on the first platform in this iteration
717
        // All is good
718
 
719
        if( isGeneric_d)
720
            return true;
721
 
722
        //  If I am generic and the dependent is generic, then I can be built at this time
723
        // 
724
        //  Will not reach here as we have already said that if dependenbt is generic, then all is good
725
        //  if (isGeneric_d && isGeneric)
726
        //  {
727
        //      return true;
728
        //  }
729
 
730
 
731
        //  If I am generic then I must wait for ALL dependent to be built on all platforms
732
        //  Thus I can't be built this round
733
        //
734
        if (isGeneric)
735
            return false;
736
 
737
        //
738
        //  If the two sets of build standards don't have generic, BUT are identical
739
        //  the we can assume that there is no cross breading. and that windows bits build from windows bits
740
        //  and solaris bits build from solaris
741
        //
742
        if (isIdentical)
743
            return true;
744
 
745
        //  The two sets of build standards are not an exact match
746
        //  Cheap solution: Assume that I can't be built this round
747
        //  Possible solution: If I am a subset of the dependent - then I can be built
748
        //                     If I am a superset of the dependent - then ???
749
        return false;
750
 
751
    }
752
 
753
    /**
7048 dpurdie 754
     * Applies the required version number change. Will calculate mNextVersion
6914 dpurdie 755
     * 
756
     * @param releaseManager    Release Manager instance to work against
757
     * @param rtag_id           Release Identifier
758
     * 
7048 dpurdie 759
     * @return 0 : success
760
     *         1 : cannot work with non standard versioning
761
     *         2 : ripple field limitations prevent a ripple build
762
     *         3 : Invalid Change Type
6914 dpurdie 763
     * @exception Exception
764
     */
7049 dpurdie 765
//TODO - Remove the rtag_id parameter. Its not needed, although its badly used.    
6914 dpurdie 766
    int applyPV(ReleaseManager releaseManager, int rtag_id) throws Exception
767
    {
768
        String logInfo = "applyPV," + mName;
769
        mLogger.debug("applyPV on Package " + mName);
7048 dpurdie 770
 
6914 dpurdie 771
        //
7048 dpurdie 772
        //  This method used to actually perform the version change
773
        //  Now it just calculates the potential value
774
        //  Must not alter mVersion as it will be used if the package is not selected to be built,
775
        //  but some of the code assumes that it can be.
776
        //
777
        String originalVersion = mVersion;
778
 
779
        //
6914 dpurdie 780
        // Four scenarios, only applyPV for 3 of them
781
        // mDirectlyPlanned mIndirectlyPlanned mArchivalExistence mForcedRipple
782
        // Action
783
        // WIP/test build exists: true true don't care don't care applyPV
784
        // Package version is out of date: false true true don't care applyPV
785
        // Forced ripple: false true don't care > 0 applyPV
786
        // Package version does not exist: false true false = 0 do not applyPV
787
        //
788
        if (!mDirectlyPlanned && mIndirectlyPlanned && !mArchivalExistence && mForcedRippleInstruction == 0)
789
        {
790
            // the package has an mIndirectlyPlanned flag set true in daemon
791
            // mode because the package does not exist in an archive
792
            // do not apply a different package version
7048 dpurdie 793
            mLogger.info("applyPV. Rebuild Package {}", mName);
6914 dpurdie 794
            mLogger.info("applyPv returned 0");
795
            return 0;
796
        }
797
 
798
        // override - no longer doing a rebuild - version number change from this point on
799
        if (mTestBuildInstruction == 0)
800
        {
801
            mRequiresSourceControlInteraction = true;
802
        }
803
 
804
        //	Force test builds to use a sensible version number
805
        if (mTestBuildInstruction > 0 )
806
        {
807
        	mChangeType.resetData();		// Resolve conflict via build numbers
808
        	mRippleField.setBuild();
809
        	mVersion = "99.99.98999";		// Such that rippling build number will goto 99.99.99000
810
        }
811
 
812
        //
813
        // Detect invalid change type
814
        // Flagged when package instance is created
815
        //
816
        if (mChangeType.mUnknown)
817
        {
7048 dpurdie 818
            mLogger.info("Package Version specified on Package {} New Version: {}", mName, mVersion);
6914 dpurdie 819
            mLogger.info("applyPv returned 3");
820
            return 3;
821
        }
822
 
823
        // If we are not calculating the new package version because the user
824
        // has fixed the version of the package. We are given the new package version.
825
        if (mChangeType.mFixed)
826
        {
827
            // mVersion is already setup
828
 
7048 dpurdie 829
            mNextVersion = mFixedVersion;
830
            mLogger.info("Package Version specified on Package {} New Version: {}", mName,  mVersion);
6914 dpurdie 831
            mLogger.info("applyPv returned 0");
832
            return 0;
833
        }
834
 
835
        // We need to calculate the new version number
836
        //
837
        MutableInt major = new MutableInt(0);
838
        MutableInt minor = new MutableInt(0);
839
        MutableInt patch = new MutableInt(1000);
840
 
841
        // Planned packages have a previous version number to be used as the bases for the
842
        // calculation. Ripples won't.
843
        //
844
        if (mPrevVersion != null)
845
        {
846
        	mVersion = mPrevVersion;
847
        }
848
        String[] field = mVersion.split("\\D");
849
        String nonStandardCotsVersion = "";
850
        logInfo += ", Prev:" + mVersion;
851
 
852
        if (field.length == 3)
853
        {
854
            major.value = Integer.parseInt(field[0]);
855
            minor.value = Integer.parseInt(field[1]);
856
            patch.value = Integer.parseInt(field[2]);
857
        } 
858
        else
859
        {
860
            //
861
            // Can ripple a .cots package under very controlled conditions
862
            // Its ends with a .patchBuild field
863
            // Package is marked as ripple via build number
864
            // Change type of Major and Minor are not allowed
865
            //
7048 dpurdie 866
            if (!mChangeType.mMajor && !mChangeType.mMinor && mRippleField.mBuild && mExtension.compareTo(".cots") == 0 && field.length > 0)
6914 dpurdie 867
            {
868
                // allow and work with (ripple build) versions a.b.c.d....xxxx
869
                // where xxxx.length > 3
870
                String patchStr = field[field.length - 1];
871
                int patchLen = patchStr.length();
872
 
873
                // check patchStr is the last (at least 4) digits
874
                if (patchLen > 3
875
                        && mVersion.substring(mVersion.length() - patchLen, mVersion.length()).compareTo(patchStr) == 0)
876
                {
877
                    patch.value = Integer.parseInt(patchStr);
878
                    nonStandardCotsVersion = mVersion.substring(0, mVersion.length() - patchLen);
879
                }
880
            }
881
 
882
            if (nonStandardCotsVersion.length() == 0)
883
            {
884
                // cannot work with non standard versioning
885
                mLogger.error("applyPV cannot work with non standard versioning");
886
                mLogger.info("applyPv returned 1");
887
                return 1;
888
            }
889
        }
890
 
891
        if (nonStandardCotsVersion.length() == 0 && patch.value < 1000 && field[2].substring(0, 1).compareTo("0") != 0)
892
        {
7048 dpurdie 893
            mLogger.info("applyPV accomodate old style Version of the form 1.0.1");
6914 dpurdie 894
            patch.value = patch.value * 1000;
895
        }
896
 
897
        // mChangeType overrides mRippleField
898
        do
899
        {
900
            if (mChangeType.mMajor)
901
            {
902
                logInfo += ",CT Major";
903
                if (!incrementFieldsAccordingToLimits(4, major, minor, patch))
904
                {
905
                    mLogger.info("applyPv returned 2");
7048 dpurdie 906
                    mVersion = originalVersion;
6914 dpurdie 907
                    return 2;
908
                }
909
            } else if (mChangeType.mMinor)
910
            {
911
                logInfo += ",CT Minor";
912
                if (!incrementFieldsAccordingToLimits(3, major, minor, patch))
913
                {
914
                    mLogger.info("applyPv returned 2");
7048 dpurdie 915
                    mVersion = originalVersion;
6914 dpurdie 916
                    return 2;
917
                }
918
            } else if (mChangeType.mPatch)
919
            {
920
                logInfo += ",CT Patch";
921
                if (!incrementFieldsAccordingToLimits(2, major, minor, patch))
922
                {
923
                    mLogger.info("applyPv returned 2");
7048 dpurdie 924
                    mVersion = originalVersion;
6914 dpurdie 925
                    return 2;
926
                }
927
            } else
928
            {
929
                if (mRippleField.mMajor)
930
                {
931
                    logInfo += ",R Major";
932
                    major.value++;
7048 dpurdie 933
                    mLogger.info("applyPV mRippleField.mMajor {}", major.value);
6914 dpurdie 934
                    minor.value = 0;
935
                    patch.value = 0;
936
                } else if (mRippleField.mMinor)
937
                {
938
                    logInfo += ",R Minor";
939
                    minor.value++;
7048 dpurdie 940
                    mLogger.info("applyPV mRippleField.mMinor {}", minor.value);
6914 dpurdie 941
                    patch.value = 0;
942
                } else if (mRippleField.mPatch)
943
                {
944
                    logInfo += ",R Patch";
945
                    patch.value = ((patch.value / 1000) + 1) * 1000; 
7048 dpurdie 946
                    mLogger.info("applyPV mRippleField.mPatch {}", patch.value);
6914 dpurdie 947
                } else if (mRippleField.mBuild)
948
                {
949
                    logInfo += ", R Build";
950
                    patch.value++;
7048 dpurdie 951
                    mLogger.info("applyPV mRippleField.mBuild {}", patch.value);
6914 dpurdie 952
                } else
953
                {
954
                    if (!incrementFieldsAccordingToLimits(1, major, minor, patch))
955
                    {
956
                        mLogger.info("applyPv returned 2");
7048 dpurdie 957
                        mVersion = originalVersion;
6914 dpurdie 958
                        return 2;
959
                    }
960
                }
961
            }
962
 
963
            if (nonStandardCotsVersion.length() == 0)
964
            {
965
                mVersion = String.valueOf(major.value) + "." + String.valueOf(minor.value) + ".";
966
            } else
967
            {
968
                mVersion = nonStandardCotsVersion;
969
            }
970
 
971
            if (patch.value < 10)
972
            {
973
                mVersion += "000";
974
            } else if (patch.value < 100)
975
            {
976
                mVersion += "00";
977
            } else if (patch.value < 1000)
978
            {
979
                mVersion += "0";
980
            }
981
 
982
            mVersion += String.valueOf(patch.value);
983
        } while (exists(releaseManager, rtag_id));
984
 
7048 dpurdie 985
        logInfo += ", Next Version:" + mVersion;
7033 dpurdie 986
        mLogger.error(logInfo);
6914 dpurdie 987
        mLogger.info("applyPv returned 0");
7048 dpurdie 988
        mNextVersion = mVersion;
989
        mVersion = originalVersion;
6914 dpurdie 990
        return 0;
991
    }
992
 
993
    /**
994
     * increments fields according to mRippleField.mLimit if necessary will
995
     * apply it to the field passed as follows 1 = build 2 = patch 3 = minor
996
     * other = major returns true on success false on ripple field limitations
997
     * prevent a ripple build
998
     */
999
    private boolean incrementFieldsAccordingToLimits(int field, MutableInt major, MutableInt minor, MutableInt patch)
1000
    {
1001
        boolean retVal = true;
1002
 
1003
        if (!mChangeType.mLimit && !mRippleField.mLimit)
1004
        {
1005
            // simple case
1006
            // no need to take field limits into consideration
1007
            switch (field)
1008
            {
1009
            case 1:
1010
                // unreachable
1011
                // the only scenario involving build number manipulation
1012
                // involves the mRippleField.mLimit being set
1013
                retVal = false;
1014
                break;
1015
            case 2:
1016
                do
1017
                {
1018
                    patch.value++;
1019
                } while ((patch.value / 1000) * 1000 != patch.value);
1020
                mLogger.info("incrementFieldsAccordingToLimits patch " + patch.value);
1021
                break;
1022
            case 3:
1023
                minor.value++;
1024
                mLogger.info("incrementFieldsAccordingToLimits minor " + minor.value);
1025
                patch.value = 0;
1026
                break;
1027
            default:
1028
                major.value++;
1029
                mLogger.info("incrementFieldsAccordingToLimits major " + major.value);
1030
                minor.value = 0;
1031
                patch.value = 0;
1032
            }
1033
        } else
1034
        {
1035
            // take field limits into consideration
1036
            boolean changeOccurred = false;
1037
            boolean incrementField = true;
1038
 
1039
            switch (field)
1040
            {
1041
            case 1:
1042
                if (mBuildLimit != 0)
1043
                {
1044
                    // increment or reset the patch build number
1045
                    int buildNumber = patch.value - (patch.value / 1000) * 1000;
1046
 
1047
                    if (buildNumber < mBuildLimit)
1048
                    {
1049
                        // can increment the patch build number
1050
                        patch.value++;
1051
                        mLogger.info("incrementFieldsAccordingToLimits mRippleField.mLimit build number " + patch.value);
1052
                        changeOccurred = true;
1053
                        incrementField = false;
1054
                    } else
1055
                    {
1056
                        if (mPatchLimit == 0)
1057
                        {
1058
                            // reset the patch number and patch build number
1059
                            patch.value = 0;
1060
                        }
1061
                    }
1062
                }
1063
                // no break by design
1064
            case 2:
1065
                if (mPatchLimit != 0 && incrementField)
1066
                {
1067
                    // increment or reset the patch number
1068
                    if ((patch.value / 1000) < mPatchLimit)
1069
                    {
1070
                        do
1071
                        {
1072
                            patch.value++;
1073
                        } while ((patch.value / 1000) * 1000 != patch.value);
1074
 
1075
                        mLogger.info("incrementFieldsAccordingToLimits mRippleField.mLimit patch " + patch.value);
1076
                        changeOccurred = true;
1077
                        incrementField = false;
1078
                    } else
1079
                    {
1080
                        // reset the patch number and patch build number
1081
                        patch.value = 0;
1082
                    }
1083
                }
1084
                // no break by design
1085
            case 3:
1086
                if (mMinorLimit != 0 && incrementField)
1087
                {
1088
                    // increment or reset the minor number
1089
                    if (minor.value < mMinorLimit)
1090
                    {
1091
                        minor.value++;
1092
                        patch.value = 0;
1093
                        mLogger.info("incrementFieldsAccordingToLimits mRippleField.mLimit minor " + minor.value);
1094
                        changeOccurred = true;
1095
                        incrementField = false;
1096
                    } else
1097
                    {
1098
                        // reset the minor number
1099
                        minor.value = 0;
1100
                    }
1101
                }
1102
                // no break by design
1103
            default:
1104
                if (mMajorLimit != 0 && incrementField)
1105
                {
1106
                    // increment or reset the major number
1107
                    if (major.value < mMajorLimit)
1108
                    {
1109
                        // increment the major number
1110
                        changeOccurred = true;
1111
                        major.value++;
1112
                        minor.value = 0;
1113
                        patch.value = 0;
1114
                        mLogger.info("incrementFieldsAccordingToLimits mRippleField.mLimit major " + major.value);
1115
                    }
1116
                }
1117
            }
1118
 
1119
            if (!changeOccurred)
1120
            {
1121
                // unable to increment a field due to field limitations
1122
                mLogger.error("incrementFieldsAccordingToLimits ripple field limitations prevent a ripple build");
1123
                mLogger.info("incrementFieldsAccordingToLimits returned false");
1124
                retVal = false;
1125
            }
1126
        }
1127
 
1128
        return retVal;
1129
    }
1130
 
1131
    /**
7048 dpurdie 1132
     * Check if a specified Version of the package exists in dpkg_archive or the Release Manager Database
6914 dpurdie 1133
     * 
1134
     * @param releaseManager Release Manager Instance
1135
     * @param rtag_id        Release Tag Identifier
1136
     * 
1137
     * @return True if the Package Version exists within the Release Manager Database
1138
     * @exception Exception
1139
     */
1140
    private boolean exists(ReleaseManager releaseManager, int rtag_id) throws Exception
1141
    {
1142
        mLogger.debug("exists on Package " + mName + " version " + mVersion + " extension " + mExtension);
1143
        boolean retVal = false;
1144
 
7048 dpurdie 1145
        if (!releaseManager.mUseDatabase)
6914 dpurdie 1146
        {
1147
            mLogger.info("exists !releaseManager.mUseDatabase");
1148
        }
1149
        else
1150
        {
1151
            //  Check Package Archive
1152
            retVal = existsInDpkgArchive();
1153
            if (!retVal)
1154
            {
1155
                //  Check Release Manager Database
1156
                retVal = releaseManager.queryPackageVersions(mPid, mVersion + mExtension);
1157
            }
1158
        }
1159
 
1160
        mLogger.info("exists returned " + retVal);
1161
        return retVal;
1162
    }
1163
 
1164
 
1165
    /**
1166
     * Check to see if a package exists in dpkg_archive
1167
     * 
1168
     * @return true if the version exists in dpkg_archive
1169
     * @exception Exception Thrown if dpkg_archive does not exist. The 'cause' of 'mRecoverable' is special and
1170
     *                      will be trapped later to determine if this is a recoverable exception.
1171
     */
1172
    boolean existsInDpkgArchive() throws Exception
1173
    {
1174
        mLogger.debug("existsInDpkgArchive on " + mName);
1175
        boolean retVal = false;
1176
        String name = utilities.catDir(mName, mVersion + mExtension );
1177
 
1178
        //  If a replica exists, then check it first
1179
        //  If we are configured with a replica its because access to the main archive is slow
1180
        //  and we want this check to be fast
1181
        //
1182
        if (mGbeDpkgReplica != null && mGbeDpkgReplica.length() > 0)
1183
        {
1184
            File dpkg = new File(mGbeDpkgReplica);
1185
            if (!dpkg.exists())
1186
            {
7033 dpurdie 1187
                mLogger.error("existsInDpkgArchive. mGbeDpkgReplica not accessable. " + mRecoverable);
6914 dpurdie 1188
                throw new Exception(mRecoverable);
1189
            }
1190
 
1191
            if( utilities.freshFileExists(utilities.catDir(mGbeDpkgReplica, name) ) )
1192
            {
1193
                mLogger.info("existsInDpkgArchive mGbeDpkgReplica");
1194
                retVal = true;
1195
            }
1196
        }
1197
 
1198
        //  Check (possibly remote) dpkg_archive for files existence if it was not found locally
1199
        //
1200
        if ( !retVal )
1201
        {
1202
 
1203
            //  If the package archive does not exist at the moment, then we have a network issue
1204
            //  This is a recoverable error
1205
 
1206
            File dpkg = new File(mGbeDpkg);
1207
            if (!dpkg.exists())
1208
            {
7033 dpurdie 1209
                mLogger.error("existsInDpkgArchive. mGbeDpkg not accessable. " + mRecoverable);
6914 dpurdie 1210
                throw new Exception(mRecoverable);
1211
            }
1212
 
1213
            if( utilities.freshFileExists(utilities.catDir(mGbeDpkg, name) ) )
1214
            {
1215
                mLogger.info("existsInDpkgArchive mGbeDpkg");
1216
                retVal = true;
1217
            }
1218
        }
1219
 
1220
        mArchivalExistence = retVal;
1221
        mLogger.info("existsInDpkgArchive returned " + retVal);
1222
        return retVal;
1223
    }
1224
 
1225
    /**
1226
     * returns true if the required package archives (dpkg_archive) exist
1227
     * attempt to recover from their transient loss
1228
     */
1229
    public static boolean recover()
1230
    {
1231
        mLogger.debug("recover");
1232
        boolean retVal = false;
1233
 
1234
        String Release = mGbeDpkg;
1235
        if (Release != null)
1236
        {
1237
            if ( utilities.freshFileExists(mGbeDpkg) )
1238
            {
1239
                retVal = true;
7033 dpurdie 1240
                mLogger.error("recover: dpkg_archive access has been restored");
6914 dpurdie 1241
            }
1242
        }
1243
 
1244
        mLogger.info("recover returned " + retVal);
1245
        return retVal;
1246
    }
1247
 
1248
    /**
1249
     * Returns a data structure of unique email addresses
1250
     * Uses the Global Email Collection and the packages own failure email collection
1251
     */
1252
    private LinkedHashSet<String>buildEmailList(RippleEngine rippleEngine)
1253
    {
1254
        //  Create a single list of email targets ensuring only one instance of each email address
1255
        //  ie: Remove duplicates, null and empty strings
1256
        LinkedHashSet<String> hs = new LinkedHashSet<String>();
1257
 
1258
        // Global and Project Wide emails
1259
        for (Iterator<String> it = rippleEngine.mMailGlobalCollection.iterator(); it.hasNext();)
1260
        {
1261
            String item = it.next();
1262
            if (item != null && item.length() > 0) 
1263
            {
1264
                hs.add(item);
1265
            }
1266
        }
1267
 
1268
        // Package specific collection
1269
        for (Iterator<String> it = mBuildFailureEmailCollection.iterator(); it.hasNext();)
1270
        {
1271
            String item = it.next();
1272
            if (item != null && item.length() > 0) 
1273
            {
1274
                hs.add(item);
1275
            }
1276
        }
1277
 
1278
        return hs;
1279
    }
1280
 
1281
    /**
1282
     * Add email information in a form suitable for creating an Ant file
1283
     * @param   rippleEngine    - Ripple Engine Instance
1284
     * @param   xml             - An XmlBuilder element to extend
1285
     */
1286
    void emailInfo(RippleEngine rippleEngine, XmlBuilder xml)
1287
    {
1288
 
1289
        //  Create a single list of email targets ensuring only one instance of each email address
1290
        //  ie: Remove duplicates
1291
        LinkedHashSet<String> hs = buildEmailList(rippleEngine);
1292
 
1293
        for (Iterator<String> it = hs.iterator(); it.hasNext();)
1294
        {
1295
            String email = it.next();
1296
            XmlBuilder entry = xml.addNewElement("owner");
1297
            entry.addAttribute("email", email);
1298
        }
1299
    }
1300
 
1301
    /**
1302
     * Returns email information in a form suitable for direct use
1303
     * @param   rippleEngine Current Release Manager context
1304
     * @return  A comma separated list of user names. May return a 'null' String
1305
     */
1306
    String emailInfoNonAntTask(RippleEngine rippleEngine)
1307
    {
1308
        //  Create a single list of email targets ensuring only one instance of each email address
1309
        //  ie: Remove duplicates
1310
        LinkedHashSet<String> hs = buildEmailList(rippleEngine);
1311
 
1312
        String retVal = null;
1313
        for (Iterator<String> it = hs.iterator(); it.hasNext();)
1314
        {
1315
            String email = it.next();
1316
 
1317
            if (retVal == null)
1318
            {
1319
                retVal = new String();
1320
            } else
1321
            {
1322
                retVal += ",";
1323
            }
1324
            retVal += email;
1325
        }
1326
 
1327
        return retVal;
1328
    }
1329
 
1330
    /**
1331
     * Adds email to mBuildFailureEmailCollection.
1332
     * Do not worry about multiple entries. These will be handled when the data is extracted
7048 dpurdie 1333
     * @param   email   - Email address. Null is allowed and will not be added
6914 dpurdie 1334
     */
1335
    public void addEmail(String email)
1336
    {
7048 dpurdie 1337
        if (email != null) 
6914 dpurdie 1338
        {
7048 dpurdie 1339
            mBuildFailureEmailCollection.add(email);
6914 dpurdie 1340
        }
1341
    }
1342
 
1343
    /**
1344
     * End of Processing for a Test Build
1345
     *     - sends email notification
1346
     *     - marks the instruction complete in the database
1347
     */
1348
    public void completeTestBuild(RippleEngine rippleEngine, boolean success) throws SQLException, Exception
1349
    {
1350
        mLogger.debug("completeTestBuild");
1351
 
1352
        if (mTestBuildInstruction == 0)
1353
        {
1354
            mLogger.info("completeTestBuild. Not Build Instruction");
1355
            return;
1356
        }
1357
 
1358
        //  Email Subject
1359
        String subject = (success == true ? "TEST BUILD COMPLETED SUCCESSFULLY" : "TEST BUILD FAILED") 
1360
                + " on package "
1361
                + mAlias;
1362
 
1363
        // Email Body
1364
        String mailBody = "";
1365
        if ( success != true)
1366
        {
1367
            mailBody += "Test build issues are identified in preceding build failure email.<p>"; 
1368
        }
1369
        mailBody += "Release: " + rippleEngine.mBaselineName + "<br>" 
1370
                  + "Package: " + mAlias + "<br>" 
1371
                  + "Rm Ref: " + CreateUrls.generateRmUrl(rippleEngine.getRtagId(), mId) + "<br>" 
1372
                  + "VcsTag: " + mVcsTag + "<br>"
1373
                  + "Build dependencies:<br>";
1374
 
1375
        String indentString = "&nbsp;&nbsp;&nbsp;&nbsp;";
1376
 
1377
        for (Iterator<Package> it3 = mPackageDependencyCollection.iterator(); it3.hasNext();)
1378
        {
1379
            Package depend = it3.next();
1380
 
1381
            String dependsExtension = depend.mExtension;
1382
            String dependsVersion = depend.mVersion;
1383
 
1384
            if (dependsExtension.length() > 0)
1385
            {
1386
                dependsVersion += dependsExtension;
1387
            }
1388
            mailBody += indentString + RippleEngine.quoteString(depend.mName, dependsVersion) + "<br>";
1389
        }
1390
 
1391
        mailBody += "<br>Build standards:<br>";
1392
 
1393
        for (Iterator<BuildStandard> it = mBuildStandardCollection.iterator(); it.hasNext();)
1394
        {
1395
            BuildStandard bs = it.next();
1396
 
1397
            String bsText = bs.getBuildStandardText();
1398
            if (bsText.length() > 0)
1399
            {
1400
                mailBody += indentString + bsText + "<br>";
1401
            }
1402
        }
1403
 
1404
        mailBody += "<p><hr>";
1405
        try
1406
        {
1407
            String target = emailInfoNonAntTask(rippleEngine);
7033 dpurdie 1408
            mLogger.error("completeTestBuildEmail Server: " + rippleEngine.getMailServer());
1409
            mLogger.error("completeTestBuildEmail Sender: " + rippleEngine.getMailSender());
1410
            mLogger.error("completeTestBuildEmail Target: " + target);
6914 dpurdie 1411
 
1412
            Smtpsend.send(rippleEngine.getMailServer(), // mailServer
1413
                    rippleEngine.getMailSender(),       // source
1414
                    target,                         // target
1415
                    rippleEngine.getMailSender(),       // cc
1416
                    null,                           // bcc
1417
                    subject,                        // subject
1418
                    mailBody,                       // body
1419
                    null                            // attachment
1420
            );
1421
        } catch (Exception e)
1422
        {
1423
            mLogger.warn("Email Failure: completeTestBuild:" + e.getMessage());
1424
        }
1425
 
1426
        // Update the Release Manager Database
1427
        rippleEngine.mReleaseManager.markDaemonInstCompleted(mTestBuildInstruction);
7033 dpurdie 1428
        mLogger.error("completeTest. Returning");
6914 dpurdie 1429
    }
1430
 
1431
 
1432
    /**
1433
     * Returns true if the package is a part of a circular dependency
1434
     * 
1435
     * If the package depends on a package with a circular dependency then the function
1436
     * will return false.
1437
     */
1438
    public boolean hasCircularDependency(RippleEngine ripEng)
1439
    {
7048 dpurdie 1440
        mLogger.debug("hasCircularDependency: {}", mAlias);
6914 dpurdie 1441
        boolean retVal = detectCircularDependency(mAlias, ripEng, null);
7048 dpurdie 1442
        mLogger.info("hasCircularDependency returned {} ", retVal);
6914 dpurdie 1443
        return retVal;
1444
    }
1445
 
1446
    /**
1447
     * Returns true is a part of a circular dependency
1448
     * Will examine all the packages sub dependencies and mark those that do have a
1449
     * circular dependency.
1450
     * 
1451
     * This process works by descending the dependency tree and dropping a bread crumb
1452
     * If the bread crumb is seen during the decent, then a circle has been detected and
1453
     * the package (with the bread crumb) will be marked as having a circular dependency
1454
     * 
1455
     *  Assumes that the caller will walk ALL packages and flag those with a circular
1456
     *  dependence AND those that depend on that package.
1457
     * 
1458
     */
1459
    private boolean detectCircularDependency(String alias, RippleEngine ripEng, Package parent)
1460
    {
1461
        mLogger.debug("detectCircularDependency");
1462
        boolean retVal = false;
1463
 
1464
        // if this package has yet to be checked for circular dependency
1465
        if (!mCheckedCircularDependency)
1466
        {
1467
            // Will be set as we drill down through dependencies
1468
            // If we see this marker (bread crumb) then we have a loop
1469
            if (mBreadCrumb != 0)
1470
            {
1471
                mBreadCrumb = 3;
1472
 
1473
                mHasCircularDependency = true;
1474
                if(parent != null && parent.mBreadCrumb != 3 )
1475
                {
1476
                    parent.mBreadCrumb = 2;
1477
                }
1478
            }
1479
            else
1480
            {
1481
                // Mark this package as potentially having a circular dependency
1482
                // Will now drill down and see if we hit a marker
1483
                mBreadCrumb = 1;
1484
 
1485
                // Recurse down the dependencies and sub dependencies
1486
                for (Iterator<String> it2 = mDependencyCollection.iterator(); it2.hasNext();)
1487
                {
1488
                    String dependencyAlias = it2.next();
1489
                    Package dependency = ripEng.findPackage(dependencyAlias);
1490
                    dependency.detectCircularDependency(alias, ripEng, this);
1491
                }
1492
 
1493
                if (mBreadCrumb == 2)
1494
                {
1495
                    mHasCircularDependency = true;
1496
                    if(parent != null && parent.mBreadCrumb != 3 )
1497
                    {
1498
                        parent.mBreadCrumb = 2;
1499
                    }
1500
                }
1501
                mBreadCrumb = 0;
1502
            }
1503
 
1504
            // Flag package as having been examined
1505
            mCheckedCircularDependency = true;
1506
        } 
1507
 
1508
        // return the persisted circular dependency outcome
1509
        retVal = mHasCircularDependency;
7048 dpurdie 1510
        mLogger.info("detectCircularDependency 2 returned {}", retVal);
6914 dpurdie 1511
        return retVal;
1512
    }
7048 dpurdie 1513
 
1514
    /** Set the sequence of all packages in the list
1515
     *  Used so that the Unit Tests function can preserve the basic ordering of the list 
1516
     */
1517
    public static void setSequence(ArrayList<Package> al)
1518
    {
1519
        int seq = 1;
1520
        for (Iterator<Package> it = al.iterator(); it.hasNext(); )
1521
        {
1522
            Package p = it.next();
1523
            p.mSeqId = seq++;
1524
        }
1525
    }
6914 dpurdie 1526
 
7048 dpurdie 1527
 
1528
    /** Comparator for sorting package collections by mSeqId
1529
     *  Used to preserve order for unit testing
1530
     */
1531
    public static final Comparator<Package> SeqComparator = new Comparator<Package>() {
1532
 
1533
        /**
1534
         * Returns -ve: p1 is less than p2
1535
         *           0: p1 = p2
1536
         *         +ve: p1 > p2
1537
         */
1538
        public int compare (Package p1, Package p2) {
1539
                return p1.mSeqId - p2.mSeqId;
1540
        }
1541
    };    
1542
 
6914 dpurdie 1543
    /**
1544
     * entity class supporting the ERG version numbering standard:
1545
     * <major>.<minor>.<patch/build> patch/build is at least a 4 digit number
1546
     * whose last 3 digits represent the build
1547
     */
1548
    public class VersionNumberingStandard
1549
    {
1550
        /**
1551
         * in terms of the mChangeType Package field, when true indicates the
1552
         * contract of the package has changed in a non backwardly compatible
1553
         * manner in terms of the mRippleField Package field, when true indicates
1554
         * the major version number will be incremented
1555
         * 
1556
         * @attribute
1557
         */
1558
        private boolean mMajor = false;
1559
 
1560
        /**
1561
         * in terms of the mChangeType Package field, when true indicates the
1562
         * contract of the package has changed in a backwardly compatible manner
1563
         * in terms of the mRippleField Package field, when true indicates the
1564
         * minor version number will be incremented
1565
         * 
1566
         * @attribute
1567
         */
1568
        private boolean mMinor = false;
1569
 
1570
        /**
1571
         * in terms of the mChangeType Package field, when true indicates the
1572
         * contract of the package has not changed, but the package has changed
1573
         * internally in terms of the mRippleField Package field, when true
1574
         * indicates the minor version number will be incremented
1575
         * 
1576
         * @attribute
1577
         */
1578
        private boolean mPatch = false;
1579
 
1580
        /**
1581
         * in terms of the mChangeType Package field, when true indicates the
1582
         * package has not changed, its dependencies potentially have in terms
1583
         * of the mRippleField Package field, when true indicates the build
1584
         * number will be incremented
1585
         * 
1586
         * @attribute
1587
         */
1588
        private boolean mBuild = true;
1589
 
1590
        /**
1591
         * in terms of the mChangeType Package field, when true indicates the
1592
         * major, minor, and patch number will be incremented according to field
1593
         * limits in terms of the mRippleField Package field, when true indicates
1594
         * the major, minor, patch and build number will be incremented
1595
         * according to field limits
1596
         * 
1597
         * @attribute
1598
         */
1599
        private boolean mLimit = false;
1600
 
1601
        /**
1602
         * in terms of the mChangeType Package field, when true indicates the
1603
         * package version number will not be rippled. The user will have fixed
1604
         * the version number. This is only application to WIP packages
1605
         * 
1606
         * @attribute
1607
         */
1608
        private boolean mFixed = false;
1609
 
1610
        /**
1611
         * in terms of the mChangeType Package field, when true indicates the
1612
         * method of rippling a package version number is not known.
1613
         * 
1614
         * @attribute
1615
         */
1616
        private boolean mUnknown = false;
1617
 
1618
        /**
1619
         * constructor
1620
         */
1621
        private VersionNumberingStandard()
1622
        {
1623
            mLogger.debug("VersionNumberingStandard");
1624
        }
1625
 
1626
        /**
1627
         * Reset all values to a known state
1628
         * 
1629
         */
1630
        void resetData()
1631
        {
1632
            mBuild = false;
1633
            mMajor = false;
1634
            mMinor = false;
1635
            mPatch = false;
1636
            mLimit = false;
1637
            mFixed = false;
1638
            mUnknown = false;
1639
        }
1640
 
1641
        /**
1642
         * sets mBuild true, mMajor false, mMinor false, mPatch false, mLimit
1643
         * false
1644
         */
1645
        void setBuild()
1646
        {
1647
            mLogger.debug("setBuild");
1648
            resetData();
1649
            mBuild = true;
1650
        }
1651
 
1652
        /**
1653
         * sets mBuild false, mMajor true, mMinor false, mPatch false, mLimit
1654
         * false
1655
         */
1656
        void setMajor()
1657
        {
1658
            mLogger.debug("setMajor");
1659
            resetData();
1660
            mMajor = true;
1661
        }
1662
 
1663
        /**
1664
         * sets mBuild false, mMajor true, mMinor false, mPatch false, mLimit
1665
         * limit
1666
         */
1667
        void setMajor(boolean limit)
1668
        {
1669
            mLogger.debug("setMajor " + limit);
1670
            resetData();
1671
            mMajor = true;
1672
            mLimit = limit;
1673
        }
1674
 
1675
        /**
1676
         * sets mBuild false, mMajor false, mMinor true, mPatch false, mLimit
1677
         * false
1678
         */
1679
        void setMinor()
1680
        {
1681
            mLogger.debug("setMinor");
1682
            resetData();
1683
            mMinor = true;
1684
        }
1685
 
1686
        /**
1687
         * sets mBuild false, mMajor false, mMinor true, mPatch false, mLimit
1688
         * limit
1689
         */
1690
        void setMinor(boolean limit)
1691
        {
1692
            mLogger.debug("setMinor " + limit);
1693
            resetData();
1694
            mMinor = true;
1695
            mLimit = limit;
1696
        }
1697
 
1698
        /**
1699
         * sets mBuild false, mMajor false, mMinor false, mPatch true, mLimit
1700
         * false
1701
         */
1702
        void setPatch()
1703
        {
1704
            mLogger.debug("setPatch");
1705
            resetData();
1706
            mPatch = true;
1707
        }
1708
 
1709
        /**
1710
         * sets mBuild false, mMajor false, mMinor false, mPatch true, mLimit
1711
         * limit
1712
         */
1713
        void setPatch(boolean limit)
1714
        {
1715
            mLogger.debug("setPatch");
1716
            resetData();
1717
            mPatch = true;
1718
            mLimit = limit;
1719
        }
1720
 
1721
        /**
1722
         * sets mBuild false, mMajor false, mMinor false, mPatch false, mLimit
1723
         * true
1724
         */
1725
        void setLimit()
1726
        {
1727
            mLogger.debug("setPatch");
1728
            resetData();
1729
            mLimit = true;
1730
        }
1731
 
1732
        /**
1733
         * sets parameters to indicate that the change type is Fixed. The
1734
         * version number is set by the user and a ripple will not be calculated
1735
         */
1736
        void setFixed()
1737
        {
1738
            mLogger.debug("setFixed");
1739
            resetData();
1740
            mFixed = true;
1741
        }
1742
 
1743
        /**
1744
         * Sets parameters to indicate that the change type is not known
1745
         * 
1746
         */
1747
        void setUnknown()
1748
        {
1749
            resetData();
1750
            mUnknown = true;
1751
        }
1752
 
1753
    }
1754
 
1755
}