source: main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/XMLTransformer.java@ 24361

Last change on this file since 24361 was 24361, checked in by ak19, 13 years ago

Log messages that were written out as errors but which are actually informative debug statements are now written out as debug statements

  • Property svn:keywords set to Author Date Id Revision
File size: 14.4 KB
Line 
1/*
2 * XMLTransformer.java
3 * Copyright (C) 2002 New Zealand Digital Library, http://www.nzdl.org
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19package org.greenstone.gsdl3.util;
20
21// XML classes
22import javax.xml.transform.Transformer;
23import javax.xml.transform.TransformerFactory;
24import javax.xml.transform.TransformerConfigurationException;
25import javax.xml.transform.TransformerException;
26import javax.xml.transform.ErrorListener;
27
28import javax.xml.transform.stream.StreamSource;
29import javax.xml.transform.dom.DOMSource;
30import javax.xml.transform.stream.StreamResult;
31import javax.xml.transform.dom.DOMResult;
32
33import javax.xml.parsers.DocumentBuilderFactory;
34import javax.xml.parsers.DocumentBuilder;
35import org.w3c.dom.Element;
36import org.w3c.dom.Document;
37
38import org.w3c.dom.Node;
39import org.w3c.dom.NodeList;
40
41// other java classes
42import java.io.StringReader;
43import java.io.StringWriter;
44import java.io.File;
45import java.util.HashMap;
46import java.util.Set;
47import java.util.Map;
48import java.util.Iterator;
49
50import org.apache.xml.utils.DefaultErrorHandler;
51
52import org.apache.log4j.*;
53
54/** XMLTransformer - utility class for greenstone
55 *
56 * transforms xml using xslt
57 *
58 * @author <a href="mailto:[email protected]">Katherine Don</a>
59 * @version $Revision: 24361 $
60 */
61public class XMLTransformer {
62
63 static Logger logger = Logger.getLogger(org.greenstone.gsdl3.util.XMLTransformer.class.getName());
64
65 /** The transformer factory we're using */
66 TransformerFactory t_factory=null;
67
68 /**
69 * The no-arguments constructor.
70 *
71 * Any exceptions thrown are caught internally
72 *
73 * @see javax.xml.transform.TransformerFactory
74 */
75 public XMLTransformer() {
76 // http://download.oracle.com/docs/cd/E17476_01/javase/1.5.0/docs/api/index.html?javax/xml/transform/TransformerFactory.html states that
77 // TransformerFactory.newInstance() looks in jar files for a Factory specified in META-INF/services/javax.xml.transform.TransformerFactory,
78 // else it will use the "platform default"
79 // In this case: xalan.jar's META-INF/services/javax.xml.transform.TransformerFactory contains org.apache.xalan.processor.TransformerFactoryImpl
80 // as required.
81
82 // This means we no longer have to do a System.setProperty("javax.xml.transform.TransformerFactory", "org.apache.xalan.processor.TransformerFactoryImpl");
83 // followed by a this.t_factory = org.apache.xalan.processor.TransformerFactoryImpl.newInstance();
84 // The System.setProperty step to force the TransformerFactory implementation that gets used, conflicts with
85 // Fedora (visiting the Greenstone server pages breaks the Greenstone-tomcat hosted Fedora pages) as Fedora
86 // does not include the xalan.jar and therefore can't then find the xalan TransformerFactory explicitly set.
87
88 // Gone back to forcing use of xalan transformer, since other jars like crimson.jar, which may be on some
89 // classpaths, could be be chosen as the TransformerFactory implementation over xalan. This is what used to
90 // give problems before. Instead, have placed copies of the jars that Fedora needs (xalan.jar and serializer.jar
91 // and the related xsltc.jar which it may need) into packages/tomcat/lib so that it's on the server's classpath
92 // and will be found by Fedora.
93
94 // make sure we are using the xalan transformer
95 System.setProperty("javax.xml.transform.TransformerFactory", "org.apache.xalan.processor.TransformerFactoryImpl");
96 try {
97 this.t_factory = org.apache.xalan.processor.TransformerFactoryImpl.newInstance();
98 //this.t_factory = TransformerFactory.newInstance();
99 } catch (Exception e) {
100 logger.error("exception creating t_factory "+e.getMessage());
101 }
102 }
103
104
105
106 /**
107 * Transform an XML document using a XSLT stylesheet
108 *
109 * @param stylesheet a filename for an XSLT stylesheet
110 * @param xml_in the XML to be transformed
111 * @return the transformed XML
112 */
113 public String transform(String stylesheet, String xml_in) {
114
115 try {
116 // Use the TransformerFactory to process the stylesheet Source and generate a Transformer.
117 Transformer transformer = this.t_factory.newTransformer(new StreamSource(stylesheet));
118 transformer.setErrorListener(new TransformErrorListener());
119
120 // Use the Transformer to transform an XML Source and send the output to a Result object.
121 StringWriter output = new StringWriter();
122
123 transformer.transform(new StreamSource(new StringReader(xml_in)), new StreamResult(output));
124 return output.toString();
125 } catch (TransformerConfigurationException e) {
126 logger.error("couldn't create transformer object: "+e.getMessageAndLocation());
127 logger.error(e.getLocationAsString());
128 return "";
129 } catch (TransformerException e) {
130 logger.error("couldn't transform the source: " + e.getMessageAndLocation());
131 return "";
132 }
133 }
134
135 public String transformToString(Document stylesheet, Document source) {
136 return transformToString(stylesheet, source, null);
137 }
138
139 public String transformToString(Document stylesheet, Document source, HashMap parameters) {
140
141 try {
142 // Use the TransformerFactory to process the stylesheet Source and generate a Transformer.
143 Transformer transformer = this.t_factory.newTransformer(new DOMSource(stylesheet));
144 transformer.setErrorListener(new TransformErrorListener());
145 if (parameters != null) {
146 Set params = parameters.entrySet();
147 Iterator i = params.iterator();
148 while (i.hasNext()) {
149 Map.Entry m = (Map.Entry)i.next();
150 transformer.setParameter((String)m.getKey(), m.getValue());
151 }
152 }
153 //transformer.setParameter("page_lang", source.getDocumentElement().getAttribute(GSXML.LANG_ATT));
154
155
156 // Use the Transformer to transform an XML Source and send the output to a Result object.
157 StringWriter output = new StringWriter();
158
159 transformer.transform(new DOMSource(source), new StreamResult(output));
160 return output.toString();
161 } catch (TransformerConfigurationException e) {
162 logger.error("couldn't create transformer object: "+e.getMessageAndLocation());
163 logger.error(e.getLocationAsString());
164 return "";
165 } catch (TransformerException e) {
166 logger.error("couldn't transform the source: " + e.getMessageAndLocation());
167 return "";
168 }
169 }
170
171 public Node transform(Document stylesheet, Document source) {
172 return transform(stylesheet, source, null, null);
173 }
174
175 public Node transform(Document stylesheet, Document source, HashMap parameters, Document docDocType) {
176 try {
177 // Use the TransformerFactory to process the stylesheet Source and generate a Transformer.
178 Transformer transformer = this.t_factory.newTransformer(new DOMSource(stylesheet));
179 logger.debug("XMLTransformer transformer is " + transformer);
180 transformer.setErrorListener(new TransformErrorListener());
181 if (parameters != null) {
182 Set params = parameters.entrySet();
183 Iterator i = params.iterator();
184 while (i.hasNext()) {
185 Map.Entry m = (Map.Entry)i.next();
186 transformer.setParameter((String)m.getKey(), m.getValue());
187 }
188 }
189
190 // When we transform the DOMResult, we need to make sure the result of
191 // the transformation has a DocType. For that to happen, we need to create
192 // the DOMResult using a Document with a predefined docType.
193 // If we don't have a DocType then do the transformation with a DOMResult
194 // that does not contain any doctype (like we use to do before).
195 DOMResult result = docDocType == null ? new DOMResult() : new DOMResult(docDocType);
196 transformer.transform(new DOMSource(source), result);
197 return result.getNode(); // pass the entire document
198 }
199 catch (TransformerConfigurationException e) {
200 return transformError("XMLTransformer.transform(Doc, Doc, HashMap, Doc)"
201 + "\ncouldn't create transformer object", e);
202 }
203 catch (TransformerException e) {
204 return transformError("XMLTransformer.transform(Doc, Doc, HashMap, Doc)"
205 + "\ncouldn't transform the source", e);
206 }
207 }
208
209 public Node transform(File stylesheet, File source) {
210 try {
211 Transformer transformer = this.t_factory.newTransformer(new StreamSource(stylesheet));
212 transformer.setErrorListener(new TransformErrorListener());
213 DOMResult result = new DOMResult();
214 transformer.transform(new StreamSource(source), result);
215 return result.getNode().getFirstChild();
216 } catch (TransformerConfigurationException e) {
217 return transformError("XMLTransformer.transform(File, File)"
218 + "\ncouldn't create transformer object for files\n"
219 + stylesheet + "\n" + source, e);
220 }
221 catch (TransformerException e) {
222 return transformError("XMLTransformer.transform(File, File)"
223 + "\ncouldn't transform the source for files\n"
224 + stylesheet + "\n" + source, e);
225 }
226 }
227
228 public Node transform(File stylesheet, File source, Document docDocType) {
229 try {
230 Transformer transformer = this.t_factory.newTransformer(new StreamSource(stylesheet));
231 transformer.setErrorListener(new TransformErrorListener());
232 DOMResult result = new DOMResult(docDocType);
233 transformer.transform(new StreamSource(source), result);
234 return result.getNode().getFirstChild();
235 } catch (TransformerConfigurationException e) {
236 return transformError("XMLTransformer.transform(File, File, Doc)"
237 + "\ncouldn't create transformer object for files\n"
238 + stylesheet + "\n" + source, e);
239 }
240 catch (TransformerException e) {
241 return transformError("XMLTransformer.transform(File, File, Doc)"
242 + "\ncouldn't transform the source for files\n"
243 + stylesheet + "\n" + source, e);
244 }
245 }
246
247 // Given a heading string on the sort of transformation error that occurred and the exception object itself,
248 // this method prints the exception to the tomcat window (system.err) and the greenstone log and then returns
249 // an xhtml error page that is constructed from it.
250 protected Node transformError(String heading, TransformerException e) {
251 String message = heading + "\n" + e.getMessage();
252 logger.error(heading + ": " + e.getMessage());
253
254 String location = e.getLocationAsString();
255 if(location != null) {
256 logger.error(location);
257 message = message + "\n" + location;
258 }
259 System.err.println("****\n" + message + "\n****");
260 return constructErrorXHTMLPage(message);
261 }
262
263 // Given an error message, splits it into separate lines based on any newlines present and generates an xhtml page
264 // (xml Element) with paragraphs for each line. This is then returned so that it can be displayed in the browser.
265 public static Element constructErrorXHTMLPage(String message) {
266 try{
267 String[] lines = message.split("\n");
268
269 Document xhtmlDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
270 // <html></html>
271 Node htmlNode = xhtmlDoc.createElement("html");
272 xhtmlDoc.appendChild(htmlNode);
273 // <head></head>
274 Node headNode = xhtmlDoc.createElement("head");
275 htmlNode.appendChild(headNode);
276 // <title></title>
277 Node titleNode = xhtmlDoc.createElement("title");
278 headNode.appendChild(titleNode);
279 Node titleString = xhtmlDoc.createTextNode("Error occurred");
280 titleNode.appendChild(titleString);
281
282 // <body></body>
283 Node bodyNode = xhtmlDoc.createElement("body");
284 htmlNode.appendChild(bodyNode);
285
286 // finally put the message in the body
287 Node h1Node = xhtmlDoc.createElement("h1");
288 bodyNode.appendChild(h1Node);
289 Node headingString = xhtmlDoc.createTextNode("The following error occurred:");
290 h1Node.appendChild(headingString);
291
292 //Node textNode = xhtmlDoc.createTextNode(message);
293 //bodyNode.appendChild(textNode);
294
295 for (int i = 0; i < lines.length; i++) {
296 Node pNode = xhtmlDoc.createElement("p");
297 Node textNode = xhtmlDoc.createTextNode(lines[i]);
298 pNode.appendChild(textNode);
299 bodyNode.appendChild(pNode);
300 }
301
302 return xhtmlDoc.getDocumentElement();
303
304 }catch(Exception e) {
305 String errmsg = "Exception trying to construct error xhtml page from message: " + message
306 + "\n" + e.getMessage();
307 System.err.println(errmsg);
308 logger.error(errmsg);
309 return null;
310 }
311 }
312
313 // ErrorListener class that can be used to register a handler for any fatal errors, errors and warnings that may
314 // occur when transforming an xml file with an xslt stylesheet. The errors are printed both to the greenstone.log and
315 // to the tomcat console (System.err), and the error message is stored in the errorMessage variable so that it can
316 // be retrieved and be used to generate an xhtml error page.
317 static public class TransformErrorListener implements ErrorListener {
318 protected String errorMessage = null;
319
320 // Receive notification of a recoverable error.
321 public void error(TransformerException exception) {
322 handleError("Error:\n", exception);
323 }
324 // Receive notification of a non-recoverable error.
325 public void fatalError(TransformerException exception) {
326 handleError("Fatal Error:\n", exception);
327 }
328 // Receive notification of a warning.
329 public void warning(TransformerException exception) {
330 handleError("Warning:\n", exception);
331 }
332
333 public String toString(TransformerException e) {
334 String location = e.getLocationAsString();
335 if(location == null) {
336 return e.getMessage();
337 }
338 return e.getMessage() + "\n" + location;
339 }
340
341 // clears the errorPage variable after first call to this method
342 public String getErrorMessage() {
343 String errMsg = this.errorMessage;
344 if(this.errorMessage != null) {
345 this.errorMessage = null;
346 }
347 return errMsg;
348 }
349
350 // sets the errorMessage member variable to the data stored in the exception
351 // and writes the errorMessage to the logger and tomcat's System.err
352 protected void handleError(String errorType, TransformerException exception) {
353 this.errorMessage = errorType + toString(exception);
354 System.err.println("\n****Error transforming xml:\n" + this.errorMessage + "\n****\n");
355 logger.error(this.errorMessage);
356 }
357 }
358}
Note: See TracBrowser for help on using the repository browser.