KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sourceforge > cruisecontrol > publishers > WeblogPublisher


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.publishers;
38
39 import java.io.File JavaDoc;
40 import java.io.IOException JavaDoc;
41 import java.io.StringWriter JavaDoc;
42 import java.net.MalformedURLException JavaDoc;
43 import java.net.URL JavaDoc;
44 import java.util.Calendar JavaDoc;
45 import java.util.Collection JavaDoc;
46 import java.util.HashMap JavaDoc;
47 import java.util.Hashtable JavaDoc;
48 import java.util.Iterator JavaDoc;
49 import java.util.Map JavaDoc;
50 import java.util.StringTokenizer JavaDoc;
51 import java.util.Vector JavaDoc;
52
53 import javax.xml.transform.Transformer JavaDoc;
54 import javax.xml.transform.TransformerException JavaDoc;
55 import javax.xml.transform.TransformerFactory JavaDoc;
56 import javax.xml.transform.stream.StreamResult JavaDoc;
57 import javax.xml.transform.stream.StreamSource JavaDoc;
58
59 import net.sourceforge.cruisecontrol.CruiseControlException;
60 import net.sourceforge.cruisecontrol.Publisher;
61 import net.sourceforge.cruisecontrol.util.XMLLogHelper;
62
63 import org.apache.log4j.Logger;
64 import org.apache.xmlrpc.XmlRpcClient;
65 import org.jdom.Element;
66
67 /**
68  * <p>
69  * Used to publish a blog entry based on the build report using the Blogger API,
70  * MetaWeblog API or the LiveJournal API.
71  * </p>
72  * <p>
73  * Here's a sample of the publisher element to put into your <tt>config.xml</tt>:
74  * </p>
75  *
76  * <pre>
77  * &lt;weblog blogurl=&quot;http://yourblogserver:port/blog/xmlrpc&quot;
78  * api=&quot;metaweblog&quot;
79  * blogid=&quot;yourblog&quot;
80  * username=&quot;user1&quot;
81  * password=&quot;secret&quot;
82  * category=&quot;cruisecontrol&quot;
83  * reportsuccess=&quot;fixes&quot;
84  * subjectprefix=&quot;[CC]&quot;
85  * buildresultsurl=&quot;http://yourbuildserver:port/cc/buildresults&quot;
86  * logdir=&quot;/var/cruisecontrol/logs/YourProject&quot;
87  * xsldir=&quot;/opt/cruisecontrol/reporting/jsp/xsl&quot;
88  * css=&quot;/opt/cruisecontrol/reporting/jsp/css/cruisecontrol.css&quot;
89  * /&gt;
90  * </pre>
91  *
92  * <p>
93  * And you also need to register the 'weblog' task with the following entry if
94  * you're using this task with an older version of CruiseControl which doesn't
95  * have the WeblogPublisher registered by default.
96  *
97  * <pre>
98  * &lt;project name=&quot;foo&quot;&gt;
99  * &lt;plugin name=&quot;weblog&quot;
100  * classname=&quot;net.sourceforge.cruisecontrol.publishers.WeblogPublisher&quot;/&gt;
101  * ...
102  * &lt;/project&gt;
103  * </pre>
104  *
105  * @author Lasse Koskela
106  */

107 public class WeblogPublisher implements Publisher {
108
109     private static final Logger LOG = Logger.getLogger(WeblogPublisher.class);
110
111     private static final String JavaDoc APP_KEY = "CruiseControl Blog Publisher";
112
113     private static final String JavaDoc DEFAULT_API = "metaweblog";
114
115     private static final String JavaDoc DEFAULT_REPORTSUCCESS = "always";
116
117     private static final boolean DEFAULT_SPAMWHILEBROKEN = true;
118
119     // blogging configurations
120

121     private String JavaDoc blogId;
122
123     private String JavaDoc api = DEFAULT_API;
124
125     private String JavaDoc username;
126
127     private String JavaDoc password;
128
129     private String JavaDoc category = "";
130
131     private String JavaDoc blogUrl;
132
133     private String JavaDoc buildResultsURL;
134
135     private String JavaDoc reportSuccess = DEFAULT_REPORTSUCCESS;
136
137     private boolean spamWhileBroken = DEFAULT_SPAMWHILEBROKEN;
138
139     private String JavaDoc subjectPrefix;
140
141     // transformation resources
142

143     private String JavaDoc xslFile;
144
145     private String JavaDoc xslDir;
146
147     private String JavaDoc css;
148
149     private String JavaDoc logDir;
150
151     private String JavaDoc[] xslFileNames = { "header.xsl", "maven.xsl",
152             "checkstyle.xsl", "compile.xsl", "javadoc.xsl", "unittests.xsl",
153             "modifications.xsl", "distributables.xsl" };
154
155     private static final Map JavaDoc API_CLIENTS = new HashMap JavaDoc();
156     static {
157         API_CLIENTS.put("metaweblog", MetaWeblogApiClient.class);
158         API_CLIENTS.put("blogger", BloggerApiClient.class);
159         API_CLIENTS.put("livejournal", LiveJournalApiClient.class);
160     }
161
162     // --- ACCESSORS ---
163

164     /**
165      * If xslFile is set then both xslDir and css are ignored. Specified xslFile
166      * must take care of entire document -- html open/close, body tags, styles,
167      * etc.
168      */

169     public void setXSLFile(String JavaDoc fullPathToXslFile) {
170         xslFile = fullPathToXslFile;
171     }
172
173     /**
174      * Directory where xsl files are located.
175      */

176     public void setXSLDir(String JavaDoc xslDirectory) {
177         xslDir = xslDirectory;
178     }
179
180     /**
181      * Method to override the default list of file names that will be looked for
182      * in the directory specified by xslDir. By default these are the standard
183      * CruseControl xsl files: <br>
184      * <ul>
185      * header.xsl maven.xsl etc ...
186      * </ul>
187      * I expect this to be used by a derived class to allow someone to change
188      * the order of xsl files or to add/remove one to/from the list or a
189      * combination.
190      *
191      * @param fileNames
192      */

193     protected void setXSLFileNames(String JavaDoc[] fileNames) {
194         if (fileNames == null) {
195             throw new IllegalArgumentException JavaDoc(
196                     "xslFileNames can't be null (but can be empty)");
197         }
198         xslFileNames = fileNames;
199     }
200
201     /**
202      * Provided as an alternative to setXSLFileNames for changing the list of
203      * files to use.
204      *
205      * @return xsl files to use in generating the email
206      */

207     protected String JavaDoc[] getXslFileNames() {
208         return xslFileNames;
209     }
210
211     /**
212      * Path to cruisecontrol.css. Only used with xslDir, not xslFile.
213      */

214     public void setCSS(String JavaDoc cssFilename) {
215         css = cssFilename;
216     }
217
218     /**
219      * Path to the log file as set in the log element of the configuration xml
220      * file.
221      */

222     public void setLogDir(String JavaDoc directory) {
223         if (directory == null) {
224             throw new IllegalArgumentException JavaDoc("logDir cannot be null!");
225         }
226         this.logDir = directory;
227     }
228
229     /**
230      * The API used for posting to your blog. Currently, acceptable values are
231      * <tt>blogger</tt>,<tt>metaweblog</tt> and <tt>livejournal</tt>.
232      */

233     public void setApi(String JavaDoc api) {
234         this.api = api;
235     }
236
237     /**
238      * The "blog ID" for the blog you're posting to. The value depends on your
239      * particular weblog product.
240      */

241     public void setBlogId(String JavaDoc blogId) {
242         this.blogId = blogId;
243     }
244
245     /**
246      * The URL where your blog's remote API is running at. For example, the
247      * value could look like <tt>http://www.yoursite.com/blog/xmlrpc</tt> or
248      * <tt>http://www.livejournal.com/interface/xmlrpc</tt>.
249      */

250     public void setBlogUrl(String JavaDoc blogUrl) {
251         this.blogUrl = blogUrl;
252     }
253
254     /**
255      * The username to use for authentication.
256      */

257     public void setUsername(String JavaDoc username) {
258         this.username = username;
259     }
260
261     /**
262      * The password to use for authentication.
263      */

264     public void setPassword(String JavaDoc password) {
265         this.password = password;
266     }
267
268     /**
269      * The category to set for the blog entry. When using the MetaWeblogAPI, you
270      * can also use a comma-separated list of several categories.
271      */

272     public void setCategory(String JavaDoc category) {
273         this.category = category;
274     }
275
276     /**
277      * The prefix to be used before the title of the blog entry. If
278      * <tt>null</tt>, no prefix will be used.
279      */

280     public void setSubjectPrefix(String JavaDoc prefix) {
281         this.subjectPrefix = prefix;
282     }
283
284     /**
285      * The base build results URL where your CruiseControl reporting application
286      * is running. For example, <tt>http://buildserver:8080/cc/myproject</tt>.
287      */

288     public void setBuildResultsURL(String JavaDoc url) {
289         this.buildResultsURL = url;
290     }
291
292     /**
293      * The rule for posting a blog entry for successful builds. Accepted values
294      * are <tt>never</tt>,<tt>always</tt> and <tt>fixes</tt>.
295      */

296     public void setReportSuccess(String JavaDoc reportSuccess) {
297         this.reportSuccess = reportSuccess;
298     }
299
300     /**
301      * The rule for posting a blog entry for each subsequent failed build.
302      * Accepted values are <tt>true</tt> and <tt>false</tt>.
303      */

304     public void setSpamWhileBroken(boolean spamWhileBroken) {
305         this.spamWhileBroken = spamWhileBroken;
306     }
307
308     // --- METHODS ---
309

310     /**
311      * Implementing the <code>Publisher</code> interface.
312      *
313      * @param cruisecontrolLog
314      * The build results XML
315      */

316     public void publish(Element cruisecontrolLog) {
317         XMLLogHelper helper = new XMLLogHelper(cruisecontrolLog);
318         try {
319             if (shouldSend(helper)) {
320                 postBlogEntry(createSubject(helper), createMessage(helper
321                         .getProjectName(), helper.getLogFileName()));
322             } else {
323                 LOG.debug("shouldSend() indicated we should not"
324                         + " post a blog entry at this time");
325             }
326         } catch (CruiseControlException e) {
327             LOG.error("", e);
328         }
329     }
330
331     /**
332      * The interface for abstracting away the specific blogging API being used.
333      *
334      * @author Lasse Koskela
335      */

336     interface BloggingApi {
337         /**
338          * Post a new blog entry.
339          *
340          * @param subject
341          * The blog entry's subject.
342          * @param content
343          * The blog entry's content.
344          * @return The newly created blog entry's identifier.
345          */

346         public Object JavaDoc newPost(String JavaDoc blogUrl, String JavaDoc blogId, String JavaDoc username,
347                 String JavaDoc password, String JavaDoc category, String JavaDoc subject, String JavaDoc content);
348     }
349
350     /**
351      * A <tt>BloggingApi</tt> implementation for the Blogger API.
352      *
353      * @author Lasse Koskela
354      */

355     public static class BloggerApiClient implements BloggingApi {
356
357         public Object JavaDoc newPost(String JavaDoc blogUrl, String JavaDoc blogId, String JavaDoc username,
358                 String JavaDoc password, String JavaDoc category, String JavaDoc subject, String JavaDoc content) {
359             // the Blogger API doesn't support titles for blog entries so
360
// we're using the common (de facto standard) workaround to embed
361
// the title into the content and let the weblog software parse
362
// the title from there, if supported.
363
content = "<title>" + subject + "</title>" + content;
364             Object JavaDoc postId = null;
365             try {
366                 XmlRpcClient xmlrpc = new XmlRpcClient(blogUrl);
367                 Vector JavaDoc params = new Vector JavaDoc();
368                 params.add(APP_KEY);
369                 params.add(blogId);
370                 params.add(username);
371                 params.add(password);
372                 params.add(content);
373                 params.add(Boolean.TRUE);
374                 postId = xmlrpc.execute("blogger.newPost", params);
375             } catch (Exception JavaDoc e) {
376                 LOG.error("", e);
377             }
378             return postId;
379         }
380     }
381
382     /**
383      * A <tt>BloggingApi</tt> implementation for the MetaWeblogAPI.
384      *
385      * @author Lasse Koskela
386      */

387     public static class MetaWeblogApiClient implements BloggingApi {
388
389         public Object JavaDoc newPost(String JavaDoc blogUrl, String JavaDoc blogId, String JavaDoc username,
390                 String JavaDoc password, String JavaDoc category, String JavaDoc subject, String JavaDoc content) {
391             Object JavaDoc postId = null;
392             try {
393                 XmlRpcClient xmlrpc = new XmlRpcClient(blogUrl);
394                 Vector JavaDoc params = new Vector JavaDoc();
395                 params.add(blogId);
396                 params.add(username);
397                 params.add(password);
398
399                 // MetaWeblogAPI expects the blog entry data elements in an
400
// internal map-structure unlike Blogger API does.
401
Hashtable JavaDoc struct = new Hashtable JavaDoc();
402                 struct.put("title", subject);
403                 struct.put("description", content);
404                 Vector JavaDoc categories = new Vector JavaDoc();
405                 if (category != null) {
406                     StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(category, ",");
407                     while (tok.hasMoreTokens()) {
408                         categories.add(tok.nextToken().trim());
409                     }
410                 }
411                 struct.put("categories", categories);
412
413                 params.add(struct);
414                 params.add(Boolean.TRUE);
415                 postId = xmlrpc.execute("metaWeblog.newPost", params);
416             } catch (Exception JavaDoc e) {
417                 LOG.error("", e);
418             }
419             return postId;
420         }
421     }
422
423     /**
424      * A <tt>BloggingApi</tt> implementation for the LiveJournal API.
425      *
426      * @author Lasse Koskela
427      */

428     public static class LiveJournalApiClient implements BloggingApi {
429
430         /**
431          * TODO: make this smarter so that it won't strip away linefeeds from
432          * within &lt;pre&gt;formatted blocks...
433          */

434         private String JavaDoc stripLineFeeds(String JavaDoc input) {
435             StringBuffer JavaDoc s = new StringBuffer JavaDoc();
436             char[] chars = input.toCharArray();
437             for (int i = 0; i < chars.length; i++) {
438                 if (chars[i] != '\n' && chars[i] != '\r') {
439                     s.append(chars[i]);
440                 }
441             }
442             return s.toString();
443         }
444
445         public Object JavaDoc newPost(String JavaDoc blogUrl, String JavaDoc blogId, String JavaDoc username,
446                 String JavaDoc password, String JavaDoc category, String JavaDoc subject, String JavaDoc content) {
447             Object JavaDoc postId = null;
448             try {
449                 XmlRpcClient xmlrpc = new XmlRpcClient(blogUrl);
450                 Vector JavaDoc params = new Vector JavaDoc();
451                 Hashtable JavaDoc struct = new Hashtable JavaDoc();
452                 struct.put("username", username);
453
454                 // TODO: use challenge-based security
455
struct.put("auth_method", "clear");
456                 struct.put("password", password);
457
458                 struct.put("subject", subject);
459                 struct.put("event", stripLineFeeds(content));
460                 struct.put("lineendings", "\n");
461                 struct.put("security", "public");
462                 Calendar JavaDoc now = Calendar.getInstance();
463                 struct.put("year", "" + now.get(Calendar.YEAR));
464                 struct.put("mon", "" + (now.get(Calendar.MONTH) + 1));
465                 struct.put("day", "" + now.get(Calendar.DAY_OF_MONTH));
466                 struct.put("hour", "" + now.get(Calendar.HOUR_OF_DAY));
467                 struct.put("min", "" + now.get(Calendar.MINUTE));
468                 params.add(struct);
469                 postId = xmlrpc.execute("LJ.XMLRPC.postevent", params);
470             } catch (Exception JavaDoc e) {
471                 LOG.error("", e);
472             }
473             return postId;
474         }
475     }
476
477     /**
478      * Selects a <tt>BloggingApi</tt> implementation based on a user-friendly
479      * name.
480      *
481      * @param apiName
482      * The name of the blogging API to use. One of <tt>blogger</tt>,
483      * <tt>metaweblog</tt> or <tt>livejournal</tt>.
484      * @return The <tt>BloggingApi</tt> implementation or <tt>null</tt> if
485      * no matching implementation was found.
486      * @throws CruiseControlException
487      */

488     public BloggingApi getBloggingApiImplementation(String JavaDoc apiName)
489             throws CruiseControlException {
490         Class JavaDoc implClass = (Class JavaDoc) API_CLIENTS.get(apiName);
491         if (implClass != null) {
492             LOG.debug("Mapped " + apiName + " to " + implClass.getName());
493             try {
494                 return (BloggingApi) implClass.newInstance();
495             } catch (Exception JavaDoc e) {
496                 throw new CruiseControlException(
497                         "Failed to instantiate Blogging API implementation, "
498                                 + implClass.getName() + ", due to a "
499                                 + e.getClass().getName() + ": "
500                                 + e.getMessage());
501             }
502         }
503         return null;
504     }
505
506     /**
507      * Posts the build results to the blog.
508      *
509      * @param subject
510      * The subject for the blog entry.
511      * @param content
512      * The content for the blog entry.
513      */

514     public void postBlogEntry(String JavaDoc subject, String JavaDoc content) {
515         LOG.debug("Posting a blog entry to " + blogUrl);
516         LOG.debug(" blogId=" + blogId);
517         LOG.debug(" username=" + username);
518         LOG.debug(" subject=" + subject);
519         LOG.debug(" content=" + content);
520         try {
521             BloggingApi apiClient = getBloggingApiImplementation(api);
522             if (apiClient != null) {
523                 Object JavaDoc postId = apiClient.newPost(blogUrl, blogId, username,
524                         password, category, subject, content);
525                 if (postId != null) {
526                     LOG.info("Blog entry " + postId + " created at " + blogUrl);
527                 } else {
528                     LOG.debug("Blog entry ID not available from " + blogUrl);
529                 }
530             } else {
531                 LOG.error("No API associated with '" + api + "'");
532             }
533         } catch (Exception JavaDoc e) {
534             LOG.error("", e);
535         }
536     }
537
538     /**
539      * Called after the configuration is read to make sure that all the
540      * mandatory parameters were specified..
541      *
542      * @throws CruiseControlException
543      * if there was a configuration error.
544      */

545     public void validate() throws CruiseControlException {
546         validateRequiredField("username", username);
547         validateRequiredField("password", password);
548         validateRequiredField("blogid", blogId);
549         validateRequiredField("blogurl", blogUrl);
550         validateURL("blogurl", blogUrl);
551         validateOneOf("api", API_CLIENTS.keySet(), api);
552
553         if (buildResultsURL != null) {
554             validateURL("buildresultsurl", buildResultsURL);
555         }
556
557         if (logDir != null) {
558             verifyDirectory("WeblogPublisher.logDir", logDir);
559         } else {
560             LOG.info("Using default log directory \"logs/<projectname>\"");
561         }
562
563         if (xslFile == null) {
564             verifyDirectory("WeblogPublisher.xslDir", xslDir);
565             verifyFile("WeblogPublisher.css", css);
566
567             String JavaDoc[] fileNames = getXslFileNames();
568             if (fileNames == null) {
569                 throw new CruiseControlException(
570                         "WeblogPublisher.getXslFileNames() can't return null");
571             }
572             for (int i = 0; i < fileNames.length; i++) {
573                 verifyFile("WeblogPublisher.xslDir/" + fileNames[i], new File JavaDoc(
574                         xslDir, fileNames[i]));
575             }
576         } else {
577             verifyFile("WeblogPublisher.xslFile", xslFile);
578         }
579     }
580
581     private void validateOneOf(String JavaDoc fieldName, Collection JavaDoc validValues,
582             String JavaDoc value) throws CruiseControlException {
583         if (!validValues.contains(value)) {
584             throw new CruiseControlException("Value for '" + fieldName
585                     + "' must be one of " + commaSeparated(validValues));
586         }
587     }
588
589     private String JavaDoc commaSeparated(Collection JavaDoc values) {
590         StringBuffer JavaDoc s = new StringBuffer JavaDoc();
591         Iterator JavaDoc i = values.iterator();
592         while (i.hasNext()) {
593             s.append("'").append(i.next()).append("'");
594             if (i.hasNext()) {
595                 s.append(", ");
596             }
597         }
598         return s.toString();
599     }
600
601     private void validateURL(String JavaDoc fieldName, String JavaDoc url)
602             throws CruiseControlException {
603         try {
604             new URL JavaDoc(url);
605         } catch (MalformedURLException JavaDoc e) {
606             throw new CruiseControlException(fieldName
607                     + " must be a valid URL: " + url);
608         }
609     }
610
611     private void validateRequiredField(String JavaDoc fieldName, String JavaDoc value)
612             throws CruiseControlException {
613         if (value == null) {
614             throw new CruiseControlException("Attribute " + fieldName
615                     + " is required.");
616         }
617     }
618
619     private void verifyDirectory(String JavaDoc dirName, String JavaDoc dir)
620             throws CruiseControlException {
621         if (dir == null) {
622             throw new CruiseControlException(dirName
623                     + " not specified in configuration file");
624         }
625         File JavaDoc dirFile = new File JavaDoc(dir);
626         if (!dirFile.exists()) {
627             throw new CruiseControlException(dirName + " does not exist : "
628                     + dirFile.getAbsolutePath());
629         }
630         if (!dirFile.isDirectory()) {
631             throw new CruiseControlException(dirName + " is not a directory : "
632                     + dirFile.getAbsolutePath());
633         }
634     }
635
636     private void verifyFile(String JavaDoc fileName, String JavaDoc file)
637             throws CruiseControlException {
638         if (file == null) {
639             throw new CruiseControlException(fileName
640                     + " not specified in configuration file");
641         }
642         verifyFile(fileName, new File JavaDoc(file));
643     }
644
645     private void verifyFile(String JavaDoc fileName, File JavaDoc file)
646             throws CruiseControlException {
647         if (!file.exists()) {
648             throw new CruiseControlException(fileName + " does not exist: "
649                     + file.getAbsolutePath());
650         }
651         if (!file.isFile()) {
652             throw new CruiseControlException(fileName + " is not a file: "
653                     + file.getAbsolutePath());
654         }
655     }
656
657     /**
658      * Determines if the conditions are right for the blog entry to be posted.
659      *
660      * @param logHelper
661      * <code>XMLLogHelper</code> wrapper for the build log.
662      * @return whether or not the mail message should be sent.
663      */

664     boolean shouldSend(XMLLogHelper logHelper) throws CruiseControlException {
665         if (logHelper.isBuildSuccessful()) {
666             return shouldSendForSuccessfulBuild(logHelper);
667         } else {
668             return shouldSendForFailedBuild(logHelper);
669         }
670     }
671
672     /**
673      * Determines if the conditions are right for the blog entry to be posted.
674      *
675      * @param logHelper
676      * <code>XMLLogHelper</code> wrapper for the build log.
677      * @return whether or not the mail message should be sent.
678      */

679     boolean shouldSendForFailedBuild(XMLLogHelper logHelper)
680             throws CruiseControlException {
681         if (!logHelper.wasPreviousBuildSuccessful()
682                 && logHelper.isBuildNecessary() && !spamWhileBroken) {
683             LOG.debug("spamWhileBroken is false, not sending email");
684             return false;
685         } else {
686             return true;
687         }
688     }
689
690     /**
691      * Determines if the conditions are right for the blog entry to be posted.
692      *
693      * @param logHelper
694      * <code>XMLLogHelper</code> wrapper for the build log.
695      * @return whether or not the mail message should be sent.
696      */

697     boolean shouldSendForSuccessfulBuild(XMLLogHelper logHelper)
698             throws CruiseControlException {
699         if (reportSuccess.equalsIgnoreCase(DEFAULT_REPORTSUCCESS)) {
700             return true;
701         } else if (reportSuccess.equalsIgnoreCase("never")) {
702             return false;
703         } else if (reportSuccess.equalsIgnoreCase("fixes")) {
704             if (logHelper.wasPreviousBuildSuccessful()) {
705                 LOG.debug("reportSuccess is set to 'fixes', "
706                         + "not sending emails for repeated "
707                         + "successful builds.");
708                 return false;
709             }
710         }
711         return true;
712     }
713
714     /**
715      * Creates the subject for the blog entry.
716      *
717      * @param logHelper
718      * <code>XMLLogHelper</code> wrapper for the build log.
719      * @return <code>String</code> containing the subject line.
720      */

721     String JavaDoc createSubject(XMLLogHelper logHelper) throws CruiseControlException {
722         String JavaDoc projectName = logHelper.getProjectName();
723         String JavaDoc label = logHelper.getLabel();
724         boolean buildSuccessful = logHelper.isBuildSuccessful();
725         boolean isFix = logHelper.isBuildFix();
726         return createSubject(projectName, label, buildSuccessful, isFix);
727     }
728
729     /**
730      * Creates the subject for the blog entry.
731      *
732      * @return <code>String</code> containing the subject line.
733      */

734     String JavaDoc createSubject(String JavaDoc projectName, String JavaDoc label,
735             boolean buildSuccessful, boolean isFix)
736             throws CruiseControlException {
737         StringBuffer JavaDoc subject = new StringBuffer JavaDoc();
738         if (subjectPrefix != null && subjectPrefix.trim().length() > 0) {
739             subject.append(subjectPrefix).append(" ");
740         }
741         subject.append(projectName);
742         if (buildSuccessful) {
743             if (label.length() > 0) {
744                 subject.append(" ").append(label);
745             }
746             subject.append(isFix ? " - Build Fixed" : " - Build Successful");
747         } else {
748             subject.append(" - Build Failed");
749         }
750         return subject.toString();
751     }
752
753     /**
754      * Create the text to be blogged.
755      *
756      * @return created message; empty string if logDir not set
757      */

758     String JavaDoc createMessage(String JavaDoc projectName, String JavaDoc logFileName) {
759         String JavaDoc message;
760         File JavaDoc inFile = null;
761         try {
762             if (logDir == null) {
763                 logDir = getDefaultLogDir(projectName);
764             }
765             inFile = new File JavaDoc(logDir, logFileName);
766             message = transform(inFile);
767         } catch (Exception JavaDoc ex) {
768             LOG.error("error transforming " + (inFile != null ? inFile.getAbsolutePath() : ""), ex);
769             message = createLinkLine(logFileName);
770         }
771         return message;
772     }
773
774     String JavaDoc getDefaultLogDir(String JavaDoc projectName) throws CruiseControlException {
775         // TODO: extract this duplication with ProjectXMLHelper.getLog()
776
// into a single method somewhere
777
return "logs" + File.separator + projectName;
778     }
779
780     String JavaDoc transform(File JavaDoc xml) throws TransformerException JavaDoc, IOException JavaDoc {
781         StringBuffer JavaDoc messageBuffer = new StringBuffer JavaDoc();
782         if (xslFile != null) {
783             transformWithSingleStylesheet(xml, messageBuffer);
784         } else {
785             messageBuffer.append(createLinkLine(xml.getName()));
786             transformWithMultipleStylesheets(xml, messageBuffer);
787         }
788         return messageBuffer.toString();
789     }
790
791     void transformWithMultipleStylesheets(File JavaDoc inFile,
792             StringBuffer JavaDoc messageBuffer) throws IOException JavaDoc,
793             TransformerException JavaDoc {
794         TransformerFactory JavaDoc tFactory = TransformerFactory.newInstance();
795         File JavaDoc xslDirectory = new File JavaDoc(xslDir);
796         String JavaDoc[] fileNames = getXslFileNames();
797         for (int i = 0; i < fileNames.length; i++) {
798             String JavaDoc fileName = fileNames[i];
799             File JavaDoc xsl = new File JavaDoc(xslDirectory, fileName);
800             messageBuffer.append("<p>\n");
801             appendTransform(inFile, xsl, messageBuffer, tFactory);
802         }
803     }
804
805     void transformWithSingleStylesheet(File JavaDoc inFile, StringBuffer JavaDoc messageBuffer)
806             throws IOException JavaDoc, TransformerException JavaDoc {
807         TransformerFactory JavaDoc tFactory = TransformerFactory.newInstance();
808         appendTransform(inFile, new File JavaDoc(xslFile), messageBuffer, tFactory);
809     }
810
811     void appendTransform(File JavaDoc xml, File JavaDoc xsl, StringBuffer JavaDoc messageBuffer,
812             TransformerFactory JavaDoc tFactory) throws TransformerException JavaDoc {
813         LOG.debug("Transforming file " + xml.getName() + " with "
814                 + xsl.getName() + " ...");
815         Transformer JavaDoc tformer = tFactory.newTransformer(new StreamSource JavaDoc(xsl));
816         StringWriter JavaDoc sw = new StringWriter JavaDoc();
817         try {
818             tformer.transform(new StreamSource JavaDoc(xml), new StreamResult JavaDoc(sw));
819             LOG.debug("Transformed file " + xml.getName() + " with "
820                     + xsl.getName() + " ...");
821         } catch (Exception JavaDoc e) {
822             LOG.error("error transforming with xslFile " + xsl.getName(), e);
823             return;
824         }
825         messageBuffer.append(sw.toString());
826     }
827
828     String JavaDoc createLinkLine(String JavaDoc logFileName) {
829         if (buildResultsURL == null) {
830             return "";
831         }
832         String JavaDoc url = createBuildResultsUrl(logFileName);
833         StringBuffer JavaDoc linkLine = new StringBuffer JavaDoc();
834         linkLine.append("<p>View results here -&gt; <a HREF=\"");
835         linkLine.append(url);
836         linkLine.append("\">");
837         linkLine.append(url);
838         linkLine.append("</a></p>");
839         return linkLine.toString();
840     }
841
842     String JavaDoc createBuildResultsUrl(String JavaDoc logFileName) {
843         int startName = logFileName.lastIndexOf(File.separator) + 1;
844         int endName = logFileName.lastIndexOf(".");
845         String JavaDoc baseLogFileName = logFileName.substring(startName, endName);
846         StringBuffer JavaDoc url = new StringBuffer JavaDoc(buildResultsURL);
847         if (buildResultsURL.indexOf("?") == -1) {
848             url.append("?");
849         } else {
850             url.append("&");
851         }
852         url.append("log=");
853         url.append(baseLogFileName);
854         return url.toString();
855     }
856 }
857
Popular Tags