001: /********************************************************************************
002: * CruiseControl, a Continuous Integration Toolkit
003: * Copyright (c) 2001, ThoughtWorks, Inc.
004: * 200 E. Randolph, 25th Floor
005: * Chicago, IL 60601 USA
006: * All rights reserved.
007: *
008: * Redistribution and use in source and binary forms, with or without
009: * modification, are permitted provided that the following conditions
010: * are met:
011: *
012: * + Redistributions of source code must retain the above copyright
013: * notice, this list of conditions and the following disclaimer.
014: *
015: * + Redistributions in binary form must reproduce the above
016: * copyright notice, this list of conditions and the following
017: * disclaimer in the documentation and/or other materials provided
018: * with the distribution.
019: *
020: * + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
021: * names of its contributors may be used to endorse or promote
022: * products derived from this software without specific prior
023: * written permission.
024: *
025: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
026: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
027: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
028: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
029: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
030: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
031: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
032: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
033: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
034: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
035: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
036: ********************************************************************************/package net.sourceforge.cruisecontrol;
037:
038: import java.io.File;
039: import java.io.FileInputStream;
040: import java.io.ObjectInputStream;
041: import java.io.Serializable;
042: import java.util.ArrayList;
043: import java.util.Collections;
044: import java.util.Iterator;
045: import java.util.List;
046:
047: import javax.management.JMException;
048: import javax.management.MBeanServer;
049:
050: import net.sourceforge.cruisecontrol.config.DefaultPropertiesPlugin;
051: import net.sourceforge.cruisecontrol.config.PluginPlugin;
052: import net.sourceforge.cruisecontrol.util.ValidationHelper;
053:
054: import org.apache.log4j.Logger;
055:
056: /**
057: * A plugin that represents the project node
058: *
059: * @author <a href="mailto:jerome@coffeebreaks.org">Jerome Lacoste</a>
060: */
061: public class ProjectConfig implements ProjectInterface {
062: private static final long serialVersionUID = -893779421250033198L;
063:
064: private static final Logger LOG = Logger
065: .getLogger(ProjectConfig.class);
066:
067: private String name;
068: private boolean buildAfterFailed = true;
069: private boolean forceOnly = false;
070: private boolean requiremodification = true;
071: private transient CCDateFormat dateFormat;
072:
073: private transient Bootstrappers bootstrappers;
074: private transient LabelIncrementer labelIncrementer;
075: private transient Listeners listeners;
076: private transient Log log;
077: private transient ModificationSet modificationSet;
078: private transient Publishers publishers;
079: private transient Schedule schedule;
080:
081: private Project project;
082:
083: /**
084: * Called after the configuration is read to make sure that all the mandatory parameters were specified..
085: *
086: * @throws CruiseControlException
087: * if there was a configuration error.
088: */
089: public void validate() throws CruiseControlException {
090: if (dateFormat != null) {
091: dateFormat.validate();
092: }
093: ValidationHelper.assertTrue(schedule != null,
094: "project requires a schedule");
095:
096: if (bootstrappers != null) {
097: bootstrappers.validate();
098: }
099:
100: if (listeners != null) {
101: listeners.validate();
102: }
103:
104: if (log == null) {
105: log = new Log();
106: }
107: log.setProjectName(name);
108: log.validate();
109:
110: if (modificationSet != null) {
111: modificationSet.validate();
112: }
113:
114: if (schedule != null) {
115: schedule.validate();
116: }
117:
118: if (publishers != null) {
119: publishers.validate();
120: }
121: }
122:
123: public void setName(String name) {
124: this .name = name;
125: }
126:
127: public void setBuildAfterFailed(boolean buildAfterFailed) {
128: this .buildAfterFailed = buildAfterFailed;
129: }
130:
131: /**
132: * Defines a name/value pair used in configuration.
133: */
134: public void add(DefaultPropertiesPlugin plugin) {
135: // currently only declared for documentation generation purposes
136: }
137:
138: /**
139: * Registers a classname with an alias.
140: */
141: public void add(PluginPlugin plugin) {
142: // currently only declared for documentation generation purposes
143: }
144:
145: public void add(CCDateFormat dateFormat) {
146: this .dateFormat = dateFormat;
147: }
148:
149: public void add(LabelIncrementer labelIncrementer) {
150: this .labelIncrementer = labelIncrementer;
151: }
152:
153: public void add(Listeners listeners) {
154: this .listeners = listeners;
155: }
156:
157: public void add(ModificationSet modificationSet) {
158: this .modificationSet = modificationSet;
159: }
160:
161: public void add(Bootstrappers bootstrappers) {
162: this .bootstrappers = bootstrappers;
163: }
164:
165: public void add(Publishers publishers) {
166: this .publishers = publishers;
167: }
168:
169: public void add(Schedule schedule) {
170: this .schedule = schedule;
171: }
172:
173: public void add(Log log) {
174: this .log = log;
175: }
176:
177: public CCDateFormat getDateFormat() {
178: return dateFormat;
179: }
180:
181: public boolean shouldBuildAfterFailed() {
182: return buildAfterFailed;
183: }
184:
185: public Log getLog() {
186: return log;
187: }
188:
189: public List getBootstrappers() {
190: return bootstrappers == null ? Collections.EMPTY_LIST
191: : bootstrappers.getBootstrappers();
192: }
193:
194: public List getListeners() {
195: return listeners == null ? Collections.EMPTY_LIST : listeners
196: .getListeners();
197: }
198:
199: public List getPublishers() {
200: return publishers == null ? Collections.EMPTY_LIST : publishers
201: .getPublishers();
202: }
203:
204: public ModificationSet getModificationSet() {
205: return modificationSet;
206: }
207:
208: public Schedule getSchedule() {
209: return schedule;
210: }
211:
212: public LabelIncrementer getLabelIncrementer() {
213: return labelIncrementer;
214: }
215:
216: public String getName() {
217: return name;
218: }
219:
220: public static class Bootstrappers implements Serializable {
221: private static final long serialVersionUID = 7428779281399848035L;
222: private List bootstrappers = new ArrayList();
223:
224: public void add(Bootstrapper bootstrapper) {
225: bootstrappers.add(bootstrapper);
226: }
227:
228: public List getBootstrappers() {
229: return bootstrappers;
230: }
231:
232: public void validate() throws CruiseControlException {
233: for (Iterator iterator = bootstrappers.iterator(); iterator
234: .hasNext();) {
235: Bootstrapper nextBootstrapper = (Bootstrapper) iterator
236: .next();
237: nextBootstrapper.validate();
238: }
239: }
240: }
241:
242: public static class Listeners implements Serializable {
243: private static final long serialVersionUID = -3816080104514876038L;
244: private List listeners = new ArrayList();
245:
246: public void add(Listener listener) {
247: listeners.add(listener);
248: }
249:
250: public List getListeners() {
251: return listeners;
252: }
253:
254: public void validate() throws CruiseControlException {
255: for (Iterator iterator = listeners.iterator(); iterator
256: .hasNext();) {
257: Listener nextListener = (Listener) iterator.next();
258: nextListener.validate();
259: }
260: }
261: }
262:
263: public static class Publishers implements Serializable {
264: private static final long serialVersionUID = -410933401108345152L;
265: private List publishers = new ArrayList();
266:
267: public void add(Publisher publisher) {
268: publishers.add(publisher);
269: }
270:
271: public List getPublishers() {
272: return publishers;
273: }
274:
275: public void validate() throws CruiseControlException {
276: for (Iterator iterator = publishers.iterator(); iterator
277: .hasNext();) {
278: Publisher nextPublisher = (Publisher) iterator.next();
279: nextPublisher.validate();
280: }
281:
282: }
283: }
284:
285: /**
286: * @param forceOnly
287: * the forceOnly to set
288: */
289: public void setForceOnly(boolean forceOnly) {
290: this .forceOnly = forceOnly;
291: }
292:
293: /**
294: * @return the forceOnly
295: */
296: public boolean isForceOnly() {
297: return forceOnly;
298: }
299:
300: /**
301: * @return the requiremodification
302: */
303: public boolean isRequiremodification() {
304: return requiremodification;
305: }
306:
307: /**
308: * @param requiremodification
309: * the requiremodification to set
310: */
311: public void setRequiremodification(boolean requiremodification) {
312: this .requiremodification = requiremodification;
313: }
314:
315: public void configureProject() throws CruiseControlException {
316: Project myProject = readProject(name);
317: myProject.setName(name);
318: myProject.setProjectConfig(this );
319: myProject.init();
320: this .project = myProject;
321: }
322:
323: /**
324: * Reads project configuration from a previously serialized Project or creates a new instance. The name of the
325: * serialized project file is derived from the name of the project.
326: *
327: * @param projectName
328: * name of the serialized project
329: * @return Deserialized Project or a new Project if there are any problems reading the serialized Project; should
330: * never return null
331: */
332: Project readProject(String projectName) {
333: File serializedProjectFile = new File(projectName + ".ser");
334: LOG.debug("Reading serialized project from: "
335: + serializedProjectFile.getAbsolutePath());
336:
337: if (!serializedProjectFile.exists()
338: || !serializedProjectFile.canRead()) {
339: serializedProjectFile = ProjectConfig
340: .tryOldSerializedFileName(projectName);
341: }
342:
343: if (!serializedProjectFile.exists()
344: || !serializedProjectFile.canRead()
345: || serializedProjectFile.isDirectory()) {
346: Project temp = new Project();
347: temp.setName(projectName);
348: LOG.warn("No previously serialized project found ["
349: + serializedProjectFile.getAbsolutePath()
350: + ".ser], forcing a build.");
351: Project newProject = new Project();
352: newProject.setBuildForced(true);
353: return newProject;
354: }
355:
356: try {
357: ObjectInputStream s = new ObjectInputStream(
358: new FileInputStream(serializedProjectFile));
359: return (Project) s.readObject();
360: } catch (Exception e) {
361: LOG.warn("Error deserializing project file from "
362: + serializedProjectFile.getAbsolutePath(), e);
363: return new Project();
364: }
365: }
366:
367: private static File tryOldSerializedFileName(String projectName) {
368: File serializedProjectFile;
369: serializedProjectFile = new File(projectName);
370: LOG
371: .debug(projectName
372: + ".ser not found, looking for serialized project file "
373: + serializedProjectFile.getAbsolutePath());
374: return serializedProjectFile;
375: }
376:
377: public boolean equals(Object arg0) {
378: if (arg0 == null) {
379: return false;
380: }
381:
382: if (arg0.getClass().getName().equals(getClass().getName())) {
383: ProjectConfig thatProject = (ProjectConfig) arg0;
384: return thatProject.name.equals(name);
385: }
386:
387: return false;
388: }
389:
390: public int hashCode() {
391: return name.hashCode();
392: }
393:
394: public void getStateFromOldProject(ProjectInterface oldProject)
395: throws CruiseControlException {
396: ProjectConfig oldProjectConfig = (ProjectConfig) oldProject;
397: project = oldProjectConfig.project;
398: project.setProjectConfig(this );
399: project.init();
400: }
401:
402: public void execute() {
403: project.execute();
404: }
405:
406: public void register(MBeanServer server) throws JMException {
407: project.register(server);
408: }
409:
410: public void setBuildQueue(BuildQueue buildQueue) {
411: project.setBuildQueue(buildQueue);
412: }
413:
414: public void start() {
415: project.start();
416: }
417:
418: public void stop() {
419: project.stop();
420: }
421:
422: // TODO remove this. only here till tests are fixed up.
423: Project getProject() {
424: return project;
425: }
426:
427: public String getStatus() {
428: return project.getStatus();
429: }
430:
431: public String getBuildStartTime() {
432: return project.getBuildStartTime();
433: }
434:
435: public boolean isPaused() {
436: return project.isPaused();
437: }
438:
439: public List getModifications() {
440: return getModificationSet().getCurrentModifications();
441: }
442:
443: public boolean isInState(ProjectState state) {
444: return project.getState().equals(state);
445: }
446: }
|