| <?xml version="1.0" encoding="UTF-8" standalone="no"?> |
| <!-- |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| --> |
| <!-- $Id$ --> |
| <!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V2.0//EN" "http://forrest.apache.org/dtd/document-v20.dtd"> |
| <document> |
| <header> |
| <title>Apache™ FOP: Servlets</title> |
| <subtitle>How to use Apache™ FOP in a Servlet</subtitle> |
| <version>$Revision$</version> |
| </header> |
| <body> |
| <section id="overview"> |
| <title>Overview</title> |
| <p> |
| This page discusses topic all around using Apache™ FOP in a servlet environment. |
| </p> |
| </section> |
| <section id="example-servlets"> |
| <title>Example Servlets in the FOP distribution</title> |
| <p> |
| In the directory {fop-dir}/src/java/org/apache/fop/servlet, you'll find a working example |
| of a FOP-enabled servlet. |
| </p> |
| <p> |
| The servlet is automatically built when you build Apache FOP using the supplied Ant script. After building |
| the servlet, drop fop.war into the webapps directory of Apache Tomcat (or any other web container). Then, you can use |
| URLs like the following to generate PDF files: |
| </p> |
| <ul> |
| <li>http://localhost:8080/fop/fop?fo=/home/path/to/fofile.fo</li> |
| <li>http://localhost:8080/fop/fop?xml=/home/path/to/xmlfile.xml&xsl=/home/path/to/xslfile.xsl</li> |
| </ul> |
| <p/> |
| <p>The source code for the servlet can be found under {fop-dir}/src/java/org/apache/fop/servlet/FopServlet.java.</p> |
| <note> |
| This example servlet should not be used on a public web server connected to the Internet as it does not contain |
| any measures to prevent Denial-of-Service-Attacks. It is provided as an example and as a starting point for |
| your own servlet. |
| </note> |
| </section> |
| <section id="servlet"> |
| <title>Create your own Servlet</title> |
| <note> |
| This section assumes you are familiar with <a href="embedding.html">embedding FOP</a>. |
| </note> |
| <section id="minimal-servlet"> |
| <title>A minimal Servlet</title> |
| <p> |
| Here is a minimal code snippet to demonstrate the basics: |
| </p> |
| <source>private FopFactory fopFactory = FopFactory.newInstance(); |
| private TransformerFactory tFactory = TransformerFactory.newInstance(); |
| |
| public void doGet(HttpServletRequest request, |
| HttpServletResponse response) throws ServletException { |
| try { |
| response.setContentType("application/pdf"); |
| Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, response.getOutputStream()); |
| Transformer transformer = tFactory.newTransformer(); |
| Source src = new StreamSource("foo.fo"); |
| Result res = new SAXResult(fop.getDefaultHandler()); |
| transformer.transform(src, res); |
| } catch (Exception ex) { |
| throw new ServletException(ex); |
| } |
| }</source> |
| <note> |
| There are numerous problems with the code snippet above. |
| Its purpose is only to demonstrate the basic concepts. |
| See below for details. |
| </note> |
| </section> |
| <section id="xslt"> |
| <title>Adding XSL tranformation (XSLT)</title> |
| <p> |
| A common requirement is to transform an XML source to |
| XSL-FO using an XSL transformation. It is recommended to use |
| JAXP for this task. The following snippet shows the basic |
| code: |
| </p> |
| <source>private FopFactory fopFactory = FopFactory.newInstance(); |
| private TransformerFactory tFactory = TransformerFactory.newInstance(); |
| |
| public void init() throws ServletException { |
| //Optionally customize the FopFactory and TransformerFactory here |
| } |
| |
| [..] |
| |
| //Setup a buffer to obtain the content length |
| ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| |
| //Setup FOP |
| Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, out); |
| |
| //Setup Transformer |
| Source xsltSrc = new StreamSource(new File("foo-xml2fo.xsl")); |
| Transformer transformer = tFactory.newTransformer(xsltSrc); |
| |
| //Make sure the XSL transformation's result is piped through to FOP |
| Result res = new SAXResult(fop.getDefaultHandler()); |
| |
| //Setup input |
| Source src = new StreamSource(new File("foo.xml")); |
| |
| //Start the transformation and rendering process |
| transformer.transform(src, res); |
| |
| //Prepare response |
| response.setContentType("application/pdf"); |
| response.setContentLength(out.size()); |
| |
| //Send content to Browser |
| response.getOutputStream().write(out.toByteArray()); |
| response.getOutputStream().flush();</source> |
| <note> |
| Buffering the generated PDF in a ByteArrayOutputStream is done to avoid potential |
| problems with the Acrobat Reader Plug-in in Microsoft Internet Explorer. |
| </note> |
| <p> |
| The <code>Source</code> instance used above is simply an |
| example. If you have to read the XML from a string, supply |
| a <code>new StreamSource(new |
| StringReader(xmlstring))</code>. Constructing and reparsing |
| an XML string is generally less desirable than using a |
| SAXSource if you generate your XML. You can alternatively |
| supply a DOMSource as well. You may also use dynamically |
| generated XSL if you like. |
| </p> |
| <p> |
| Because you have an explicit <code>Transformer</code> object, you can also use it to |
| explicitely set parameters for the transformation run. |
| </p> |
| </section> |
| <section id="cfg"> |
| <title>Custom configuration</title> |
| <p> |
| You can easily set up your own FOUserAgent as demonstrated on the <a href="embedding.html">Embedding page</a>. |
| </p> |
| </section> |
| <section id="performance"> |
| <title>Improving performance</title> |
| <p> |
| There are several options to consider: |
| </p> |
| <ul> |
| <li> |
| Instead of java.io.ByteArrayOutputStream consider using the ByteArrayOutputStream |
| implementation from the <a href="ext:commons-io">Jakarta Commons IO project</a> which allocates less memory. |
| The full class name is: <code>org.apache.commons.io.output.ByteArrayOutputStream</code> |
| </li> |
| <li> |
| In certain cases it can help to write the generated PDF to a temporary file so |
| you can quickly reuse the file. This is especially useful, if Internet Explorer |
| calls the servlet multiple times with the same request or if you often generate |
| equal PDFs. |
| </li> |
| </ul> |
| <p> |
| Of course, the |
| <a href="embedding.html#performance">performance hints from the Embedding page</a> |
| apply here, too. |
| </p> |
| </section> |
| <section id="uriresolver"> |
| <title>Accessing resources in your web application</title> |
| <p> |
| Often, you will want to use resources (stylesheets, images etc.) which are bundled with |
| your web application. FOP provides a URIResolver implementation that lets you access |
| files via the Servlet's ServletContext. The class is called |
| <code>org.apache.fop.servlet.ServletContextURIResolver</code>. |
| </p> |
| <p> |
| Here's how to set it up in your servlet. Instantiate a new instance in the servlet's |
| init() method: |
| </p> |
| <source><![CDATA[ |
| /** URIResolver for use by this servlet */ |
| protected URIResolver uriResolver; |
| |
| public void init() throws ServletException { |
| this.uriResolver = new ServletContextURIResolver(getServletContext()); |
| [..] |
| }]]></source> |
| <p> |
| The ServletContextURIResolver reacts on URIs beginning with "servlet-context:". If you |
| want to access an image in a subdirectory of your web application, you could, for |
| example, use: "servlet-context:/images/myimage.png". Don't forget the leading slash |
| after the colon! |
| </p> |
| <p> |
| Further down, you can use the URIResolver for various things: |
| </p> |
| <ul> |
| <li> |
| With the Transformer (JAXP/XSLT) so things like document() functions can resolver |
| "servlet-context:" URIs. |
| </li> |
| <li> |
| With the FopFactory so every resource FOP loads can be loaded using a "servlet-context:" |
| URI. |
| </li> |
| <li> |
| You can the ServletContextURIResolver yourself in your servlet code to access |
| stylesheets or XML files bundled with your web application. |
| </li> |
| </ul> |
| <p> |
| Here are some example snippets: |
| </p> |
| <source><![CDATA[ |
| //Setting up the JAXP TransformerFactory |
| this.transFactory = TransformerFactory.newInstance(); |
| this.transFactory.setURIResolver(this.uriResolver); |
| |
| [..] |
| |
| //Setting up the FOP factory |
| this.fopFactory = FopFactory.newInstance(); |
| this.fopFactory.setURIResolver(this.uriResolver); |
| |
| [..] |
| |
| //The stylesheet for the JAXP Transfomer |
| Source xsltSrc = this.uriResolver.resolve( |
| "servlet-context:/xslt/mystylesheet.xsl", null); |
| Transformer transformer = this.transFactory.newTransformer(xsltSrc); |
| transformer.setURIResolver(this.uriResolver);]]></source> |
| </section> |
| </section> |
| <section id="ie"> |
| <title>Notes on Microsoft Internet Explorer</title> |
| <p> |
| Some versions of Internet Explorer will not automatically show the PDF or call the servlet multiple times. |
| These are well-known limitations of Internet Explorer and are not a problem of the servlet. |
| However, Internet Explorer can still be used to download the PDF so that it can be viewed later. |
| Here are some suggestions in this context: |
| </p> |
| <ul> |
| <li> |
| Use an URL ending in <code>.pdf</code>, like |
| <code>http://myserver/servlet/stuff.pdf</code>. Yes, the servlet can |
| be configured to handle this. If the URL has to contain parameters, |
| try to have <strong>both</strong> the base URL as well as the last parameter end in |
| <code>.pdf</code>, if necessary append a dummy parameter, like |
| <code>http://myserver/servlet/stuff.pdf?par1=a&par2=b&d=.pdf</code>. The |
| effect may depend on IEx version. |
| </li> |
| <li> |
| Give IEx the opportunity to cache. In particular, ensure the |
| server does not set any headers causing IEx not to cache the |
| content. This may be a real problem if the document is sent |
| over HTTPS, because most IEx installations will by default |
| <em>not</em> cache any content retrieved over HTTPS. |
| Setting the <code>Expires</code> header entry may help in |
| this case:<br/> <code>response.setDateHeader("Expires", |
| System.currentTimeMillis() + cacheExpiringDuration * |
| 1000);</code><br/> Consult your server manual and the |
| relevant RFCs for further details on HTTP headers and |
| caching. |
| </li> |
| <li> |
| Cache in the server. It may help to include a parameter in |
| the URL which has a timestamp as the value min order to |
| decide whether a request is repeated. IEx is reported to |
| retrieve a document up to three times, but never more often. |
| </li> |
| </ul> |
| </section> |
| <section id="servlet-engine"> |
| <title>Servlet Engines</title> |
| <p> |
| When using a servlet engine, there are potential CLASSPATH issues, and potential conflicts |
| with existing XML/XSLT libraries. Servlet containers also often use their own classloaders |
| for loading webapps, which can cause bugs and security problems. |
| </p> |
| <section id="tomcat"> |
| <title>Tomcat</title> |
| <p> |
| Check Tomcat's documentation for detailed instructions about installing FOP and Cocoon. |
| There are known bugs that must be addressed, particularly for Tomcat 4.0.3. |
| </p> |
| </section> |
| <section id="websphere"> |
| <title>WebSphere 3.5</title> |
| <p> |
| Put a copy of a working parser in some directory where WebSphere can access it. |
| For example, if /usr/webapps/yourapp/servlets is the CLASSPATH for your servlets, |
| copy the Xerces jar into it (any other directory would also be fine). |
| Do not add the jar to the servlet CLASSPATH, but add it to the CLASSPATH of the |
| application server which contains your web application. |
| In the WebSphere administration console, click on the "environment" button in the |
| "general" tab. In the "variable name" box, enter "CLASSPATH". |
| In the "value" box, enter the correct path to the parser jar file |
| (/usr/webapps/yourapp/servlets/Xerces.jar in our example here). |
| Press "OK", then apply the change and restart the application server. |
| </p> |
| </section> |
| </section> |
| <section id="complex-usecases"> |
| <title>Handling complex use cases</title> |
| <p> |
| Sometimes the requirements for a servlet get quite sophisticated: SQL data sources, |
| multiple XSL transformations, merging of several datasources etc. In such a case |
| consider using <a class="fork" href="ext:cocoon">Apache Cocoon</a> instead |
| of a custom servlet to accomplish your goal. |
| </p> |
| </section> |
| </body> |
| </document> |