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

Last change on this file since 22370 was 22370, checked in by ak19, 14 years ago
  1. Bugfix: Dr Bainbridge discovered that the reason the fedora pages off the GS3 tomcat server stopped functioning after visiting the GS3 pages off the same server, was that XMLTransformer was fixing the TransformerFactory property for the entire system. Unfortunately it set this to one (xalan) that is not included in the libraries that fedora ships with. The solution is to now let Java work out, which it already does correctly, that GS3 is using the one that's part of its included jar files (xalan.jar), instead of fixing this with System.setProperty. Now the Fedora pages run off the GS3 tomcat server at all times. 2. FedoraServiceProxy will use any fedora connection specifics in the info element coming from buildConfig.xml (for protocol, host, port, authentication), otherwise it will use defaults.
  • Property svn:keywords set to Author Date Id Revision
File size: 13.7 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: 22370 $
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 // make sure we are using the xalan transformer
77
78 // http://download.oracle.com/docs/cd/E17476_01/javase/1.5.0/docs/api/index.html?javax/xml/transform/TransformerFactory.html states that
79 // TransformerFactory.newInstance() looks in jar files for a Factory specified in META-INF/services/javax.xml.transform.TransformerFactory,
80 // else it will use the "platform default"
81 // In this case: xalan.jar's META-INF/services/javax.xml.transform.TransformerFactory contains org.apache.xalan.processor.TransformerFactoryImpl
82 // as required.
83
84 // This means we no longer have to do a System.setProperty("javax.xml.transform.TransformerFactory", "org.apache.xalan.processor.TransformerFactoryImpl");
85 // followed by a this.t_factory = org.apache.xalan.processor.TransformerFactoryImpl.newInstance();
86 // The System.setProperty step to force the TransformerFactory implementation that gets used conflicts with
87 // Fedora (visiting the Greenstone server pages breaks the Greenstone-tomcat hosted Fedora pages) as Fedora
88 // does not include the xalan.jar and therefore can't then find the xalan TransformerFactory explicitly set.
89
90 try {
91 this.t_factory = TransformerFactory.newInstance();
92 } catch (Exception e) {
93 logger.error("exception "+e.getMessage());
94 }
95 }
96
97
98
99 /**
100 * Transform an XML document using a XSLT stylesheet
101 *
102 * @param stylesheet a filename for an XSLT stylesheet
103 * @param xml_in the XML to be transformed
104 * @return the transformed XML
105 */
106 public String transform(String stylesheet, String xml_in) {
107
108 try {
109 // Use the TransformerFactory to process the stylesheet Source and generate a Transformer.
110 Transformer transformer = this.t_factory.newTransformer(new StreamSource(stylesheet));
111 transformer.setErrorListener(new TransformErrorListener());
112
113 // Use the Transformer to transform an XML Source and send the output to a Result object.
114 StringWriter output = new StringWriter();
115
116 transformer.transform(new StreamSource(new StringReader(xml_in)), new StreamResult(output));
117 return output.toString();
118 } catch (TransformerConfigurationException e) {
119 logger.error("couldn't create transformer object: "+e.getMessageAndLocation());
120 logger.error(e.getLocationAsString());
121 return "";
122 } catch (TransformerException e) {
123 logger.error("couldn't transform the source: " + e.getMessageAndLocation());
124 return "";
125 }
126 }
127
128 public String transformToString(Document stylesheet, Document source) {
129 return transformToString(stylesheet, source, null);
130 }
131
132 public String transformToString(Document stylesheet, Document source, HashMap parameters) {
133
134 try {
135 // Use the TransformerFactory to process the stylesheet Source and generate a Transformer.
136 Transformer transformer = this.t_factory.newTransformer(new DOMSource(stylesheet));
137 transformer.setErrorListener(new TransformErrorListener());
138 if (parameters != null) {
139 Set params = parameters.entrySet();
140 Iterator i = params.iterator();
141 while (i.hasNext()) {
142 Map.Entry m = (Map.Entry)i.next();
143 transformer.setParameter((String)m.getKey(), m.getValue());
144 }
145 }
146 //transformer.setParameter("page_lang", source.getDocumentElement().getAttribute(GSXML.LANG_ATT));
147
148
149 // Use the Transformer to transform an XML Source and send the output to a Result object.
150 StringWriter output = new StringWriter();
151
152 transformer.transform(new DOMSource(source), new StreamResult(output));
153 return output.toString();
154 } catch (TransformerConfigurationException e) {
155 logger.error("couldn't create transformer object: "+e.getMessageAndLocation());
156 logger.error(e.getLocationAsString());
157 return "";
158 } catch (TransformerException e) {
159 logger.error("couldn't transform the source: " + e.getMessageAndLocation());
160 return "";
161 }
162 }
163
164 public Node transform(Document stylesheet, Document source) {
165 return transform(stylesheet, source, null, null);
166 }
167
168 public Node transform(Document stylesheet, Document source, HashMap parameters, Document docDocType) {
169 try {
170 // Use the TransformerFactory to process the stylesheet Source and generate a Transformer.
171 Transformer transformer = this.t_factory.newTransformer(new DOMSource(stylesheet));
172 transformer.setErrorListener(new TransformErrorListener());
173 if (parameters != null) {
174 Set params = parameters.entrySet();
175 Iterator i = params.iterator();
176 while (i.hasNext()) {
177 Map.Entry m = (Map.Entry)i.next();
178 transformer.setParameter((String)m.getKey(), m.getValue());
179 }
180 }
181
182 // When we transform the DOMResult, we need to make sure the result of
183 // the transformation has a DocType. For that to happen, we need to create
184 // the DOMResult using a Document with a predefined docType.
185 // If we don't have a DocType then do the transformation with a DOMResult
186 // that does not contain any doctype (like we use to do before).
187 DOMResult result = docDocType == null ? new DOMResult() : new DOMResult(docDocType);
188 transformer.transform(new DOMSource(source), result);
189 return result.getNode(); // pass the entire document
190 }
191 catch (TransformerConfigurationException e) {
192 return transformError("XMLTransformer.transform(Doc, Doc, HashMap, Doc)"
193 + "\ncouldn't create transformer object", e);
194 }
195 catch (TransformerException e) {
196 return transformError("XMLTransformer.transform(Doc, Doc, HashMap, Doc)"
197 + "\ncouldn't transform the source", e);
198 }
199 }
200
201 public Node transform(File stylesheet, File source) {
202 try {
203 Transformer transformer = this.t_factory.newTransformer(new StreamSource(stylesheet));
204 transformer.setErrorListener(new TransformErrorListener());
205 DOMResult result = new DOMResult();
206 transformer.transform(new StreamSource(source), result);
207 return result.getNode().getFirstChild();
208 } catch (TransformerConfigurationException e) {
209 return transformError("XMLTransformer.transform(File, File)"
210 + "\ncouldn't create transformer object for files\n"
211 + stylesheet + "\n" + source, e);
212 }
213 catch (TransformerException e) {
214 return transformError("XMLTransformer.transform(File, File)"
215 + "\ncouldn't transform the source for files\n"
216 + stylesheet + "\n" + source, e);
217 }
218 }
219
220 public Node transform(File stylesheet, File source, Document docDocType) {
221 try {
222 Transformer transformer = this.t_factory.newTransformer(new StreamSource(stylesheet));
223 transformer.setErrorListener(new TransformErrorListener());
224 DOMResult result = new DOMResult(docDocType);
225 transformer.transform(new StreamSource(source), result);
226 return result.getNode().getFirstChild();
227 } catch (TransformerConfigurationException e) {
228 return transformError("XMLTransformer.transform(File, File, Doc)"
229 + "\ncouldn't create transformer object for files\n"
230 + stylesheet + "\n" + source, e);
231 }
232 catch (TransformerException e) {
233 return transformError("XMLTransformer.transform(File, File, Doc)"
234 + "\ncouldn't transform the source for files\n"
235 + stylesheet + "\n" + source, e);
236 }
237 }
238
239 // Given a heading string on the sort of transformation error that occurred and the exception object itself,
240 // this method prints the exception to the tomcat window (system.err) and the greenstone log and then returns
241 // an xhtml error page that is constructed from it.
242 protected Node transformError(String heading, TransformerException e) {
243 String message = heading + "\n" + e.getMessage();
244 logger.error(heading + ": " + e.getMessage());
245
246 String location = e.getLocationAsString();
247 if(location != null) {
248 logger.error(location);
249 message = message + "\n" + location;
250 }
251 System.err.println("****\n" + message + "\n****");
252 return constructErrorXHTMLPage(message);
253 }
254
255 // Given an error message, splits it into separate lines based on any newlines present and generates an xhtml page
256 // (xml Element) with paragraphs for each line. This is then returned so that it can be displayed in the browser.
257 public static Element constructErrorXHTMLPage(String message) {
258 try{
259 String[] lines = message.split("\n");
260
261 Document xhtmlDoc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
262 // <html></html>
263 Node htmlNode = xhtmlDoc.createElement("html");
264 xhtmlDoc.appendChild(htmlNode);
265 // <head></head>
266 Node headNode = xhtmlDoc.createElement("head");
267 htmlNode.appendChild(headNode);
268 // <title></title>
269 Node titleNode = xhtmlDoc.createElement("title");
270 headNode.appendChild(titleNode);
271 Node titleString = xhtmlDoc.createTextNode("Error occurred");
272 titleNode.appendChild(titleString);
273
274 // <body></body>
275 Node bodyNode = xhtmlDoc.createElement("body");
276 htmlNode.appendChild(bodyNode);
277
278 // finally put the message in the body
279 Node h1Node = xhtmlDoc.createElement("h1");
280 bodyNode.appendChild(h1Node);
281 Node headingString = xhtmlDoc.createTextNode("The following error occurred:");
282 h1Node.appendChild(headingString);
283
284 //Node textNode = xhtmlDoc.createTextNode(message);
285 //bodyNode.appendChild(textNode);
286
287 for (int i = 0; i < lines.length; i++) {
288 Node pNode = xhtmlDoc.createElement("p");
289 Node textNode = xhtmlDoc.createTextNode(lines[i]);
290 pNode.appendChild(textNode);
291 bodyNode.appendChild(pNode);
292 }
293
294 return xhtmlDoc.getDocumentElement();
295
296 }catch(Exception e) {
297 String errmsg = "Exception trying to construct error xhtml page from message: " + message
298 + "\n" + e.getMessage();
299 System.err.println(errmsg);
300 logger.error(errmsg);
301 return null;
302 }
303 }
304
305 // ErrorListener class that can be used to register a handler for any fatal errors, errors and warnings that may
306 // occur when transforming an xml file with an xslt stylesheet. The errors are printed both to the greenstone.log and
307 // to the tomcat console (System.err), and the error message is stored in the errorMessage variable so that it can
308 // be retrieved and be used to generate an xhtml error page.
309 static public class TransformErrorListener implements ErrorListener {
310 protected String errorMessage = null;
311
312 // Receive notification of a recoverable error.
313 public void error(TransformerException exception) {
314 handleError("Error:\n", exception);
315 }
316 // Receive notification of a non-recoverable error.
317 public void fatalError(TransformerException exception) {
318 handleError("Fatal Error:\n", exception);
319 }
320 // Receive notification of a warning.
321 public void warning(TransformerException exception) {
322 handleError("Warning:\n", exception);
323 }
324
325 public String toString(TransformerException e) {
326 String location = e.getLocationAsString();
327 if(location == null) {
328 return e.getMessage();
329 }
330 return e.getMessage() + "\n" + location;
331 }
332
333 // clears the errorPage variable after first call to this method
334 public String getErrorMessage() {
335 String errMsg = this.errorMessage;
336 if(this.errorMessage != null) {
337 this.errorMessage = null;
338 }
339 return errMsg;
340 }
341
342 // sets the errorMessage member variable to the data stored in the exception
343 // and writes the errorMessage to the logger and tomcat's System.err
344 protected void handleError(String errorType, TransformerException exception) {
345 this.errorMessage = errorType + toString(exception);
346 System.err.println("\n****Error transforming xml:\n" + this.errorMessage + "\n****\n");
347 logger.error(this.errorMessage);
348 }
349 }
350}
Note: See TracBrowser for help on using the repository browser.