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.sql.SQLException;
4
import java.util.ArrayList;
5
import java.util.Collections;
6
import java.util.Iterator;
7
import java.util.List;
8
import java.util.ListIterator;
9
 
10
import org.apache.log4j.Logger;
11
 
12
import com.erggroup.buildtool.ripple.BuildFile.BuildFileState;
13
import com.erggroup.buildtool.ripple.ReleaseManager.BuildReason;
14
import com.erggroup.buildtool.smtp.CreateUrls;
15
import com.erggroup.buildtool.smtp.Smtpsend;
16
import com.erggroup.buildtool.utilities.StringAppender;
17
import com.erggroup.buildtool.utilities.XmlBuilder;
18
 
19
/**Plans release impact by generating a set of Strings containing build file content.
20
 */
21
public class RippleEngine
22
{
23
 
24
    /**configured mail server
25
     * @attribute
26
     */
27
    private String mMailServer = "";
28
 
29
    /**configured mail sender user
30
     * @attribute
31
     */
32
    private String mMailSender = "";
33
 
34
    /**configured global email target
35
     * @attribute
36
     */
37
    private String mMailGlobalTarget = "";
38
 
39
    /** Vector of email addresses for global and project wide use
40
     *  Most emails will be sent to this list of email recipients
41
     */
42
    public List<String> mMailGlobalCollection = new ArrayList<String>();
43
 
44
    /**name associated with the baseline
45
     * @attribute
46
     */
47
    public String mBaselineName = "";
48
 
49
    /**collection of released pv_ids associated with the release
50
     * @attribute
51
     */
52
    public List<Integer> mReleasedPvIDCollection = new ArrayList<Integer>();
53
 
54
    /**Collection of build exceptions associated with the baseline
55
     * Used to determine (and report) what change in build exceptions happens as part of planRelease
56
     * Daemon centric
57
     * @aggregation shared
58
     * @attribute
59
     */
60
    ArrayList<BuildExclusion> mBuildExclusionCollection = new ArrayList<BuildExclusion>();
61
 
62
    /**Logger
63
     * @attribute
64
     */
65
    private static final Logger mLogger = Logger.getLogger(RippleEngine.class);
66
 
67
    /** Escrow information - commands to set up escrow
68
     * @attribute
69
     */
70
    private String mEscrowSetup;
71
 
72
    /** Escrow information - Raw data (May not be used)
73
     * @attribute
74
     */
75
    private String mEscrowRawData;
76
 
77
    /**package versions representing the baseline
78
     * escrow centric
79
     * @aggregation shared
80
     * @attribute
81
     */
82
    private ArrayList<Package> mPackageCollection = new ArrayList<Package>();
83
 
84
    /**index to current String item
85
     * @attribute
86
     */
87
    private int mBuildIndex;
88
 
89
    /**Database abstraction
90
     * @attribute
91
     */
92
    ReleaseManager mReleaseManager;
93
 
94
    /**Baseline identifier (rtag_id for a release manager baseline, bom_id for deployment manager baseline)
95
     * @attribute
96
     */
97
    private int mBaseline;
98
 
99
    /** Escrow Only: SBOM_ID
100
     */
101
    private int mSbomId;
102
 
103
    /** RTAG_ID
104
     *  Set from mBaseline
105
     */
106
    private int mRtagId;
107
 
108
    /**When true, mBuildCollection contains one item based on a release manager rtag_id and contains a daemon property
109
     * When false, mBuildCollection contains at least one item based on a deployment manager bom_id
110
     * Will be accessed by the Package class to calculate its mAlias
111
     * @attribute
112
     */
113
    public boolean mDaemon;
114
 
115
    /**collection of build file content in String form
116
     * @attribute
117
     */
118
    private ArrayList<BuildFile> mBuildCollection = new ArrayList<BuildFile>();
119
 
120
    /** List of packages that we plan to build
121
     * Used to provide feedback into RM
122
     * Only the first entry is about to be built as we re-plan every cycle
123
     */
124
    private ArrayList<Package> mBuildOrder = new ArrayList  <Package>();
125
    /**Warning message
126
     * @attribute
127
     */
128
    private static final String mAnyBuildPlatforms = "Warning. The following package versions are not reproducible on any build platform: ";
129
 
130
    /**Flag to control output to standard out
131
     * @attribute
132
     */
133
    private boolean mAnyBuildPlatformsFlag = true;
134
 
135
    /**Warning message
136
     * @attribute
137
     */
138
    private static final String mAssocBuildPlatforms = "Warning. The following package versions are not reproducible on the build platforms associated with this baseline: ";
139
 
140
    /**Flag to control output to standard out
141
     * @attribute
142
     */
143
    private boolean mAssocBuildPlatformsFlag = true;
144
 
145
    /**Warning message
146
     * @attribute
147
     */
148
    private static final String mNotInBaseline = "Warning. The following package versions are not reproducible as they are directly dependent upon package versions not in the baseline: ";
149
 
150
    /**Flag to control output to standard out
151
     * @attribute
152
     */
153
    private boolean mNotInBaselineFlag = true;
154
 
155
    /**Warning message
156
     * @attribute
157
     */
158
    private static final String mDependent = "Warning. The following package versions are not reproducible as they are directly/indirectly dependent upon not reproducible package versions: ";
159
 
160
    /**Flag to control output to standard out
161
     * @attribute
162
     */
163
    private boolean mDependentFlag = true;
164
 
165
    /**Warning message
166
     * @attribute
167
     */
168
    private static final String mCircularDependency = "Warning. The following package versions are not reproducible as they have circular dependencies: ";
169
 
170
    /**Flag to control output to standard out
171
     * @attribute
172
     */
173
    private boolean mCircularDependencyFlag = true;
174
 
175
    /** String used to terminate lines
176
     * @attribute
177
     */
178
    private static final  String mlf = System.getProperty("line.separator");
179
 
180
    /** XML File Prefix
181
     */
182
    private static final String mXmlHeader = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>" + mlf;
183
 
184
    /**RippleEngine constructor
185
     * @param releaseManager  - Associated releaseManager instance
186
     * @param rtagId         - Release Identifier
187
     * @param isDaemon        - Mode of operation. False: Escrow, True: Daemon
188
     */
189
    public RippleEngine(ReleaseManager releaseManager, int rtagId, boolean isDaemon)
190
    {
191
        mLogger.debug("RippleEngine rtag_id " + rtagId + " isDaemon " + isDaemon);
192
        mReleaseManager = releaseManager;
193
        mBaseline = rtagId;
194
        mRtagId = rtagId;
195
        mDaemon = isDaemon;
196
        mReleaseManager.setDaemonMode(mDaemon);
197
    }
198
 
199
    /**
200
     * getRtagId
201
     */
202
    public int getRtagId()
203
    {
204
        return mRtagId;
205
    }
206
 
207
    /**Plan what is to be built
208
     * 	<br>Discards all build file content
209
     * 	<br>Generates new build file content
210
     * 
211
     * @param lastBuildActive 		- False. Daemon Mode. The last build was a dummy. 
212
     *                                This planning session may not result in a build
213
     *                              - True. Daemon Mode. The last build was not a dummy.
214
     *                                There is a very good chance that this planning session
215
     *                                will result in a build, so it is given priority to connect 
216
     *                                to the database.	
217
     */
218
    public void planRelease(final boolean lastBuildActive) throws SQLException, Exception
219
    {
220
        mLogger.warn("planRelease mDaemon " + mDaemon);
221
 
222
        mBuildCollection.clear();
223
        mPackageCollection.clear();
224
        mReleasedPvIDCollection.clear();
225
        mBuildOrder.clear();
226
        mEscrowRawData = "";
227
        mEscrowSetup = "";
228
        Phase phase = new Phase("Plan");
229
 
230
        // use finally block in planRelease to ensure the connection is released
231
        try
232
        {
233
            phase.setPhase("connectForPlanning");
234
            mReleaseManager.connectForPlanning(lastBuildActive);
235
 
236
            if ( mDaemon )
237
            {
238
                // claim the mutex
239
                mLogger.warn("planRelease claimMutex");
240
                phase.setPhase("claimMutex");
241
                mReleaseManager.claimMutex();
242
 
243
                // Populate the mBuildExclusionCollection
244
                //
245
                // Builds are either 'Directly excluded' or 'Indirectly excluded'
246
                // Direct excludes result from:
247
                //      User request
248
                //      Build failure
249
                //      Inability to build package
250
                // Indirectly excluded packages result from have a build dependency on a package
251
                // that is directly excluded.
252
                //  
253
                // In the following code we will extract from the Database all build exclusions
254
                // We will then add 'Relevant' entries to mBuildExclusionCollection
255
                // and delete, from the database those that are no longer relevant - ie indirectly excluded
256
                // items where the root cause is no longer in the set.
257
                phase.setPhase("mBuildExclusionCollection");
258
                mBuildExclusionCollection.clear();
259
                ArrayList<BuildExclusion> tempBuildExclusionCollection = new ArrayList<BuildExclusion>();
260
 
261
                mLogger.debug("planRelease queryBuildExclusions");
262
                mReleaseManager.queryBuildExclusions(tempBuildExclusionCollection, mBaseline);
263
 
264
                // only populate mBuildExclusionCollection with tempBuildExclusionCollection entries which have a relevant root_pv_id
265
                // The entry is relevant if:
266
                //     It is for a directly excluded package,other than a RippleStop
267
                //     It is for an indirectly excluded package AND the reason for exclusion still exists
268
                //
269
 
270
                for (Iterator<BuildExclusion> it = tempBuildExclusionCollection.iterator(); it.hasNext(); )
271
                {
272
                    BuildExclusion buildExclusion = it.next();
273
 
274
                    if ( buildExclusion.isRelevant(tempBuildExclusionCollection) )
275
                    {
276
                        mBuildExclusionCollection.add(buildExclusion);
277
                    }
278
                    else
279
                    {
280
                        // Remove the indirectly excluded entry as its root cause
281
                        // is no longer present.
282
                        buildExclusion.includeToBuild(mReleaseManager, mBaseline);
283
                    }
284
                }
285
            }
286
 
287
            //-----------------------------------------------------------------------
288
            //	Query package versions
289
            //
290
            phase.setPhase("queryPackageVersions");
291
            mLogger.debug("planRelease queryPackageVersions");
292
            mReleaseManager.queryPackageVersions(this, mPackageCollection, mBaseline);
293
 
294
            //------------------------------------------------------------------------
295
            //    Process packages collected
296
            //    Determine and tag those we can't build
297
            phase.setPhase("processPackages");
298
            processPackages();
299
 
300
            //-----------------------------------------------------------------------
301
            //    At this point we have tagged all the packages that we cannot build
302
            //    Now we can determine what we are building
303
            phase.setPhase("planBuildOrder");
304
            mLogger.debug("planRelease process Remaining");
305
            planBuildOrder();
306
 
307
            //  Report excluded packages and the build plan
308
            //  This is being done with the MUTEX being held, but the 
309
            //  trade off is the cost of getting a connection.
310
            //
311
            if ( mDaemon )
312
            {
313
                phase.setPhase("Report Change");
314
                reportChange();
315
                phase.setPhase("Report Plan");            
316
                reportPlan();
317
            }
318
 
319
            //
320
            //	Generate the build Files
321
            //
322
            phase.setPhase("generateBuildFiles");
323
            generateBuildFiles();
324
        }
325
        finally
326
        {
327
            mLogger.debug("planRelease finally");
328
            // this block is executed regardless of what happens in the try block
329
            // even if an exception is thrown
330
            // ensure the SELECT FOR UPDATE is released
331
            try
332
            {
333
                if ( mDaemon )
334
                {
335
                    // attempt to release the SELECT FOR UPDATE through a commit
336
                    // a commit must be done in the normal case
337
                    // a commit may as well be done in the Exception case
338
                    // in the case of a SQLException indicating database connectivity has been lost
339
                    // having a go at the commit is superfluous
340
                    // as the SELECT FOR UPDATE will have been released upon disconnection
341
                    phase.setPhase("releaseMutex");
342
                    mReleaseManager.releaseMutex();
343
                }
344
            }
345
            finally
346
            {
347
                // ensure disconnect under all error conditions
348
                phase.setPhase("disconnectForPlanning");
349
                mReleaseManager.disconnectForPlanning(lastBuildActive);
350
            }
351
        }
352
 
353
        mLogger.warn("planRelease mDaemon " + mDaemon + " returned");
354
        phase.setPhase("EndPlan");
355
    }
356
 
357
    /** Process packages that have been collected as a part of the plan
358
     * @param phase
359
     * @throws SQLException
360
     * @throws Exception
361
     */
362
    private void processPackages() throws SQLException, Exception {
363
 
364
        // Deal with test builds here as they may impact upon package attributes
365
        //    eg dependency collection and build standard differences
366
        //    This gives test builds preferential treatment
367
        //    Note: Done before mPackageDependencyCollection is setup
368
        //
369
        if ( mDaemon )
370
        {
371
            // process test builds
372
            for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
373
            {
374
                Package p = it.next();
375
 
376
                if (p.mBuildFile == 0)
377
                {
378
                    // package has yet to be processed
379
                    if (  p.mTestBuildInstruction > 0 )
380
                    {
381
                        mLogger.info("planRelease package test build " + p.mName);
382
 
383
                        //
384
                        //    Cannot test build an SDK based package or a Pegged Package
385
                        //        Remove the test build request from the database
386
                        //        Send a warning email
387
                        //
388
                        if(p.mIsPegged || p.mIsSdk)
389
                        {
390
                            String reason;
391
                            reason = (p.mIsPegged) ? "Pegged" : "SDK Based";
392
 
393
                            mLogger.fatal("planRelease Daemon Instruction of " + reason + " package deleted: " + p.mName);
394
                            mReleaseManager.markDaemonInstCompleted( p.mTestBuildInstruction );
395
                            emailRejectedDaemonInstruction("Cannot 'Test Build' a " + reason + " package",p);
396
                        }
397
 
398
                        // force patch for test build numbering
399
                        p.mDirectlyPlanned = true;
400
                        p.mChangeType.setPatch();
401
                        p.mRequiresSourceControlInteraction = false;
402
                        p.mBuildReason = BuildReason.Test;
403
                        rippleIndirectlyPlanned(p);
404
 
405
                        // put the mTestBuildAttributes to work
406
                        p.mVcsTag = p.mTestBuildVcsTag;
407
                        p.mId = p.mTestBuildPvId;
408
                        p.setTestEmail();
409
                        p.setTestDependencyCollection();
410
                        p.setTestBuildStandardCollection();
411
                        p.mHasAutomatedUnitTests = p.mTestBuildHasAutomatedUnitTests;
412
                    }
413
                }
414
            }
415
        }
416
 
417
        // Set up mPackageDependencyCollection on each package
418
        //    Examine the dependencies by alias and convert this to a 'package' selected from the released package set
419
        mLogger.debug("planRelease setup mPackageDependencyCollection");
420
        for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
421
        {
422
            Package p = it.next();
423
 
424
            for (Iterator<String> it2 = p.mDependencyCollection.iterator(); it2.hasNext(); )
425
            {
426
                String alias = it2.next();
427
                Package dependency = findPackage(alias);
428
 
429
                p.mPackageDependencyCollection.add(dependency);
430
            }
431
        }
432
 
433
        // DEVI 56479 detect and deal with circular dependencies
434
        mLogger.debug("planRelease deal with circular dependencies");
435
        for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
436
        {
437
            Package p = it.next();
438
 
439
            if ( p.hasCircularDependency( this ) )
440
            {
441
                mLogger.info("planRelease circular dependency detected " + p.mAlias);
442
 
443
                //  Force this package to be marked as having a circular dependency - even if its been excluded
444
                p.mBuildFile = 0;
445
 
446
                // exclude all dependent packages
447
                // max 50 chars
448
                rippleBuildExclude(p, p.mId, "Package has circular dependency", null, null, true);
449
 
450
                // take the package out of the build
451
                p.mBuildFile = -6;
452
                mLogger.info("planRelease set mBuildFile to -6 for package " + p.mAlias );
453
                standardOut(mCircularDependency, p.mAlias, mCircularDependencyFlag);
454
                mCircularDependencyFlag = false;
455
            }
456
        }
457
 
458
        // Scan for packages with missing dependencies
459
        //    ie: The dependent package is not in the package Collection
460
        //        DEVI 55483 now use the fully built mPackageDependencyCollection (in rippleBuildExclude)
461
        mLogger.debug("planRelease use the fully built mPackageDependencyCollection");
462
        for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
463
        {
464
            Package p = it.next();
465
 
466
            if ( mDaemon )
467
            {
468
                //  Daemon Mode Only
469
                //  Not interested in the dependencies of a pegged package or a package provided from an SDK.
470
                //  Such packages will be deemed to not have missing dependencies
471
                if (p.mIsPegged || p.mIsSdk)
472
                {
473
                    continue;
474
                }
475
            }
476
 
477
            for (Iterator<String> it2 = p.mDependencyCollection.iterator(); it2.hasNext(); )
478
            {
479
                String alias = it2.next();
480
                Package dependency = findPackage(alias);
481
 
482
                if (dependency == ReleaseManager.NULL_PACKAGE)
483
                {
484
                    mLogger.info("planRelease dependency is not in the baseline " + alias);
485
                    // exclude all dependent packages
486
                    // max 50 chars
487
                    rippleBuildExclude(p, p.mId, "Package build dependency not in the release", null, null, true);
488
 
489
                    // take the package out of the build
490
                    p.mBuildFile = -4;
491
                    mLogger.info("planRelease set mBuildFile to -4 for package " + p.mAlias );
492
                    standardOut(mNotInBaseline, p.mAlias, mNotInBaselineFlag);
493
                    mNotInBaselineFlag = false;
494
                    break;
495
                }
496
            }
497
        }
498
 
499
        // Process packages which are not reproducible, and all packages dependent upon them
500
        //    ie: Have no build standard      
501
        mLogger.debug("planRelease process packages which are not reproducible");
502
        for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
503
        {
504
            Package p = it.next();
505
 
506
            if (p.mBuildFile == 0)
507
            {
508
                if ( mDaemon )
509
                {
510
                    //  Daemon Mode Only
511
                    //  Not interested in the reproducibility of a pegged package or a package provided from an SDK.
512
                    //  Such packages will be deemed to be reproducible - but out side of this release
513
                    if (p.mIsPegged || p.mIsSdk)
514
                    {
515
                        continue;
516
                    }
517
                }
518
 
519
                // package has yet to be processed
520
                if (!p.isReproducible())
521
                {
522
                    // for escrow build purposes, exclude all dependent package versions
523
                    mLogger.info("planRelease package not reproducible " + p.mName);
524
                    // max 50 chars
525
                    rippleBuildExclude(p, p.mId, "Package has no build environment", null, null, true);
526
 
527
                    // package is not reproducible, discard
528
                    p.mBuildFile = -1;
529
                    mLogger.info("planRelease set mBuildFile to -1 for package " + p.mAlias );
530
                    standardOut(mAnyBuildPlatforms, p.mAlias, mAnyBuildPlatformsFlag);
531
                    mAnyBuildPlatformsFlag = false;
532
                }
533
            }
534
        }
535
 
536
        //    Process packages which are not reproducible on the configured set of  build machines.
537
        //
538
        //    Test each package and determine if the package contains a buildStandard that
539
        //    can be processed by one of the machines in the build set
540
        //
541
        //    ie: Package: Win32:Production and we have a BM with a class of 'Win32'
542
        //
543
        //    July-2014 
544
        //    Only exclude the failing package and not its dependents
545
        //    May be legitimate in the release
546
        //
547
        mLogger.debug("planRelease process packages which are not reproducible2");
548
        for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
549
        {
550
            Package p = it.next();
551
 
552
            if (p.mBuildFile == 0)
553
            {
554
                if ( mDaemon )
555
                {
556
                    //  Daemon Mode Only
557
                    //  Not interested in the reproducibility of a pegged package or a package provided from an SDK.
558
                    //  Such packages will be deemed to be reproducible - but out side of this release
559
                    if (p.mIsPegged || p.mIsSdk)
560
                    {
561
                        continue;
562
                    }
563
                }
564
 
565
                // package has yet to be processed
566
                // assume it does not need to be reproduced for this baseline
567
                //
568
                //    For each machineClass in the buildset
569
                boolean reproduce = false;
570
 
571
                for (Iterator<String> it2 = mReleaseManager.mReleaseConfigCollection.mMachineClasses.iterator(); it2.hasNext(); )
572
                {
573
                    String machineClass = it2.next();
574
 
575
                    if ( p.canBeBuildby(machineClass))
576
                    {
577
                        reproduce = true;
578
                        mLogger.info("planRelease package built on " + machineClass + " " + p.mAlias );
579
                        break;
580
                    }
581
                }
582
 
583
                if ( !reproduce )
584
                {
585
                    mLogger.info("planRelease package not reproducible on the build platforms configured for this baseline " + p.mName);
586
 
587
                    if (mDaemon)
588
                    {
589
                        // DEVI 54816
590
                        // for escrow build purposes, do not exclude all dependent package versions
591
                        // max 50 chars
592
                        rippleBuildExclude(p, p.mId, "Package not built for configured platforms", null, null, false);
593
                    }
594
 
595
                    // package is not reproducible on the build platforms configured for this baseline, discard
596
                    p.mBuildFile = -2;
597
                    mLogger.info("planRelease set mBuildFile to -2 for package " + p.mAlias );
598
                    standardOut(mAssocBuildPlatforms, p.mAlias, mAssocBuildPlatformsFlag);
599
                    mAssocBuildPlatformsFlag = false;
600
                }
601
            }
602
        }      
603
 
604
        if (mDaemon)
605
        {
606
            // Daemon Mode Only
607
            // Process packages which are not ripple buildable, and all packages dependent upon them
608
            mLogger.debug("planRelease process packages which are not ripple buildable");
609
            for (ListIterator<BuildExclusion> it = mBuildExclusionCollection.listIterator(); it.hasNext(); )
610
            {
611
                BuildExclusion be = it.next();
612
 
613
                for (Iterator<Package> it1 = mPackageCollection.iterator(); it1.hasNext(); )
614
                {
615
                    Package p = it1.next();
616
 
617
                    // ensure only root cause, non test build, build exclusions are excluded
618
                    // mBuildExclusionCollection is at this point based on
619
                    // relevant (direct and indirect) excluded pv's in the database
620
                    if ( be.compare(p.mId) && be.isARootCause() && p.mTestBuildInstruction == 0 && p.mForcedRippleInstruction == 0 )
621
                    {
622
                        // package is not reproducible, discard
623
                        rippleBuildExclude( p, p.mId, null, it, be, true );
624
                        p.mBuildFile = -3;
625
                        mLogger.info("planRelease set mBuildFile to -3 for package " + p.mAlias );
626
                        break;
627
                    }
628
                }
629
            }
630
 
631
            //  Daemon Mode Only
632
            //  Process packages which need to be ripple built
633
            mLogger.debug("planRelease process packages which need to be ripple built");
634
            for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
635
            {
636
                Package p = it.next();
637
 
638
                if (p.mBuildFile == 0)
639
                {
640
                    //  Not interested in a pegged package or a package provided from an SDK.
641
                    //  Such packages are not rippled
642
                    if (p.mIsPegged || p.mIsSdk)
643
                    {
644
                        continue;
645
                    }
646
 
647
                    // package has yet to be processed
648
                    if (p.mDirectlyPlanned)
649
                    {
650
                        // a WIP exists on the package, or it is a Test Build
651
                        // exclude all dependent package versions
652
                        mLogger.info("planRelease package has WIP " + p.mName);
653
                        if ( p.mBuildReason == null)
654
                            p.mBuildReason = BuildReason.NewVersion;
655
                        rippleIndirectlyPlanned(p);
656
                    }
657
                    else
658
                    {
659
                        //    Examine this packages dependencies
660
                        //    If one of them does not exist in the 'official release' set then
661
                        //    the package needs to be built against the official release set
662
                        //    and we can't build any of its dependent packages
663
                        Iterator<Integer> it2 = p.mDependencyIDCollection.iterator();
664
                        Iterator<Package> it3 = p.mPackageDependencyCollection.iterator();
665
                        while ( it2.hasNext() && it3.hasNext() )
666
                        {
667
                            Integer dpvId = it2.next();
668
                            Package dependency = it3.next();
669
 
670
                            if ( !dependency.mAdvisoryRipple )
671
                            {
672
                                // not advisory, ie: has ripple build impact
673
                                if ( !isInRelease(dpvId) )
674
                                {
675
                                    // the package is out of date
676
                                    // exclude all dependent package versions
677
                                    mLogger.info("planRelease package out of date " + p.mName);
678
                                    p.mBuildReason = BuildReason.Ripple;
679
                                    rippleIndirectlyPlanned(p);
680
 
681
                                    //  This package needs to be rippled
682
                                    //  If this package has a rippleStop marker of 's', then we cannot
683
                                    //  build this package at the moment.
684
                                    //  Packages that depend on this package have been excluded
685
                                    //  We need to exclude this one too
686
                                    //
687
                                    if (p.mRippleStop == 's' || p.mRippleStop == 'w') 
688
                                    {
689
                                        // Package marked as a rippleStop
690
                                        // max 50 chars
691
                                        rippleBuildExclude(p, -2, "Ripple Required." + " Waiting for user", null, null, false);
692
 
693
                                        // package is unBuildable, mark reason and discard
694
                                        p.mBuildFile = -11;
695
 
696
                                        if (p.mRippleStop == 's' ) {
697
                                            // Need to flag to users that the package build is waiting user action
698
                                            mLogger.info("planRelease " + "Ripple Required." + " Stopped by flag" + p.mName);
699
                                            mReleaseManager.setRippleStopWait(mRtagId,p);
700
                                        }
701
                                    }
702
 
703
 
704
                                    break;
705
                                }
706
                            }
707
                        }
708
                    }
709
                }
710
            }
711
 
712
            //  Daemon Mode Only
713
            //  Process packages which do not exist in the archive
714
            //  For unit test purposes, assume all packages exist in the archive if released
715
            mLogger.debug("planRelease process packages which do not exist in the archive");
716
            if ( ReleaseManager.mUseDatabase )
717
            {
718
                for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
719
                {
720
                    Package p = it.next();
721
 
722
                    if (p.mBuildFile == 0)
723
                    {
724
                        // package has yet to be processed
725
                        if (!p.mDirectlyPlanned && !p.mIndirectlyPlanned && p.mForcedRippleInstruction == 0)
726
                        {
727
                            // check package version archive existence
728
                            if (!p.existsInDpkgArchive())
729
                            {
730
                                if (! p.mIsBuildable)
731
                                {
732
                                    //  Package does not exist in dpkg_archive and it has been flagged as unbuildable
733
                                    //  This may be because is Unbuildable or Manually built
734
                                    mLogger.info("planRelease " + "Unbuildable" + " package not found in archive " + p.mName);
735
                                    // max 50 chars
736
                                    rippleBuildExclude(p, p.mId, "Unbuildable" + " package not found in archive", null, null, true);
737
 
738
                                    // package is unBuildable, mark reason and discard
739
                                    p.mBuildFile = -10;
740
 
741
                                }
742
                                //  Not interested in a pegged package or a package provided from an SDK.
743
                                //  Such packages are not rippled
744
                                else if (p.mIsPegged || p.mIsSdk)
745
                                {
746
                                    String reason;
747
                                    reason = (p.mIsPegged) ? "Pegged" : "SDK";
748
 
749
                                    //  Pegged packages or packages provided from an SDK MUST exist in dpkg_archive
750
                                    //  They will not be built within the context of this release. It is the responsibility
751
                                    //  of another release to build them.
752
                                    mLogger.info("planRelease " + reason + " package not found in archive " + p.mName);
753
                                    // max 50 chars
754
                                    rippleBuildExclude(p, p.mId, reason + " package not found in archive", null, null, true);
755
 
756
                                    // package is not reproducible, mark reason and discard
757
                                    p.mBuildFile = -7;
758
 
759
                                }
760
                                else if (p.mForcedRippleInstruction == 0)
761
                                {
762
                                    //  [JATS-331] Unable to rebuild package with Advisory Ripple dependencies
763
                                    //    Examine this packages dependencies
764
                                    //    If one of them does not exist in the 'official release' set then
765
                                    //    the package cannot be rebuilt in this release.
766
                                    for (Iterator<Integer> it2 = p.mDependencyIDCollection.iterator() ; it2.hasNext() ;)
767
                                    {
768
                                        Integer dpvId = it2.next();
769
                                        if ( !isInRelease(dpvId) )
770
                                        {
771
                                            // This package cannot be rebuilt as one of its dependents is NOT in this release
772
                                            // exclude all dependent package versions
773
 
774
                                            mLogger.info("planRelease package not found in archive. Cannot be rebuilt du to  " + p.mName);
775
                                            // max 50 chars
776
                                            rippleBuildExclude(p, p.mId, "Package cannot be rebuilt in this release", null, null, true);
777
 
778
                                            // package is not reproducible, mark reason and discard
779
                                            p.mBuildFile = -4;
780
                                            break;
781
                                        }
782
                                    }
783
 
784
                                    //  The package has not been excluded from the build
785
                                    if (p.mBuildFile == 0)
786
                                    {
787
                                        mLogger.info("planRelease package not found in archive " + p.mName);
788
                                        // DEVI 47395 the cause of this build is not WIP or ripple induced,
789
                                        // it simply does not exist in the archive (has been removed)
790
                                        // prevent source control interaction
791
                                        p.mRequiresSourceControlInteraction = false;
792
                                        p.mBuildReason = BuildReason.Restore;
793
                                        rippleIndirectlyPlanned(p);
794
                                    }
795
                                }
796
                            }
797
                        }
798
                    }
799
                }
800
            }
801
 
802
            //  Daemon Mode Only
803
            //  Process forced ripples
804
            mLogger.debug("planRelease process forced ripples");
805
            for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
806
            {
807
                Package p = it.next();
808
 
809
                if (p.mBuildFile == 0)
810
                {
811
                    // package has yet to be processed
812
                    if ( p.mForcedRippleInstruction > 0 )
813
                    {
814
                        //
815
                        //    Cannot force a ripple on an SDK based package or a Pegged Package
816
                        //        Remove the daemon instruction from the database
817
                        //        Send a warning email
818
                        //
819
                        if(p.mIsPegged || p.mIsSdk)
820
                        {
821
                            String reason;
822
                            reason = (p.mIsPegged) ? "Pegged" : "SDK Based";
823
 
824
                            mLogger.fatal("planRelease Daemon Instruction of " + reason + " package deleted: " + p.mName);
825
                            mReleaseManager.markDaemonInstCompleted( p.mForcedRippleInstruction );
826
                            emailRejectedDaemonInstruction("Cannot 'Ripple' a " + reason + " package",p);
827
 
828
                            p.mBuildFile = -8; 
829
                        }
830
                        else
831
                        {
832
                            mLogger.info("planRelease package forced ripple " + p.mName);
833
                            p.mBuildReason = BuildReason.Ripple;
834
                            rippleIndirectlyPlanned(p);
835
                        }
836
                    }
837
                }
838
            }
839
 
840
            //  Daemon Mode Only
841
            //  Mark Pegged and SDK packages as not to be built
842
            //  Have previously detected conflicts between pegged/sdk packages and daemon instructions
843
            //
844
            mLogger.debug("planRelease remove pegged and SDK packages from the build set");
845
            for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
846
            {
847
                Package p = it.next();
848
 
849
                if (p.mBuildFile == 0)
850
                {
851
                    //  Not interested in a pegged package or a package provided from an SDK.
852
                    //  Such packages are not built
853
                    if (p.mIsPegged || p.mIsSdk)
854
                    {
855
                        String reason;
856
                        reason = (p.mIsPegged) ? "Pegged" : "SDK";
857
 
858
                        mLogger.info("planRelease " + reason + " not built in this release " + p.mName);
859
                        p.mBuildFile = -8;
860
                    }
861
                }
862
            }
863
 
864
            //  Daemon Mode Only
865
            //  Locate Test Build Requests that cannot be satisfied - will not be built due to
866
            //  errors in dependent packages. Report the error to the user and remove the request
867
            //
868
            for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
869
            {
870
                Package p = it.next();
871
 
872
                if (p.mTestBuildInstruction > 0)
873
                {
874
                    if (p.mBuildFile < 0)
875
                    {
876
                        String reason;
877
                        switch (p.mBuildFile)
878
                        {
879
                        case -1: reason = "Not reproducible"; break;
880
                        case -2: reason = "Not reproducible on configured build platforms"; break;
881
                        case -3: reason = "Marked as 'Do not ripple'"; break;
882
                        case -4: reason = "Dependent on a package not in the release"; break;
883
                        case -5: reason = "Indirectly dependent on a package not reproducible in the release"; break;
884
                        case -6: reason = "Has a circular dependency"; break;
885
                        case -7: reason = "Pegged or SDK package not in dpkg_archive"; break;
886
                        case -8: reason = "Is a Pegged or SDK package"; break;
887
                        case -9: reason = "Rejected Daemon Instruction"; break;
888
                        case -10: reason = "Unbuildable package not in dpkg_archive"; break;
889
                        case -11: reason = "Marked as 'RippleStop'"; break;
890
                        default: reason = "Unknown reason. Code:" + p.mBuildFile; break;
891
                        }
892
                        mLogger.fatal("planRelease Test Build of an unbuildable of package deleted: " + p.mName);
893
                        mReleaseManager.markDaemonInstCompleted( p.mTestBuildInstruction );
894
                        emailRejectedDaemonInstruction(reason,p);
895
                    }
896
                }
897
            }
898
        }
899
        else
900
        {
901
            // escrow reporting only
902
            // Report packages that are not reproducible
903
            //
904
            //  Note: I don't believe this code can be executed
905
            //        The -3 is only set in daemon mode
906
            for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
907
            {
908
                Package p = it.next();
909
 
910
                if (p.mBuildFile == -3)
911
                {
912
                    standardOut(mDependent, p.mAlias, mDependentFlag);
913
                    mDependentFlag = false;
914
                }
915
            }
916
        }
917
    }
918
 
919
    /** Plan the build order.
920
     *  Assumes that a great deal of work has been done.
921
     *  This is a stand alone method to contain the work
922
     *  
923
     * @throws Exception
924
     * @throws SQLException
925
     */
926
    private void planBuildOrder() throws Exception, SQLException {
927
 
928
        // Process remaining packages which are need to be reproduced for this baseline.
929
        //    Determine the build file for each package
930
        //    For daemon builds:
931
        //      Determine the first package that can be built now, this means the first package 
932
        //      not dependent upon packages also to be built.
933
        //      Set its mBuildNumber to 1, all remaining reproducible packages to 2
934
        //      Add buildable packages to mBuildableCollection
935
        //    For escrow builds:
936
        //      Determine the package versions that can be built in the build iteration
937
        //      Set their mBuildNumber to the build iteration
938
        //      Increment the build iteration and repeat until all package versions 
939
        //      that need to be reproduced have been assigned a build iteration        
940
 
941
        if ( mDaemon )
942
        {
943
            // Sort the build order such that the unit tests are sorted by instruction id 
944
            //  and placed at the head of the list. This is an attempt to have the oldest 
945
            //  unit tests at the head of the list.
946
            //
947
            //  The order of all other elements is preserved
948
            //  The order of the other elements is basically
949
            //      planned - ordered by modified date-time
950
            //      others  - order by pv_id
951
            //
952
            Package.setSequence(mPackageCollection);
953
            Collections.sort(mPackageCollection, Package.UnitTestComparator);
954
        }
955
 
956
        boolean allProcessed = false;
957
        int buildFile = 1;
958
 
959
        do
960
        {
961
            boolean allDependenciesProcessed = true;
962
            do
963
            {
964
                // assume all dependencies have been processed
965
                allDependenciesProcessed = true;
966
 
967
                for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
968
                {
969
                    Package p = it.next();
970
 
971
                    if ( (  mDaemon && ( ( !p.mDirectlyPlanned && !p.mIndirectlyPlanned ) || p.mBuildFile < 0 ) ) ||
972
                         ( !mDaemon && p.mBuildFile == -2 ) )
973
                    {
974
                        // Daemon: Flag packages with no build requirement as processed
975
                        // Escrow: Flag packages with a foreign build environment as processed
976
                        p.mProcessed = true;
977
                        mLogger.info("planRelease package has no build requirement " + p.mName);            
978
                    }
979
                    else if ( ( p.mBuildFile == 0 ) && ( (mDaemon && ( p.mDirectlyPlanned || p.mIndirectlyPlanned ) ) || ( !mDaemon ) ) )
980
                    {
981
                        // package yet to be processed and
982
                        //  Daemon mode: Has a build requirement
983
                        //  Escrow mode: Can be reproduced
984
                        boolean canBeBuiltNow = true;
985
                        boolean allDependenciesForThisPackageProcessed = true;
986
 
987
                        //  Scan the packages dependencies
988
                        for ( Iterator<Package> it2 = p.mPackageDependencyCollection.iterator(); it2.hasNext(); )
989
                        {
990
                            Package dependency = it2.next();
991
 
992
                            if ( !dependency.mProcessed )
993
                            {
994
                                //  If all the dependencies have been processed, then we can potentially build this package
995
                                //  If any of them have not been processed, then cannot calculate canBeBuiltNow until this 
996
                                //  dependency has been processed
997
                                allDependenciesForThisPackageProcessed = false;
998
                                allDependenciesProcessed = false;
999
                            }
1000
                            else if (  (  mDaemon && ( dependency.mDirectlyPlanned ) || ( dependency.mIndirectlyPlanned ) ) 
1001
                                    || ( !mDaemon && ( ( dependency.mBuildFile == 0 ) ||
1002
                                                           ( dependency.mBuildFile == buildFile &&
1003
                                                           ( !p.haveSameBuildStandards(dependency)  ) ) ) ) )
1004
                            {
1005
                                // Daemon mode:
1006
                                //    This processed dependency has a build requirement - so can't build
1007
                                // Escrow mode: This package cannot be built now if
1008
                                //    This processed dependency has not been assigned to a build iteration or
1009
                                //    This processed dependency has been assigned to this build iteration, but the 
1010
                                //    build standards of the package and this dependency prevent the package being
1011
                                //    built in this iteration.   
1012
                                canBeBuiltNow = false;
1013
                                mLogger.info("planRelease package cannot be built in this iteration " + p.mName);
1014
                                break;
1015
                            }
1016
                        }
1017
 
1018
                        if (allDependenciesForThisPackageProcessed)
1019
                        {
1020
                            p.mProcessed = true;
1021
 
1022
                            if ( mDaemon )
1023
                            {
1024
                                mBuildOrder.add(p);
1025
                                if ( canBeBuiltNow )
1026
                                {
1027
                                    // flag package with build requirement, may get down graded to future build requirement
1028
                                    p.mBuildFile = 1;
1029
                                    mLogger.info("planRelease set mBuildFile to 1 for package " + p.mAlias );
1030
                                }
1031
                                else
1032
                                {
1033
                                    // flag package with future build requirement
1034
                                    p.mBuildFile = 2;
1035
                                    mLogger.info("planRelease set mBuildFile to 2 for package " + p.mAlias );
1036
                                }
1037
                            }
1038
                            else
1039
                            {
1040
                                // Escrow Mode
1041
                                if ( canBeBuiltNow )
1042
                                {
1043
                                    mBuildOrder.add(p);
1044
                                    p.mBuildFile = buildFile;
1045
                                    mLogger.info("planRelease set mBuildFile to " + buildFile + " for package " + p.mAlias );
1046
                                }
1047
                            }
1048
                        }
1049
                    }
1050
                }
1051
            } while( !allDependenciesProcessed );
1052
 
1053
            //
1054
            //  Daemon Mode:
1055
            //  Locate all packages that we would like to build and
1056
            //      Calculate new version - report errors
1057
            //      Determine a package we would like to build
1058
            //
1059
            if ( mDaemon )
1060
            {
1061
                for (Iterator<Package> it = mBuildOrder.iterator(); it.hasNext(); )
1062
                {
1063
                    Package p = it.next();
1064
 
1065
                    if ( p.mProcessed && p.mBuildFile == 1 )
1066
                    {
1067
                        p.mBuildFile = buildFile;
1068
                        mLogger.info("planRelease 2 set mBuildFile to " + buildFile + " for package " + p.mAlias );
1069
 
1070
                        if ( buildFile == 1 )
1071
                        {
1072
                            int pvApplied = p.applyPV(mReleaseManager, mBaseline);
1073
 
1074
                            if ( pvApplied == 1 )
1075
                            {
1076
                                // max 50 chars
1077
                                rippleBuildExclude(p, p.mId, "Package has non standard versioning", null, null, true);
1078
                            }
1079
                            else if ( pvApplied == 2 )
1080
                            {
1081
                                // max 50 chars
1082
                                rippleBuildExclude(p, p.mId, "Package has reached ripple field limitations", null, null, true);
1083
                            }
1084
                            else if ( pvApplied == 3 )
1085
                            {
1086
                                // max 50 chars
1087
                                rippleBuildExclude(p, p.mId, "Package has invalid change type", null, null, true);
1088
                            }
1089
                            else
1090
                            {
1091
                                //    Have found a package to build
1092
                                //        Prevent finding others
1093
                                buildFile = 2;
1094
 
1095
                                if ( p.mForcedRippleInstruction > 0 )
1096
                                {
1097
                                    mReleaseManager.markDaemonInstCompleted( p.mForcedRippleInstruction );
1098
                                }
1099
 
1100
                                if ( p.mTestBuildInstruction > 0 )
1101
                                {
1102
                                    mReleaseManager.markDaemonInstInProgress( p.mTestBuildInstruction );
1103
                                }
1104
                            }
1105
                        }
1106
                        else
1107
                        {
1108
                            mLogger.info("planRelease package has future (downgraded) build requirement " + p.mName + " " + buildFile);              
1109
                        }
1110
                    }
1111
                }
1112
            }
1113
 
1114
            // are more build files required
1115
            allProcessed = true;
1116
 
1117
            if (mDaemon)
1118
            {
1119
                //  Recalculate mBuildOrder
1120
                //  Remove any packages that cannot be built due to errors encountered during
1121
                //  the use of applyPV()
1122
                //
1123
                ArrayList<Package> mTempBuildOrder = new ArrayList<Package>();
1124
 
1125
                //  Seed mBuildOrder with packages that were prime buildable candidates
1126
                //  This includes test builds
1127
                //      Insert the active build at the head of the list - should only be one
1128
                //
1129
                for (Iterator<Package> it = mBuildOrder.iterator(); it.hasNext(); )
1130
                {
1131
                    Package p = it.next();
1132
                    if(p.mBuildFile > 0)
1133
                    {
1134
                        mTempBuildOrder.add(p);
1135
                    }
1136
                }
1137
                mBuildOrder = mTempBuildOrder;
1138
 
1139
 
1140
                for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
1141
                {
1142
                    Package p = it.next();
1143
 
1144
                    if ( p.mBuildFile < 0 || ( !p.mDirectlyPlanned && !p.mIndirectlyPlanned ) )
1145
                    {
1146
                        // at this point...
1147
                        // only 1 package with a build requirement has a mBuildFile of 1,
1148
                        // all other packages with a build requirement have an mBuildFile of 2
1149
                        // give packages with no build requirement, reproducible or not, an mBuildFile of 3
1150
                        p.mNoBuildReason = p.mBuildFile;
1151
                        p.mBuildFile = 3;
1152
                        mLogger.info("planRelease 1 set mBuildFile to 3 for package " + p.mAlias );
1153
                    }
1154
                }
1155
            }
1156
            else
1157
            {
1158
                // this is escrow mode centric
1159
                for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
1160
                {
1161
                    Package p = it.next();
1162
 
1163
                    if ( p.mBuildFile == 0 )
1164
                    {
1165
                        // more build files are required
1166
                        allProcessed = false;
1167
                        mLogger.info("planRelease more build files are required for " + p.mName);
1168
                        break;
1169
                    }
1170
                }
1171
 
1172
                buildFile++;
1173
            }
1174
        } while( !allProcessed );
1175
    }
1176
 
1177
 
1178
    /** Determine if a given PVID is a member of the current release.
1179
     *  Used to determine if a package dependency is out of date
1180
     * 
1181
     * @param dpvId
1182
     * @return true - specified pvid is a full member of the Release
1183
     */
1184
    private boolean isInRelease(Integer dpvId) {
1185
 
1186
        boolean found = false;
1187
 
1188
        for ( Iterator<Integer> it4 = mReleasedPvIDCollection.iterator(); it4.hasNext(); )
1189
        {
1190
            Integer pvId = it4.next();
1191
 
1192
            if ( pvId.compareTo(dpvId) == 0 )
1193
            {
1194
                found = true;
1195
                break;
1196
            }
1197
        }
1198
        return found;
1199
    }
1200
 
1201
 
1202
    /**reports what change in build exceptions happens as part of planRelease
1203
     */
1204
    public void reportChange() throws SQLException, Exception
1205
    {
1206
        int counter = 0;
1207
        for (Iterator<BuildExclusion> it = mBuildExclusionCollection.iterator(); it.hasNext(); )
1208
        {
1209
            BuildExclusion buildExclusion = it.next();
1210
 
1211
            if ( !buildExclusion.isProcessed() )
1212
            {
1213
                // notify
1214
                buildExclusion.excludeFromBuild(mReleaseManager, mBaseline);
1215
                buildExclusion.email(this, mPackageCollection);
1216
                counter++;
1217
            }
1218
        }
1219
        mLogger.fatal("reportChange exclusion count: " + counter);
1220
    }
1221
 
1222
    /**reports the build plan
1223
     */
1224
    public void reportPlan() throws SQLException, Exception
1225
    {
1226
        mReleaseManager.reportPlan(mRtagId, mBuildOrder);
1227
    }
1228
 
1229
    /**Returns the first build file from the collection
1230
     * The build file will be flagged as empty if none exists
1231
     */
1232
    public BuildFile getFirstBuildFileContent()
1233
    {
1234
        mLogger.debug("getFirstBuildFileContent");
1235
 
1236
        mBuildIndex = -1;
1237
        return getNextBuildFileContent();
1238
    }
1239
 
1240
    /**Returns next build file from the collection
1241
     * The build file will be flagged as empty if none exists
1242
     */
1243
    public BuildFile getNextBuildFileContent()
1244
    {
1245
        mLogger.debug("getNextBuildFileContent");
1246
        BuildFile retVal = null;
1247
 
1248
        try
1249
        {
1250
            mBuildIndex++;
1251
            retVal = mBuildCollection.get( mBuildIndex );
1252
        }
1253
        catch( IndexOutOfBoundsException e )
1254
        {
1255
            // Ignore exception. retVal is still null.
1256
        }
1257
 
1258
        if (retVal == null)
1259
        {
1260
            retVal = new BuildFile();
1261
        }
1262
 
1263
        mLogger.debug("getNextBuildFileContent returned " + retVal.state.name() );
1264
        return retVal;
1265
    }
1266
 
1267
 
1268
    /**collects meta data associated with the baseline
1269
     * this is sufficient to send an indefinite pause email notification 
1270
     *  
1271
     * Escrow: Used once to collect information about the SBOM and associated Release 
1272
     *         mBaseline is an SBOMID 
1273
     * Daemon: Used each build cycle to refresh the information 
1274
     *          mBaseline is an RTAGID
1275
     */
1276
    public void collectMetaData() throws SQLException, Exception
1277
    {
1278
        Phase phase = new Phase("cmd");
1279
        mLogger.debug("collectMetaData mDaemon " + mDaemon);
1280
 
1281
        try
1282
        {
1283
            phase.setPhase("connect");
1284
            mReleaseManager.connect();
1285
 
1286
            if (! mDaemon)
1287
            {
1288
                mSbomId = mBaseline;
1289
                phase.setPhase("queryRtagIdForSbom");
1290
                mRtagId = mReleaseManager.queryRtagIdForSbom(mBaseline);
1291
                if (mRtagId == 0)
1292
                {
1293
                    mLogger.fatal("SBOM does not have a matching Release. Cannot be used as a base for an Escrow"); 
1294
                    throw new Exception("rtagIdForSbom show stopper. SBOM does not have an associated Release");
1295
                }
1296
            }
1297
 
1298
            phase.setPhase("queryReleaseConfig");
1299
            mReleaseManager.queryReleaseConfig(mRtagId);
1300
 
1301
            if (mDaemon)
1302
            {
1303
                phase.setPhase("queryMailServer");
1304
                setMailServer(mReleaseManager.queryMailServer());
1305
                phase.setPhase("queryMailSender");
1306
                setMailSender(mReleaseManager.queryMailSender());
1307
                phase.setPhase("queryGlobalAddresses");
1308
                setMailGlobalTarget(mReleaseManager.queryGlobalAddresses());
1309
                phase.setPhase("queryProjectEmail");
1310
                mMailGlobalCollection = mReleaseManager.queryProjectEmail(mBaseline);
1311
                phase.setPhase("mMailGlobalTarget");
1312
                mMailGlobalCollection.add(0,getMailGlobalTarget());
1313
            }
1314
            phase.setPhase("queryBaselineName");
1315
            mBaselineName = mReleaseManager.queryBaselineName(mBaseline);
1316
            phase.setPhase("Done");
1317
        }
1318
        finally
1319
        {
1320
            // this block is executed regardless of what happens in the try block
1321
            // even if an exception is thrown
1322
            // ensure disconnect
1323
            mReleaseManager.disconnect();
1324
        }
1325
    }
1326
 
1327
    /**
1328
     * Find Package by package alias
1329
     * Searches the complete package collection
1330
     * @param   alias               - alias of package to locate
1331
     * @return  Package with the matching mAlias or NULL_PACKAGE if no package has the mAlias
1332
     */
1333
    public Package findPackage(String alias)
1334
    {
1335
        mLogger.debug("findPackage");
1336
 
1337
        Package retVal = mReleaseManager.findPackage(alias, mPackageCollection);
1338
 
1339
        mLogger.info("findPackage returned " + retVal.mName);
1340
        return retVal;
1341
    }
1342
 
1343
    /**
1344
     * Sets the mBuildFile to -5 (indirectly dependent on package versions which are not reproducible) 
1345
     * for the package and all dependent packages 
1346
     * @param p           The package being excluded 
1347
     * @param rootPvId  The PVID of the package that is causing the exclusion. Null or -ve values are special
1348
     *                  This package is the root cause, -2: Excluded by Ripple Stop 
1349
     *                   
1350
     * @param rootCause  Text message. Max 50 characters imposed by RM database 
1351
     * @param list        If not null, add build exclusion to specified list, else use default(mBuildExclusionCollection) list. Allows insertions while iterating the list.
1352
     * @param be          If not null, process provided BuildExclusion element, otherwise find-existing/create a BuildExclusion element
1353
     * @param dependentToo True: Exclude all packages that depend on the excluded package 
1354
     */
1355
    private void rippleBuildExclude(Package p, int rootPvId, String rootCause, ListIterator<BuildExclusion> list, BuildExclusion be, boolean dependentToo )
1356
    {
1357
        mLogger.debug("rippleBuildExclude");
1358
        if ( p.mBuildFile == 0 || p.mBuildFile == 1 )
1359
        {
1360
            p.mBuildFile = -5;
1361
            mLogger.info("rippleBuildExclude set mBuildFile to -5 for package " + p.mAlias );
1362
 
1363
            if ( be != null )
1364
            {
1365
                be.process();
1366
            }
1367
            else
1368
            {
1369
                //  If no be item has been provided, then scan the complete collection looking for a matching item
1370
                //  If found, process it, else add it (unprocessed)
1371
                boolean found = false;
1372
                for (Iterator<BuildExclusion> it = mBuildExclusionCollection.iterator(); it.hasNext(); )
1373
                {
1374
                    BuildExclusion buildExclusion = it.next();
1375
 
1376
                    if ( buildExclusion.compare(p.mId, rootPvId, rootCause))
1377
                    {
1378
                        buildExclusion.process();
1379
                        found = true;
1380
                        break;
1381
                    }
1382
                }
1383
 
1384
                if (!found)
1385
                {
1386
                    // Entry not found in the mBuildExclusionCollection
1387
                    // Mark all occurrences for this package as processed
1388
                    // These will be superseded by a new build exclusion entry
1389
                    for (Iterator<BuildExclusion> it = mBuildExclusionCollection.iterator(); it.hasNext(); )
1390
                    {
1391
                        BuildExclusion buildExclusion = it.next();
1392
 
1393
                        if ( buildExclusion.compare(p.mId))
1394
                        {
1395
                            buildExclusion.process();
1396
                        }
1397
                    }
1398
 
1399
                    BuildExclusion buildExclusion = new BuildExclusion(p.mId, rootPvId, rootCause, p.mTestBuildInstruction);
1400
 
1401
                    //
1402
                    //  Add the new build exclusion to a list
1403
                    //    Either the default list or a user-specified list
1404
                    if ( list == null )
1405
                    {
1406
                        mBuildExclusionCollection.add(buildExclusion);
1407
                    }
1408
                    else
1409
                    {
1410
                        // must use the ListIterator interface to add to the collection whilst iterating through it
1411
                        list.add(buildExclusion);
1412
                    }
1413
                }
1414
            }
1415
 
1416
            //  Locate ALL packages that depend on this package and exclude them too
1417
            //  This process will be recursive
1418
            //    Not sure that this it is efficient 
1419
            //  Only ripple a build exclusion through for non test builds
1420
            //
1421
            if ( p.mTestBuildInstruction == 0 && dependentToo)
1422
            {
1423
                for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
1424
                {
1425
                    Package pkg = it.next();
1426
 
1427
                    if ( pkg != p )
1428
                    {
1429
                        for (Iterator<Package> it2 = pkg.mPackageDependencyCollection.iterator(); it2.hasNext(); )
1430
                        {
1431
                            Package dependency = it2.next();
1432
 
1433
                            if ( dependency == p )
1434
                            {
1435
                                rippleBuildExclude( pkg, rootPvId, null, list, null, dependentToo );
1436
                                break;
1437
                            }
1438
                        }
1439
                    }
1440
                }
1441
            }
1442
        }
1443
        mLogger.info("rippleBuildExclude set " + p.mName + " " + p.mBuildFile);
1444
    }
1445
 
1446
    /**Simple XML string escaping
1447
     * 
1448
     * @param xml		- String to escape
1449
     * @return		- A copy of the string with XML-unfriendly characters escaped 
1450
     */
1451
    public static String escapeXml( String xml )
1452
    {
1453
        xml = xml.replaceAll("&", "&amp;");
1454
        xml = xml.replaceAll("<", "&lt;");
1455
        xml = xml.replaceAll(">", "&gt;");
1456
        xml = xml.replaceAll("\"","&quot;");
1457
        xml = xml.replaceAll("'", "&apos;");
1458
        xml = xml.replaceAll("\\$", "\\$\\$");
1459
 
1460
        return xml;
1461
    }
1462
 
1463
    /** Quote a string or a string pair
1464
     *  If two strings are provided, then they will be joined with a comma.
1465
     *   
1466
     * @param text		- First string to quote
1467
     * @param text2		- Optional, second string
1468
     * @return A string of the form 'text','text2'
1469
     */
1470
    public static String quoteString(String text, String text2)
1471
    {
1472
        String result;
1473
        result =  "\'" + text + "\'";
1474
        if (text2 != null )
1475
        {
1476
            result +=  ",\'" + text2 + "\'";  
1477
        }
1478
        return result;
1479
    }
1480
 
1481
    /** Generate build file information
1482
     * 
1483
     */
1484
    private void generateBuildFiles() 
1485
    {
1486
 
1487
        // persist the build files
1488
        boolean allProcessed = false;
1489
        int buildFile = 1;
1490
        StringBuilder rawData = new StringBuilder();
1491
        StringBuilder setUp = new StringBuilder();
1492
 
1493
        mLogger.debug("generateBuildFiles");
1494
 
1495
        if ( mDaemon )
1496
        {
1497
            // all interesting packages in daemon mode match the following filter
1498
            buildFile = 3;
1499
        }
1500
 
1501
        //-----------------------------------------------------------------------
1502
        //    Generate the build file
1503
        do
1504
        {
1505
            BuildFile buildEntry = new  BuildFile();
1506
            buildEntry.state = BuildFileState.Dummy;
1507
            XmlBuilder xml = generateBuildFileHeader();
1508
 
1509
 
1510
            //	Generate properties for each package in this build level or lower build levels
1511
            //	The properties link the packageAlias to the PackageName and PackageVersion
1512
            //
1513
            //	[DEVI 54816] In escrow mode all unreproducible packages are included 
1514
            for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
1515
            {
1516
                Package p = it.next();
1517
 
1518
                if ( ( ( p.mBuildFile > 0 ) && ( p.mBuildFile <= buildFile ) ) || ( !mDaemon && p.mBuildFile == -2 ) )
1519
                {
1520
                    xml.addProperty(p.mAlias, p.mName + " " + p.mVersion + p.mExtension);
1521
                }
1522
            }
1523
 
1524
            //	UTF Support
1525
            //	Insert additional info into the build file to provide extra checking
1526
            //
1527
            if ( ! ReleaseManager.mUseDatabase )
1528
            {
1529
                // UTF Support
1530
                // Insert per-package planning information
1531
                //
1532
                xml.addComment("mPackageCollection");
1533
                for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
1534
                {
1535
                    Package p = it.next();
1536
                    generatePackageInfo(xml, p);
1537
                }
1538
 
1539
                // UTF Support
1540
                // Insert build exclusion information
1541
                xml.addComment("mBuildExclusionCollection");
1542
                for (Iterator<BuildExclusion> it = mBuildExclusionCollection.iterator(); it.hasNext(); )
1543
                {
1544
                    BuildExclusion buildExclusion = it.next();
1545
                    if (!buildExclusion.isProcessed())
1546
                    {
1547
                        xml.addComment(buildExclusion.info());
1548
                    }
1549
                }
1550
 
1551
                // UTF Support
1552
                // Insert build Plan
1553
                if (mDaemon)
1554
                {
1555
                    xml.addComment("mBuildOrder");
1556
                    int order = 0;
1557
                    for (Iterator<Package> it = mBuildOrder.iterator(); it.hasNext(); )
1558
                    {
1559
                        Package p = it.next();
1560
                        String comment =
1561
                                "pvid="+ p.mId +
1562
                                " order=" +(++order) +
1563
                                " name=\"" + p.mAlias + "\"";
1564
                        xml.addComment(comment);
1565
                    }
1566
                }
1567
            }
1568
 
1569
            //  Generate Taskdef information
1570
            generateTaskdef(xml);
1571
 
1572
            //
1573
            //  Insert known Machine Information
1574
            //  Escrow usage: 
1575
            //      Map machType to machClass
1576
            //  Also serves as a snapshot of the required build configuration
1577
            //  ie: machine types and buildfilters
1578
            //
1579
            if (!mDaemon)
1580
            {
1581
                generateMachineInfo(xml, null);
1582
            }
1583
 
1584
            //
1585
            //	Generate target rules for each package to be built within the current build file
1586
            //
1587
            boolean daemonHasTarget = false;
1588
 
1589
            for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
1590
            {
1591
                Package p = it.next();
1592
 
1593
                if ( p.mBuildFile > 0 && p.mBuildFile <= buildFile )
1594
                {
1595
                    generateTarget(xml, buildEntry, p, buildFile);
1596
 
1597
                    if ( p.mBuildFile == 1 )
1598
                    {
1599
                        daemonHasTarget = true;
1600
 
1601
                        // Retain information about the target package
1602
                        buildEntry.mPkgId = p.mPid;
1603
                        buildEntry.mPvId = p.mId;
1604
                    }
1605
                }
1606
 
1607
                //	Generate escrow extraction commands
1608
                //	Only done on the first pass though the packages
1609
                //
1610
                if ( !mDaemon && buildFile == 1 )
1611
                {
1612
                    setUp.append("jats jats_vcsrelease -extractfiles"
1613
                                + " \"-label=" + p.mVcsTag + "\""
1614
                                + " \"-view=" + p.mAlias + "\""
1615
                                + " -root=. -noprefix"
1616
                                + mlf );
1617
                }
1618
 
1619
                //	Generate escrow raw CSV data
1620
                //	Note: I don't think this data is used at all
1621
 
1622
                if ( !mDaemon && ( p.mBuildFile == buildFile))
1623
                {
1624
                    StringAppender machines = new StringAppender(",");
1625
                    for (Iterator<BuildStandard> it1 = p.mBuildStandardCollection.iterator(); it1.hasNext();)
1626
                    {
1627
                        BuildStandard bs = it1.next();
1628
                        machines.append(bs.mMachClass);
1629
                    }
1630
 
1631
                    rawData.append(p.mAlias + "," +
1632
                                    buildFile + "," +
1633
                                    machines +
1634
                                    mlf);
1635
                }
1636
            }
1637
 
1638
            if ( mDaemon && !daemonHasTarget )
1639
            {
1640
                // must have AbtTestPath, AbtSetUp, AbtTearDown, and AbtPublish targets
1641
                XmlBuilder target = xml.addNewElement("target");
1642
                target.addAttribute("name", "AbtTestPath");
1643
 
1644
                target = xml.addNewElement("target");
1645
                target.addAttribute("name", "AbtSetUp");
1646
 
1647
                target = xml.addNewElement("target");
1648
                target.addAttribute("name", "AbtTearDown");
1649
 
1650
                target = xml.addNewElement("target");
1651
                target.addAttribute("name", "AbtPublish");
1652
            }
1653
 
1654
            generateDefaultTarget( xml, buildFile);
1655
 
1656
            //	Convert the Xml structure into text and save it in the build file
1657
            //	Add this build file to the mBuildCollection
1658
            buildEntry.content = mXmlHeader + xml.toString();
1659
            mBuildCollection.add(buildEntry);
1660
 
1661
            // are more build files required
1662
            allProcessed = true;
1663
 
1664
            if (!mDaemon)
1665
            {
1666
                // this is escrow mode centric
1667
                for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
1668
                {
1669
                    Package p = it.next();
1670
 
1671
                    if ( p.mBuildFile > buildFile )
1672
                    {
1673
                        // more build files are required
1674
                        allProcessed = false;
1675
                        mLogger.info("planRelease reiterating package has no build requirement " + p.mName + " " + p.mBuildFile + " " + buildFile);
1676
                        break;
1677
                    }
1678
                } 
1679
 
1680
                buildFile++;
1681
            }
1682
 
1683
        } while( !allProcessed );
1684
 
1685
        //	Save additional escrow data
1686
        if ( !mDaemon )
1687
        {
1688
            mEscrowSetup = setUp.toString();
1689
            mEscrowRawData = rawData.toString();
1690
        }
1691
    }
1692
 
1693
    /**returns a build file header for the mBaseline
1694
     */
1695
    private XmlBuilder generateBuildFileHeader()
1696
    {
1697
        mLogger.debug("generateBuildFileHeader");
1698
        XmlBuilder element = new XmlBuilder("project");
1699
 
1700
        element.addAttribute("name", "mass");
1701
        element.addAttribute("default", "full");
1702
        element.addAttribute("basedir", ".");
1703
 
1704
        if ( mDaemon )
1705
        {
1706
            element.addProperty("abt_mail_server", getMailServer());
1707
            element.addProperty("abt_mail_sender", getMailSender()); 
1708
            element.addProperty("abt_rtag_id", mBaseline);
1709
            element.addProperty("abt_daemon", mReleaseManager.currentTimeMillis());
1710
            element.makePropertyTag("abt_packagetarball", true);
1711
            element.makePropertyTag("abt_usetestarchive", !ReleaseManager.getUseMutex());
1712
 
1713
            for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
1714
            {
1715
                Package p = it.next();
1716
 
1717
                if ( p.mBuildFile == 1 )
1718
                {
1719
                    element.addProperty("abt_package_name", p.mName);
1720
                    element.addProperty("abt_package_version", p.mVersion + p.mExtension);
1721
                    element.addProperty("abt_package_extension", p.mExtension);
1722
                    element.addProperty("abt_package_location", getBuildLocation(p));
1723
                    element.addProperty("abt_package_ownerlist", p.emailInfoNonAntTask(this));
1724
                    element.addProperty("abt_package_build_info", buildInfoText(p));
1725
 
1726
                    // depends in the form 'cs','25.1.0000.cr';'Dinkumware_STL','1.0.0.cots'
1727
                    StringAppender depends = new StringAppender(";");
1728
 
1729
                    for (Iterator<Package> it3 = p.mPackageDependencyCollection.iterator(); it3.hasNext(); )
1730
                    {
1731
                        Package depend = it3.next();
1732
                        depends.append( quoteString(depend.mName, depend.mVersion + depend.mExtension) );
1733
                    }
1734
 
1735
                    element.addProperty("abt_package_depends", depends.toString());
1736
                    element.addProperty("abt_is_ripple", p.mDirectlyPlanned ? "0" : "1");
1737
                    element.addProperty("abt_build_reason", p.mBuildReason.toString());
1738
                    element.addProperty("abt_package_version_id", p.mId);
1739
                    element.addProperty("abt_does_not_require_source_control_interaction", ! p.mRequiresSourceControlInteraction ? "true" : "false" );
1740
                    element.addProperty("abt_test_build_instruction", p.mTestBuildInstruction);
1741
                }
1742
            }
1743
        }
1744
        else
1745
        {
1746
            //    Escrow Mode
1747
            element.addProperty("abt_rtag_id", mRtagId);
1748
            element.addProperty("abt_sbom_id", mSbomId);
1749
        }
1750
 
1751
        element.addProperty("abt_release", escapeXml(mBaselineName));
1752
        element.addProperty("abt_buildtool_version", mReleaseManager.getMajorVersionNumber() );
1753
 
1754
        element.addNewElement("condition")
1755
        .addAttribute("property", "abt_family")
1756
        .addAttribute("value", "windows")
1757
        .addNewElement("os")
1758
        .addAttribute("family", "windows");
1759
        element.addProperty("abt_family", "unix");
1760
 
1761
        return element;
1762
    }
1763
 
1764
    /** Add task def XML items taskdef for the abt ant task
1765
     * @param xml 	- xml item to extend
1766
     */
1767
    private void generateTaskdef(XmlBuilder xml)
1768
    {
1769
        mLogger.debug("generateTaskdef");
1770
        xml.addNewElement("taskdef")
1771
        .addAttribute("name", "abt")
1772
        .addAttribute("classname", "com.erggroup.buildtool.abt.ABT");
1773
 
1774
        xml.addNewElement("taskdef")
1775
        .addAttribute("name", "abtdata")
1776
        .addAttribute("classname", "com.erggroup.buildtool.abt.ABTData");
1777
    }
1778
 
1779
    /** returns the command abtdata items
1780
     *  <br>Common machine information
1781
     *  <br>Common email address
1782
     *  
1783
     *  
1784
     *  @param	xml - Xml element to extend   
1785
     *  @param    p - Package (May be null)
1786
     */
1787
    private void generateMachineInfo(XmlBuilder xml, Package p)
1788
    {
1789
        XmlBuilder element = xml.addNewElement("abtdata");
1790
        element.addAttribute("id", "global-abt-data");
1791
 
1792
        //
1793
        //  Iterate over all the machines and create a nice entry
1794
        //
1795
        for (Iterator<ReleaseConfig> it = mReleaseManager.mReleaseConfigCollection.mReleaseConfig.iterator(); it.hasNext(); )
1796
        {
1797
            ReleaseConfig rc = it.next();
1798
            element.addElement(rc.getMachineEntry());
1799
        }
1800
 
1801
        //
1802
        //  Now the email information
1803
        //
1804
        if ( p != null)
1805
        {
1806
            p.emailInfo(this, element);
1807
        }
1808
    }
1809
 
1810
    /** Generate package information
1811
     *  Only when running unit tests
1812
     *  
1813
     * @param xml	- An xml element to append data to
1814
     * @param p	- Package to process
1815
     */
1816
    private void generatePackageInfo (XmlBuilder xml, Package p)
1817
    {
1818
 
1819
        String comment =
1820
                "pvid="+ p.mId + 
1821
                " name=\"" + p.mAlias + "\""+
1822
                " reason=" + p.mNoBuildReason +" "+ 
1823
                " buildFile=" + p.mBuildFile + " "+
1824
                " directlyPlanned=" + p.mDirectlyPlanned + " "+
1825
                " indirectlyPlanned=" + p.mIndirectlyPlanned;
1826
 
1827
        xml.addComment(comment);            
1828
    }
1829
 
1830
    /**returns an ant target for the passed Package
1831
     * in daemon mode:
1832
     *  packages are categorized with one of three mBuildFile values:
1833
     *   1 the package to be built by this buildfile
1834
     *   2 the packages with a future build requirement
1835
     *   3 the packages with no build requirement
1836
     *  the returned target depends on this categorization and will have
1837
     *   1 full abt info
1838
     *   2 full dependency info to determine future build ordering but no abt info (will not build this package)
1839
     *   3 only a name attribute (will not build this package)
1840
     * in escrow mode:
1841
     *  if the passed Package's mBuildFile is different (less than) the passed build file,
1842
     *  the returned target have only a name attribute (will not build this package)
1843
     *   
1844
     * @param xml - Xml element to extend
1845
     * @param buildEntry - Record build type (dummy/generic/not generic)
1846
     * @param p - Package to process
1847
     * @param buildFile - buildfile level being processed
1848
     */
1849
    private void generateTarget(XmlBuilder xml, BuildFile buildEntry, Package p, int buildFile)
1850
    {
1851
        mLogger.debug("generateTarget");
1852
 
1853
        //---------------------------------------------------------------------
1854
        //  Generate the AbtData - common machine and email information
1855
        //  Only used by the daemon builds
1856
        //
1857
        if ( mDaemon && p.mBuildFile == 1 )
1858
        {
1859
            generateMachineInfo(xml, p );
1860
        }
1861
 
1862
        //-------------------------------------------------------------------------
1863
        //  Generate the <target name=... /> construct
1864
        //  There are two types
1865
        //      1) Simple dummy place holder. Just has the PackageName.PackageExt
1866
        //      2) Full target. Has all information to build the package including
1867
        //              AbtSetUp, AbtTearDown and AbtPublish targets
1868
        //
1869
 
1870
        if ( !mDaemon && ( p.mBuildFile < buildFile ) )
1871
        {
1872
            XmlBuilder target = xml.addNewElement("target");
1873
            target.addAttribute("name", p.mAlias);
1874
        }
1875
        else
1876
        {
1877
            if (!mDaemon) 
1878
            {
1879
                //  Escrow Only:
1880
                //  Generate the 'wrapper' target
1881
                //  This is used to ensure that the required dependencies have been built - I think
1882
                //
1883
                StringAppender dependList = new StringAppender(",");
1884
                if ( !p.mPackageDependencyCollection.isEmpty() )
1885
                {
1886
                    for (Iterator<Package> it = p.mPackageDependencyCollection.iterator(); it.hasNext(); )
1887
                    {
1888
                        Package dependency = it.next();
1889
                        if ( !mDaemon && dependency.mBuildFile == -2 )
1890
                        {
1891
                            // ignore targets which build in foreign environments in escrow mode
1892
                            continue;
1893
                        }
1894
                        dependList.append(dependency.mAlias);
1895
                    }
1896
                }
1897
 
1898
                XmlBuilder target = xml.addNewElement("target").isExpanded();
1899
                target.addAttribute("name", p.mAlias + ".wrap");
1900
 
1901
                if (dependList.length() > 0)
1902
                {
1903
                    target.addAttribute("depends", dependList.toString() );
1904
                }
1905
 
1906
                if ( !mDaemon )
1907
                {
1908
                    boolean hasDependenciesBuiltInThisIteration = false;
1909
                    if ( ( !p.mPackageDependencyCollection.isEmpty()) )
1910
                    {
1911
                        for (Iterator<Package> it = p.mPackageDependencyCollection.iterator(); it.hasNext(); )
1912
                        {
1913
                            Package dependency = it.next();
1914
 
1915
                            if ( dependency.mBuildFile == buildFile )
1916
                            {
1917
                                hasDependenciesBuiltInThisIteration = true;
1918
                                break;
1919
                            }
1920
                        }
1921
                    }
1922
 
1923
                    if ( hasDependenciesBuiltInThisIteration )
1924
                    {
1925
                        XmlBuilder condition = target.addNewElement("condition");
1926
                        condition.addAttribute("property",  p.mAlias + ".build");
1927
 
1928
                        XmlBuilder and = condition.addNewElement("and");
1929
 
1930
                        for (Iterator<Package> it = p.mPackageDependencyCollection.iterator(); it.hasNext(); )
1931
                        {
1932
                            Package dependency = it.next();
1933
 
1934
                            if ( dependency.mBuildFile == buildFile )
1935
                            {
1936
                                XmlBuilder or = and.addNewElement("or");
1937
 
1938
                                XmlBuilder equal1 = or.addNewElement("equals");
1939
                                equal1.addAttribute("arg1", "${" + dependency.mAlias + ".res}");
1940
                                equal1.addAttribute("arg2", "0");
1941
 
1942
                                XmlBuilder equal2 = or.addNewElement("equals");
1943
                                equal2.addAttribute("arg1", "${" + dependency.mAlias + ".res}");
1944
                                equal2.addAttribute("arg2", "257");
1945
                            }
1946
                        }
1947
                    }
1948
                    else
1949
                    {
1950
                        target.addProperty(p.mAlias + ".build", "");
1951
                    }
1952
                }
1953
            }
1954
 
1955
 
1956
            //
1957
            //  Generate the 'body' of the target package
1958
            //  Escrow Mode: Always
1959
            //  Daemon Mode: Only for the one target we are building
1960
            //                  Simplifies the XML
1961
            //                  Reduces noise in the logs
1962
            //              Don't add target dependencies. 
1963
            //                  We are only building one target and the 
1964
            //                  dependency management has been done way before now.
1965
            //                  All it does is makes the logs noisy.
1966
            //
1967
            if ( ( mDaemon && p.mBuildFile == 1 ) || !mDaemon )
1968
            {
1969
                XmlBuilder target = xml.addNewElement("target").isExpanded();
1970
                target.addAttribute("name", p.mAlias);
1971
 
1972
                if ( !mDaemon )
1973
                {
1974
                    target.addAttribute("depends",  p.mAlias + ".wrap");
1975
                    target.addAttribute("if",p.mAlias + ".build");
1976
                }
1977
 
1978
                if ( mDaemon && p.mBuildFile == 1 )
1979
                {
1980
                    target.addProperty(p.mAlias + "pkg_id",p.mPid);
1981
                    target.addProperty(p.mAlias + "pv_id",p.mId);
1982
                }
1983
 
1984
                target.addProperty(p.mAlias + "packagename",p.mName);        		
1985
                target.addProperty(p.mAlias + "packageversion",p.mVersion);
1986
                target.addProperty(p.mAlias + "packageextension",p.mExtension);
1987
                target.addProperty(p.mAlias + "packagevcstag",p.mVcsTag);
1988
 
1989
                target.makePropertyTag(p.mAlias + "directchange", p.mDirectlyPlanned); 
1990
                target.makePropertyTag(p.mAlias + "doesnotrequiresourcecontrolinteraction", ! p.mRequiresSourceControlInteraction);
1991
 
1992
                buildEntry.state = BuildFile.BuildFileState.NonGeneric;
1993
                if ( p.isGeneric() )
1994
                {
1995
                    buildEntry.state = BuildFile.BuildFileState.Generic;
1996
                    target.makePropertyTag(p.mAlias + "generic", true); 
1997
                }
1998
 
1999
                target.addProperty(p.mAlias + "loc", getBuildLocation(p));
2000
                target.makePropertyTag(p.mAlias + "unittests", p.mHasAutomatedUnitTests && mDaemon);
2001
 
2002
                //    Add our own task and associated information
2003
                //
2004
                XmlBuilder abt = target.addNewElement("abt").isExpanded();
2005
 
2006
                for (Iterator<Package> it = p.mPackageDependencyCollection.iterator(); it.hasNext(); )
2007
                {
2008
                    Package dependency = it.next();
2009
                    XmlBuilder depend = abt.addNewElement("depend");
2010
                    depend.addAttribute("package_alias", "${" + dependency.mAlias + "}");
2011
                }
2012
 
2013
                buildInfo(abt,p);
2014
 
2015
                if ( mDaemon && p.mBuildFile == 1 )
2016
                {
2017
                    //    AbtTestPath
2018
                    target = xml.addNewElement("target").isExpanded();
2019
                    target.addAttribute("name", "AbtTestPath");
2020
                    target.addProperty("AbtTestPathpackagevcstag", p.mVcsTag);
2021
                    target.addProperty("AbtTestPathpackagename", p.mName);
2022
                    abt = target.addNewElement("abt").isExpanded();
2023
                    buildInfo(abt, p);
2024
 
2025
 
2026
                    //    AbtSetUp
2027
                    target = xml.addNewElement("target").isExpanded();
2028
                    target.addAttribute("name", "AbtSetUp");
2029
                    target.addProperty("AbtSetUppackagevcstag", p.mVcsTag);
2030
                    target.addProperty("AbtSetUppackagename", p.mName);
2031
 
2032
                    abt = target.addNewElement("abt").isExpanded();
2033
                    buildInfo(abt, p);
2034
 
2035
                    //    AbtTearDown
2036
                    target = xml.addNewElement("target").isExpanded();
2037
                    target.addAttribute("name", "AbtTearDown");
2038
                    target.addProperty("AbtTearDownpackagevcstag", p.mVcsTag);
2039
                    target.addProperty("AbtTearDownpackagename", p.mName);
2040
                    target.addProperty("AbtTearDownpackageversion", p.mVersion);
2041
                    target.addProperty("AbtTearDownpackageextension", p.mExtension);
2042
                    target.makePropertyTag(p.mAlias + "generic", p.isGeneric());
2043
 
2044
                    abt = target.addNewElement("abt").isExpanded();
2045
                    buildInfo(abt, p);
2046
 
2047
 
2048
                    //  AbtPublish
2049
                    target = xml.addNewElement("target").isExpanded();
2050
                    target.addAttribute("name", "AbtPublish");
2051
 
2052
                    target.addProperty("AbtPublishpackagevcstag", p.mVcsTag);
2053
                    target.addProperty("AbtPublishpackagename", p.mName);
2054
                    target.addProperty("AbtPublishpackageversion", p.mVersion);
2055
                    target.addProperty("AbtPublishpackageextension", p.mExtension);
2056
                    target.makePropertyTag("AbtPublishdirectchange", p.mDirectlyPlanned);
2057
                    target.makePropertyTag("AbtPublishdoesnotrequiresourcecontrolinteraction", ! p.mRequiresSourceControlInteraction);
2058
                    target.makePropertyTag("AbtPublishgeneric", p.isGeneric());
2059
                    target.addProperty("AbtPublishloc", getBuildLocation(p));
2060
 
2061
                    abt = target.addNewElement("abt").isExpanded();
2062
                    buildInfo(abt, p);
2063
 
2064
                }
2065
            }
2066
        }
2067
    }
2068
 
2069
    /** Extends the xml object. Adds ant default target for the current build iteration
2070
     * @param xml - The XmlBuilder Object to extend
2071
     * @param buildFile - The current build file level. This differs for Daemon and Escrow mode. In Daemon mode it will not be a '1' 
2072
     */
2073
    private void generateDefaultTarget(XmlBuilder xml, int buildFile)
2074
    {
2075
        mLogger.debug("generateDefaultTarget");
2076
 
2077
        XmlBuilder target = xml.addNewElement("target").isExpanded();
2078
        target.addAttribute("name", "fullstart");
2079
 
2080
        if (buildFile == 1)
2081
        {
2082
            antEcho(target, "${line.separator}" + mAnyBuildPlatforms + "${line.separator}${line.separator}");
2083
            for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
2084
            {
2085
                Package p = it.next();
2086
 
2087
                if ( p.mBuildFile == -1 )
2088
                {
2089
                    antEcho(target, "${line.separator}" + p.mAlias + "${line.separator}");
2090
                }
2091
            }
2092
 
2093
            antEcho(target, "${line.separator}" + mAssocBuildPlatforms + "${line.separator}${line.separator}");
2094
            for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
2095
            {
2096
                Package p = it.next();
2097
 
2098
                if ( p.mBuildFile == -2 )
2099
                {
2100
                    antEcho(target, "${line.separator}" + p.mAlias + "${line.separator}");
2101
                }
2102
            }
2103
 
2104
            antEcho(target, "${line.separator}" + mNotInBaseline + "${line.separator}${line.separator}");
2105
            for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
2106
            {
2107
                Package p = it.next();
2108
 
2109
                if ( p.mBuildFile == -4 )
2110
                {
2111
                    antEcho(target, "${line.separator}" + p.mAlias + "${line.separator}");
2112
                }
2113
            }
2114
 
2115
            antEcho(target, "${line.separator}" + mDependent + "${line.separator}${line.separator}");
2116
            for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
2117
            {
2118
                Package p = it.next();
2119
 
2120
                if ( p.mBuildFile == -5 )
2121
                {
2122
                    antEcho(target, "${line.separator}" + p.mAlias + "${line.separator}");
2123
                }
2124
            }
2125
        }
2126
 
2127
        if ( !mDaemon )
2128
        {
2129
            antEcho(target, "${line.separator}Build Started:${line.separator}${line.separator}");
2130
        }
2131
 
2132
        //
2133
        //	Create a comma separated list of all the required targets
2134
        //      Escrow : All packages
2135
        //      Daemon : Just the package being built
2136
        //
2137
        StringAppender dependList = new StringAppender(",");
2138
        dependList.append("fullstart");
2139
        for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
2140
        {
2141
            Package p = it.next();
2142
 
2143
            if ( ( p.mBuildFile > 0 ) && ( p.mBuildFile <= buildFile ) )
2144
            {
2145
                if ((mDaemon && p.mBuildFile == 1) || !mDaemon)
2146
                {
2147
                    dependList.append(p.mAlias);                    
2148
                }
2149
            }
2150
        }
2151
 
2152
        target = xml.addNewElement("target").isExpanded();
2153
        target.addAttribute("name", "full");
2154
        target.addAttribute("depends", dependList.toString());
2155
 
2156
        if ( !mDaemon )
2157
        {
2158
            antEcho(target, "${line.separator}Build Finished${line.separator}");
2159
        }
2160
    }
2161
 
2162
    /** Internal helper function to create an ant 'echo statement
2163
     *  Many of the parameters are fixed
2164
     */
2165
    private void antEcho( XmlBuilder xml, String message)
2166
    {
2167
        XmlBuilder msg = xml.addNewElement("echo");
2168
        msg.addAttribute("message", message);
2169
        msg.addAttribute("file", "publish.log");
2170
        msg.addAttribute("append", "true");
2171
    }
2172
 
2173
    /**sets the mIndirectlyPlanned true for the package and all dependent packages
2174
     */
2175
    private void rippleIndirectlyPlanned(Package p)
2176
    {
2177
        mLogger.debug("rippleIndirectlyPlanned");
2178
        if ( !p.mIndirectlyPlanned && p.mBuildFile == 0 )
2179
        {
2180
            p.mIndirectlyPlanned = true;
2181
 
2182
            for (Iterator<Package> it = mPackageCollection.iterator(); it.hasNext(); )
2183
            {
2184
                Package pkg = it.next();
2185
 
2186
                if ( pkg != p )
2187
                {
2188
                    for (Iterator<Package> it2 = pkg.mPackageDependencyCollection.iterator(); it2.hasNext(); )
2189
                    {
2190
                        Package dependency = it2.next();
2191
 
2192
                        if ( dependency == p )
2193
                        {
2194
                            rippleIndirectlyPlanned( pkg );
2195
                            break;
2196
                        }
2197
                    }
2198
                }
2199
            }
2200
        }
2201
        mLogger.info("rippleIndirectlyPlanned set " + p.mName + " " + p.mIndirectlyPlanned);    
2202
    }
2203
 
2204
    /**accessor method
2205
     */
2206
    public String getEscrowSetUp()
2207
    {
2208
        mLogger.debug("getEscrowSetUp");
2209
        String retVal = mEscrowSetup;
2210
 
2211
        mLogger.debug("getEscrowSetUp returned " + retVal);
2212
        return retVal;
2213
    }
2214
 
2215
    /**accessor method
2216
     */
2217
    public String getRawData()
2218
    {
2219
        mLogger.debug("getRawData");
2220
        String retVal = mEscrowRawData;
2221
 
2222
        mLogger.debug("getRawData returned " + retVal);
2223
        return retVal;
2224
    }
2225
 
2226
    /**Get the build loc (location)
2227
     * This is package specific and will depend on the build mode (Escrow/Daemon)
2228
     * 
2229
     * @param	p - Package being built
2230
     * @return A string that describes the build location
2231
     */
2232
    private String getBuildLocation(Package p)
2233
    {
2234
        mLogger.debug("locationProperty");
2235
        String location = "";
2236
 
2237
        if (mDaemon)
2238
        {
2239
            // Daemon: Start in root of view/workspace
2240
            location += mBaseline;
2241
        }
2242
        else
2243
        {
2244
            // Escrow: mAlias used with jats -extractfiles -view
2245
            location += p.mAlias;
2246
        }
2247
 
2248
        //
2249
        //  Always use '/' as a path separator - even if user has specified '\'
2250
        //  Ant can handle it.
2251
        //
2252
        location = location.replace('\\', '/');
2253
        return location;
2254
    }
2255
 
2256
    /**Adds package build into as XML elements 
2257
     * @param	  xml 	- Xml element to extend
2258
     * @param   p       - Package to process
2259
     */
2260
    private void buildInfo(XmlBuilder xml, Package p)
2261
    {
2262
        mLogger.debug("buildInfo");
2263
 
2264
        //
2265
        // Create the xml build information
2266
        // <platform gbe_machtype="linux_i386" type="jats" arg="all"/>
2267
        //
2268
        for (Iterator<BuildStandard> it = p.mBuildStandardCollection.iterator(); it.hasNext();)
2269
        {
2270
            BuildStandard bs = it.next();
2271
            bs.getBuildStandardXml(xml);
2272
        }
2273
    }
2274
 
2275
    /**returns the buildInfo as a single line of text
2276
     * Used for reporting purposes only
2277
     * @param   p       - Package to process
2278
     * 
2279
     */
2280
    public String buildInfoText(Package p)
2281
    {
2282
        mLogger.debug("buildInfoText");
2283
 
2284
        StringAppender result = new StringAppender (";");
2285
 
2286
        //
2287
        //  Create platform:standards
2288
        //      
2289
        for (Iterator<BuildStandard> it = p.mBuildStandardCollection.iterator(); it.hasNext(); )
2290
        {
2291
            BuildStandard bs = it.next();
2292
 
2293
            if ( bs.isActive() )
2294
            {
2295
                String info = bs.getBuildStandardText();
2296
                result.append(info);
2297
            }
2298
        }
2299
 
2300
        mLogger.info("buildInfoText returned " + result);
2301
        return result.toString();
2302
    }
2303
 
2304
    /**prints to standard out in escrow mode only
2305
     * <br>Prints a title and information. The title is only printed once.
2306
     * 
2307
     * @param header - The message title to display, if printMessage is true
2308
     * @param text - A package name. Really just the 2nd line of the message
2309
     * @param printHeader -  Controls the printing of the message argument
2310
     */
2311
    private void standardOut(final String header, final String text, boolean printHeader)
2312
    {
2313
        mLogger.debug("standardOut");
2314
        if (!mDaemon)
2315
        {
2316
            if ( printHeader )
2317
            {
2318
                System.out.println(header);
2319
            }
2320
 
2321
            System.out.println(text);
2322
        }
2323
    }
2324
 
2325
 
2326
    /**
2327
     *  Email users about a rejected daemon instruction
2328
     *  @param  reason  - Reason for the rejection
2329
     *  @param  pkg     - Package to be affected by the instruction
2330
     *  
2331
     */
2332
    public void emailRejectedDaemonInstruction(String reason, Package p)
2333
    {
2334
        mLogger.debug("emailRejectedDaemonInstruction");
2335
 
2336
        //  Email Subject
2337
        String subject = "BUILD FAILURE of Daemon Instruction on package " + p.mAlias;
2338
 
2339
        // Email Body
2340
        String mailBody = "The build system reject the the Daemon Instruction";
2341
        mailBody += "<br>Reason: " + reason; 
2342
 
2343
        mailBody += "<p>Release: " + mBaselineName 
2344
                +  "<br>Package: " + p.mAlias 
2345
                +  "<br>Rm Ref: " + CreateUrls.generateRmUrl(getRtagId(), p.mId); 
2346
 
2347
        mailBody += "<p><hr>";
2348
 
2349
        //    Generate the list of email recipients
2350
        //        Transfer the daemon instruction email list into the package before use
2351
 
2352
        p.setTestEmail();
2353
        String target = p.emailInfoNonAntTask(this);
2354
 
2355
        mLogger.fatal("emailRejectedDaemonInstruction Server: " + getMailServer());
2356
        mLogger.fatal("emailRejectedDaemonInstruction Sender: " + getMailSender());
2357
        mLogger.fatal("emailRejectedDaemonInstruction Target: " + target);
2358
 
2359
        try
2360
        {
2361
            //    
2362
            Smtpsend.send(getMailServer(), // mailServer
2363
                    getMailSender(),       // source
2364
                    target,            // target
2365
                    getMailSender(),       // cc
2366
                    null,              // bcc
2367
                    subject,           // subject
2368
                    mailBody,          // body
2369
                    null               // attachment
2370
                    );
2371
        } catch (Exception e)
2372
        {
2373
            mLogger.warn("Email Failure: emailRejectedDaemonInstruction:" + e.getMessage());
2374
        }
2375
    }
2376
 
2377
    /**
2378
     * @return the mMailServer
2379
     */
2380
    public String getMailServer() {
2381
        return mMailServer;
2382
    }
2383
 
2384
    /**
2385
     * @param mMailServer the mMailServer to set
2386
     */
2387
    public void setMailServer(String mMailServer) {
2388
        this.mMailServer = mMailServer;
2389
    }
2390
 
2391
    /**
2392
     * @return the mMailSender
2393
     */
2394
    public String getMailSender() {
2395
        return mMailSender;
2396
    }
2397
 
2398
    /**
2399
     * @param mMailSender the mMailSender to set
2400
     */
2401
    public void setMailSender(String mMailSender) {
2402
        this.mMailSender = mMailSender;
2403
    }
2404
 
2405
    /**
2406
     * @return the mMailGlobalTarget
2407
     */
2408
    public String getMailGlobalTarget() {
2409
        return mMailGlobalTarget;
2410
    }
2411
 
2412
    /**
2413
     * @param mMailGlobalTarget the mMailGlobalTarget to set
2414
     */
2415
    public void setMailGlobalTarget(String mMailGlobalTarget) {
2416
        this.mMailGlobalTarget = mMailGlobalTarget;
2417
    }
2418
 
2419
}