KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sourceforge > cruisecontrol > Project


1 /********************************************************************************
2  * CruiseControl, a Continuous Integration Toolkit
3  * Copyright (c) 2001-2003, ThoughtWorks, Inc.
4  * 651 W Washington Ave. Suite 600
5  * Chicago, IL 60661 USA
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * + Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * + Redistributions in binary form must reproduce the above
16  * copyright notice, this list of conditions and the following
17  * disclaimer in the documentation and/or other materials provided
18  * with the distribution.
19  *
20  * + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
21  * names of its contributors may be used to endorse or promote
22  * products derived from this software without specific prior
23  * written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
29  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36  ********************************************************************************/

37 package net.sourceforge.cruisecontrol;
38
39 import java.io.FileOutputStream JavaDoc;
40 import java.io.IOException JavaDoc;
41 import java.io.ObjectInputStream JavaDoc;
42 import java.io.ObjectOutputStream JavaDoc;
43 import java.io.Serializable JavaDoc;
44 import java.util.ArrayList JavaDoc;
45 import java.util.Date JavaDoc;
46 import java.util.HashMap JavaDoc;
47 import java.util.Iterator JavaDoc;
48 import java.util.List JavaDoc;
49 import java.util.Map JavaDoc;
50
51 import net.sourceforge.cruisecontrol.events.BuildProgressEvent;
52 import net.sourceforge.cruisecontrol.events.BuildProgressListener;
53 import net.sourceforge.cruisecontrol.events.BuildResultEvent;
54 import net.sourceforge.cruisecontrol.events.BuildResultListener;
55 import net.sourceforge.cruisecontrol.listeners.ProjectStateChangedEvent;
56 import net.sourceforge.cruisecontrol.util.DateUtil;
57
58 import org.apache.log4j.Logger;
59 import org.jdom.Element;
60
61 /**
62  * Represents a single logical project consisting of source code that needs to
63  * be built. Project is associated with bootstrappers that run before builds
64  * and a Schedule that determines when builds occur.
65  */

66 public class Project implements Serializable JavaDoc, Runnable JavaDoc {
67     static final long serialVersionUID = 2656877748476842326L;
68     private static final Logger LOG = Logger.getLogger(Project.class);
69
70     private transient ProjectState state;
71
72     private transient ProjectConfig projectConfig;
73     private transient LabelIncrementer labelIncrementer;
74
75     /**
76      * If this attribute is set, then it means that the user has overriden
77      * the build interval specified in the Schedule element, probably
78      * using the JMX interface.
79      */

80     private transient Long JavaDoc overrideBuildInterval;
81
82     private transient Date JavaDoc buildStartTime;
83     private transient Object JavaDoc pausedMutex;
84     private transient Object JavaDoc scheduleMutex;
85     private transient Object JavaDoc waitMutex;
86     private transient BuildQueue queue;
87     private transient List JavaDoc progressListeners;
88     private transient List JavaDoc resultListeners;
89
90     private int buildCounter = 0;
91     private Date JavaDoc lastBuild = DateUtil.getMidnight();
92     private Date JavaDoc lastSuccessfulBuild = lastBuild;
93     private boolean wasLastBuildSuccessful = true;
94     private String JavaDoc label;
95     private String JavaDoc name;
96     private boolean buildForced = false;
97     private String JavaDoc buildTarget = null;
98     private boolean isPaused = false;
99     private boolean buildAfterFailed = true;
100     private boolean stopped = true;
101
102     public Project() {
103         initializeTransientFields();
104     }
105
106     private void initializeTransientFields() {
107         state = ProjectState.STOPPED;
108
109         pausedMutex = new Object JavaDoc();
110         scheduleMutex = new Object JavaDoc();
111         waitMutex = new Object JavaDoc();
112         progressListeners = new ArrayList JavaDoc();
113         resultListeners = new ArrayList JavaDoc();
114     }
115
116     private void readObject(ObjectInputStream JavaDoc stream) throws IOException JavaDoc, ClassNotFoundException JavaDoc {
117         stream.defaultReadObject();
118         initializeTransientFields();
119     }
120
121     public void execute() {
122         if (stopped) {
123             LOG.warn("not building project " + name + " because project has been stopped.");
124             buildFinished();
125             return;
126         }
127         
128         synchronized (pausedMutex) {
129             if (isPaused) {
130                 LOG.info("not building project " + name + " because project has been paused.");
131                 buildFinished();
132                 return;
133             }
134         }
135
136         try {
137             init();
138             build();
139         } catch (CruiseControlException e) {
140             LOG.error("exception attempting build in project " + name, e);
141         } finally {
142             buildFinished();
143         }
144     }
145
146     /**
147      * Unless paused, runs any bootstrappers and then the entire build.
148      */

149     protected void build() throws CruiseControlException {
150         if (projectConfig == null) {
151             throw new IllegalStateException JavaDoc("projectConfig must be set on project before calling build()");
152         }
153         
154         if (stopped) {
155             LOG.warn("not building project " + name + " because project has been stopped.");
156             return;
157         }
158         
159         try {
160             setBuildStartTime(new Date JavaDoc());
161             Schedule schedule = projectConfig.getSchedule();
162             if (schedule == null) {
163                 throw new IllegalStateException JavaDoc("project must have a schedule");
164             }
165             if (schedule.isPaused(buildStartTime)) {
166                 // a regularly scheduled paused
167
// is different than ProjectState.PAUSED
168
return;
169             }
170
171             bootstrap();
172
173             boolean buildWasForced = buildForced;
174             String JavaDoc target = useAndResetBuildTargetIfBuildWasForced(buildWasForced);
175             resetBuildForcedOnlyIfBuildWasForced(buildWasForced);
176
177             // getModifications will only return null if we don't need to build
178
Element modifications = getModifications(buildWasForced);
179
180             if (modifications == null) {
181                 return;
182             }
183
184             projectConfig.getLog().addContent(modifications);
185
186             Date JavaDoc now = new Date JavaDoc();
187             if (projectConfig.getModificationSet() != null) {
188                 now = projectConfig.getModificationSet().getTimeOfCheck();
189             }
190
191             if (getLabelIncrementer().isPreBuildIncrementer()) {
192                 label = getLabelIncrementer().incrementLabel(label, projectConfig.getLog().getContent());
193             }
194
195             // collect project information
196
projectConfig.getLog().addContent(getProjectPropertiesElement(now));
197
198             setState(ProjectState.BUILDING);
199             Element buildLog = schedule.build(buildCounter, lastBuild, now, getProjectPropertiesMap(now), target);
200             projectConfig.getLog().addContent(buildLog.detach());
201
202             boolean buildSuccessful = projectConfig.getLog().wasBuildSuccessful();
203             fireResultEvent(new BuildResultEvent(this, buildSuccessful));
204
205             if (!getLabelIncrementer().isPreBuildIncrementer() && buildSuccessful) {
206                 label = getLabelIncrementer().incrementLabel(label, projectConfig.getLog().getContent());
207             }
208
209             setState(ProjectState.MERGING_LOGS);
210             projectConfig.getLog().writeLogFile(now);
211
212             // If we only want to build after a check in, even when broken, set the last build to now,
213
// regardless of success or failure (buildAfterFailed = false in config.xml)
214
if (!buildAfterFailed) {
215                 lastBuild = now;
216             }
217
218             // If this was a successful build, update both last build and last successful build
219
if (buildSuccessful) {
220                 lastBuild = now;
221                 lastSuccessfulBuild = now;
222                 info("build successful");
223             } else {
224                 info("build failed");
225             }
226
227             buildCounter++;
228             setWasLastBuildSuccessful(buildSuccessful);
229
230             serializeProject();
231
232             publish();
233             projectConfig.getLog().reset();
234         } finally {
235             setState(ProjectState.IDLE);
236         }
237     }
238
239     private String JavaDoc useAndResetBuildTargetIfBuildWasForced(boolean buildWasForced) {
240         String JavaDoc target = null;
241         if (buildWasForced) {
242             target = buildTarget;
243             buildTarget = null;
244         }
245         return target;
246     }
247
248     private void resetBuildForcedOnlyIfBuildWasForced(boolean buildWasForced) {
249         if (buildWasForced) {
250             buildForced = false;
251         }
252     }
253
254     void setBuildStartTime(Date JavaDoc date) {
255         buildStartTime = date;
256     }
257
258     public void run() {
259         LOG.info("Project " + name + " started");
260         try {
261             while (!stopped) {
262                 try {
263                     waitIfPaused();
264                     waitForNextBuild();
265                     if (!stopped) {
266                         setState(ProjectState.QUEUED);
267                         synchronized (scheduleMutex) {
268                             queue.requestBuild(this);
269                             waitForBuildToFinish();
270                         }
271                     }
272                 } catch (InterruptedException JavaDoc e) {
273                     String JavaDoc message = "Project " + name + ".run() interrupted";
274                     LOG.error(message, e);
275                     throw new RuntimeException JavaDoc(message);
276                 }
277             }
278         } finally {
279             stopped = true;
280             LOG.info("Project " + name + " stopped");
281         }
282     }
283
284     void waitIfPaused() throws InterruptedException JavaDoc {
285         synchronized (pausedMutex) {
286             while (isPaused) {
287                 setState(ProjectState.PAUSED);
288                 pausedMutex.wait(10 * DateUtil.ONE_MINUTE);
289             }
290         }
291     }
292
293     void waitForNextBuild() throws InterruptedException JavaDoc {
294         long waitTime = getTimeToNextBuild(new Date JavaDoc());
295         if (needToWaitForNextBuild(waitTime) && !buildForced) {
296             info("next build in " + DateUtil.formatTime(waitTime));
297             synchronized (waitMutex) {
298                 setState(ProjectState.WAITING);
299                 waitMutex.wait(waitTime);
300             }
301         }
302     }
303
304     long getTimeToNextBuild(Date JavaDoc now) {
305         long waitTime = projectConfig.getSchedule().getTimeToNextBuild(now, getBuildInterval());
306         if (waitTime == 0) {
307             // check for the exceptional case that we're dealing with a
308
// project that has just built within a minute time
309
if (buildStartTime != null) {
310                 long millisSinceLastBuild = now.getTime() - buildStartTime.getTime();
311                 if (millisSinceLastBuild < Schedule.ONE_MINUTE) {
312                     debug("build finished within a minute, getting new time to next build");
313                     Date JavaDoc oneMinuteInFuture = new Date JavaDoc(now.getTime() + Schedule.ONE_MINUTE);
314                     waitTime = projectConfig.getSchedule().getTimeToNextBuild(oneMinuteInFuture, getBuildInterval());
315                     waitTime += Schedule.ONE_MINUTE;
316                 }
317             }
318         }
319         return waitTime;
320     }
321
322     static boolean needToWaitForNextBuild(long waitTime) {
323         return waitTime > 0;
324     }
325
326     void forceBuild() {
327         synchronized (waitMutex) {
328             waitMutex.notify();
329         }
330     }
331     
332     public void forceBuildWithTarget(String JavaDoc buildTarget) {
333         this.buildTarget = buildTarget;
334         setBuildForced(true);
335     }
336
337     void waitForBuildToFinish() throws InterruptedException JavaDoc {
338         synchronized (scheduleMutex) {
339             debug("waiting for build to finish");
340             scheduleMutex.wait();
341         }
342     }
343
344     void buildFinished() {
345         synchronized (scheduleMutex) {
346             debug("build finished");
347             scheduleMutex.notify();
348         }
349     }
350
351     /**
352      * @return Element
353      */

354     Element getModifications(boolean buildWasForced) {
355         setState(ProjectState.MODIFICATIONSET);
356         Element modifications;
357
358         ModificationSet modificationSet = projectConfig.getModificationSet();
359         if (modificationSet == null) {
360             debug("no modification set, nothing to detect.");
361             if (buildWasForced) {
362                 info("no modification set but build was forced");
363                 return new Element("modifications");
364             }
365             return null;
366         }
367         
368         boolean checkNewChangesFirst = checkOnlySinceLastBuild();
369         if (checkNewChangesFirst) {
370             debug("getting changes since last build");
371             modifications = modificationSet.getModifications(lastBuild);
372         } else {
373             debug("getting changes since last successful build");
374             modifications = modificationSet.getModifications(lastSuccessfulBuild);
375         }
376
377         if (!modificationSet.isModified()) {
378             info("No modifications found, build not necessary.");
379
380             // Sometimes we want to build even though we don't have any
381
// modifications. This is in fact current default behaviour.
382
// Set by <project buildafterfailed="true/false">
383
if (buildAfterFailed && !wasLastBuildSuccessful) {
384                 info("Building anyway, since buildAfterFailed is true and last build failed.");
385             } else {
386                 if (buildWasForced) {
387                     info("Building anyway, since build was explicitly forced.");
388                 } else {
389                     return null;
390                 }
391             }
392         }
393
394         if (checkNewChangesFirst) {
395             debug("new changes found; now getting complete set");
396             modifications = modificationSet.getModifications(lastSuccessfulBuild);
397         }
398
399         return modifications;
400     }
401
402     /**
403      * @return boolean
404      */

405     boolean checkOnlySinceLastBuild() {
406         if (lastBuild == null || lastSuccessfulBuild == null) {
407             return false;
408         }
409
410         long lastBuildLong = lastBuild.getTime();
411         long timeDifference = lastBuildLong - lastSuccessfulBuild.getTime();
412         boolean moreThanASecond = timeDifference > DateUtil.ONE_SECOND;
413
414         return !buildAfterFailed && moreThanASecond;
415     }
416
417     /**
418      * Serialize the project to allow resumption after a process bounce
419      */

420     public void serializeProject() {
421         ObjectOutputStream JavaDoc s = null;
422         try {
423             s = new ObjectOutputStream JavaDoc(new FileOutputStream JavaDoc(name + ".ser"));
424             s.writeObject(this);
425             s.flush();
426             debug("Serializing project to [" + name + ".ser]");
427         } catch (Exception JavaDoc e) {
428             LOG.warn("Error serializing project to [" + name + ".ser]: "
429                     + e.getMessage(), e);
430         } finally {
431            if (s != null) {
432                try {
433                    s.close();
434                } catch (Exception JavaDoc ignore) {
435                }
436            }
437         }
438     }
439
440     public void setLabelIncrementer(LabelIncrementer incrementer) throws CruiseControlException {
441         if (incrementer == null) {
442             throw new IllegalArgumentException JavaDoc("label incrementer can't be null");
443         }
444         labelIncrementer = incrementer;
445         if (label == null) {
446             label = labelIncrementer.getDefaultLabel();
447         }
448         validateLabel(label, labelIncrementer);
449     }
450
451     public LabelIncrementer getLabelIncrementer() {
452         return labelIncrementer;
453     }
454
455     /** deprecated */
456     public void setLogXmlEncoding(String JavaDoc encoding) {
457         projectConfig.getLog().setEncoding(encoding);
458     }
459
460     public void setName(String JavaDoc projectName) {
461         name = projectName;
462     }
463
464     public String JavaDoc getName() {
465         return name;
466     }
467
468     public void setLabel(String JavaDoc newLabel) {
469         label = newLabel;
470     }
471
472     public String JavaDoc getLabel() {
473         return label;
474     }
475
476     /**
477      * @param newLastBuild string containing the build date in the format
478      * yyyyMMddHHmmss
479      * @throws CruiseControlException if the date cannot be extracted from the
480      * input string
481      */

482     public void setLastBuild(String JavaDoc newLastBuild) throws CruiseControlException {
483         lastBuild = DateUtil.parseFormattedTime(newLastBuild, "lastBuild");
484     }
485
486     /**
487      * @param newLastSuccessfulBuild string containing the build date in the format
488      * yyyyMMddHHmmss
489      * @throws CruiseControlException if the date cannot be extracted from the
490      * input string
491      */

492     public void setLastSuccessfulBuild(String JavaDoc newLastSuccessfulBuild)
493             throws CruiseControlException {
494         lastSuccessfulBuild = DateUtil.parseFormattedTime(newLastSuccessfulBuild, "lastSuccessfulBuild");
495     }
496
497     public String JavaDoc getLastBuild() {
498         if (lastBuild == null) {
499             return null;
500         }
501         return DateUtil.getFormattedTime(lastBuild);
502     }
503
504     public boolean getBuildForced() {
505         return buildForced;
506     }
507
508     public void setBuildForced(boolean forceNewBuildNow) {
509         buildForced = forceNewBuildNow;
510         if (forceNewBuildNow) {
511             forceBuild();
512         }
513     }
514
515     public String JavaDoc getLastSuccessfulBuild() {
516         if (lastSuccessfulBuild == null) {
517             return null;
518         }
519         return DateUtil.getFormattedTime(lastSuccessfulBuild);
520     }
521
522     public String JavaDoc getLogDir() {
523         return projectConfig.getLog().getLogDir();
524     }
525
526     /**
527      * Returns the build interval. This value is initially specified on the
528      * schedule, but the user may override that value using the JMX interface.
529      * If the user hasn't override the Schedule, then this method will
530      * return the Schedule's interval, otherwise the overriden value will
531      * be returned.
532      */

533     public long getBuildInterval() {
534         if (overrideBuildInterval == null) {
535             return projectConfig.getSchedule().getInterval();
536         } else {
537             return overrideBuildInterval.longValue();
538         }
539     }
540
541     /**
542      * Sets the build interval that this Project should use. This method
543      * overrides the value initially specified in the Schedule attribute.
544      */

545     public void overrideBuildInterval(long sleepMillis) {
546         overrideBuildInterval = new Long JavaDoc(sleepMillis);
547     }
548
549     public boolean isPaused() {
550         return isPaused;
551     }
552
553     public void setPaused(boolean paused) {
554         synchronized (pausedMutex) {
555             if (isPaused && !paused) {
556                 pausedMutex.notifyAll();
557             }
558             isPaused = paused;
559         }
560     }
561
562     public void setBuildAfterFailed(boolean rebuildEvenWithNoNewModifications) {
563         buildAfterFailed = rebuildEvenWithNoNewModifications;
564     }
565
566     public String JavaDoc getStatus() {
567         return getState().getDescription();
568     }
569
570     public String JavaDoc getStatusWithQueuePosition() {
571         if (ProjectState.QUEUED.equals(getState())) {
572             return getState().getDescription() + " - " + queue.findPosition(this);
573         } else {
574             return getState().getDescription();
575         }
576     }
577
578     public ProjectState getState() {
579         return state;
580     }
581
582     private void setState(ProjectState newState) {
583         state = newState;
584         info(getStatus());
585         notifyListeners(new ProjectStateChangedEvent(name, getState()));
586         fireProgressEvent(new BuildProgressEvent(this, getState()));
587     }
588
589     public void setBuildQueue(BuildQueue buildQueue) {
590         queue = buildQueue;
591     }
592
593     public String JavaDoc getBuildStartTime() {
594         return DateUtil.getFormattedTime(buildStartTime);
595     }
596
597     public Log getLog() {
598         return this.projectConfig.getLog();
599     }
600
601     /**
602      * Initialize the project. Uses ProjectXMLHelper to parse a project file.
603      */

604     protected void init() throws CruiseControlException {
605         if (projectConfig == null) {
606             throw new IllegalStateException JavaDoc("projectConfig must be set on project before calling init()");
607         }
608
609         buildAfterFailed = projectConfig.shouldBuildAfterFailed();
610         
611         if (lastBuild == null) {
612             lastBuild = DateUtil.getMidnight();
613         }
614
615         if (lastSuccessfulBuild == null) {
616             lastSuccessfulBuild = lastBuild;
617         }
618
619         // should we move this to build? This singleton thingy is scaring me..
620
setDateFormat(projectConfig.getDateFormat());
621
622         if (LOG.isDebugEnabled()) {
623             debug("buildInterval = [" + getBuildInterval() + "]");
624             debug("buildForced = [" + buildForced + "]");
625             debug("buildAfterFailed = [" + buildAfterFailed + "]");
626             debug("buildCounter = [" + buildCounter + "]");
627             debug("isPaused = [" + isPaused + "]");
628             debug("label = [" + label + "]");
629             debug("lastBuild = [" + DateUtil.getFormattedTime(lastBuild) + "]");
630             debug("lastSuccessfulBuild = [" + DateUtil.getFormattedTime(lastSuccessfulBuild) + "]");
631             debug("logDir = [" + projectConfig.getLog().getLogDir() + "]");
632             debug("logXmlEncoding = [" + projectConfig.getLog().getLogXmlEncoding() + "]");
633             debug("wasLastBuildSuccessful = [" + wasLastBuildSuccessful + "]");
634         }
635     }
636
637     private void setDateFormat(CCDateFormat dateFormat) {
638         if (dateFormat != null && dateFormat.getFormat() != null) {
639             DateFormatFactory.setFormat(dateFormat.getFormat());
640         }
641     }
642
643     protected Element getProjectPropertiesElement(Date JavaDoc now) {
644         Element infoElement = new Element("info");
645         addProperty(infoElement, "projectname", name);
646         String JavaDoc lastBuildString = DateUtil.getFormattedTime(lastBuild == null ? now : lastBuild);
647         addProperty(infoElement, "lastbuild", lastBuildString);
648         String JavaDoc lastSuccessfulBuildString =
649                 DateUtil.getFormattedTime(lastSuccessfulBuild == null ? now : lastSuccessfulBuild);
650         addProperty(infoElement, "lastsuccessfulbuild", lastSuccessfulBuildString);
651         addProperty(infoElement, "builddate", DateFormatFactory.getDateFormat().format(now));
652         if (now != null) {
653             addProperty(infoElement, "cctimestamp", DateUtil.getFormattedTime(now));
654         }
655         addProperty(infoElement, "label", label);
656         addProperty(infoElement, "interval", Long.toString(getBuildInterval() / 1000L));
657         addProperty(infoElement, "lastbuildsuccessful", String.valueOf(wasLastBuildSuccessful));
658         
659         return infoElement;
660     }
661
662     private void addProperty(Element parent, String JavaDoc key, String JavaDoc value) {
663         Element propertyElement = new Element("property");
664         propertyElement.setAttribute("name", key);
665         propertyElement.setAttribute("value", value);
666         parent.addContent(propertyElement);
667     }
668     
669     protected Map JavaDoc getProjectPropertiesMap(Date JavaDoc now) {
670         Map JavaDoc buildProperties = new HashMap JavaDoc();
671         buildProperties.put("label", label);
672         buildProperties.put("cvstimestamp", DateUtil.formatCVSDate(now));
673         buildProperties.put("cctimestamp", DateUtil.getFormattedTime(now));
674         buildProperties.put("cclastgoodbuildtimestamp", getLastSuccessfulBuild());
675         buildProperties.put("cclastbuildtimestamp", getLastBuild());
676         buildProperties.put("lastbuildsuccessful", String.valueOf(isLastBuildSuccessful()));
677         if (projectConfig.getModificationSet() != null) {
678             buildProperties.putAll(projectConfig.getModificationSet().getProperties());
679         }
680         return buildProperties;
681     }
682
683     /**
684      * Iterate over all of the registered <code>Publisher</code>s and call
685      * their respective <code>publish</code> methods.
686      */

687     protected void publish() throws CruiseControlException {
688         setState(ProjectState.PUBLISHING);
689         for (Iterator JavaDoc i = projectConfig.getPublishers().iterator(); i.hasNext(); ) {
690             Publisher publisher = (Publisher) i.next();
691             // catch all errors, Publishers shouldn't cause failures in the build method
692
try {
693                 publisher.publish(projectConfig.getLog().getContent());
694             } catch (Throwable JavaDoc t) {
695                 StringBuffer JavaDoc message = new StringBuffer JavaDoc("exception publishing results");
696                 message.append(" with ").append(publisher.getClass().getName());
697                 message.append(" for project ").append(name);
698                 LOG.error(message.toString(), t);
699             }
700         }
701     }
702
703     /**
704      * Iterate over all of the registered <code>Bootstrapper</code>s and call
705      * their respective <code>bootstrap</code> methods.
706      */

707     protected void bootstrap() throws CruiseControlException {
708         setState(ProjectState.BOOTSTRAPPING);
709         for (Iterator JavaDoc i = projectConfig.getBootstrappers().iterator(); i.hasNext(); ) {
710             ((Bootstrapper) i.next()).bootstrap();
711         }
712     }
713
714     /**
715      * Ensure that label is valid for the specified LabelIncrementer
716      *
717      * @param oldLabel target label
718      * @param incrementer target LabelIncrementer
719      * @throws CruiseControlException if label is not valid
720      */

721     protected void validateLabel(String JavaDoc oldLabel, LabelIncrementer incrementer)
722             throws CruiseControlException {
723         if (!incrementer.isValidLabel(oldLabel)) {
724             final String JavaDoc message = oldLabel + " is not a valid label for labelIncrementer "
725                     + incrementer.getClass().getName();
726             debug(message);
727             throw new CruiseControlException(message);
728         }
729     }
730
731     public boolean isLastBuildSuccessful() {
732         return wasLastBuildSuccessful;
733     }
734
735     void setWasLastBuildSuccessful(boolean buildSuccessful) {
736         wasLastBuildSuccessful = buildSuccessful;
737     }
738
739     /**
740      * Logs a message to the application log, not to be confused with the
741      * CruiseControl build log.
742      */

743     private void info(String JavaDoc message) {
744         LOG.info("Project " + name + ": " + message);
745     }
746
747     private void debug(String JavaDoc message) {
748         LOG.debug("Project " + name + ": " + message);
749     }
750
751     public void start() {
752         if (stopped || getState() == ProjectState.STOPPED) {
753             stopped = false;
754             LOG.info("Project " + name + " starting");
755             setState(ProjectState.IDLE);
756             createNewSchedulingThread();
757         }
758     }
759
760     protected void createNewSchedulingThread() {
761         Thread JavaDoc projectSchedulingThread = new Thread JavaDoc(this, "Project " + getName() + " thread");
762         projectSchedulingThread.start();
763
764         // brief nap to allow thread to start
765
try {
766             Thread.sleep(100);
767         } catch (InterruptedException JavaDoc ie) {
768             LOG.warn("interrupted while waiting for scheduling thread to start", ie);
769         }
770     }
771
772     public void stop() {
773         LOG.info("Project " + name + " stopping");
774         stopped = true;
775         setState(ProjectState.STOPPED);
776     }
777
778     public String JavaDoc toString() {
779         StringBuffer JavaDoc sb = new StringBuffer JavaDoc("Project ");
780         sb.append(getName());
781         sb.append(": ");
782         sb.append(getStatus());
783         if (isPaused) {
784             sb.append(" (paused)");
785         }
786         return sb.toString();
787     }
788
789     public void addBuildProgressListener(BuildProgressListener listener) {
790         synchronized (progressListeners) {
791             progressListeners.add(listener);
792         }
793     }
794
795     protected void fireProgressEvent(BuildProgressEvent event) {
796         synchronized (progressListeners) {
797             for (Iterator JavaDoc i = progressListeners.iterator(); i.hasNext();) {
798                 BuildProgressListener listener = (BuildProgressListener) i.next();
799                 listener.handleBuildProgress(event);
800             }
801         }
802     }
803
804     public void addBuildResultListener(BuildResultListener listener) {
805         synchronized (resultListeners) {
806             resultListeners.add(listener);
807         }
808     }
809
810     protected void fireResultEvent(BuildResultEvent event) {
811         synchronized (resultListeners) {
812             for (Iterator JavaDoc i = resultListeners.iterator(); i.hasNext();) {
813                 BuildResultListener listener = (BuildResultListener) i.next();
814                 listener.handleBuildResult(event);
815             }
816         }
817     }
818
819     List JavaDoc getListeners() {
820         return projectConfig.getListeners();
821     }
822
823     public void setProjectConfig(ProjectConfig projectConfig) throws CruiseControlException {
824         if (projectConfig == null) {
825             throw new IllegalArgumentException JavaDoc("project config can't be null");
826         }
827         this.projectConfig = projectConfig;
828         setLabelIncrementer(projectConfig.getLabelIncrementer());
829     }
830
831     void notifyListeners(ProjectEvent event) {
832         if (projectConfig == null) {
833             throw new IllegalStateException JavaDoc("projectConfig is null");
834         }
835         
836         for (Iterator JavaDoc i = projectConfig.getListeners().iterator(); i.hasNext(); ) {
837             Listener listener = (Listener) i.next();
838             try {
839                 listener.handleEvent(event);
840             } catch (CruiseControlException e) {
841                 StringBuffer JavaDoc message = new StringBuffer JavaDoc("exception notifying listener ");
842                 message.append(listener.getClass().getName());
843                 message.append(" for project ").append(name);
844                 LOG.error(message.toString(), e);
845             }
846         }
847     }
848
849     public boolean equals(Object JavaDoc arg0) {
850         if (arg0 == null) {
851             return false;
852         }
853
854         if (arg0.getClass().getName().equals(getClass().getName())) {
855             Project thatProject = (Project) arg0;
856             return thatProject.name.equals(name);
857         }
858
859         return false;
860     }
861
862     public int hashCode() {
863         return name.hashCode();
864     }
865
866 }
867
Popular Tags