Subversion Repositories DevTools

Rev

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