KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sourceforge > cruisecontrol > taglib > XSLTag


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 package net.sourceforge.cruisecontrol.taglib;
38
39 import java.io.BufferedReader JavaDoc;
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.io.InputStream JavaDoc;
45 import java.io.InputStreamReader JavaDoc;
46 import java.io.OutputStream JavaDoc;
47 import java.io.Reader JavaDoc;
48 import java.io.Writer JavaDoc;
49 import java.net.URL JavaDoc;
50 import java.net.URLConnection JavaDoc;
51 import java.util.Enumeration JavaDoc;
52 import java.util.HashMap JavaDoc;
53 import java.util.Iterator JavaDoc;
54 import java.util.Map JavaDoc;
55
56 import javax.servlet.ServletConfig JavaDoc;
57 import javax.servlet.ServletContext JavaDoc;
58 import javax.servlet.jsp.JspException JavaDoc;
59 import javax.servlet.jsp.JspTagException JavaDoc;
60 import javax.xml.transform.Transformer JavaDoc;
61 import javax.xml.transform.TransformerException JavaDoc;
62 import javax.xml.transform.TransformerFactory JavaDoc;
63 import javax.xml.transform.stream.StreamResult JavaDoc;
64 import javax.xml.transform.stream.StreamSource JavaDoc;
65
66 import net.sourceforge.cruisecontrol.LogFile;
67 import net.sourceforge.cruisecontrol.util.CCTagException;
68
69 /**
70  * JSP custom tag to handle xsl transforms. This tag also caches the output of the transform to disk, reducing the
71  * number of transforms necessary.
72  *
73  * @author alden almagro, ThoughtWorks, Inc. 2002
74  * @author <a HREF="mailto:hak@2mba.dk">Hack Kampbjorn</a>
75  */

76 public class XSLTag extends CruiseControlTagSupport {
77     private static final String JavaDoc XSLT_PARAMETER_PREFIX = "xslt.";
78     private String JavaDoc xslFileName;
79     private static final String JavaDoc CACHE_DIR = "_cache";
80
81     public void release() {
82         xslFileName = null;
83     }
84
85
86     /**
87      * Perform an xsl transform. This body of this method is based upon the xalan sample code.
88      *
89      * @param xmlFile the xml file to be transformed
90      * @param style resource containing the xsl stylesheet
91      * @param out stream to output the results of the transformation
92      */

93     protected void transform(LogFile xmlFile, URL JavaDoc style, OutputStream JavaDoc out) throws JspTagException JavaDoc {
94         InputStream JavaDoc in = null;
95
96         try {
97             TransformerFactory JavaDoc tFactory = TransformerFactory.newInstance();
98             Transformer JavaDoc transformer = tFactory.newTransformer(new StreamSource JavaDoc(style.toExternalForm()));
99             Map JavaDoc parameters = getXSLTParameters();
100             if (!parameters.isEmpty()) {
101                 transformer.clearParameters();
102                 for (Iterator JavaDoc i = parameters.entrySet().iterator(); i.hasNext(); ) {
103                     Map.Entry JavaDoc entry = (Map.Entry JavaDoc) i.next();
104                     transformer.setParameter((String JavaDoc) entry.getKey(), entry.getValue());
105                 }
106             }
107
108             try {
109                 in = xmlFile.getInputStream();
110             } catch (IOException JavaDoc ioex) {
111                 err(ioex);
112                 throw new CCTagException("Cannot read logfile: "
113                         + ioex.getMessage(), ioex);
114             }
115             transformer.transform(new StreamSource JavaDoc(in), new StreamResult JavaDoc(out));
116         } catch (ArrayIndexOutOfBoundsException JavaDoc e) {
117             err(e);
118             throw new CCTagException("Error transforming '" + xmlFile.getName()
119                     + "'. You might be experiencing XML parser issues."
120                     + " Are your xalan & xerces jar files mismatched? Check your JVM version. "
121                     + e.getMessage(), e);
122         } catch (TransformerException JavaDoc e) {
123             err(e);
124             throw new CCTagException("Error transforming '" + xmlFile.getName()
125                     + "': " + e.getMessage(), e);
126         } finally {
127             closeQuietly(in);
128         }
129     }
130
131     /**
132      * Determine whether the cache file is current or not. The file will be current if it is newer than both the
133      * xml log file and the xsl file used to create it.
134      *
135      * @return true if the cache file is current.
136      */

137     protected boolean isCacheFileCurrent(File JavaDoc xmlFile, File JavaDoc cacheFile) {
138         if (!cacheFile.exists() || cacheFile.length() == 0) {
139             return false;
140         }
141         boolean isCurrent = false;
142         long xmlLastModified = xmlFile.lastModified();
143         long cacheLastModified = cacheFile.lastModified();
144         try {
145             URL JavaDoc xslUrl = getPageContext().getServletContext().getResource(xslFileName);
146             URLConnection JavaDoc con = xslUrl.openConnection();
147             long xslLastModified = con.getLastModified();
148             isCurrent = (cacheLastModified > xmlLastModified) && (cacheLastModified > xslLastModified);
149         } catch (Exception JavaDoc e) {
150             err("Failed to retrieve lastModified of xsl file " + xslFileName);
151         }
152         return isCurrent;
153     }
154
155     /**
156      * Serves the cached copy rather than re-performing the xsl transform for every request.
157      *
158      * @param cacheFile The filename of the cached copy of the transform.
159      * @param out The writer to write to
160      */

161     protected void serveCachedCopy(File JavaDoc cacheFile, Writer JavaDoc out) throws JspTagException JavaDoc {
162         try {
163             InputStream JavaDoc input = new FileInputStream JavaDoc(cacheFile);
164             copy(input, out);
165         } catch (IOException JavaDoc e) {
166             err(e);
167             throw new CCTagException("Error reading file '"
168                     + cacheFile.getName() + "': " + e.getMessage(), e);
169         }
170     }
171
172     private void copy(InputStream JavaDoc input, Writer JavaDoc out) throws IOException JavaDoc {
173         BufferedReader JavaDoc in;
174         in = new BufferedReader JavaDoc(new InputStreamReader JavaDoc(input, "UTF-8"));
175
176         try {
177             char[] cbuf = new char[8192];
178             while (true) {
179                 int charsRead = in.read(cbuf);
180                 if (charsRead == -1) {
181                     break;
182                 }
183                 out.write(cbuf, 0, charsRead);
184             }
185         } finally {
186             closeQuietly(in);
187         }
188     }
189
190     /**
191      * Create a filename for the cached copy of this transform. This filename will be the concatenation of the
192      * log file and the xsl file used to create it.
193      *
194      * @param xmlFile The log file used as input to the transform
195      * @return The filename for the cached file
196      */

197     protected String JavaDoc getCachedCopyFileName(File JavaDoc xmlFile) {
198         String JavaDoc xmlFileName = xmlFile.getName().substring(0, xmlFile.getName().lastIndexOf("."));
199
200         // The use of '/' is correct, xslFileName is a resource URL so it will
201
// always start with a slash and only always use normal slashes
202
int slashIndex = xslFileName.lastIndexOf("/");
203         String JavaDoc styleSheetName = xslFileName.substring(slashIndex + 1, xslFileName.lastIndexOf("."));
204         return xmlFileName + "-" + styleSheetName + ".html";
205     }
206
207     Map JavaDoc getXSLTParameters() {
208         Map JavaDoc xsltParameters = new HashMap JavaDoc();
209         ServletConfig JavaDoc config = pageContext.getServletConfig();
210         Enumeration JavaDoc names = config.getInitParameterNames();
211         while (names.hasMoreElements()) {
212             String JavaDoc parameterName = (String JavaDoc) names.nextElement();
213             if (parameterName.startsWith(XSLT_PARAMETER_PREFIX)) {
214                 String JavaDoc value = config.getInitParameter(parameterName);
215                 String JavaDoc name = parameterName.substring(XSLT_PARAMETER_PREFIX.length());
216                 info("using XSLT parameter: " + name + "=" + value);
217                 xsltParameters.put(name, value);
218             }
219         }
220         ServletContext JavaDoc context = config.getServletContext();
221         names = context.getInitParameterNames();
222         while (names.hasMoreElements()) {
223             String JavaDoc parameterName = (String JavaDoc) names.nextElement();
224             if (parameterName.startsWith(XSLT_PARAMETER_PREFIX)) {
225                 String JavaDoc value = context.getInitParameter(parameterName);
226                 String JavaDoc name = parameterName.substring(XSLT_PARAMETER_PREFIX.length());
227                 info("using XSLT parameter: " + name + "=" + value);
228                 xsltParameters.put(name, value);
229             }
230         }
231
232         return xsltParameters;
233     }
234
235     /**
236      * Sets the xsl file to use. It is expected that this can be found by the <code>ServletContext</code> for this
237      * web application.
238      *
239      * @param xslFile The path to the xslFile.
240      */

241     public void setXslFile(String JavaDoc xslFile) {
242         xslFileName = xslFile;
243     }
244
245     /**
246      * Prepare the content if there's need to.
247      * The content must be prepared if a transformation is required.
248      *
249      * @return the file to serve
250      */

251     File JavaDoc prepareContent() throws JspException JavaDoc {
252         LogFile xmlFile = findLogFile();
253         File JavaDoc cacheFile = findCacheFile(xmlFile);
254         if (!isCacheFileCurrent(xmlFile.getFile(), cacheFile)) {
255             info("Updating cached copy: " + cacheFile.getAbsolutePath());
256             updateCacheFile(xmlFile, cacheFile);
257         } else {
258             info("Using cached copy: " + cacheFile.getAbsolutePath());
259         }
260         return cacheFile;
261     }
262
263     protected void updateCacheFile(LogFile xmlFile, File JavaDoc cacheFile) throws JspTagException JavaDoc {
264         OutputStream JavaDoc out = null;
265         try {
266             out = new FileOutputStream JavaDoc(cacheFile);
267             URL JavaDoc style = getPageContext().getServletContext().getResource(xslFileName);
268             transform(xmlFile, style, out);
269         } catch (IOException JavaDoc e) {
270             err(e);
271             throw new CCTagException("Error saving a cached transformation '"
272                     + cacheFile.getName() + "': " + e.getMessage(), e);
273         } finally {
274             closeQuietly(out);
275         }
276     }
277
278     private File JavaDoc findCacheFile(LogFile xmlFile) {
279         String JavaDoc cacheRoot = getContextParam("cacheRoot");
280         File JavaDoc cacheDir = cacheRoot == null
281             ? new File JavaDoc(xmlFile.getLogDirectory(), CACHE_DIR)
282             : new File JavaDoc(cacheRoot + File.separator + getProject());
283         if (!cacheDir.exists()) {
284             cacheDir.mkdir();
285         }
286         File JavaDoc cacheFile = new File JavaDoc(cacheDir, getCachedCopyFileName(xmlFile.getFile()));
287         return cacheFile;
288     }
289
290     public int doEndTag() throws JspException JavaDoc {
291         File JavaDoc cachedFile = prepareContent();
292         serveCachedCopy(cachedFile, getPageContext().getOut());
293         return EVAL_PAGE;
294     }
295
296     private void closeQuietly(InputStream JavaDoc in) {
297         if (in != null) {
298             try {
299                 in.close();
300             } catch (IOException JavaDoc ioex) {
301                 info("Ignored " + ioex.getMessage() + " while closing stream");
302             }
303         }
304     }
305     private void closeQuietly(Reader JavaDoc in) {
306         if (in != null) {
307             try {
308                 in.close();
309             } catch (IOException JavaDoc ioex) {
310                 info("Ignored " + ioex.getMessage() + " while closing reader");
311             }
312         }
313     }
314     private void closeQuietly(OutputStream JavaDoc out) {
315         if (out != null) {
316             try {
317                 out.close();
318             } catch (IOException JavaDoc ioex) {
319                 info("Ignored " + ioex.getMessage() + " while closing stream");
320             }
321         }
322     }
323 }
324
Popular Tags