Subversion Repositories DevTools

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
814 mhunt 1
package com.erggroup.buildtool.daemon;
2
 
3
import com.erggroup.buildtool.daemon.ResumeTimerTask;
4
import com.erggroup.buildtool.ripple.Package;
5
import com.erggroup.buildtool.ripple.ReleaseManager;
6
 
7
import java.io.BufferedReader;
8
import java.io.File;
9
import java.io.FileNotFoundException;
10
import java.io.FileOutputStream;
11
import java.io.FileWriter;
12
import java.io.FilenameFilter;
13
 
14
import java.io.IOException;
15
import java.io.PrintStream;
16
import java.io.StringReader;
17
 
18
import java.sql.SQLException;
19
 
20
import java.util.Date;
21
import java.util.Timer;
22
 
23
import org.apache.log4j.Logger;
24
import org.apache.tools.ant.BuildException;
25
import org.apache.tools.ant.DefaultLogger;
26
import org.apache.tools.ant.Project;
27
import org.apache.tools.ant.ProjectHelper;
28
 
29
/**Build Thread sub component
30
 */
31
public abstract class BuildThread
32
  extends Thread
33
{
34
  /**baseline identifier (which release manager release this BuildThread is dealing with)
35
   * @attribute
36
   */
37
  protected int mRtagId = 0;
38
 
39
  /**unique identifier of this BuildThread
40
   * @attribute
41
   */
42
  protected int mRconId = 0;
43
 
44
  /**
45
   * @aggregation composite
46
   */
47
  protected RunLevel mRunLevel;
48
 
49
  /**
50
   * @aggregation composite
51
   */
52
  protected ReleaseManager mReleaseManager;
53
 
54
  /**configured GBEBUILDFILTER for this BuildThread
55
   * @attribute
56
   */
57
  protected String mGbebuildfilter = "";
58
 
59
  /**
60
   * @aggregation composite
61
   */
62
  protected static ResumeTimerTask mResumeTimerTask;
63
 
64
  /**
65
   * @aggregation composite
66
   */
67
  private static Timer mTimer;
68
 
69
  /**Synchroniser object
70
   * Use to Synchronize on by multiple threads
71
   * @attribute
72
   */
73
  static final Object mSynchroniser = new Object();
74
 
75
  /**BuildThread group
76
   * @attribute
77
   */
78
  public static final ThreadGroup mThreadGroup = new ThreadGroup("BuildThread");
79
 
80
  /**the advertised build file content when either
81
   * a) no package in the baseline has a build requirement
82
   * b) the next package in the baseline with a build requirement is generic
83
   * @attribute
84
   */
85
  protected static final String mDummyBuildFileContent = new String("<dummy/>");
86
 
87
  /**Set true when last build cycle was benign.
88
   * @attribute
89
   */
90
  protected boolean mSleep;
91
 
92
  /**Logger
93
   * @attribute
94
   */
95
  private static final Logger mLogger = Logger.getLogger(BuildThread.class);
96
 
97
  /**constructor
98
   */
99
  BuildThread()
100
  {
101
    super(mThreadGroup, "");
102
    mLogger.debug("BuildThread");
103
    mSleep = false;
104
    mReleaseManager = new ReleaseManager();
105
 
106
    // no need to be synchronized - BuildThreads are instantiated in a single thread
107
    if ( mResumeTimerTask == null )
108
    {
109
      mResumeTimerTask = new ResumeTimerTask();
110
      mTimer = new Timer();
111
      mResumeTimerTask.setTimer(mTimer);
112
    }
113
  }
114
 
115
  /**initially changes the run level to IDLE
116
   * determines if the BuildThread is still configured
117
   * a) determines if the BuildThread is running in scheduled downtime
118
   * b) determines if the BuildThread is directed to pause
119
   * changes the run level to PAUSED if a) or b) are true
120
   * throws ExitException when not configured
121
   * otherwise changes the run level to WAITING and returns when allowed to proceed
122
   * implements the sequence diagrams allowed to proceed, not allowed to proceed, exit
123
   */
124
  protected void allowedToProceed() throws ExitException, SQLException, Exception
125
  {
126
    mLogger.debug("allowedToProceed");
127
 
128
    if (mSleep)
129
    {
130
      try
131
      {
818 mhunt 132
        mRunLevel = RunLevel.IDLE;
133
        mLogger.warn("allowedToProceed changing run level to IDLE for rcon_id " + mRconId);
134
        mRunLevel.persist(mReleaseManager, mRconId);
135
        mReleaseManager.clearCurrentPackageBeingBuilt(mRconId);
136
 
814 mhunt 137
        mLogger.warn("allowedToProceed sleep 5 mins no build requirement");
138
        Thread.sleep(300000);
139
        mLogger.info("allowedToProceed sleep returned");
140
      }
818 mhunt 141
      catch(SQLException e)
142
      {
143
        mLogger.warn("allowedToProceed caught SQLException");
144
      }
814 mhunt 145
      catch(InterruptedException e)
146
      {
147
        mLogger.warn("allowedToProceed sleep caught InterruptedException");
148
      }
149
    }
150
 
151
    // IMPORTANT - this is done AFTER a Thread.sleep by design
152
    // In the case of an SQLException (signifying a database access issue)
153
    // avoid accessing the database immediately
154
    mRunLevel = RunLevel.IDLE;
816 mhunt 155
    mLogger.warn("allowedToProceed changing run level to IDLE for rcon_id " + mRconId);
814 mhunt 156
    mRunLevel.persist(mReleaseManager, mRconId);
157
    boolean proceed = false;
158
 
159
    while ( !proceed )
160
    {
161
      mReleaseManager.connect();
162
      if ( !mReleaseManager.queryReleaseConfig(mRtagId, mRconId, BuildDaemon.mHostname, getMode(), mGbebuildfilter) )
163
      {
164
        mReleaseManager.disconnect();
165
        mLogger.fatal("allowedToProceed queryReleaseConfig failed");
166
        throw new ExitException();
167
      }
168
 
169
      Date resumeTime = new Date( 0 );
170
      if ( !mReleaseManager.queryRunLevelSchedule(resumeTime) )
171
      {
172
        mLogger.info("allowedToProceed scheduled downtime");
173
        mReleaseManager.disconnect();
174
        mRunLevel = RunLevel.PAUSED;
816 mhunt 175
        mLogger.warn("allowedToProceed changing run level to PAUSED for rcon_id " + mRconId);
814 mhunt 176
        mRunLevel.persist(mReleaseManager, mRconId);
177
 
178
        synchronized(mSynchroniser)
179
        {
180
          // contain the schedule and wait in the same synchronized block to prevent a deadlock
181
          // eg this thread calls schedule, timer thread calls notifyall, this thread calls wait (forever)
182
          try
183
          {
184
            if (mResumeTimerTask.isCancelled())
185
            {
186
              mResumeTimerTask = new ResumeTimerTask();
187
              mTimer = new Timer();
188
              mResumeTimerTask.setTimer(mTimer);
189
            }
190
            mLogger.warn("allowedToProceed schedule passed " + resumeTime.getTime());
191
            mTimer.schedule(mResumeTimerTask, resumeTime);
192
          }
193
          catch( IllegalStateException e )
194
          {
195
            // this may be thrown by schedule if already scheduled
196
            // it signifies another BuildThread has already scheduled the ResumeTimerTask
197
             mLogger.warn("allowedToProceed already scheduled");
198
          }
199
 
200
          try
201
          {
202
            mLogger.warn("allowedToProceed wait");
203
            mSynchroniser.wait();
204
            mLogger.warn("allowedToProceed wait returned");
205
 
206
            if ( mGbebuildfilter.compareTo("unit test not allowed to proceed") == 0 )
207
            {
208
              throw new ExitException();
209
            }
210
          }
211
          catch( InterruptedException e )
212
          {
213
            mLogger.warn("allowedToProceed caught InterruptedException");
214
          }
215
        }
216
 
217
      }
218
      else
219
      {
220
        if ( !mReleaseManager.queryDirectedRunLevel(mRconId) )
221
        {
222
          mLogger.info("allowedToProceed downtime");
223
          mReleaseManager.disconnect();
832 mhunt 224
          mRunLevel = RunLevel.PAUSED;
225
          mLogger.warn("allowedToProceed changing run level to PAUSED for rcon_id " + mRconId);
226
          mRunLevel.persist(mReleaseManager, mRconId);
814 mhunt 227
          try
228
          {
229
            // to do, sleep for periodicMs
230
            mLogger.warn("allowedToProceed sleep 5 mins directed downtime");
231
            Thread.sleep(300000);
232
            mLogger.info("allowedToProceed sleep returned");
233
          }
234
          catch (InterruptedException e)
235
          {
236
            mLogger.warn("allowedToProceed caught InterruptedException");
237
          }
238
        }
239
        else
240
        {
241
          mReleaseManager.disconnect();
242
          proceed = true;
243
        }
244
      }
245
    }
246
 
247
    mRunLevel = RunLevel.WAITING;
816 mhunt 248
    mLogger.warn("allowedToProceed changing run level to WAITING for rcon_id " + mRconId);
814 mhunt 249
    mRunLevel.persist(mReleaseManager, mRconId);
250
 
251
  }
252
 
253
  /**periodically 
254
   * a) performs disk housekeeping
255
   * b) determines if a minimum threshold of disk space is available
256
   * changes the run level to CANNOT_CONTINUE if insufficient disk space
257
   * otherwise changes the run level to ACTIVE and returns
258
   * implements the sequence diagram check environment
259
   */
260
  protected void checkEnvironment() throws Exception
261
  {
262
    mLogger.debug("checkEnvironment");
263
    boolean exit = false;
264
 
265
    while( !exit )
266
    {
267
      housekeep();
268
 
269
      // attempt to exit
270
      exit = true;
271
 
272
      if ( !hasSufficientDiskSpace() )
273
      {
274
        mLogger.warn("checkEnvironment below disk free threshold");
275
        exit = false;
276
        mRunLevel = RunLevel.CANNOT_CONTINUE;
816 mhunt 277
        mLogger.warn("checkEnvironment changing run level to CANNOT_CONTINUE for rcon_id " + mRconId);
814 mhunt 278
        mRunLevel.persist(mReleaseManager, mRconId);
279
        try
280
        {
281
          // to do, sleep for periodicMs
282
          if ( mGbebuildfilter.compareTo("unit test check environment") != 0 )
283
          {
284
            mLogger.warn("checkEnvironment sleep 5 mins below disk free threshold");
285
            Thread.sleep(300000);
286
            mLogger.info("checkEnvironment sleep returned");
287
          }
288
        }
289
        catch (InterruptedException e)
290
        {
291
          mLogger.warn("checkEnvironment caught InterruptedException");
292
        }
293
      }
294
    }
295
 
296
    mRunLevel = RunLevel.ACTIVE;    
816 mhunt 297
    mLogger.warn("checkEnvironment changing run level to ACTIVE for rcon_id " + mRconId);
814 mhunt 298
    mRunLevel.persist(mReleaseManager, mRconId);
299
  }
300
 
301
  /**performs disk housekeeping which involves deleting build directories > 5 days old
302
   * refer to the sequence diagram check environment
303
   */
304
  private void housekeep()
305
  {
306
    mLogger.debug("housekeep");
307
    FilenameFilter filter = new FilenameFilter()
308
    {
309
      public boolean accept(File file, String name)
310
      {
311
        mLogger.debug("accept " + name);
312
        boolean retVal = false;
313
 
314
        if ( file.isDirectory() && !name.startsWith( "." ) )
315
        {
316
          retVal = true;
317
        }
318
 
319
        mLogger.info("accept returned " + retVal);
320
        return retVal;
321
      }
322
    };
323
 
324
    try
325
    {
842 mhunt 326
      // DEVI 46729, 46730, solaris 10 core dumps implicate deleteDirectory
327
      // let each BuildThread look after its own housekeeping
328
      File ocwd = new File( "." );
329
      File cwd = new File( ocwd.getAbsolutePath() + System.getProperty( "file.separator" ) + mRtagId );
330
 
814 mhunt 331
      File[] children = cwd.listFiles( filter );
332
 
333
      if ( children != null )
334
      {
335
        for ( int child=0; child < children.length; child++ )
336
        {
337
          // child is named uniquely after an rtag_id
338
          File[] grandchildren = children[ child ].listFiles( filter );
339
 
340
          if ( grandchildren != null )
341
          {
342
            for ( int grandchild=0; grandchild < grandchildren.length; grandchild++ )
343
            {
344
              // child is named uniquely to encapsulate a build
345
              // 5 days = 432,000,000 milliseconds
346
              if ( ( System.currentTimeMillis() - grandchildren[ grandchild ].lastModified() ) > 432000000 )
347
              {
348
                // the directory is over 5 days old
349
                mLogger.warn("housekeep deleting directory " + grandchildren[ grandchild ].getName());
350
                if ( mGbebuildfilter.compareTo("unit test check environment") != 0 )
351
                {
352
                  deleteDirectory( grandchildren[ grandchild ] );
353
                }
354
              }
355
            }
356
          }
357
        }
358
      }
359
    }
360
    catch( SecurityException e )
361
    {
362
      // this can be thrown by lastModified
363
       mLogger.warn("housekeep caught SecurityException");
364
    }
365
 
366
  }
367
 
368
  /**returns true if free disk space > 10G
369
   * this may become configurable if the need arises
370
   * refer to the sequence diagram check environment
371
   */
372
  private boolean hasSufficientDiskSpace()
373
  {
374
    mLogger.debug("hasSufficientDiskSpace");
375
    boolean retVal = true;
376
    long freeSpace = 0;
377
 
378
    try
379
    {
380
      File cwd = new File( "." );
381
 
382
      // 5G = 5368709120 bytes
383
      if ( mGbebuildfilter.compareTo("unit test check environment") == 0 )
384
      {
385
        if ( mReleaseManager.mPersistedRunLevelCollection.size() == 0 )
386
        {
387
          retVal = false;
388
        }
389
        else
390
        {
391
          retVal = true;
392
        }
393
      }
394
      else
395
      {
816 mhunt 396
        freeSpace = cwd.getUsableSpace();
814 mhunt 397
 
398
        if ( freeSpace < 5368709120L )
399
        {
816 mhunt 400
          mLogger.warn("hasSufficientDiskSpace on " + cwd.getAbsolutePath() + " freeSpace " + freeSpace);
814 mhunt 401
          retVal = false;
402
        }
403
      }
404
    }
405
    catch( SecurityException e )
406
    {
407
      // this can be thrown by getFreeSpace
408
       mLogger.warn("hasSufficientDiskSpace caught SecurityException");
409
    }
410
 
411
    mLogger.info("hasSufficientDiskSpace returned " + retVal + " " + freeSpace);
412
    return retVal;
413
  }
414
 
415
  /**abstract method
416
   */
417
  public abstract void run();
418
 
419
  /**deletes directory and all its files
420
   */
421
  protected void deleteDirectory(File directory)
422
  {
423
    mLogger.debug("deleteDirectory " + directory.getName());
424
    try
425
    {
426
      if ( directory.exists() )
427
      {
428
        FilenameFilter filter = new FilenameFilter()
429
        {
430
          public boolean accept(File file, String name)
431
          {
432
            mLogger.debug("accept " + name);
433
            boolean retVal = false;
434
 
840 mhunt 435
            if ( name.compareTo(".") != 0 && ( name.compareTo("..") != 0 ) )
814 mhunt 436
            {
437
              retVal = true;
438
            }
439
 
440
            mLogger.info("accept returned " + retVal);
441
            return retVal;
442
          }
443
        };
444
 
445
        File[] children = directory.listFiles( filter );
446
 
447
        if ( children != null )
448
        {
449
          for ( int child=0; child < children.length; child++ )
450
          {
451
            if ( children[ child ].isDirectory() )
452
            {
453
              deleteDirectory( children[ child ] );
454
            }
455
            else
456
            {
457
              children[ child ].delete();
458
            }
459
          }
460
        }
461
        directory.delete();
462
      }
463
    }
464
    catch( SecurityException e )
465
    {
466
      // this can be thrown by exists and delete
467
       mLogger.warn("deleteDirectory caught SecurityException");
468
    }
469
  }
470
 
471
  /**abstract method
472
   */
473
  protected abstract char getMode();
474
 
475
  /**injects GBE_BUILDFILTER into the passed buildFileContent
476
   * builds a buildFile from the buildFileContent
477
   * triggers ant to operate on the buildFile
478
   */
832 mhunt 479
  protected void deliverChange(String buildFileContent, String target, boolean master) throws Exception
814 mhunt 480
  {
481
    mLogger.debug("deliverChange");
482
    File buildFile = new File(mRtagId + "build.xml");
483
 
484
    try
485
    {
486
      // clear the file contents
487
      if ( buildFileContent != null && target != null && target.compareTo("AbtSetUp") == 0 )
488
      {
489
        FileOutputStream buildFileOutputStream = new FileOutputStream(buildFile, false);
490
        buildFileOutputStream.close();
491
 
492
        StringReader buildFileContentStringReader = new StringReader(buildFileContent);
493
        BufferedReader buildFileBufferedReader = new BufferedReader(buildFileContentStringReader);
494
 
495
        // sanitise the buildFileContent
496
        // it may contain line.separators of "\n", "\r", or "\r\n" variety, depending on the location of the ripple engine
497
        String sanitisedBFC = new String();
498
        String lf = new String( System.getProperty("line.separator") );
499
        int lineCount = 0;
500
        String line = new String();
501
 
502
        while( ( line = buildFileBufferedReader.readLine() ) != null)
503
        {
504
          sanitisedBFC += line + lf;
505
          lineCount++;
506
 
507
          if ( lineCount == 2 )
508
          {
509
            // have read the first two lines
510
            String inject = "<property name=\"MASTER\" value=\"";
511
 
512
            if ( master )
513
            {
514
              inject += "yes\"/>" + lf;
515
            }
516
            else
517
            {
518
              inject += "no\"/>" + lf;
519
            }
520
 
521
            // insert a GBE_BUILDFILTER property if necessary
522
            if ( mGbebuildfilter.length() > 0 )
523
            {
524
              inject += "<property name=\"GBE_BUILDFILTER\" value=\"" + mGbebuildfilter + "\"/>" + lf;
525
            }
526
 
527
            mLogger.info("deliverChange injecting " + inject);
528
            sanitisedBFC += inject;
529
          }
530
        }
531
        buildFileBufferedReader.close();
532
        FileWriter buildFileWriter = new FileWriter(buildFile);
533
        buildFileWriter.write(sanitisedBFC);
534
        buildFileWriter.close();
535
      }
536
 
832 mhunt 537
      String antHome = System.getenv("ANT_HOME");
538
 
539
      if ( antHome == null )
540
      {
541
      	mLogger.fatal("deliverChange ANT_HOME not set");
542
        throw new Exception();
543
      }
544
 
814 mhunt 545
      Project p = new Project();
546
      p.setProperty("ant.file", buildFile.getAbsolutePath());
547
      DefaultLogger dl = new DefaultLogger();
548
      PrintStream ps = new PrintStream(mRtagId + ".log");
549
      dl.setOutputPrintStream(ps);
550
      dl.setMessageOutputLevel(Project.MSG_INFO);
551
      p.addBuildListener(dl);
552
      p.init();
553
      ProjectHelper pH = ProjectHelper.getProjectHelper();
554
      p.addReference("ant.projectHelper", pH);
555
      pH.parse(p, buildFile);
556
      mLogger.warn("deliverChange ant launched on " + buildFile.getAbsolutePath());
557
 
558
      if ( target == null )
559
      {
560
        target = p.getDefaultTarget();
561
      }
562
      mLogger.warn("deliverChange ant launched against target " + target);
563
      p.executeTarget(target);
564
      mLogger.warn("deliverChange ant returned");
565
    }
566
    catch( BuildException e )
567
    {
568
      mLogger.debug("deliverChange caught BuildException, big deal, the build failed " + e.getMessage());
569
    }
570
    catch( FileNotFoundException e )
571
    {
572
      mLogger.error("deliverChange caught FileNotFoundException");
573
    }
574
    catch( IOException e )
575
    {
576
      mLogger.error("deliverChange caught IOException");
577
    }
578
 
579
  }
580
 
581
  /**sets up a ClearCase static view
582
   */
832 mhunt 583
  protected void setViewUp(String content, boolean master) throws Exception
814 mhunt 584
  {
585
    mLogger.debug("setViewUp");
586
    // run ant on the AbtSetUp target
587
    deliverChange(content, "AbtSetUp", master);
588
  }
589
 
590
  /**tears down a ClearCase static view
591
  */
832 mhunt 592
  protected void tearViewDown() throws Exception
814 mhunt 593
  {
594
    mLogger.debug("tearViewDown");
595
    // to do run ant on the AbtTearDown target
596
    deliverChange(null, "AbtTearDown", false);
597
  }
598
 
599
  /**Checks the archive for the <packageName>/<packageVersion>/built.<machtype> existence
600
   */
601
  protected boolean published(String archive, String packageName, 
602
                            String packageVersion, String machtype,
603
                            String generic)
604
  {
605
    mLogger.debug("published");
606
    boolean retVal = false;
607
 
608
    String fs = System.getProperty( "file.separator" );
609
    String destination = archive + fs + packageName + fs + packageVersion;
610
 
611
    mLogger.debug("published " + destination);
612
    String filename = "built.";
613
 
614
    if ( generic.compareTo("generic") == 0 )
615
    {
616
      filename += "generic";
617
    }
618
    else
619
    {
620
      filename += machtype;
621
    }
622
 
623
    mLogger.debug("published " + filename);
624
    File flag = new File( destination, filename );
625
 
626
    if ( flag.exists() )
627
    {
628
      retVal = true;
629
    }
630
    mLogger.debug("published returned " + retVal);
631
    return retVal;
632
  }
633
}