KickJava   Java API By Example, From Geeks To Geeks.

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


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.CharArrayWriter JavaDoc;
40 import java.io.File JavaDoc;
41 import java.io.FileNotFoundException JavaDoc;
42 import java.io.IOException JavaDoc;
43 import java.util.Iterator JavaDoc;
44 import java.util.LinkedList JavaDoc;
45 import java.util.List JavaDoc;
46 import java.util.StringTokenizer JavaDoc;
47
48 import javax.mail.Message JavaDoc;
49 import javax.mail.MessagingException JavaDoc;
50 import javax.mail.internet.MimeBodyPart JavaDoc;
51 import javax.mail.internet.MimeMultipart JavaDoc;
52 import javax.xml.transform.Source JavaDoc;
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.builders.Property;
61 import net.sourceforge.cruisecontrol.util.ValidationHelper;
62 import net.sourceforge.cruisecontrol.util.XMLLogHelper;
63 import net.sourceforge.cruisecontrol.util.Util;
64
65 import org.apache.log4j.Logger;
66 import org.apache.tools.ant.launch.Locator;
67
68 /**
69  * Used to publish an HTML e-mail that includes the build report
70  *
71  * @author Jeffrey Fredrick
72  * @author Alden Almagro
73  * @author <a HREF="vwiewior@valuecommerce.ne.jp">Victor Wiewiorowski</a>
74  */

75 public class HTMLEmailPublisher extends EmailPublisher {
76
77     private static final Logger LOG = Logger.getLogger(HTMLEmailPublisher.class);
78
79     private String JavaDoc xslFile;
80     private String JavaDoc xslDir;
81     private String JavaDoc css;
82     private String JavaDoc logDir;
83     private String JavaDoc messageMimeType = "text/html";
84     private String JavaDoc charset;
85
86     // Should reflect the same stylesheets as buildresults.jsp in the JSP
87
// reporting application
88
private String JavaDoc[] xslFileNames =
89         {
90             "header.xsl",
91             "buildresults.xsl"
92         };
93
94     private List JavaDoc xsltParameters = new LinkedList JavaDoc();
95
96     /*
97      * Called after the configuration is read to make sure that all the mandatory parameters
98      * were specified..
99      *
100      * @throws CruiseControlException if there was a configuration error.
101      */

102     public void validate() throws CruiseControlException {
103         super.validate();
104
105         if (logDir != null) {
106             verifyDirectory("HTMLEmailPublisher.logDir", logDir);
107         } else {
108             LOG.debug("Using default logDir \"logs/<projectname>\"");
109         }
110
111         if (xslFile == null) {
112             if (xslDir == null) {
113                 // try to obtain the dir relative to the current classpath
114
xslDir = getXslDirFromClasspath();
115             }
116             verifyDirectory("HTMLEmailPublisher.xslDir", xslDir);
117             if (css == null) {
118                 // same for css
119
css = getCssFromClasspath();
120             }
121             verifyFile("HTMLEmailPublisher.css", css);
122
123             String JavaDoc[] fileNames = getXslFileNames();
124             
125             if (fileNames == null) {
126                 throw new CruiseControlException("HTMLEmailPublisher.getXslFileNames() can't return null");
127             }
128
129             for (int i = 0; i < fileNames.length; i++) {
130                 String JavaDoc fileName = fileNames[i];
131                 verifyFile(
132                     "HTMLEmailPublisher.xslDir/" + fileName,
133                     new File JavaDoc(xslDir, fileName));
134             }
135         } else {
136             verifyFile("HTMLEmailPublisher.xslFile", xslFile);
137         }
138     }
139
140     /**
141      */

142     public Property createParameter() {
143         Property param = new Property();
144         xsltParameters.add(param);
145         return param;
146     }
147
148     /**
149      * @return the absolute path where the cruisecontrol.css file is located,
150      * or null if it can't be found.
151      */

152     private String JavaDoc getCssFromClasspath() {
153         File JavaDoc cssFile = new File JavaDoc(getCruiseRootDir(), "reporting/jsp/webcontent/css/cruisecontrol.css");
154         if (cssFile.exists()) {
155             return cssFile.getAbsolutePath();
156         }
157         return null;
158     }
159
160     /**
161      * @return the absolute path where the xsl dir is located,
162      * or null if it can't be found.
163      */

164     private String JavaDoc getXslDirFromClasspath() {
165         File JavaDoc xsl = new File JavaDoc(getCruiseRootDir(), "reporting/jsp/webcontent/xsl");
166         if (xsl.isDirectory()) {
167             return xsl.getAbsolutePath();
168         }
169         return null;
170     }
171
172     /**
173      * @return the root directory of the running cruisecontrol installation.
174      * Uses Ant's Locator.
175      */

176     private File JavaDoc getCruiseRootDir() {
177         File JavaDoc classDir = Locator.getClassSource(getClass());
178         if (classDir != null) {
179             try {
180                 // we're probably in main/dist/cruisecontrol.jar, so three parents up
181
File JavaDoc rootDir = classDir.getParentFile().getParentFile().getParentFile();
182                 if (LOG.isDebugEnabled()) {
183                     LOG.debug("rootDir seems to be " + rootDir.getAbsolutePath()
184                             + " (classDir = " + classDir.getAbsolutePath() + ")");
185                 }
186                 return rootDir;
187             } catch (NullPointerException JavaDoc npe) {
188                 // don't know where we are, then...
189
return null;
190             }
191         }
192         return null;
193     }
194
195     private void verifyDirectory(String JavaDoc dirName, String JavaDoc dir) throws CruiseControlException {
196         ValidationHelper.assertFalse(dir == null, dirName + " not specified in configuration file");
197         File JavaDoc dirFile = new File JavaDoc(dir);
198         ValidationHelper.assertTrue(dirFile.exists(), dirFile + " does not exist: " + dirFile.getAbsolutePath());
199         ValidationHelper.assertTrue(dirFile.isDirectory(),
200                 dirFile + " is not a directory: " + dirFile.getAbsolutePath());
201     }
202
203     private void verifyFile(String JavaDoc fileName, String JavaDoc file) throws CruiseControlException {
204         ValidationHelper.assertFalse(file == null, fileName + " not specified in configuration file");
205         verifyFile(fileName, new File JavaDoc(file));
206     }
207
208     private void verifyFile(String JavaDoc fileName, File JavaDoc file) throws CruiseControlException {
209         ValidationHelper.assertTrue(file.exists(), fileName + " does not exist: " + file.getAbsolutePath());
210         ValidationHelper.assertTrue(file.isFile(), fileName + " is not a file: " + file.getAbsolutePath());
211     }
212
213     /**
214      * sets the content as an attachment w/proper mime-type
215      */

216     protected void addContentToMessage(String JavaDoc htmlContent, Message JavaDoc msg) throws MessagingException JavaDoc {
217         MimeMultipart JavaDoc attachments = new MimeMultipart JavaDoc();
218         MimeBodyPart JavaDoc textbody = new MimeBodyPart JavaDoc();
219         String JavaDoc contentType = getContentType();
220         textbody.setContent(htmlContent, contentType);
221         attachments.addBodyPart(textbody);
222
223         msg.setContent(attachments);
224     }
225
226     String JavaDoc getContentType() {
227         if (charset != null) {
228             return messageMimeType + "; charset=\"" + charset + "\"";
229         } else {
230             return messageMimeType;
231         }
232     }
233
234
235     /**
236      * updates xslFileNames, based on value of xslFileList
237      * If first character is + the list is appended, otherwise
238      * the list is replaced. xslFileNames is comma or space-separated
239      * list of existing files, located in xslDir. These files are used,
240      * in-order, to generate HTML email. If xslFileNames is not
241      * specified, xslFileList remains as default.
242      * if xslFile is set, this is ignored.
243      */

244     public void setXSLFileList(String JavaDoc relativePathToXslFile) {
245         if (relativePathToXslFile == null || relativePathToXslFile.equals("")) {
246             throw new IllegalArgumentException JavaDoc("xslFileList shouldn't be null or empty");
247         }
248
249         relativePathToXslFile = relativePathToXslFile.trim();
250         boolean appending = relativePathToXslFile.startsWith("+");
251         
252         if (appending) {
253             relativePathToXslFile = relativePathToXslFile.substring(1);
254         }
255
256         StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(relativePathToXslFile, " ,");
257         int numTokens = st.countTokens();
258
259         int i;
260         if (appending) {
261             i = xslFileNames.length;
262         } else {
263             i = 0;
264         }
265         String JavaDoc[] newXSLFileNames = new String JavaDoc[i + numTokens];
266         System.arraycopy(xslFileNames, 0, newXSLFileNames, 0, i);
267         
268         while (st.hasMoreTokens()) {
269             newXSLFileNames[i++] = st.nextToken();
270         }
271         
272         setXSLFileNames(newXSLFileNames);
273     }
274
275     /**
276      * If xslFile is set then both xslDir and css are ignored. Specified xslFile
277      * must take care of entire document -- html open/close, body tags, styles,
278      * etc.
279      */

280     public void setXSLFile(String JavaDoc fullPathToXslFile) {
281         xslFile = fullPathToXslFile;
282     }
283
284     /**
285      * Directory where xsl files are located.
286      */

287     public void setXSLDir(String JavaDoc xslDirectory) {
288         xslDir = xslDirectory;
289     }
290
291     /**
292      * Method to override the default list of file names that will be looked
293      * for in the directory specified by xslDir. By default these are the
294      * standard CruseControl xsl files: <br>
295      * <ul>
296      * header.xsl
297      * maven.xsl
298      * etc ...
299      * </ul>
300      * I expect this to be used by a derived class to allow someone to
301      * change the order of xsl files or to add/remove one to/from the list
302      * or a combination.
303      * @param fileNames
304      */

305     protected void setXSLFileNames(String JavaDoc[] fileNames) {
306         if (fileNames == null) {
307             throw new IllegalArgumentException JavaDoc("xslFileNames can't be null (but can be empty)");
308         }
309         xslFileNames = fileNames;
310     }
311
312     /**
313      * Provided as an alternative to setXSLFileNames for changing the list of
314      * files to use.
315      * @return xsl files to use in generating the email
316      */

317     protected String JavaDoc[] getXslFileNames() {
318         return xslFileNames;
319     }
320
321     /**
322      * Path to cruisecontrol.css. Only used with xslDir, not xslFile.
323      */

324     public void setCSS(String JavaDoc cssFilename) {
325         css = cssFilename;
326     }
327
328     /**
329      * Path to the log file as set in the log element of the configuration
330      * xml file.
331      */

332     public void setLogDir(String JavaDoc directory) {
333         if (directory == null) {
334             throw new IllegalArgumentException JavaDoc("logDir cannot be null!");
335         }
336
337         logDir = directory;
338     }
339
340     public void setCharset(String JavaDoc characterSet) {
341         charset = characterSet;
342     }
343
344     /**
345      * Create the message to be mailed
346      *
347      * @param logHelper utility object that has parsed the log files
348      * @return created message; empty string if logDir not set
349      */

350
351     // TODO: address whether this should ever return null;
352
// dependent also on transform(File) and createLinkLine()
353
protected String JavaDoc createMessage(XMLLogHelper logHelper) {
354         String JavaDoc message = "";
355
356         File JavaDoc inFile = null;
357         try {
358             if (logDir == null) {
359                 // use the same default as ProjectXMLHelper.getLog()
360
logDir = "logs" + File.separator + logHelper.getProjectName();
361             }
362             inFile = new File JavaDoc(logDir, logHelper.getLogFileName());
363             message = transform(inFile);
364         } catch (Exception JavaDoc ex) {
365             LOG.error("error transforming " + (inFile == null ? null : inFile.getAbsolutePath()), ex);
366             try {
367                 String JavaDoc logFileName = logHelper.getLogFileName();
368                 message = createLinkLine(logFileName);
369             } catch (CruiseControlException ccx) {
370                 LOG.error("exception getting logfile name", ccx);
371             }
372         }
373
374         return message;
375     }
376
377     protected String JavaDoc transform(File JavaDoc inFile) throws TransformerException JavaDoc, FileNotFoundException JavaDoc, IOException JavaDoc {
378         StringBuffer JavaDoc messageBuffer = new StringBuffer JavaDoc();
379
380         TransformerFactory JavaDoc tFactory = TransformerFactory.newInstance();
381
382         if (xslFile != null) {
383             File JavaDoc xslFileAsFile = new File JavaDoc(xslFile);
384             appendTransform(inFile, messageBuffer, tFactory, xslFileAsFile);
385         } else {
386             appendHeader(messageBuffer);
387             messageBuffer.append(createLinkLine(inFile.getName()));
388
389             File JavaDoc xslDirectory = new File JavaDoc(xslDir);
390             String JavaDoc[] fileNames = getXslFileNames();
391             for (int i = 0; i < fileNames.length; i++) {
392                 String JavaDoc fileName = fileNames[i];
393                 File JavaDoc xsl = new File JavaDoc(xslDirectory, fileName);
394                 messageBuffer.append("<p>\n");
395                 appendTransform(inFile, messageBuffer, tFactory, xsl);
396             }
397
398             appendFooter(messageBuffer);
399         }
400
401         return messageBuffer.toString();
402     }
403
404     protected String JavaDoc createLinkLine(String JavaDoc logFileName) {
405         StringBuffer JavaDoc linkLine = new StringBuffer JavaDoc("");
406         String JavaDoc buildResultsURL = getBuildResultsURL();
407
408         if (buildResultsURL == null) {
409             return "";
410         }
411
412         int startName = logFileName.lastIndexOf(File.separator) + 1;
413         int endName = logFileName.lastIndexOf(".");
414         String JavaDoc baseLogFileName = logFileName.substring(startName, endName);
415         StringBuffer JavaDoc url = new StringBuffer JavaDoc(buildResultsURL);
416         if (buildResultsURL.indexOf("?") == -1) {
417             url.append("?");
418         } else {
419             url.append("&");
420         }
421         url.append("log=");
422         url.append(baseLogFileName);
423
424         // JDK-1.4: just append the url as a StringBuffer
425
linkLine.append("View results here -> <a HREF=\"");
426         linkLine.append(url.toString());
427         linkLine.append("\">");
428         linkLine.append(url.toString());
429         linkLine.append("</a>");
430
431         return linkLine.toString();
432     }
433
434     protected void appendTransform(File JavaDoc inFile, StringBuffer JavaDoc messageBuffer, TransformerFactory JavaDoc tFactory, File JavaDoc xsl) {
435         try {
436             String JavaDoc result = transformFile(new StreamSource JavaDoc(inFile), tFactory, new StreamSource JavaDoc(xsl));
437             messageBuffer.append(result);
438         } catch (Exception JavaDoc e) {
439             LOG.error("error transforming with xslFile " + xsl.getName(), e);
440         }
441     }
442     protected String JavaDoc transformFile(Source JavaDoc logFile, TransformerFactory JavaDoc tFactory, Source JavaDoc xsl)
443         throws IOException JavaDoc, TransformerException JavaDoc {
444         Transformer JavaDoc transformer = tFactory.newTransformer(xsl);
445         CharArrayWriter JavaDoc writer = new CharArrayWriter JavaDoc();
446         if (!xsltParameters.isEmpty()) {
447             Iterator JavaDoc i = xsltParameters.iterator();
448             while (i.hasNext()) {
449                 Property param = (Property) i.next();
450                 transformer.setParameter(param.getName(), param.getValue());
451             }
452         }
453         transformer.transform(logFile, new StreamResult JavaDoc(writer));
454         return writer.toString();
455     }
456
457     protected void appendHeader(StringBuffer JavaDoc messageBuffer) throws IOException JavaDoc {
458         messageBuffer.append("<html><head>\n");
459         String JavaDoc baseUrl = getBuildResultsURL();
460         if (baseUrl != null) {
461             messageBuffer.append("<base HREF=\"").append(baseUrl).append("\">\n");
462         }
463         messageBuffer.append("<style>\n");
464
465         Util.appendFileToBuffer(css, messageBuffer);
466
467         messageBuffer.append("\n</style>\n</head><body>\n");
468     }
469
470     protected void appendFooter(StringBuffer JavaDoc messageBuffer) {
471         messageBuffer.append("\n</body></html>");
472     }
473 }
474
Popular Tags