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

Last change on this file since 23405 was 23405, checked in by sjb48, 13 years ago

FormatAction constructs message containing format string that is sent to the collection. The message knows the service, and if it is the browse service, then it also knows the classifer list number.

  • 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: 23405 $
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 transformer.setErrorListener(new TransformErrorListener());
180 if (parameters != null) {
181 Set params = parameters.entrySet();
182 Iterator i = params.iterator();
183 while (i.hasNext()) {
184 Map.Entry m = (Map.Entry)i.next();
185 transformer.setParameter((String)m.getKey(), m.getValue());
186 }
187 }
188
189 // When we transform the DOMResult, we need to make sure the result of
190 // the transformation has a DocType. For that to happen, we need to create
191 // the DOMResult using a Document with a predefined docType.
192 // If we don't have a DocType then do the transformation with a DOMResult
193 // that does not contain any doctype (like we use to do before).
194 DOMResult result = docDocType == null ? new DOMResult() : new DOMResult(docDocType);
195 transformer.transform(new DOMSource(source), result);
196 return result.getNode(); // pass the entire document
197 }
198 catch (TransformerConfigurationException e) {
199 return transformError("XMLTransformer.transform(Doc, Doc, HashMap, Doc)"
200 + "\ncouldn't create transformer object", e);
201 }
202 catch (TransformerException e) {
203 return transformError("XMLTransformer.transform(Doc, Doc, HashMap, Doc)"
204 + "\ncouldn't transform the source", e);
205 }
206 }
207
208 public Node transform(File stylesheet, File source) {
209 try {
210 Transformer transformer = this.t_factory.newTransformer(new StreamSource(stylesheet));
211 transformer.setErrorListener(new TransformErrorListener());
212 DOMResult result = new DOMResult();
213 transformer.transform(new StreamSource(source), result);
214 return result.getNode().getFirstChild();
215 } catch (TransformerConfigurationException e) {
216 return transformError("XMLTransformer.transform(File, File)"
217 + "\ncouldn't create transformer object for files\n"
218 + stylesheet + "\n" + source, e);
219 }
220 catch (TransformerException e) {
221 return transformError("XMLTransformer.transform(File, File)"
222 + "\ncouldn't transform the source for files\n"
223 + stylesheet + "\n" + source, e);
224 }
225 }
226
227 public Node transform(File stylesheet, File source, Document docDocType) {
228 try {
229 Transformer transformer = this.t_factory.newTransformer(new StreamSource(stylesheet));
230 transformer.setErrorListener(new TransformErrorListener());
231 DOMResult result = new DOMResult(docDocType);
232 transformer.transform(new StreamSource(source), result);
233 return result.getNode().getFirstChild();
234 } catch (TransformerConfigurationException e) {
235 return transformError("XMLTransformer.transform(File, File, Doc)"
236 + "\ncouldn't create transformer object for files\n"
237 + stylesheet + "\n" + source, e);
238 }
239 catch (TransformerException e) {
240 return transformError("XMLTransformer.transform(File, File, Doc)"
241 + "\ncouldn't transform the source for files\n"
242 + stylesheet + "\n" + source, e);
243 }
244 }
245
246 // Given a heading string on the sort of transformation error that occurred and the exception object itself,
247 // this method prints the exception to the tomcat window (system.err) and the greenstone log and then returns
248 // an xhtml error page that is constructed from it.
249 protected Node transformError(String heading, TransformerException e) {
250 String message = heading + "\n" + e.getMessage();
251 logger.error(heading + ": " + e.getMessage());
252
253 String location = e.getLocationAsString();
254 if(location != null) {
255 logger.error(location);
256 message = message + "\n" + location;
257 }
258 System.err.println("****\n" + message + "\n****");
259 return constructErrorXHTMLPage(message);
260 }
261
262 // Given an error message, splits it into separate lines based on any newlines present and generates an xhtml page
263 // (xml Element) with paragraphs for each line. This is then returned so that it can be displayed in the browser.
264 public static Element constructErrorXHTMLPage(String message) {
265 try{
266 String[] lines = message.split("\n");
267
268 Document xhtmlDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
269 // <html></html>
270 Node htmlNode = xhtmlDoc.createElement("html");
271 xhtmlDoc.appendChild(htmlNode);
272 // <head></head>
273 Node headNode = xhtmlDoc.createElement("head");
274 htmlNode.appendChild(headNode);
275 // <title></title>
276 Node titleNode = xhtmlDoc.createElement("title");
277 headNode.appendChild(titleNode);
278 Node titleString = xhtmlDoc.createTextNode("Error occurred");
279 titleNode.appendChild(titleString);
280
281 // <body></body>
282 Node bodyNode = xhtmlDoc.createElement("body");
283 htmlNode.appendChild(bodyNode);
284
285 // finally put the message in the body
286 Node h1Node = xhtmlDoc.createElement("h1");
287 bodyNode.appendChild(h1Node);
288 Node headingString = xhtmlDoc.createTextNode("The following error occurred:");
289 h1Node.appendChild(headingString);
290
291 //Node textNode = xhtmlDoc.createTextNode(message);
292 //bodyNode.appendChild(textNode);
293
294 for (int i = 0; i < lines.length; i++) {
295 Node pNode = xhtmlDoc.createElement("p");
296 Node textNode = xhtmlDoc.createTextNode(lines[i]);
297 pNode.appendChild(textNode);
298 bodyNode.appendChild(pNode);
299 }
300
301 return xhtmlDoc.getDocumentElement();
302
303 }catch(Exception e) {
304 String errmsg = "Exception trying to construct error xhtml page from message: " + message
305 + "\n" + e.getMessage();
306 System.err.println(errmsg);
307 logger.error(errmsg);
308 return null;
309 }
310 }
311
312 // ErrorListener class that can be used to register a handler for any fatal errors, errors and warnings that may
313 // occur when transforming an xml file with an xslt stylesheet. The errors are printed both to the greenstone.log and
314 // to the tomcat console (System.err), and the error message is stored in the errorMessage variable so that it can
315 // be retrieved and be used to generate an xhtml error page.
316 static public class TransformErrorListener implements ErrorListener {
317 protected String errorMessage = null;
318
319 // Receive notification of a recoverable error.
320 public void error(TransformerException exception) {
321 handleError("Error:\n", exception);
322 }
323 // Receive notification of a non-recoverable error.
324 public void fatalError(TransformerException exception) {
325 handleError("Fatal Error:\n", exception);
326 }
327 // Receive notification of a warning.
328 public void warning(TransformerException exception) {
329 handleError("Warning:\n", exception);
330 }
331
332 public String toString(TransformerException e) {
333 String location = e.getLocationAsString();
334 if(location == null) {
335 return e.getMessage();
336 }
337 return e.getMessage() + "\n" + location;
338 }
339
340 // clears the errorPage variable after first call to this method
341 public String getErrorMessage() {
342 String errMsg = this.errorMessage;
343 if(this.errorMessage != null) {
344 this.errorMessage = null;
345 }
346 return errMsg;
347 }
348
349 // sets the errorMessage member variable to the data stored in the exception
350 // and writes the errorMessage to the logger and tomcat's System.err
351 protected void handleError(String errorType, TransformerException exception) {
352 this.errorMessage = errorType + toString(exception);
353 System.err.println("\n****Error transforming xml:\n" + this.errorMessage + "\n****\n");
354 logger.error(this.errorMessage);
355 }
356 }
357}
Note: See TracBrowser for help on using the repository browser.