KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > faceless > report > PDFProxyServlet


1 // $Id: PDFProxyServlet.java,v 1.16 2003/11/05 20:46:43 mike Exp $
2
//
3
package org.faceless.report;
4
5 import java.util.*;
6 import java.io.*;
7 import java.net.*;
8 import javax.servlet.*;
9 import javax.servlet.http.*;
10 import org.faceless.pdf2.*;
11 import org.faceless.util.*;
12 import org.faceless.report.*;
13 import org.xml.sax.*;
14
15 /**
16  * <p>
17  * The PDFProxyServlet is a basic servlet which accepts a request,
18  * reads an XML report from another URL and turns it into a PDF to be
19  * returned to the client. Those using the Servlet 2.3 architecture may
20  * want to consider using the {@link PDFFilter} class instead.
21  * </p>
22  * <p>
23  * Most overriders should be able to just override the <tt>getProxyURL()</tt>
24  * method, to alter where the report XML is kept.
25  * </p>
26  * <p>
27  * Meta tags that aren't already known to the Report Generator and that
28  * begin with "<tt>HTTP-</tt>" are added to the response header (minus the
29  * "HTTP-" prefix). An example would be to place
30  * <tt>&lt;meta name="HTTP-Expires" value="Mon, 01 Jan 1999 12:00:00 GMT"&gt;</tt>
31  * in the head of the XML, which would set the "Expires" header in the HTTP
32  * response.
33  * </p>
34  * <p>
35  * The following custom Meta-Tags may also be used to control the behaviour of
36  * the servlet.
37  * <ul>
38  * <li><tt>Servlet-Cache</tt> - set the length of time to cache the generated
39  * PDF. The cache is actually implemented in the ProxyServlet, and is completely
40  * independent of any external caches between the server and the client. Examples
41  * of valid values are "1h30m", to cache the document for 1.5 hours, "45s" to cache
42  * for 45 seconds or "1d" to cache for 1 day. Any combination of days, hours, minutes
43  * and seconds is valid. There is no way to flush this document from the client browser.</li>
44  * <li><tt>Servlet-FileName</tt> - ask the client browser to save the file as the
45  * specified filename instead of displaying it inline. This uses the <tt>Content-Disposition</tt>
46  * header, which <i>in theory</i> is accepted by NS4+ and IE5+. On our test system
47  * at least however, this didn't work, and in IE5.5 actually prevented us from viewing
48  * the document at all! Use with caution.</li>
49  * </ul>
50  * Some initialization parameters can be set in the <tt>web.xml</tt> file to further
51  * control various internal aspects of the servlet:
52  * <ul>
53  * <li><tt>org.xml.sax.driver</tt> - may be set to the base class of your SAX parsers
54  * <tt>XMLReader</tt> implementation, if the generator is unable to locate it.</li>
55  * <li><tt>org.faceless.util.proxytimeout</tt> - may be set to the number of
56  * milliseconds to wait for a response from the proxy request. Defaults to 15000,
57  * or 15 seconds.</li>
58  * </ul>
59  * </p>
60  * <p>
61  * This class also implements <tt>org.xml.sax.ErrorHandler</tt>, to deal with
62  * any errors found during the XML parsing process. Currently all warnings and
63  * errors are fatal, and logged to <tt>System.err</tt>.
64  * </p>
65  *
66  * @version $Revision: 1.16 $
67  */

68 public abstract class PDFProxyServlet extends HttpProxyServlet implements ErrorHandler
69 {
70     public void init()
71         throws ServletException
72     {
73         super.init();
74     String JavaDoc license = (String JavaDoc)getServletConfig().getInitParameter("license");
75     if (license!=null) {
76         org.faceless.report.ReportParser.setLicenseKey(license);
77     }
78     }
79
80     /**
81      * Implements the <tt>getRequest</tt> method of the generic <tt>HttpProxyServlet</tt>.
82      * Subclasses should be able to leave this method untouched.
83      */

84     public HttpRequestWriter getRequest(HttpRequestReader request, HttpServletResponse response)
85         throws ServletException, IOException
86     {
87         HttpRequestWriter writer = null;
88     String JavaDoc url = getProxyURL(request.getUnderlyingRequest(), response);
89     if (url!=null) {
90         writer = new HttpRequestWriter(request);
91         writer.setURL(url);
92         writer.clearHeader("Accept-Encoding");
93     }
94     return writer;
95     }
96
97     /**
98      * Implements the <tt>getResponse</tt> method of the generic <tt>HttpProxyServlet</tt>.
99      * Subclasses should be able to leave this method untouched.
100      */

101     public HttpResponseWriter getResponse(HttpResponseReader in)
102         throws ServletException, IOException
103     {
104     HttpResponseWriter out = null;
105
106     // At this point we have the response from the proxy, after any 300
107
// level responses (redirections) have been processed. Check it's
108
// a valid response, if it is send the PDF, if not send the error
109
//
110
if (in.getStatus()>=200 && in.getStatus()<=299 && in.getStatus()!=204)
111     {
112         out = new HttpResponseWriter();
113
114         // I don't think I've ever seen a 2xx response other than 200 and
115
// 204, so I'm not sure if this behavious is correct for the others
116
// (or even if it matters).
117
//
118
out.setStatus(in.getStatus(), in.getStatusMessage());
119         out.setHeader("Content-Type", "application/pdf");
120
121         // Create the input source. Be sure to set the System ID, or
122
// every relative URL in the document will be wrong
123
//
124
InputSource xmlin = new InputSource();
125         xmlin.setByteStream(in.getInputStream());
126         xmlin.setSystemId(in.getURL());
127
128         // Here we go. Watch closely!
129
//
130
PDF pdf=null;
131         try {
132         // Create the parser, using the "org.xml.sax.driver"
133
// initialization parameter if specified, or null (meaning
134
// "find it yourself") if not.
135
//
136
ReportParser parser = ReportParser.getInstance((String JavaDoc)getServletConfig().getInitParameter("org.xml.sax.driver"));
137         initParser(parser);
138
139         // Set the unknown meta tag callback to our helper class.
140
//
141
parser.setMetaHandler(new MetaCallback(in, out, this));
142
143         // Go go go!
144
//
145
pdf = parser.parse(xmlin);
146         } catch (SAXException e) {
147         if (e.getException()!=null) {
148             throw new ServletException(e.getException());
149         } else {
150             throw new ServletException(e);
151         }
152         }
153
154         // That's 95% of the job done. Now we need to render to the
155
// OutputStream.
156
//
157
pdf.render(out.getOutputStream());
158     }
159     else
160     {
161         // Something went wrong. Pass the error back to the original
162
// requester. This may include "401 Authorization Required"
163
// responses (haven't tested this yet), or the more common
164
// "500 Internal Error", which we test hundreds of times a day.
165
//
166
out = new HttpResponseWriter(in);
167     }
168     return out;
169     }
170
171     // A class to pass the unknown Meta tags back to a context where
172
// we have a HttpServletResponse to use them
173
//
174
private class MetaCallback implements MetaHandler
175     {
176     private HttpResponseReader reader;
177     private HttpResponseWriter writer;
178     private PDFProxyServlet prox;
179
180         public MetaCallback(HttpResponseReader reader, HttpResponseWriter writer, PDFProxyServlet prox)
181     {
182         this.reader=reader;
183         this.writer=writer;
184         this.prox=prox;
185     }
186
187     // Whenever this is called, pass the request back to the servlet
188
// to handle it (so the method can be overridden).
189
//
190
public void handleMetaTag(String JavaDoc key, String JavaDoc val)
191         throws SAXException
192     {
193         try {
194         prox.metaTag(reader, writer, key, val);
195         } catch (Exception JavaDoc e) {
196             throw new SAXException(e);
197         }
198     }
199     }
200
201
202     //-------------------------------------------------------------------------
203
//
204
// If there are any methods to override, they're after here
205
//
206
//-------------------------------------------------------------------------
207

208     /**
209      * Handle any meta tags that aren't recognised by the core Report Generator.
210      * This method recognises tags begining with <tt>HTTP-</tt>, as well as
211      * <tt>Servlet-FileName</tt> and <tt>Servlet-Cache</tt>.
212      * @param request the Servlet request
213      * @param response the Servlet request
214      * @param name the "name" attribute from the meta tag
215      * @param value the "value" attribute from the meta tag
216      */

217     public void metaTag(HttpResponseReader reader, HttpResponseWriter writer, String JavaDoc name, String JavaDoc value)
218         throws ServletException, IOException
219     {
220         if (name.toUpperCase().startsWith("HTTP-")) {
221         writer.setHeader(name.substring(5), value);
222     } else if (name.equalsIgnoreCase("Servlet-FileName")) {
223         writer.setHeader("Content-Disposition", "attachment; filename=\""+value+"\"");
224     } else if (name.equalsIgnoreCase("Servlet-Cache")) {
225         int len=0,s=0,e=0;
226         int[] mul = { 86400, 3600, 60, 1 };
227         if (Character.isDigit(value.charAt(value.length()-1))) value=value+"s";
228         try {
229         for (;e<value.length();e++) {
230             int p = "dhms".indexOf(value.charAt(e));
231             if (p>=0) {
232             len += Integer.parseInt(value.substring(s,e)) * mul[p];
233             s=e+1;
234             }
235         }
236         } catch (NumberFormatException JavaDoc ex) {
237             len=0;
238         }
239         writer.setHeader("X-Proxy-Cache-Control", "max-age="+len);
240         writer.setHeader("Cache-Control", "max-age="+len);
241     }
242     }
243
244
245     // SAX error handlers from here on
246

247     public void warning(SAXParseException exception)
248     throws SAXException
249     {
250     System.err.println("PDF WARNING"+(exception.getLineNumber()>=0 ? " AT "+exception.getSystemId()+" line "+exception.getLineNumber() : "")+": "+exception.getMessage());
251         throw exception;
252     }
253
254     public void error(SAXParseException exception)
255     throws SAXException
256     {
257     System.err.println("PDF ERROR"+(exception.getLineNumber()>=0 ? " AT "+exception.getSystemId()+" line "+exception.getLineNumber() : "")+": "+exception.getMessage());
258     throw exception;
259     }
260
261     public void fatalError(SAXParseException exception)
262     throws SAXException
263     {
264     System.err.println("PDF FATAL ERROR"+(exception.getLineNumber()>=0 ? " AT "+exception.getSystemId()+" line "+exception.getLineNumber() : "")+": "+exception.getMessage());
265     throw exception;
266     }
267
268     /**
269      * Set any initialization options for the parser, if necessary.
270      * Overriders may optionally use this method to set parser flags
271      * (by default, this method does nothing).
272      * @since 1.0.4
273      */

274     public void initParser(ReportParser parser)
275     {
276     }
277
278     /**
279      * <p>
280      * Returns the <i>absolute</i> URL to send the proxy request to. This method
281      * is the only method that needs to be overriden by concrete subclasses of
282      * this class.
283      * </p><p>
284      * If no valid URL can be constructed due to an incorrect request, this method
285      * should return <tt>null</tt> and an appropriate error written to the
286      * <tt>response</tt>.
287      * </p>
288      * @param request the incoming request from the client
289      * @param response the outgoing response to the client - should only be written
290      * to if an error has occurred and the method is returning null
291      * @return the absolute URL to proxy the request to, or <tt>null</tt> if an
292      * error has occurred and no request should be made.
293      */

294     public abstract String JavaDoc getProxyURL(HttpServletRequest request, HttpServletResponse response)
295         throws IOException, ServletException;
296 }
297
Popular Tags