View Javadoc
1   /* 
2    * Licensed under the Apache License, Version 2.0 (the "License");
3    * you may not use this file except in compliance with the License.
4    * You may obtain a copy of the License at
5    *
6    * http://www.apache.org/licenses/LICENSE-2.0
7    *
8    * Unless required by applicable law or agreed to in writing, software
9    * distributed under the License is distributed on an "AS IS" BASIS,
10   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11   * See the License for the specific language governing permissions and
12   * limitations under the License.
13   *
14   */
15  
16  package org.esigate.xml;
17  
18  import java.io.IOException;
19  import java.io.InputStream;
20  import java.io.StringReader;
21  import java.io.Writer;
22  
23  import javax.xml.transform.Source;
24  import javax.xml.transform.Transformer;
25  import javax.xml.transform.TransformerConfigurationException;
26  import javax.xml.transform.TransformerException;
27  import javax.xml.transform.TransformerFactory;
28  import javax.xml.transform.dom.DOMResult;
29  import javax.xml.transform.dom.DOMSource;
30  import javax.xml.transform.stream.StreamSource;
31  import javax.xml.XMLConstants;
32  
33  import nu.validator.htmlparser.common.DoctypeExpectation;
34  import nu.validator.htmlparser.dom.Dom2Sax;
35  import nu.validator.htmlparser.dom.HtmlDocumentBuilder;
36  
37  import org.apache.commons.io.IOUtils;
38  import org.apache.http.client.methods.CloseableHttpResponse;
39  import org.esigate.Driver;
40  import org.esigate.HttpErrorPage;
41  import org.esigate.Renderer;
42  import org.esigate.http.HttpResponseUtils;
43  import org.esigate.impl.DriverRequest;
44  import org.w3c.dom.Document;
45  import org.xml.sax.InputSource;
46  import org.xml.sax.SAXException;
47  
48  /**
49   * Applies an XSLT transformation to the retrieved data.
50   * 
51   * If no namespace is specified in the document to transform, it is asumed as:<br>
52   * xmlns="http://www.w3.org/1999/xhtml"
53   * 
54   * @author Stanislav Bernatskyi
55   * @author Francois-Xavier Bonnet
56   */
57  public class XsltRenderer implements Renderer {
58      private static final TransformerFactory TRANSFORMER_FACTORY = TransformerFactory.newInstance();
59      private Transformer transformer;
60  
61      /**
62       * @param template
63       *            The path to the xsl template, relative to the context root
64       * @param driver
65       *            driver
66       * @param originalRequest
67       * @throws IOException
68       *             If an error occurs while writing to the output
69       * @throws HttpErrorPage
70       */
71      public XsltRenderer(String template, Driver driver, DriverRequest originalRequest) throws IOException,
72              HttpErrorPage {
73          StringBuilder templateStringBuilder = new StringBuilder();
74          CloseableHttpResponse response = driver.render(template, originalRequest.getOriginalRequest());
75          templateStringBuilder.append(HttpResponseUtils.toString(response));
76          transformer = createTransformer(IOUtils.toInputStream(templateStringBuilder));
77      }
78  
79      /**
80       * @param xsl
81       *            The xsl template to apply as a String
82       * @throws IOException
83       *             If an error occurs while writing to the output
84       */
85      public XsltRenderer(String xsl) throws IOException {
86          InputStream templateStream = IOUtils.toInputStream(xsl);
87          transformer = createTransformer(templateStream);
88      }
89  
90      private static Transformer createTransformer(InputStream templateStream) throws IOException {
91          try {
92              // Ensure XSLT cannot use advanced extensions during processing.
93              TRANSFORMER_FACTORY.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
94              return TRANSFORMER_FACTORY.newTransformer(new StreamSource(templateStream));
95          } catch (TransformerConfigurationException e) {
96              throw new ProcessingFailedException("Failed to create XSLT template", e);
97          } finally {
98              templateStream.close();
99          }
100     }
101 
102     /** {@inheritDoc} */
103     @Override
104     public void render(DriverRequest httpRequest, String src, Writer out) throws IOException {
105         try {
106             HtmlDocumentBuilder htmlDocumentBuilder = new HtmlDocumentBuilder();
107             htmlDocumentBuilder.setDoctypeExpectation(DoctypeExpectation.NO_DOCTYPE_ERRORS);
108             Document document = htmlDocumentBuilder.parse(new InputSource(new StringReader(src)));
109             Source source = new DOMSource(document);
110             DOMResult result = new DOMResult();
111             transformer.transform(source, result);
112             XhtmlSerializer serializer = new XhtmlSerializer(out);
113             Dom2Sax dom2Sax = new Dom2Sax(serializer, serializer);
114             dom2Sax.parse(result.getNode());
115         } catch (TransformerException e) {
116             throw new ProcessingFailedException("Failed to transform source", e);
117         } catch (SAXException e) {
118             throw new ProcessingFailedException("Failed serialize transformation result", e);
119         }
120     }
121 }