KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sourceforge > cruisecontrol > builders > AntBuilder


1 /********************************************************************************
2  * CruiseControl, a Continuous Integration Toolkit
3  * Copyright (c) 2001, 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
38 package net.sourceforge.cruisecontrol.builders;
39
40 import java.io.File JavaDoc;
41 import java.io.FileInputStream JavaDoc;
42 import java.io.FileOutputStream JavaDoc;
43 import java.io.IOException JavaDoc;
44 import java.util.ArrayList JavaDoc;
45 import java.util.List JavaDoc;
46 import java.util.Map JavaDoc;
47
48 import net.sourceforge.cruisecontrol.Builder;
49 import net.sourceforge.cruisecontrol.CruiseControlException;
50 import net.sourceforge.cruisecontrol.util.EmptyElementFilter;
51 import net.sourceforge.cruisecontrol.util.Util;
52 import net.sourceforge.cruisecontrol.util.ValidationHelper;
53
54 import org.apache.log4j.Logger;
55 import org.jdom.Element;
56 import org.jdom.input.SAXBuilder;
57 import org.xml.sax.SAXException JavaDoc;
58 import org.xml.sax.XMLFilter JavaDoc;
59 import org.xml.sax.helpers.XMLFilterImpl JavaDoc;
60
61 /**
62  * we often see builds that fail because the previous build is still holding on to some resource.
63  * we can avoid this by just building in a different process which will completely die after every
64  * build.
65  */

66 public class AntBuilder extends Builder {
67
68     protected static final String JavaDoc DEFAULT_LOGGER = "org.apache.tools.ant.XmlLogger";
69     private static final Logger LOG = Logger.getLogger(AntBuilder.class);
70
71     private String JavaDoc antWorkingDir = null;
72     private String JavaDoc buildFile = "build.xml";
73     private String JavaDoc target = "";
74     private String JavaDoc tempFileName = "log.xml";
75     private String JavaDoc antScript;
76     private String JavaDoc antHome;
77     private boolean useLogger;
78     private List JavaDoc args = new ArrayList JavaDoc();
79     private List JavaDoc properties = new ArrayList JavaDoc();
80     private boolean useDebug = false;
81     private boolean useQuiet = false;
82     private String JavaDoc loggerClassName = DEFAULT_LOGGER;
83     private File JavaDoc saveLogDir = null;
84     private long timeout = ScriptRunner.NO_TIMEOUT;
85     private boolean wasValidated = false;
86
87     public void validate() throws CruiseControlException {
88         super.validate();
89
90         ValidationHelper.assertIsSet(buildFile, "buildfile", this.getClass());
91         ValidationHelper.assertIsSet(target, "target", this.getClass());
92         ValidationHelper.assertFalse(useDebug && useQuiet,
93             "'useDebug' and 'useQuiet' can't be used together");
94
95         if (!useLogger && (useDebug || useQuiet)) {
96             LOG.warn("usedebug and usequiet are ignored if uselogger is not set to 'true'!");
97         }
98
99         if (saveLogDir != null) {
100             ValidationHelper.assertTrue(saveLogDir.isDirectory(), "'saveLogDir' must exist and be a directory");
101         }
102
103         ValidationHelper.assertFalse(antScript != null && antHome != null,
104             "'antHome' and 'antscript' cannot both be set");
105
106         if (antHome != null) {
107             final File JavaDoc antHomeFile = new File JavaDoc(antHome);
108             ValidationHelper.assertTrue(antHomeFile.exists() && antHomeFile.isDirectory(),
109                 "'antHome' must exist and be a directory. Expected to find "
110                 + antHomeFile.getAbsolutePath());
111
112             final File JavaDoc antScriptInAntHome = new File JavaDoc(findAntScript(Util.isWindows()));
113             ValidationHelper.assertTrue(antScriptInAntHome.exists() && antScriptInAntHome.isFile(),
114                 "'antHome' must contain an ant execution script. Expected to find "
115                 + antScriptInAntHome.getAbsolutePath());
116
117             antScript = antScriptInAntHome.getAbsolutePath();
118         }
119
120         if (antScript != null && !args.isEmpty()) {
121             LOG.warn("jvmargs will be ignored if you specify anthome or your own antscript!");
122         }
123
124         wasValidated = true;
125     }
126
127     /**
128      * build and return the results via xml. debug status can be determined
129      * from log4j category once we get all the logging in place.
130      */

131     public Element build(Map JavaDoc buildProperties) throws CruiseControlException {
132         if (!wasValidated) {
133             throw new IllegalStateException JavaDoc("This builder was never validated."
134                  + " The build method should not be getting called.");
135         }
136
137         validateBuildFileExists();
138
139         AntScript script = new AntScript();
140         script.setBuildProperties(buildProperties);
141         script.setProperties(properties);
142         script.setUseLogger(useLogger);
143         script.setUseScript(antScript != null);
144         script.setWindows(Util.isWindows());
145         script.setAntScript(antScript);
146         script.setArgs(args);
147         script.setBuildFile(buildFile);
148         script.setTarget(target);
149         script.setLoggerClassName(loggerClassName);
150         script.setTempFileName(tempFileName);
151         script.setUseDebug(useDebug);
152         script.setUseQuiet(useQuiet);
153         script.setSystemClassPath(getSystemClassPath());
154
155         File JavaDoc workingDir = antWorkingDir != null ? new File JavaDoc(antWorkingDir) : null;
156
157         boolean scriptCompleted = new ScriptRunner().runScript(workingDir, script, timeout);
158
159         File JavaDoc logFile = new File JavaDoc(antWorkingDir, tempFileName);
160         Element buildLogElement;
161         if (!scriptCompleted) {
162             LOG.warn("Build timeout timer of " + timeout + " seconds has expired");
163             buildLogElement = new Element("build");
164             buildLogElement.setAttribute("error", "build timeout");
165             // although log file is most certainly empty, let's try to preserve it
166
// somebody should really fix ant's XmlLogger
167
if (logFile.exists()) {
168                 try {
169                     buildLogElement.setText(Util.readFileToString(logFile));
170                 } catch (IOException JavaDoc likely) {
171                 }
172             }
173         } else {
174             //read in log file as element, return it
175
buildLogElement = getAntLogAsElement(logFile);
176             saveAntLog(logFile);
177             logFile.delete();
178         }
179         return buildLogElement;
180     }
181     
182     public Element buildWithTarget(Map JavaDoc properties, String JavaDoc buildTarget) throws CruiseControlException {
183         String JavaDoc origTarget = target;
184         try {
185             target = buildTarget;
186             return build(properties);
187         } finally {
188             target = origTarget;
189         }
190     }
191
192     void validateBuildFileExists() throws CruiseControlException {
193         File JavaDoc build = new File JavaDoc(buildFile);
194         if (!build.isAbsolute() && antWorkingDir != null) {
195             build = new File JavaDoc(antWorkingDir, buildFile);
196         }
197         ValidationHelper.assertExists(build, "buildfile", this.getClass());
198     }
199
200
201     /**
202      * Set the location to which the ant log will be saved before Cruise
203      * Control merges the file into its log.
204      *
205      * @param dir
206      * the absolute path to the directory where the ant log will be
207      * saved or relative path to where you started CruiseControl
208      */

209     public void setSaveLogDir(String JavaDoc dir) {
210         saveLogDir = null;
211
212         if (dir != null && !dir.trim().equals("")) {
213             saveLogDir = new File JavaDoc(dir.trim());
214         }
215     }
216
217     void saveAntLog(File JavaDoc logFile) {
218         if (saveLogDir == null) {
219             return;
220         }
221
222         try {
223             File JavaDoc newAntLogFile = new File JavaDoc(saveLogDir, tempFileName);
224             newAntLogFile.createNewFile();
225
226             FileInputStream JavaDoc in = new FileInputStream JavaDoc(logFile);
227             FileOutputStream JavaDoc out = new FileOutputStream JavaDoc(newAntLogFile);
228
229             byte[] buf = new byte[1024];
230             int len;
231             while ((len = in.read(buf)) > 0) {
232                 out.write(buf, 0, len);
233             }
234             in.close();
235             out.close();
236         } catch (IOException JavaDoc ioe) {
237             LOG.error(ioe);
238             LOG.error("Unable to create file: " + new File JavaDoc(saveLogDir, tempFileName));
239         }
240   }
241
242     /**
243      * Set the working directory where Ant will be invoked. This parameter gets
244      * set in the XML file via the antWorkingDir attribute. The directory can
245      * be relative (to the cruisecontrol current working directory) or absolute.
246      *
247      * @param dir
248      * the directory to make the current working directory.
249      */

250     public void setAntWorkingDir(String JavaDoc dir) {
251         antWorkingDir = dir;
252     }
253
254     /**
255      * Sets the Script file to be invoked (in place of calling the Ant class
256      * directly). This is a platform dependent script file.
257      *
258      * @param antScript the name of the script file
259      */

260     public void setAntScript(String JavaDoc antScript) {
261         this.antScript = antScript;
262     }
263
264     /**
265      * If set CC will use the platform specific script provided by Ant
266      *
267      * @param antHome the path to ANT_HOME
268      */

269     public void setAntHome(String JavaDoc antHome) {
270         this.antHome = antHome;
271     }
272
273     /**
274      * If the anthome attribute is set, then this method returns the correct shell script
275      * to use for a specific environment.
276      */

277     protected String JavaDoc findAntScript(boolean isWindows) throws CruiseControlException {
278         if (antHome == null) {
279             throw new CruiseControlException("anthome attribute not set.");
280         }
281
282         if (isWindows) {
283             return antHome + "\\bin\\ant.bat";
284         } else {
285             return antHome + "/bin/ant";
286         }
287     }
288
289     /**
290      * Set the name of the temporary file used to capture output.
291      *
292      * @param tempFileName
293      */

294     public void setTempFile(String JavaDoc tempFileName) {
295         this.tempFileName = tempFileName;
296     }
297
298     /**
299      * Set the Ant target(s) to invoke.
300      *
301      * @param target the target(s) name.
302      */

303     public void setTarget(String JavaDoc target) {
304         this.target = target;
305     }
306
307     /**
308      * Sets the name of the build file that Ant will use. The Ant default is
309      * build.xml, use this to override it.
310      *
311      * @param buildFile the name of the build file.
312      */

313     public void setBuildFile(String JavaDoc buildFile) {
314         this.buildFile = buildFile;
315     }
316
317     /**
318      * Sets whether Ant will use the custom loggers.
319      *
320      * @param useLogger
321      */

322     public void setUseLogger(boolean useLogger) {
323         this.useLogger = useLogger;
324     }
325
326     public Object JavaDoc createJVMArg() {
327         JVMArg arg = new JVMArg();
328         args.add(arg);
329         return arg;
330     }
331
332     public Property createProperty() {
333         Property property = new Property();
334         properties.add(property);
335         return property;
336     }
337
338     protected String JavaDoc getSystemClassPath() {
339       return System.getProperty("java.class.path");
340     }
341
342     protected static Element getAntLogAsElement(File JavaDoc file) throws CruiseControlException {
343         if (!file.exists()) {
344             throw new CruiseControlException("ant logfile " + file.getAbsolutePath() + " does not exist.");
345         } else if (file.length() == 0) {
346             throw new CruiseControlException("ant logfile " + file.getAbsolutePath()
347                     + " is empty. Your build probably failed. Check your CruiseControl logs.");
348         }
349
350         try {
351             SAXBuilder builder = new SAXBuilder("org.apache.xerces.parsers.SAXParser");
352
353             // old Ant-versions contain a bug in the XmlLogger that outputs
354
// an invalid PI containing the target "xml:stylesheet"
355
// instead of "xml-stylesheet": fix this
356
XMLFilter JavaDoc piFilter = new XMLFilterImpl JavaDoc() {
357                 public void processingInstruction(String JavaDoc target, String JavaDoc data) throws SAXException JavaDoc {
358                     if (target.equals("xml:stylesheet")) { target = "xml-stylesheet"; }
359                     super.processingInstruction(target, data);
360                 }
361             };
362
363             // get rid of empty <task>- and <message>-elements created by Ant's XmlLogger
364
XMLFilter JavaDoc emptyTaskFilter = new EmptyElementFilter("task");
365             emptyTaskFilter.setParent(piFilter);
366             XMLFilter JavaDoc emptyMessageFilter = new EmptyElementFilter("message");
367             emptyMessageFilter.setParent(emptyTaskFilter);
368             builder.setXMLFilter(emptyMessageFilter);
369             return builder.build(file).getRootElement();
370         } catch (Exception JavaDoc ee) {
371             if (ee instanceof CruiseControlException) {
372                 throw (CruiseControlException) ee;
373             }
374             File JavaDoc saveFile = new File JavaDoc(file.getParentFile(), System.currentTimeMillis() + file.getName());
375             file.renameTo(saveFile);
376             throw new CruiseControlException("Error reading : " + file.getAbsolutePath()
377                     + ". Saved as : " + saveFile.getAbsolutePath(), ee);
378         }
379     }
380
381     public void setUseDebug(boolean debug) {
382         useDebug = debug;
383     }
384
385     public void setUseQuiet(boolean quiet) {
386         useQuiet = quiet;
387     }
388
389     public String JavaDoc getLoggerClassName() {
390         return loggerClassName;
391     }
392
393     public void setLoggerClassName(String JavaDoc string) {
394         loggerClassName = string;
395     }
396
397     public class JVMArg {
398         private String JavaDoc arg;
399
400         public void setArg(String JavaDoc arg) {
401             this.arg = arg;
402         }
403
404         public String JavaDoc getArg() {
405             return arg;
406         }
407     }
408     /**
409      * @param timeout The timeout to set.
410      */

411     public void setTimeout(long timeout) {
412         this.timeout = timeout;
413     }
414 }
415
Popular Tags