aboutsummaryrefslogtreecommitdiffstats
path: root/src/documentation/content/xdocs/servlets.xml
blob: c3bbdf9d5a859dff0a99d0bd62772718990b2093 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
<?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&amp;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&amp;par2=b&amp;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$ -->