<p>Here is an example use of Driver which outputs PDF:
</p>
<source><![CDATA[
- import org.apache.fop.apps.Driver;
+import org.apache.fop.apps.Driver;
- /*..*/
+/*..*/
- Driver driver = new Driver(new InputSource(args[0]),
- new FileOutputStream(args[1]));
- driver.setRenderer(Driver.RENDER_PDF);
- driver.run();]]></source>
+Driver driver = new Driver(new InputSource(args[0]),
+ new FileOutputStream(args[1]));
+driver.setRenderer(Driver.RENDER_PDF);
+driver.run();]]></source>
<p>
In the example above, args[0] contains the path to an XSL-FO file, while
args[1] contains a path for the target PDF file.
</p>
- <p>You also need to set up logging. Global logging for all FOP
- processes is managed by MessageHandler. Per-instance logging
- is handled by Driver. You want to set both using an implementation
- of org.apache.avalon.framework.logger.Logger. See
- <jump href="#logging">below</jump> for more information.
- </p>
+ <section id="basic-logging">
+ <title>Logging</title>
+ <p>
+ You also need to set up logging. Global logging for all FOP
+ processes is managed by MessageHandler. Per-instance logging
+ is handled by Driver. You want to set both using an implementation
+ of org.apache.avalon.framework.logger.Logger. See
+ <jump href="#logging">below</jump> for more information.
+ </p>
+ <p>
+ Call <code>setLogger(Logger)</code> always immediately after
+ instantiating the Driver object. See here:
+ </p>
<source><![CDATA[
- import org.apache.avalon.framework.logger.Logger;
- import org.apache.avalon.framework.logger.ConsoleLogger;
-
- /*..*/
-
- Logger logger = new ConsoleLogger(ConsoleLogger.LEVEL_INFO);
- MessageHandler.setScreenLogger(logger);
- driver.setLogger(logger);]]></source>
+import org.apache.avalon.framework.logger.Logger;
+import org.apache.avalon.framework.logger.ConsoleLogger;
- <p>To setup the user config file you can do the following
- </p>
+/*..*/
+
+Driver driver = new Driver();
+Logger logger = new ConsoleLogger(ConsoleLogger.LEVEL_INFO);
+MessageHandler.setScreenLogger(logger);
+driver.setLogger(logger);]]></source>
+ </section>
+
+ <section id="render">
+ <title>Processing XSL-FO</title>
+ <p>
+ Once the Driver is set up, one of the <code>render()</code> methods
+ is called. Depending on whether DOM or an InputSource is being used, the
+ invocation of the method is either <code>render(Document)</code> or
+ <code>render(Parser, InputSource)</code> respectively.
+ </p>
+ <p>
+ <strong>Another possibility may be used to build the FO Tree: You can
+ call <code>getContentHandler()</code> and fire the SAX events yourself.
+ </strong>
+ You don't have to call <code>run()</code> or <code>render()</code> on the
+ Driver object if you use <code>getContentHandler()</code>.
+ </p>
+ <p>Here is an example use of Driver:</p>
<source><![CDATA[
- import org.apache.fop.apps.Options;
-
- /*..*/
-
- userConfigFile = new File(userConfig);
- options = new Options(userConfigFile);]]></source>
- <note>
- This is all you need to do, it sets up a static configuration class.
- </note>
+Driver driver = new Driver();
+//Setup logging here: driver.setLogger(...
+driver.setRenderer(Driver.RENDER_PDF);
+driver.setInputSource(new FileInputSource(args[0]));
+driver.setOutputStream(new FileOutputStream(args[1]));
+driver.run();]]></source>
+ </section>
- <p>Once the Driver is set up, the render method
- is called. Depending on whether DOM or SAX is being used, the
- invocation of the method is either <code>render(Document)</code> or
- <code>render(Parser, InputSource)</code> respectively.
- </p>
- <p>
- <strong>Another possibility may be used to build the FO Tree. You can
- call <code>getContentHandler()</code> and fire the SAX events yourself.
- </strong>
- </p>
- <p>Once the FO Tree is built, the format() and render() methods may be
- called in that order.
- </p>
- <p>Here is an example use of Driver:</p>
- <source><![CDATA[
- Driver driver = new Driver();
- driver.setRenderer(Driver.RENDER_PDF);
- driver.setInputSource(new FileInputSource(args[0]));
- driver.setOutputStream(new FileOutputStream(args[1]));
- driver.run();]]></source>
- <p>You can also specify an xml and xsl file for the input.
- </p>
- <p>Here is an example use of Driver with the XSLTInputHandler:</p>
- <source><![CDATA[
- Driver driver = new Driver();
- driver.setRenderer(Driver.RENDER_PDF);
- InputHandler inputHandler = new XSLTInputHandler(xmlFile, xslFile);
- XMLReader parser = inputHandler.getParser();
- driver.setOutputStream(new FileOutputStream(outFile));
- driver.render(parser, inputHandler.getInputSource());]]></source>
- <p>Have a look at the classes CommandLineStarter or FopServlet for complete
- examples. Also, have a look at the examples at the bottom of this page.
- </p>
+ <section id="render-with-xslt">
+ <title>Processing XSL-FO generated from XML+XSLT</title>
+ <p>
+ If you want to process XSL-FO generated from XML using XSLT we recommend
+ using standard JAXP to do the XSLT part and piping the generated SAX
+ events directly through to FOP. Here's how this would look like:
+ </p>
+ <source><![CDATA[
+Driver driver = new Driver();
+//Setup logging here: driver.setLogger(...
+driver.setRenderer(Driver.RENDER_PDF);
+
+//Setup the OutputStream for FOP
+driver.setOutputStream(new java.io.FileOutputStream(outFile));
+
+//Make sure the XSL transformation's result is piped through to FOP
+Result res = new SAXResult(driver.getContentHandler());
+
+//Setup XML input
+Source src = new StreamSource(xmlFile);
-<note>If your XSL-FO files contain SVG then Batik will be used. When Batik is
-initialised it uses certain classes in <code>java.awt</code> that
-intialises the java AWT classes. This means that a daemon thread
-is created by the JVM and on Unix it will need to connect to a
-DISPLAY.
-The thread means that the Java application will not automatically quit
-when finished, you will need to call <code>System.exit()</code>. These
-issues should be fixed in the upcoming JDK 1.4</note>
+//Setup Transformer
+Source xsltSrc = new StreamSource(xslFile);
+TransformerFactory transformerFactory = TransformerFactory.newInstance();
+Transformer transformer = transformerFactory.newTransformer(xsltSrc);
+//Start the transformation and rendering process
+transformer.transform(src, res);]]></source>
+ <note>There's no need to call <code>run()</code> or <code>render()</code>.</note>
+ <p>
+ This may look complicated at first, but it's really just the combination of an
+ XSL transformation and a FOP run. It's also easy to comment out the FOP part
+ for debugging purposes, for example when you're tracking down a bug in your
+ stylesheet. You can easily write the XSL-FO output from the XSL transformation
+ to a file to check if that part generates the expected output.
+ </p>
+ <p>
+ For fully working examples of the above and hints to some interesting
+ possibilities, see the <link href="#examples">examples section</link> below.
+ </p>
+ </section>
</section>
<section id="logging">
<title>Controlling logging</title>
- <p>FOP uses Jakarta Avalon's
- <fork href="http://jakarta.apache.org/avalon/api/org/apache/avalon/framework/logger/Logger.html">Logger</fork>
- interface to do logging. See the <fork href="http://jakarta.apache.org/avalon/">Jakarta Avalon project</fork> for more information.</p>
- <p>Per default FOP uses the ConsoleLogger which logs to System.out. If you want to do logging using a
- logging framework (such as LogKit, Log4J or JDK 1.4 Logging) you can set a
- different Logger implementation on the Driver object. Here's an example how you would use LogKit:</p>
+ <p>
+ FOP uses the
+ <fork href="http://avalon.apache.org/framework/api/org/apache/avalon/framework/logger/package-summary.html">Logger package</fork>
+ from Apache Avalon Framework to do logging. See the
+ <fork href="http://avalon.apache.org/framework/">Apache Avalon Framework</fork>
+ for more information.
+ </p>
+ <p>
+ Per default FOP uses the ConsoleLogger which logs to System.out. If you want to do logging using a
+ logging framework (such as LogKit, Log4J or JDK 1.4 Logging) you can set a
+ different Logger implementation on the Driver object. Here's an example how you would use LogKit:
+ </p>
<source><![CDATA[
- Hierarchy hierarchy = Hierarchy.getDefaultHierarchy();
- PatternFormatter formatter = new PatternFormatter(
- "[%{priority}]: %{message}\n%{throwable}" );
+Hierarchy hierarchy = Hierarchy.getDefaultHierarchy();
+PatternFormatter formatter = new PatternFormatter(
+ "[%{priority}]: %{message}\n%{throwable}" );
- LogTarget target = null;
- target = new StreamTarget(System.out, formatter);
+LogTarget target = null;
+target = new StreamTarget(System.out, formatter);
- hierarchy.setDefaultLogTarget(target);
- log = hierarchy.getLoggerFor("fop");
- log.setPriority(Priority.INFO);
+hierarchy.setDefaultLogTarget(target);
+log = hierarchy.getLoggerFor("fop");
+log.setPriority(Priority.INFO);
- driver.setLogger(new org.apache.avalon.framework.logger.LogKitLogger(log));]]></source>
- <p>The LogKitLogger class implements the Logger interface so all logging calls are being redirected to LogKit.
- More information on Jakarta LogKit can be found <fork href="http://jakarta.apache.org/avalon/logkit/index.html">here</fork>.</p>
- <p>Similar implementations exist for Log4J (org.apache.avalon.framework.logger.Log4JLogger) and
- JDK 1.4 logging (org.apache.avalon.framework.logger.Jdk14Logger).</p>
- <p>If you want FOP to be totally silent you can also set an org.apache.avalon.framework.logger.NullLogger instance.</p>
- <p>If you want to use yet another logging facility you simply have to create a class that implements org.apache.avalon.framework.logging.Logger
- and set it on the Driver object. See the existing implementations in Avalon Framework for examples.</p>
- </section>
- <section id="input">
- <title>Input Sources</title>
- <p>The input XSL-FO document is always handled internally as SAX (see the <link href="design/parsing.html">Parsing Design Document</link> for the rationale).
-However, the input itself can be provided in a variety of ways to FOP, which normalizes the input (if necessary) into SAX events:</p>
- <ul>
- <li><strong>SAX Events through SAX Handler</strong>: <code>FOTreeBuilder</code> is the SAX Handler which is obtained through <code>getContentHandler</code> on <code>Driver</code>.</li>
- <li><strong>DOM (which is converted into SAX Events)</strong>: The conversion of a DOM tree is done via the <code>render(Document)</code> method on <code>Driver</code>.</li>
- <li><strong>Data Source (which is parsed and converted into SAX Events)</strong>: The <code>Driver</code> can take an <code>InputSource</code> as input.
-This can use a <code>Stream</code>, <code>String</code> etc.</li>
- <li><strong>XML+XSLT Transformation</strong> (which is transformed using an XSLT Processor and the result is fired as SAX Events: <code>XSLTInputHandler</code> is used as an <code>InputSource</code> in the render(<code>XMLReader</code>, <code>InputSource</code>) method on <code>Driver</code>.</li>
- </ul>
- <p>There are a variety of upstream data manipulations possible.
-For example, you may have a DOM and an XSL stylesheet; or you may want to
-set variables in the stylesheet.
-Interface documentation and some cookbook solutions to these situations are provided in <fork href="http://xml.apache.org/xalan-j/usagepatterns.html">Xalan Basic Usage Patterns</fork>.</p>
- <p>
-See the <link href="#examples">Examples</link> for some variations on input.
- </p>
- </section>
- <section id="hints">
- <title>Hints</title>
- <section id="object-reuse">
- <title>Object reuse</title>
- <p>
-If FOP is going to be used multiple times within your application
-it may be useful to reuse certain objects to save time.
- </p>
- <p>
-The renderers and the driver can both be reused. A renderer is reusable
-once the previous render has been completed. The driver is reuseable
-after the rendering is complete and the reset method is called.
-You will need to setup the driver again with a new OutputStream,
-IntputStream and renderer.
- </p>
- </section>
- <section id="render-info">
- <title>Getting information on the rendering process</title>
- <p>
-To get the number of pages that were rendered by FOP you can call
-<code>Driver.getResults()</code>. This returns a FormattingResults object
-where you can lookup the number of pages produced. It also gives you the
-page-sequences that were produced along with their id attribute and their
-number of pages. This is particularly useful if you render multiple
-documents (each enclosed by a page-sequence) and have to know the number of
-pages of each document.
- </p>
- </section>
- </section>
- <section id="servlet">
- <title>Using FOP in a Servlet</title>
- <p>
- Here is a minimal code snippet to demonstrate the basics:
- </p>
- <source>response.setContentType("application/pdf");
-Driver driver=new Driver( new InputSource("foo.fo"),
- response.getOutputStream());
-driver.setRenderer(Driver.RENDER_PDF);
-driver.run();</source>
- <p>
-There are numerous problems with the code snippet above.
-Its purpose is only to demonstrate the basic concepts.
-See xml-fop/examples/servlet for a working example of FOP used in a servlet.
-After building the servlet, drop the fop.war into the webapps directory of Tomcat.
-Then access a URL as follows:
- </p>
- <p>http://localhost:8080/fop/fop?fo=/home/path/to/fofile.fo</p>
- <p>http://localhost:8080/fop/fop?xml=/home/path/to/xmlfile.xml&xsl=/home/path/to/xslfile.xsl</p>
- <p>The source code for the servlet can be found under xml-fop/examples/servlet/src/FopServlet.java.</p>
- <note>
- Some versions of Internet Explorer will not automatically show the PDF.
-This is well-known to be a limitation of Internet Explorer, and is not a problem with the servlet.
-However, Internet Explorer can still be used to download the PDF so that it can be viewed later. Also, appending ".pdf" to the end of the URL may help.
- </note>
- </section>
- <section id="servlet-transform">
- <title>Using FOP in a Servlet with an XSLT Transformation</title>
+driver.setLogger(new org.apache.avalon.framework.logger.LogKitLogger(log));]]></source>
<p>
- If both the source XML and XSL are read from files, use the TraxInputHandler:
+ The LogKitLogger class implements the Logger interface so all logging calls are being redirected to LogKit.
+ More information on Jakarta LogKit can be found <fork href="http://jakarta.apache.org/avalon/logkit/index.html">here</fork>.
</p>
- <source>response.setContentType("application/pdf");
-XSLTInputHandler input
- =new XSLTInputHandler(new File("foo.xml"), new File("foo.xsl"));
-Driver driver=new Driver();
-driver.setOutputStream(response.getOutputStream());
-driver.setRenderer(Driver.RENDER_PDF);
-driver.render(input.getParser(), input.getInputSource());</source>
<p>
- This code snippet has the same problems as the one from the <link href="#servlet">section above</link>.
+ Similar implementations exist for Log4J (org.apache.avalon.framework.logger.Log4JLogger) and
+ JDK 1.4 logging (org.apache.avalon.framework.logger.Jdk14Logger).
</p>
<p>
- If your source XML is generated on the fly (for example from a database, a web service, or another servlet), create a transformer object explicitly, and use a SAX event stream to feed the transformation result into FOP:
+ If you want FOP to be totally silent you can also set an org.apache.avalon.framework.logger.NullLogger instance.
</p>
- <source>response.setContentType("application/pdf");
-Driver driver =new Driver();
-driver.setOutputStream(response.getOutputStream());
-driver.setRenderer(Driver.RENDER_PDF);
-Transformer transformer=TransformerFactory.newInstance()
- .newTransformer(new StreamSource("foo.xsl"));
-transformer.transform(xmlsource, new SAXResult(driver.getContentHandler()));</source>
<p>
- You don't have to call run() or render() on the driver object.
+ If you want to use yet another logging facility you simply have to create a class that
+ implements org.apache.avalon.framework.logging.Logger and set it on the Driver object.
+ See the existing implementations in Avalon Framework for examples.
</p>
+ </section>
+ <section id="input">
+ <title>Input Sources</title>
<p>
- The <code>xmlsource</code> is a placeholder for your actual XML source.
-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.
+ The input XSL-FO document is always handled internally as SAX (see the
+ <link href="design/parsing.html">Parsing Design Document</link> for the rationale).
+ However, the input itself can be provided in a variety of ways to FOP,
+ which normalizes the input (if necessary) into SAX events:
</p>
+ <ul>
+ <li><strong>SAX Events through SAX Handler</strong>: <code>FOTreeBuilder</code> is the SAX Handler which is obtained through <code>getContentHandler</code> on <code>Driver</code>.</li>
+ <li><strong>DOM (which is converted into SAX Events)</strong>: The conversion of a DOM tree is done via the <code>render(Document)</code> method on <code>Driver</code>.</li>
+ <li><strong>Data Source (which is parsed and converted into SAX Events)</strong>: The <code>Driver</code> can take an <code>InputSource</code> as input.
+This can use a <code>Stream</code>, <code>String</code> etc.</li>
+ <li><strong>XML+XSLT Transformation</strong> (which is transformed using an XSLT Processor and the result is fired as SAX Events: <code>XSLTInputHandler</code> is used as an <code>InputSource</code> in the render(<code>XMLReader</code>, <code>InputSource</code>) method on <code>Driver</code>.</li>
+ </ul>
<p>
- Because you have an explicit transformer object, you can also use it to explicitly set parameters for the transformation run.
+ There are a variety of upstream data manipulations possible.
+ For example, you may have a DOM and an XSL stylesheet; or you may want to
+ set variables in the stylesheet. Interface documentation and some cookbook
+ solutions to these situations are provided in
+ <fork href="http://xml.apache.org/xalan-j/usagepatterns.html">Xalan Basic Usage Patterns</fork>.
+ </p>
+ <p>
+ See the <link href="#examples">Examples</link> for some variations on input.
</p>
</section>
<section id="config-external">
<p>
To access an external configuration:
</p>
- <source>org.apache.fop.apps.Options options = new Options(new File("userconfig.xml"));</source>
+ <source><![CDATA[
+import org.apache.fop.apps.Options;
+
+/*..*/
+
+userConfigFile = new File(userConfig);
+options = new Options(userConfigFile);]]></source>
+ <note>
+ This is all you need to do, it sets up a static configuration class.
+ </note>
<p>
No further reference to the <code>options</code> variable is necessary.
+ The "options = " is actually not even necessary.
</p>
<p>
See <link href="#multithreading">Multithreading FOP</link> for issues related to changing configuration in a multithreaded environment.
See <link href="#multithreading">Multithreading FOP</link> for issues related to changing configuration in a multithreaded environment.
</p>
</section>
+ <section id="hints">
+ <title>Hints</title>
+ <section id="object-reuse">
+ <title>Object reuse</title>
+ <p>
+If FOP is going to be used multiple times within your application
+it may be useful to reuse certain objects to save time.
+ </p>
+ <p>
+The renderers and the driver can both be reused. A renderer is reusable
+once the previous render has been completed. The driver is reuseable
+after the rendering is complete and the <code>reset()</code> method is called.
+You will need to setup the driver again with a new OutputStream,
+IntputStream and renderer.
+ </p>
+ </section>
+ <section id="awt">
+ <title>AWT issues</title>
+ <p>
+ If your XSL-FO files contain SVG then Batik will be used. When Batik is
+ initialised it uses certain classes in <code>java.awt</code> that
+ intialises the java AWT classes. This means that a daemon thread
+ is created by the JVM and on Unix it will need to connect to a
+ DISPLAY.
+ </p>
+ <p>
+ The thread means that the Java application may not automatically quit
+ when finished, you will need to call <code>System.exit()</code>. These
+ issues should be fixed in the upcoming JDK 1.4.
+ </p>
+ <p>
+ If you run into trouble running FOP on a head-less server, please see the
+ <link href="graphics.html#batik">notes on Batik</link>.
+ </p>
+ </section>
+ <section id="render-info">
+ <title>Getting information on the rendering process</title>
+ <p>
+To get the number of pages that were rendered by FOP you can call
+<code>Driver.getResults()</code>. This returns a FormattingResults object
+where you can lookup the number of pages produced. It also gives you the
+page-sequences that were produced along with their id attribute and their
+number of pages. This is particularly useful if you render multiple
+documents (each enclosed by a page-sequence) and have to know the number of
+pages of each document.
+ </p>
+ </section>
+ </section>
+ <section id="performance">
+ <title>Improving performance</title>
+ <p>
+ There are several options to consider:
+ </p>
+ <ul>
+ <li>
+ Whenever possible, try to use SAX to couple the individual components involved
+ (parser, XSL transformer, SQL datasource etc.).
+ </li>
+ <li>
+ Depending on the target OutputStream (in case of an FileOutputStream, but not
+ for a ByteArrayOutputStream, for example) it may improve performance considerably
+ if you buffer the OutputStream using a BufferedOutputStream:
+ <code>driver.setOutputStream(new java.io.BufferedOutputStream(out));</code>
+ <br/>
+ Make sure you properly close the OutputStream when FOP is finished.
+ </li>
+ <li>
+ Cache the stylesheet. If you use the same stylesheet multiple times
+ you can setup a JAXP <code>Templates</code> object and reuse it each time you do
+ the XSL transformation.
+ </li>
+ <li>
+ Use an XSLT compiler like XSLTC that comes with Xalan-J.
+ </li>
+ </ul>
+ </section>
<section id="multithreading">
<title>Multithreading FOP</title>
<p>
Here are some tips to mitigate these problems:
</p>
<ul>
- <li>To avoid having your threads blocked, create a Driver object for each thread.</li>
- <li>If possible, do not change the configuration data while there is a Driver object rendering.
-Setup the configuration only once, preferably in the <code>init()</code> method of the servlet.
+ <li>
+ To avoid having your threads blocked, create a Driver object for each thread.
</li>
- <li>If you must change the configuration data more often, or if you have multiple servlets within the same webapp using FOP, consider implementing a singleton class to encapsulate the configuration settings and to run FOP in synchronized methods.
+ <li>
+ If possible, do not change the configuration data while there is a Driver object rendering.
+ Setup the configuration only once, preferably in the <code>init()</code> method of the servlet.
+ </li>
+ <li>
+ If you must change the configuration data more often, or if you have multiple
+ servlets within the same webapp using FOP, consider implementing a singleton
+ class to encapsulate the configuration settings and to run FOP in synchronized methods.
</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="examples">
<title>Examples</title>
<p>
-The directory "xml-fop/examples/embedding" contains several working examples.
+The directory "{fop-fir}/examples/embedding" contains several working examples.
In contrast of the examples above the examples here primarily use JAXP for
XML access. This may be easier to understand for people familiar with JAXP.
</p>
--- /dev/null
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE document PUBLIC "-//APACHE//DTD Documentation V1.1//EN"
+ "http://cvs.apache.org/viewcvs.cgi/*checkout*/xml-forrest/src/resources/schema/dtd/document-v11.dtd">
+<document>
+ <header>
+ <title>Servlets</title>
+ <subtitle>How to use FOP in a Servlet</subtitle>
+ </header>
+ <body>
+ <section id="overview">
+ <title>Overview</title>
+ <p>
+ This page discusses topic all around using 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}/examples/servlet, you'll find a working example
+ of a FOP-enabled servlet.
+ </p>
+ <p>
+ You can build the servlet easily by using the supplied Ant script. After building
+ the servlet, drop fop.war into the webapps directory of Tomcat. 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 xml-fop/examples/servlet/src/FopServlet.java.</p>
+ </section>
+ <section id="servlet">
+ <title>Create your own Servlet</title>
+ <note>
+ This section assumes you are familiar with <link href="embedding.html">embedding FOP</link>.
+ </note>
+ <section id="minimal-servlet">
+ <title>A minimal Servlet</title>
+ <p>
+ Here is a minimal code snippet to demonstrate the basics:
+ </p>
+ <source>public void doGet(HttpServletRequest request,
+ HttpServletResponse response) throws ServletException {
+ try {
+ response.setContentType("application/pdf");
+ Driver driver = new Driver(new InputSource("foo.fo"),
+ response.getOutputStream());
+ driver.setRenderer(Driver.RENDER_PDF);
+ driver.run();
+ } 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 the ability to do an XSL transformation to transform some
+ XML source to XSL-FO. It is recommended that JAXP be used for this task. The following
+ snippet shows the basic code for doing this:
+ </p>
+ <source>
+protected Logger log;
+protected TransformerFactory transformerFactory;
+
+public void init() throws ServletException {
+ this.log = new ConsoleLogger(ConsoleLogger.LEVEL_WARN);
+ this.transformerFactory = TransformerFactory.newInstance();
+}
+
+[..]
+
+ //Setup FOP
+ Driver driver = new Driver();
+ driver.setLogger(this.log);
+ driver.setRenderer(Driver.RENDER_PDF);
+
+ //Setup a buffer to obtain the content length
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ driver.setOutputStream(out);
+
+ //Setup Transformer
+ Source xsltSrc = new StreamSource(new File("foo-xml2fo.xsl"));
+ Transformer transformer = this.transformerFactory.newTransformer(xsltSrc);
+
+ //Make sure the XSL transformation's result is piped through to FOP
+ Result res = new SAXResult(driver.getContentHandler());
+
+ //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.
+ </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
+ explicitly set parameters for the transformation run.
+ </p>
+ </section>
+ <section id="cfg">
+ <title>Custom configuration</title>
+ <p>
+ If you need to supply a special configuration do this in the <code>init()</code>
+ method so it will only be done once and to avoid multithreading problems.
+ </p>
+ <source>public void init() throws ServletException {
+ [..]
+ new Options(new File("userconfig.xml"));
+ //or
+ Configuration.put("baseDir", "/my/base/dir");
+}</source>
+ </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 Jakarta Commons IO project which allocates less memory.
+ </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
+ <link href="embedding.html#performance">performance hints from the Embedding page</link>
+ apply here, too.
+ </p>
+ </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. Consult
+ your server manual.
+ </li>
+ <li>
+ Setting the <code>Expires</code> header entry may help:
+ <code>response.setDateHeader("Expires", System.currentTimeMillis() + cacheExpiringDuration * 1000);</code>
+ </li>
+ <li>
+ Cache in the server. Including a parameter in the URL which has a
+ timestamp as the value may help you 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 <fork href="http://cocoon.apache.org">Apache Cocoon</fork> instead
+ of a custom servlet to accomplish your goal.
+ </p>
+ </section>
+ </body>
+</document>
+<!-- Last Line of $RCSFile$ -->