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.distributed.util;
037:
038: import java.io.BufferedReader;
039: import java.io.File;
040: import java.io.IOException;
041: import java.io.InputStream;
042: import java.io.InputStreamReader;
043: import java.rmi.RemoteException;
044: import java.util.Iterator;
045: import java.util.List;
046: import java.util.Properties;
047:
048: import net.jini.core.lookup.ServiceItem;
049: import net.jini.core.entry.Entry;
050: import net.sourceforge.cruisecontrol.CruiseControlException;
051: import net.sourceforge.cruisecontrol.PluginXMLHelper;
052: import net.sourceforge.cruisecontrol.ProjectXMLHelper;
053: import net.sourceforge.cruisecontrol.builders.DistributedMasterBuilder;
054: import net.sourceforge.cruisecontrol.distributed.BuildAgentService;
055: import net.sourceforge.cruisecontrol.distributed.core.PropertiesHelper;
056: import net.sourceforge.cruisecontrol.distributed.core.MulticastDiscovery;
057: import net.sourceforge.cruisecontrol.distributed.core.ReggieUtil;
058: import net.sourceforge.cruisecontrol.distributed.core.RemoteResult;
059: import net.sourceforge.cruisecontrol.util.Util;
060:
061: import org.apache.log4j.Logger;
062: import org.jdom.Attribute;
063: import org.jdom.Element;
064: import org.jdom.output.XMLOutputter;
065:
066: public class InteractiveBuildUtility {
067:
068: private static final Logger LOG = Logger
069: .getLogger(InteractiveBuildUtility.class);
070:
071: private static final Console CONSOLE = new Console();
072:
073: private Element distributedBuilderElement;
074:
075: public InteractiveBuildUtility() {
076: System.out
077: .print("Enter path to Cruise Control configuration file: ");
078: String configFilePath = CONSOLE.readLine();
079: new InteractiveBuildUtility(configFilePath);
080: }
081:
082: private InteractiveBuildUtility(String configFilePath) {
083: File configFile = new File(configFilePath);
084: if (!configFile.exists() || configFile.isDirectory()) {
085: String message = configFilePath
086: + " does not exist or is a directory - quitting...";
087: System.err.println(message);
088: LOG.error(message);
089: System.exit(1);
090: }
091: Element project = getProjectFromConfig(configFile);
092: distributedBuilderElement = getBuilderFromProject(project);
093: ServiceItem[] serviceItems = findAgents(distributedBuilderElement
094: .getAttribute("entries"));
095: final BuildAgentService agent = selectAgent(serviceItems);
096: final DistributedMasterBuilder distributedBuildMaster = doBuild();
097: retrieveBuildArtifacts(agent, distributedBuildMaster);
098: }
099:
100: private Element getProjectFromConfig(File configFile) {
101: Element rootElement = null;
102: Element project = null;
103: try {
104: rootElement = Util.loadRootElement(configFile);
105: } catch (CruiseControlException e) {
106: System.err.println(e.getMessage());
107: LOG.error(e.getMessage(), e);
108: System.exit(1);
109: }
110: List projects = rootElement.getChildren("project");
111: if (projects.size() == 0) {
112: String message = "No projects found in configuration file at "
113: + configFile.getAbsolutePath() + " - quitting...";
114: System.err.println(message);
115: LOG.error(message);
116: System.out.println();
117: System.exit(1);
118: } else if (projects.size() == 1) {
119: project = (Element) projects.get(0);
120: String message = "Found one project--using '"
121: + project.getAttributeValue("name") + "'";
122: System.out.println(message);
123: LOG.info(message);
124: System.out.println();
125: } else {
126: System.out
127: .println("Found multiple projects in configuration file:");
128: Iterator iter = projects.iterator();
129: for (int i = 0; iter.hasNext(); i++) {
130: Element tempProject = (Element) iter.next();
131: System.out.println(i + 1 + ") "
132: + tempProject.getAttributeValue("name"));
133: LOG.debug("Found project: "
134: + tempProject.getAttributeValue("name"));
135: }
136: System.out.print("Select project number: ");
137: int projectNumber = Integer.parseInt(CONSOLE.readLine());
138: if ((projectNumber > projects.size())
139: || (projectNumber < 1)) {
140: String message = "Not a valid project number - quitting...";
141: System.err.println(message);
142: LOG.error(message);
143: System.exit(1);
144: }
145: project = (Element) projects.get(projectNumber - 1);
146: System.out.println();
147: }
148: return project;
149: }
150:
151: private Element getBuilderFromProject(Element project) {
152: Element builder = null;
153: List schedules = project.getChildren("schedule");
154: if (schedules.size() == 0) {
155: String message = "No schedule for project -- quitting...";
156: System.err.println(message);
157: LOG.error(message);
158: System.exit(1);
159: } else if (schedules.size() > 1) {
160: String message = "More than one schedule for project -- quitting...";
161: System.err.println(message);
162: LOG.error(message);
163: System.exit(1);
164: } else {
165: List builders = ((Element) schedules.get(0)).getChildren();
166: if (builders.size() == 0) {
167: String message = "No builder for project -- quitting...";
168: System.err.println(message);
169: LOG.error(message);
170: System.exit(1);
171: } else if (builders.size() > 1) {
172: String message = "Multiple builders found -- defaulting to first builder";
173: System.out.println(message);
174: LOG.warn(message);
175: }
176: builder = (Element) builders.get(0);
177: }
178: return builder;
179: }
180:
181: private ServiceItem[] findAgents(Attribute configEntries) {
182: final String searchEntries;
183: if (configEntries == null) {
184: System.out
185: .println("Enter search entries as comma-separated name/value pairs "
186: + "(e.g. \"os.name=WinNT, fixpack=4.1\")");
187: System.out.print("Search entries: ");
188: searchEntries = CONSOLE.readLine();
189: } else {
190: searchEntries = configEntries.getValue();
191: }
192: LOG.debug("Searching for serviceItems matching entries: "
193: + searchEntries);
194: final Entry[] entries = ReggieUtil
195: .convertStringEntries(searchEntries);
196:
197: MulticastDiscovery.begin();
198: System.out
199: .println("Waiting 5 seconds for registrars to report in...");
200: try {
201: Thread.sleep(5 * 1000);
202: } catch (InterruptedException e) {
203: // ignore
204: }
205: final ServiceItem[] serviceItems;
206: try {
207: serviceItems = MulticastDiscovery.findBuildAgentServices(
208: entries,
209: MulticastDiscovery.DEFAULT_FIND_WAIT_DUR_MILLIS);
210: } catch (RemoteException e) {
211: e.printStackTrace();
212: String message = "Problem occurred finding Build Agents: "
213: + e.getMessage();
214: LOG.error(message);
215: System.err.println(message);
216: throw new RuntimeException(e);
217: }
218: if (serviceItems.length == 0) {
219: String message = "No matches for your search - quitting...";
220: System.err.println(message);
221: System.out.println();
222: LOG.error(message);
223: System.exit(1);
224: }
225: System.out.println();
226: return serviceItems;
227: }
228:
229: private BuildAgentService selectAgent(
230: final ServiceItem[] serviceItems) {
231: BuildAgentService agent = null;
232:
233: if (serviceItems.length < 1) {
234: String message = "No matching serviceItems found - quitting...";
235: LOG.error(message);
236: System.err.println(message);
237: System.exit(1);
238: } else if (serviceItems.length == 1) {
239: agent = (BuildAgentService) serviceItems[0].service;
240: String agentName = null;
241: try {
242: agentName = agent.getMachineName();
243: } catch (RemoteException e) {
244: String message = "Error getting machine name from agent - quitting...";
245: System.err.println(message);
246: LOG.error(message, e);
247: System.exit(1);
248: }
249: String infoMessage = "One matching agent found: "
250: + agentName + " -- selecting it automatically";
251: System.out.println(infoMessage);
252: LOG.debug(infoMessage);
253: System.out.println();
254: } else {
255: System.out.println("Found serviceItems:");
256: String machineName;
257: for (int i = 0; i < serviceItems.length; i++) {
258: try {
259: machineName = ((BuildAgentService) serviceItems[i].service)
260: .getMachineName();
261: System.out.println(i + 1 + ") " + machineName);
262: LOG.debug("Found agent: " + machineName);
263: } catch (RemoteException e1) {
264: String message = "Couldn't get machine name for agent - quitting...";
265: LOG.error(message, e1);
266: System.err.println(message + " - "
267: + e1.getMessage());
268: System.exit(1);
269: }
270: }
271: System.out
272: .print("Select agent # or 0 to list status of all serviceItems: ");
273: int agentNum = Integer.parseInt(CONSOLE.readLine());
274: if ((agentNum > serviceItems.length) || (agentNum < 0)) {
275: agent = null;
276: String message = "Not a valid agent number - quitting...";
277: System.err.println(message);
278: LOG.error(message);
279: System.exit(1);
280: }
281: if (agentNum == 0) {
282: displayAgentStatuses();
283: System.out.println("Done...");
284: System.exit(0);
285: } else {
286: agent = (BuildAgentService) serviceItems[agentNum - 1].service;
287: System.out.println();
288: }
289: }
290: return agent;
291: }
292:
293: private void displayAgentStatuses() {
294: // TODO unimplemented
295: System.out.println();
296: System.err
297: .println("Unimplemented feature - quitting...see BuildAgentUtility");
298: }
299:
300: private DistributedMasterBuilder doBuild() {
301:
302: System.out.println("Beginning build...");
303: System.out.println();
304: ProjectXMLHelper projectXMLHelper = new ProjectXMLHelper();
305: PluginXMLHelper pluginXMLHelper = new PluginXMLHelper(
306: projectXMLHelper);
307:
308: final DistributedMasterBuilder distributedBuildMaster;
309: try {
310: distributedBuildMaster = (DistributedMasterBuilder) pluginXMLHelper
311: .configure(distributedBuilderElement,
312: DistributedMasterBuilder.class, false);
313: XMLOutputter xmlOutputter = new XMLOutputter();
314: xmlOutputter.output(distributedBuildMaster.build(
315: new Properties(), null), System.out);
316: } catch (CruiseControlException e) {
317: String message = "Oops...";
318: LOG.error(message, e);
319: System.err.println(message + " - " + e.getMessage());
320: return null;
321: } catch (IOException e) {
322: String message = "Oops...";
323: LOG.error(message, e);
324: System.err.println(message + " - " + e.getMessage());
325: return null;
326: }
327: System.out.println();
328: return distributedBuildMaster;
329: }
330:
331: private void retrieveBuildArtifacts(final BuildAgentService agent,
332: final DistributedMasterBuilder distributedBuildMaster) {
333: try {
334: final File currentDir = new File(".");
335: DistributedMasterBuilder.getResultsFiles(agent, currentDir,
336: "projectInteractive",
337: PropertiesHelper.RESULT_TYPE_LOGS, currentDir);
338:
339: DistributedMasterBuilder.getResultsFiles(agent, currentDir,
340: "projectInteractive",
341: PropertiesHelper.RESULT_TYPE_OUTPUT, currentDir);
342:
343: final RemoteResult[] remoteResults = distributedBuildMaster
344: .getRemoteResultsInfo();
345: if (remoteResults != null) {
346: for (int i = 0; i < remoteResults.length; i++) {
347: DistributedMasterBuilder.getRemoteResult(agent,
348: currentDir, "projectInteractive",
349: remoteResults[i]);
350: }
351: }
352: agent.clearOutputFiles();
353: } catch (RemoteException e) {
354: String message = "Problem occurred getting or unzipping results";
355: LOG.debug(message);
356: System.out.println(message);
357: }
358: }
359:
360: public static void main(String[] args) {
361: if (args.length == 0) {
362: new InteractiveBuildUtility();
363: } else {
364: new InteractiveBuildUtility(args[0]);
365: }
366: }
367:
368: public static class Console {
369: private InputStream inputStream = null;
370:
371: public Console() {
372: this .inputStream = System.in;
373: }
374:
375: public String readLine() {
376: BufferedReader in = new BufferedReader(
377: new InputStreamReader(inputStream));
378: try {
379: return in.readLine().trim();
380: } catch (IOException e) {
381: String message = "Error reading input";
382: LOG.error(message, e);
383: System.err.println(message + " - " + e.getMessage());
384: throw new RuntimeException(message, e);
385: }
386: }
387: }
388: }
|