001: /********************************************************************************
002: * CruiseControl, a Continuous Integration Toolkit
003: * Copyright (c) 2001-2003, 2006, 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.util.ArrayList;
040: import java.util.Collections;
041: import java.util.EventListener;
042: import java.util.Iterator;
043: import java.util.List;
044: import java.util.Properties;
045: import java.util.Set;
046:
047: import net.sourceforge.cruisecontrol.config.XMLConfigManager;
048:
049: import org.apache.log4j.Logger;
050:
051: /**
052: * @author <a href="mailto:robertdw@users.sourceforge.net">Robert Watkins</a>
053: */
054: public class CruiseControlController {
055: private static final Logger LOG = Logger
056: .getLogger(CruiseControlController.class);
057: public static final String DEFAULT_CONFIG_FILE_NAME = "config.xml";
058: private File configFile;
059: private List projects = new ArrayList();
060: private BuildQueue buildQueue = new BuildQueue();
061: private Properties versionProperties;
062:
063: private List listeners = new ArrayList();
064: private XMLConfigManager configManager;
065:
066: private ParsingConfigMutex parsingConfigMutex = new ParsingConfigMutex();
067:
068: public CruiseControlController() {
069: buildQueue.addListener(new BuildQueueListener());
070: }
071:
072: public File getConfigFile() {
073: return configFile;
074: }
075:
076: public void setVersionProperties(Properties versionProperties) {
077: this .versionProperties = versionProperties;
078: }
079:
080: public Properties getVersionProperties() {
081: return versionProperties;
082: }
083:
084: public void setConfigFile(File configFile)
085: throws CruiseControlException {
086: if (configFile == null) {
087: throw new CruiseControlException("No config file");
088: }
089: if (!configFile.isFile()) {
090: throw new CruiseControlException("Config file not found: "
091: + configFile.getAbsolutePath());
092: }
093:
094: if (!configFile.equals(this .configFile)) {
095: this .configFile = configFile;
096: configManager = new XMLConfigManager(configFile, this );
097: }
098:
099: loadConfig();
100: }
101:
102: private void addProject(ProjectInterface project)
103: throws CruiseControlException {
104: project.configureProject();
105: projects.add(project);
106: for (Iterator listenIter = listeners.iterator(); listenIter
107: .hasNext();) {
108: LOG.debug("Informing listener of added project "
109: + project.getName());
110: Listener listener = (Listener) listenIter.next();
111: listener.projectAdded(project);
112: }
113: project.setBuildQueue(buildQueue);
114: project.start();
115: }
116:
117: private void removeProject(ProjectInterface project) {
118: projects.remove(project);
119: for (Iterator listenIter = listeners.iterator(); listenIter
120: .hasNext();) {
121: LOG.debug("Informing listener of removed project "
122: + project.getName());
123: Listener listener = (Listener) listenIter.next();
124: listener.projectRemoved(project);
125: }
126: project.stop();
127: }
128:
129: public void resume() {
130: buildQueue.start();
131: for (Iterator iterator = projects.iterator(); iterator
132: .hasNext();) {
133: ProjectInterface currentProject = (ProjectInterface) iterator
134: .next();
135: currentProject.setBuildQueue(buildQueue);
136: currentProject.start();
137: }
138: }
139:
140: public void pause() {
141: buildQueue.stop();
142: for (Iterator iterator = projects.iterator(); iterator
143: .hasNext();) {
144: ProjectInterface currentProject = (ProjectInterface) iterator
145: .next();
146: currentProject.stop();
147: }
148: }
149:
150: public void halt() {
151: pause();
152: System.exit(0);
153: }
154:
155: public String getBuildQueueStatus() {
156: if (buildQueue.isAlive()) {
157: if (buildQueue.isWaiting()) {
158: return "waiting";
159: } else {
160: return "alive";
161: }
162: } else {
163: return "dead";
164: }
165: }
166:
167: public List getProjects() {
168: return Collections.unmodifiableList(projects);
169: }
170:
171: private List getAllProjects(XMLConfigManager configManager) {
172: Set projectNames = configManager.getCruiseControlConfig()
173: .getProjectNames();
174: List allProjects = new ArrayList(projectNames.size());
175: for (Iterator it = projectNames.iterator(); it.hasNext();) {
176: String projectName = (String) it.next();
177: LOG.info("projectName = [" + projectName + "]");
178: ProjectInterface projectConfig = getConfigManager()
179: .getProject(projectName);
180: allProjects.add(projectConfig);
181: }
182: if (allProjects.size() == 0) {
183: LOG.warn("no projects found in config file");
184: }
185: return allProjects;
186: }
187:
188: protected XMLConfigManager getConfigManager() {
189: return configManager;
190: }
191:
192: public void addListener(Listener listener) {
193: LOG.debug("Listener added");
194: listeners.add(listener);
195: }
196:
197: public void reloadConfigFile() {
198: LOG.debug("reload config file called");
199: parseConfigFileIfNecessary();
200: }
201:
202: /**
203: * @return true if the config file was parsed.
204: */
205: public boolean parseConfigFileIfNecessary() {
206: boolean reloaded = false;
207: if (parsingConfigMutex.getPermissionToParse()) {
208: try {
209: try {
210: reloaded = configManager.reloadIfNecessary();
211: } catch (CruiseControlException e) {
212: LOG.error("error parsing config file "
213: + configFile.getAbsolutePath(), e);
214: return reloaded;
215: }
216:
217: if (reloaded) {
218: LOG.debug("config file changed");
219: loadConfig();
220: } else {
221: LOG.debug("config file didn't change.");
222: }
223: } finally {
224: parsingConfigMutex.doneParsing();
225: }
226: }
227: return reloaded;
228: }
229:
230: private void loadConfig() {
231: try {
232: List projectsFromFile = getAllProjects(configManager);
233:
234: List removedProjects = new ArrayList(projects);
235: removedProjects.removeAll(projectsFromFile);
236:
237: List newProjects = new ArrayList(projectsFromFile);
238: newProjects.removeAll(projects);
239:
240: List retainedProjects = new ArrayList(projects);
241: retainedProjects.removeAll(removedProjects);
242:
243: //Handled removed projects
244: Iterator removed = removedProjects.iterator();
245: while (removed.hasNext()) {
246: removeProject((ProjectInterface) removed.next());
247: }
248:
249: //Handle added projects
250: Iterator added = newProjects.iterator();
251: while (added.hasNext()) {
252: addProject((ProjectInterface) added.next());
253: }
254:
255: //Handle retained projects
256: Iterator retained = retainedProjects.iterator();
257: while (retained.hasNext()) {
258: updateProject((ProjectInterface) retained.next());
259: }
260:
261: } catch (CruiseControlException e) {
262: LOG.error("error parsing config file "
263: + configFile.getAbsolutePath(), e);
264: }
265: }
266:
267: private void updateProject(ProjectInterface oldProject)
268: throws CruiseControlException {
269: ProjectInterface newProject = getConfigManager().getProject(
270: oldProject.getName());
271: projects.remove(oldProject);
272: newProject.getStateFromOldProject(oldProject);
273: projects.add(newProject);
274: }
275:
276: public static interface Listener extends EventListener {
277: void projectAdded(ProjectInterface project);
278:
279: void projectRemoved(ProjectInterface project);
280: }
281:
282: private class BuildQueueListener implements BuildQueue.Listener {
283: public void buildRequested() {
284: parseConfigFileIfNecessary();
285: }
286: }
287:
288: public PluginDetail[] getAvailableBootstrappers() {
289: return getPluginsByType(getAvailablePlugins(),
290: PluginType.BOOTSTRAPPER);
291: }
292:
293: public PluginDetail[] getAvailablePublishers() {
294: return getPluginsByType(getAvailablePlugins(),
295: PluginType.PUBLISHER);
296: }
297:
298: public PluginDetail[] getAvailableSourceControls() {
299: return getPluginsByType(getAvailablePlugins(),
300: PluginType.SOURCE_CONTROL);
301: }
302:
303: public PluginDetail[] getAvailablePlugins() {
304: try {
305: return getPluginRegistry().getPluginDetails();
306: } catch (CruiseControlException e) {
307: return new PluginDetail[0];
308: }
309: }
310:
311: public PluginType[] getAvailablePluginTypes() {
312: return getPluginRegistry().getPluginTypes();
313: }
314:
315: public PluginRegistry getPluginRegistry() {
316: return configManager.getCruiseControlConfig().getRootPlugins();
317: }
318:
319: private static PluginDetail[] getPluginsByType(
320: PluginDetail[] details, PluginType type) {
321: List plugins = new ArrayList();
322: for (int i = 0; i < details.length; i++) {
323: if (details[i].getType().equals(type)) {
324: plugins.add(details[i]);
325: }
326: }
327:
328: return (PluginDetail[]) plugins
329: .toArray(new PluginDetail[plugins.size()]);
330: }
331:
332: private class ParsingConfigMutex {
333: private Object mutex = new Object();
334: private boolean inUse;
335:
336: boolean getPermissionToParse() {
337: synchronized (mutex) {
338: if (inUse) {
339: LOG.debug("permission denied to parse config");
340: return false;
341: }
342: inUse = true;
343: LOG.debug("permission granted to parse config");
344: return true;
345: }
346: }
347:
348: void doneParsing() {
349: LOG
350: .debug("done parsing, allow next request permission to parse");
351: inUse = false;
352: }
353: }
354: }
|