KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > enhydra > xml > xmlc > servlet > XMLCContext


1 /*
2  * Enhydra Java Application Server Project
3  *
4  * The contents of this file are subject to the Enhydra Public License
5  * Version 1.1 (the "License"); you may not use this file except in
6  * compliance with the License. You may obtain a copy of the License on
7  * the Enhydra web site ( http://www.enhydra.org/ ).
8  *
9  * Software distributed under the License is distributed on an "AS IS"
10  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
11  * the License for the specific terms governing rights and limitations
12  * under the License.
13  *
14  * The Initial Developer of the Enhydra Application Server is Lutris
15  * Technologies, Inc. The Enhydra Application Server and portions created
16  * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
17  * All Rights Reserved.
18  *
19  * Contributor(s):
20  *
21  * $Id: XMLCContext.java,v 1.5 2005/01/26 08:29:24 jkjome Exp $
22  */

23
24 package org.enhydra.xml.xmlc.servlet;
25
26 import java.io.ByteArrayOutputStream JavaDoc;
27 import java.io.IOException JavaDoc;
28 import java.io.OutputStream JavaDoc;
29 import java.io.PrintWriter JavaDoc;
30 import java.io.StringWriter JavaDoc;
31 import java.util.zip.GZIPOutputStream JavaDoc;
32
33 import javax.servlet.ServletContext JavaDoc;
34 import javax.servlet.http.HttpServlet JavaDoc;
35 import javax.servlet.http.HttpServletRequest JavaDoc;
36 import javax.servlet.http.HttpServletResponse JavaDoc;
37
38 import org.enhydra.xml.dom.DOMStats;
39 import org.enhydra.xml.io.DOMFormatter;
40 import org.enhydra.xml.io.OutputOptions;
41 import org.enhydra.xml.io.URLRewriter;
42 import org.enhydra.xml.xmlc.XMLCFactory;
43 import org.enhydra.xml.xmlc.XMLObject;
44
45 /**
46  * Class to facility the use of XMLC in a HTTP servlet. This object contains
47  * a XMLC factory to create instances of XMLC document classes and methods to
48  * output documents.
49  *
50  * <P>
51  * An instance of this object is associated with the ServletContext object
52  * for an application (one per WebApp). The object is created on first use by
53  * {@link #getContext getContext}. It provides a factory for creating XMLC
54  * document class with optional automatic reloading of out of date classes
55  * or recompilation and reloading of classes out-of-date relative to a source
56  * file. Also provided are methods to output a XMLC document as a HTTP response,
57  * with automatic setting of response content type.
58  * <P>
59  * Instance of this class are configured using the following Servlet
60  * context-wide init parameters:
61  * <UL>
62  * <LI> <tt>xmlcReloading</tt> - Control's the automatic reloading or
63  * recompilation of XMLC document classes. The following values are
64  * recognized. <strong>Note</strong>: 'reload' and 'recompile' are only
65  * supported under Enhydra Application Server because the options are
66  * server-specific and the supporting classes no longer exist under XMLC
67  * proper. Even under Enhydra, it is recommended that you use the
68  * server-agnostic deferred parsing by using the "reparse" option.
69  * <UL>
70  * <LI> <tt>off</tt> - No automatic reloading or recompilation (default).
71  * <LI> <tt>reload</tt> - Automatic reloading of modified class files.
72  * <LI> <tt>recompile</tt> - Automatic recompilation of classes that are
73  * out of date relative to their source files and reloading
74  * of modified class files.
75  * <LI> <tt>reparse</tt> - repase the ML files is updated. No class reloading
76  * or recompilation is necessary.
77  * </UL>
78  * <LI> <tt>xmlcReparseResourceDirs</tt> - A list of additional directories to
79  * load the html and meta data files separated by File.separator
80  * <LI> <tt>xmlcReparsePackagePrefixes</tt> - A list of package prefix to be
81  * remove while searching for html and meta data files.
82  * <LI> <tt>xmlcReparseDefaultMetaDataPath</tt> - A path to the default meta data
83  * file related to the classpath and resource dirs.
84  * <LI> <tt>xmlcRecompilationHandler</tt> - Defines the class of the
85  * recompilation handler. This is only used if <tt>xmlcReloading</tt>
86  * is set to <tt>recompile</tt>. Defaults to
87  * <tt>org.enhydra.xml.xmlc.reloading.StandardRecompilationHandler</tt>
88  * if not set. The specified class must implement the
89  * org.enhydra.xml.xmlc.reloading.RecompilationHandler interface.
90  * <LI> <tt>xmlcLogger</tt> - Defines the logger class. Defaults to
91  * <tt>org.enhydra.xml.xmlc.servlet.ServletXMLCLogger</tt> if not
92  * set. The specified class must implement
93  * {@link org.enhydra.xml.xmlc.XMLCLogger} and
94  * define a constructor with a signature of <code>({@link
95  * ServletContext} context, boolean enableInfo, boolean
96  * enableDebug)</code> (same as {@link ServletXMLCLogger}).
97  * <LI> <tt>xmlcSessionURLEncoding</tt> - Controls automatic encoding of session ID in
98  * URLs.
99  * <UL>
100  * <LI> <tt>auto</tt> - Automatically enable session URL encoding as needed
101  * (default)
102  * <LI> <tt>always</tt> - Always enable session URL encoding. This does not
103  * work on most servers, as they will always try to send a cookie and
104  * not encode a URL if the session is from a cookie.
105  * <LI> <tt>never</tt> - Never enable session URL encoding.
106  * </UL>
107  * <LI> <tt>xmlcLogging</tt> - Control's what runtime information XMLC logs.
108  * The value is a space-seperated list of the following values:
109  * <UL>
110  * <LI> <tt>INFO</tt> - Log basic information about notable events, such as
111  * recompiling or reloading classes.
112  * <LI> <tt>DEBUG</tt> - Log debugging information. Mostly related to
113  * recompiling and reloading.
114  * <LI> <tt>STATS</tt> - Log statistics information useful in debugging
115  * performance problems. Currently writes information about each DOM
116  * that is written. This is especially useful in looking at the
117  * how much of a Lazy DOM has been expanded.
118  * </UL>
119  * The default is <tt>INFO</tt>.
120  * <LI> <tt>xmlcCompression</tt> - Controls compression of the response
121  * sent to the client. The following values are recognized:
122  * <UL>
123  * <LI> <tt>none</tt> - Responses are never compressed
124  * <LI> <tt>gzip</tt> - Responses are compressed with gzip if the client
125  * inidcates it can handle gzip compressed content by sending an appropriate
126  * <em>Accept-Encoding</em> HTTP header.
127  * </UL>
128  * The default is <em>none</em>
129  * </UL>
130  *
131  * @see ServletContext
132  */

133 public class XMLCContext {
134     /**
135      * Value indicating that URL session encoding should be detected
136      * automatically based on if it was used for the current requested.
137      */

138     public static final SessionURLEncodingMode URL_ENCODING_AUTO
139         = new SessionURLEncodingMode("auto");
140     
141     /**
142      * Value indicating that URL session encoding should always be used.
143      */

144     public static final SessionURLEncodingMode URL_ENCODING_ALWAYS
145         = new SessionURLEncodingMode("always");
146     
147     /**
148      * Value indicating that URL session encoding should never be used.
149      */

150     public static final SessionURLEncodingMode URL_ENCODING_NEVER
151         = new SessionURLEncodingMode("never");
152
153     /**
154      * Enumerated constant indicating how URL session encoding is to
155      * be handled.
156      */

157     static public class SessionURLEncodingMode {
158         /** String name of the mode */
159         private final String JavaDoc fModeName;
160         
161         /** Constructor */
162         private SessionURLEncodingMode(String JavaDoc modeName) {
163             fModeName = modeName;
164         }
165
166         /** Get the string value of the mode */
167         public String JavaDoc toString() {
168             return fModeName;
169         }
170     }
171
172     /**
173      * Name of ServletContext attribute that contains the instance of this class
174      * for a WebApp.
175      */

176     public static final String JavaDoc CONTEXT_ATTRIBUTE
177         = "http://www.enhydra.org/xmlc/XMLCContext";
178
179     /**
180      * The servlet context we are associated with.
181      */

182     private ServletContext JavaDoc fServletContext;
183
184     /*
185      * XMLC factory to use.
186      */

187     private XMLCFactory fFactory;
188
189     /*
190      * URL encoding of session key configuration.
191      */

192     private SessionURLEncodingMode fSessionURLEncodingMode;
193
194     /*
195      * Log various statistics.
196      */

197     private boolean fLogStats;
198
199     /*
200      * Allowed compression types
201      */

202     private int fCompressionType;
203
204     /**
205      * Definition of xmlcCompression parameter values as a bit set.
206      */

207     static final int XMLC_COMPRESS_NONE = 0x00;
208     static final int XMLC_COMPRESS_GZIP = 0x01;
209
210     /**
211      * Constructor. Must be public because it is accessed via
212      * reflection, but instances should only be created through
213      * {@link #getContext getContext}.
214      */

215     public XMLCContext(ServletContext JavaDoc servletContext,
216                 XMLCFactory factory,
217                 SessionURLEncodingMode sessionURLEncodingMode,
218                 boolean logStats,
219         int compressionType) {
220         fServletContext = servletContext;
221         fFactory = factory;
222         fSessionURLEncodingMode = sessionURLEncodingMode;
223         fLogStats = logStats;
224     fCompressionType = compressionType;
225     }
226
227     /**
228      * Get the XMLC factory object associated with the context.
229      */

230     public XMLCFactory getXMLCFactory() {
231         return fFactory;
232     }
233
234     /**
235      * Explictly set XMLC factory. The XMLC factory is normally defined base
236      * on the servlet configuration parameters. This allows the user to
237      * overide the standard factories.
238      */

239     public void setXMLCFactory(XMLCFactory factory) {
240         fFactory = factory;
241     }
242
243     /**
244      * Set the session URL encoding mode. This option is normally set from
245      * the servlet configuration parameters. This allows the explicit setting
246      * of this option.
247      *
248      * @param mode Constant indicating what mode to use
249      * @see SessionURLEncodingMode
250      */

251     public void setSessionURLEncoding(SessionURLEncodingMode mode) {
252         fSessionURLEncodingMode = mode;
253     }
254
255     /*
256      * Get the session URL encoding mode.
257      *
258      * @return The constant indicating what mode should be used for encoding.
259      * @see SessionURLEncodingMode
260      */

261     public SessionURLEncodingMode getSessionURLEncoding() {
262         return fSessionURLEncodingMode;
263     }
264
265     /**
266      * Determine if a session id should be encoded URLs.
267      */

268     protected boolean shouldEncodeSessionId(HttpServletRequest JavaDoc request) {
269         // This checks if the session is from a cookie. This causes these
270
// session id to always be encoded on the first request, as well as
271
// sent in a cookied. If it doesn't come back in a cookie, we keep
272
// encoding it.
273
return (((fSessionURLEncodingMode == URL_ENCODING_AUTO)
274                  && request.isRequestedSessionIdValid()
275                  && !request.isRequestedSessionIdFromCookie())
276                 || (fSessionURLEncodingMode == URL_ENCODING_ALWAYS));
277     }
278
279     /**
280      * Setup the session URL encoder as required by configuration and
281      * request.
282      */

283     protected URLRewriter setupURLRewriter(HttpServletRequest JavaDoc request,
284                                          final HttpServletResponse JavaDoc response) {
285         if (shouldEncodeSessionId(request)) {
286             return new URLRewriter() {
287                     public String JavaDoc rewriteURL(String JavaDoc urlAttrValue) {
288                         return response.encodeURL(urlAttrValue);
289                     }
290                 };
291         } else {
292             return null;
293         }
294     }
295
296     /**
297      * Set up the encoding using the encoding specified, OutputOptions,
298      * the response, or the XMLC default encoding. Modifies OutputOptions.
299      */

300     private void setupEncoding(HttpServletResponse JavaDoc response,
301                                XMLObject document,
302                                OutputOptions options) {
303         
304         // If encoding is already specified, we don't override.
305
if (options.getEncoding() == null) {
306             // Use document default encoding.
307
String JavaDoc encoding = document.getEncoding();
308             if (encoding != null) {
309                 options.setEncoding(encoding);
310             }
311         }
312     }
313
314     /**
315      * Create an OutputOptions object for a document. Options are defaulted for
316      * the specified document. The object maybe then modified as needed to
317      * override the default values.
318      * <P>
319      * The following attributes are set in the object:
320      * <UL>
321      * <LI> encoding
322      * <LI> MIME type - Defaults for document.
323      * <LI> URLRewriter - Set if URL encoding of sessions is enabled.
324      * </UL>
325      *
326      * @param request The servlet request object. The request is required
327      * to determine if URL encoding should be done on the document.
328      * @param response The servlet response object. The response is required
329      * to set up the URL encoder.
330      * @param document The DOM object to be returned as response.
331      */

332     public OutputOptions createOutputOptions(HttpServletRequest JavaDoc request,
333                                              HttpServletResponse JavaDoc response,
334                                              XMLObject document) {
335         OutputOptions options = DOMFormatter.getDefaultOutputOptions(document);
336
337         setupEncoding(response, document, options);
338
339         // Get the default MIME type from object
340
options.setMIMEType(document.getMIMEType());
341
342         // Enable encoding of session id in URLs, if needed.
343
options.setURLRewriter(setupURLRewriter(request, response));
344         return options;
345     }
346
347     /**
348      * Write headers, disabling caching if other policy is not already
349      * set.
350      */

351     private void outputHeaders(HttpServletResponse JavaDoc response,
352                                int docLength,
353                                String JavaDoc mimeEncoding,
354                                String JavaDoc mimeType,
355                    boolean compressed) throws IOException JavaDoc {
356
357         // Set headers
358
response.setContentLength(docLength);
359         if (mimeType != null) {
360             if (mimeEncoding != null) {
361                 response.setContentType(mimeType + "; charset=" + mimeEncoding);
362             } else {
363                 response.setContentType(mimeType);
364             }
365         }
366     if (compressed) {
367         response.setHeader("Content-Encoding","gzip");
368     }
369         
370         // Disable caching of page unless otherwise specified.
371
if (response.containsHeader("Cache-Control")) {
372             response.setHeader("Cache-Control", "no-cache");
373         }
374         if (response.containsHeader("Expires")) {
375             response.setHeader("Expires", "0");
376         }
377     }
378
379     /**
380      * Output document statistics.
381      */

382     private void logDocumentStats(XMLObject document) {
383         // Buffer results so it gets logged as a single entry
384
StringWriter JavaDoc buf = new StringWriter JavaDoc(2048);
385         PrintWriter JavaDoc writer = new PrintWriter JavaDoc(buf);
386         DOMStats.printStats("Write " + document.getClass().getName(),
387                             document, 0, writer);
388         writer.flush();
389         fServletContext.log(buf.toString());
390     }
391
392     /**
393      * Do common operations for writing DOM document. Sets approriate HTTP
394      * header, including setting of cache-control headers, if not already set.
395      * Handles encoding conversion and output.
396      */

397     private void outputDocument(HttpServletResponse JavaDoc response,
398                                 XMLObject document,
399                                 OutputOptions outputOptions,
400                 boolean compress) throws IOException JavaDoc {
401
402         // Convert to bytes and output
403
DOMFormatter formatter = new DOMFormatter(outputOptions);
404         byte[] docBytes = null;
405     if (compress) {
406         ByteArrayOutputStream JavaDoc bos = new ByteArrayOutputStream JavaDoc();
407         GZIPOutputStream JavaDoc os = new GZIPOutputStream JavaDoc(bos);
408         formatter.write(document, os);
409         os.finish();
410         docBytes = bos.toByteArray();
411     } else {
412         docBytes = formatter.toBytes(document);
413     }
414
415         outputHeaders(response, docBytes.length,
416                       outputOptions.getMIMEEncoding(),
417                       outputOptions.getMIMEType(),
418               compress);
419
420         // Log if requested
421
if (fLogStats) {
422             logDocumentStats(document);
423         }
424
425         // Output
426
OutputStream JavaDoc out = response.getOutputStream();
427         out.write(docBytes);
428         out.flush();
429     }
430
431     /**
432      * Output an an XMLC document object (DOM). The document is formatted
433      * according to it's type. The MIME type of the response is automatically
434      * set.
435      *
436      * @param request The servlet request object. The request is required
437      * to determine if URL encoding should be done on the document.
438      * @param response The servlet response object. The
439      * <tt>Content-Type</tt> and <tt>Content-Length</tt> will be set from
440      * the document..
441      * @param outputFormat Object use to specify options controlling the
442      * formatting of the document.
443      * @param document The DOM object to be returned as response.
444      */

445     public void writeDOM(HttpServletRequest JavaDoc request,
446                          HttpServletResponse JavaDoc response,
447                          OutputOptions outputOptions,
448                          XMLObject document) throws IOException JavaDoc {
449         // Copy options so as to not modify when changing encoding.
450
OutputOptions options = new OutputOptions(outputOptions);
451         setupEncoding(response, document, options);
452         // Get the default MIME type from object if no mimetype is set
453
if (options.getMIMEType() == null) {
454             options.setMIMEType(document.getMIMEType());
455         }
456
457         // Enable encoding of session id in URLs, if needed and no URL
458
// rewriter has been specified in the options
459
if (options.getURLRewriter() == null) {
460             options.setURLRewriter(setupURLRewriter(request, response));
461         }
462         outputDocument(response, document, options, useCompression(request));
463     }
464
465     /**
466      * Check if the output should be compressed for this particular request
467      */

468     private boolean useCompression(HttpServletRequest JavaDoc request) {
469     // check for output compression if it is enabled
470
boolean compress = false;
471     if ((fCompressionType & XMLC_COMPRESS_GZIP) != 0) {
472         String JavaDoc enc = request.getHeader("Accept-Encoding");
473         if (enc != null) {
474         int pos = enc.indexOf("gzip");
475         // FIXME: check for a qvalue of 0 and disable compression if found.
476
// The current implementation violates the HTTP 1.1 protocol
477
// by sending gzip compressed content when the client explicitly
478
// *excluded* gzip compression :-(
479
// See RFC 2616, section 14.3 for details
480
compress = (pos > -1);
481         }
482     }
483     return compress;
484     }
485
486     /**
487      * Output an an XMLC document object (DOM). The document is formatted
488      * according to it's type. The MIME type of the response is automatically
489      * set.
490      *
491      * @param request The servlet request object. The request is required
492      * to determine if URL encoding should be done on the document.
493      * @param response The servlet response object. The
494      * <tt>Content-Type</tt> and <tt>Content-Length</tt> will be set from
495      * the document..
496      * @param document The DOM object to be returned as response.
497      */

498     public void writeDOM(HttpServletRequest JavaDoc request,
499                          HttpServletResponse JavaDoc response,
500                          XMLObject document) throws IOException JavaDoc {
501         OutputOptions options
502             = createOutputOptions(request, response, document);
503         outputDocument(response, document, options, useCompression(request));
504     }
505
506     /**
507      * Obtain the XMLCContext for the current application, creating it if
508      * it doesn't exist.
509      *
510      * @param servlet A servlet in the application. Used to obtain the servlet context.
511      * @return The XMLC context object for the application.
512      * @see #getContext(ServletContext)
513      */

514     public static XMLCContext getContext(HttpServlet JavaDoc servlet) {
515         ServletContext JavaDoc servletContext = servlet.getServletContext();
516         return getContext(servletContext);
517     }
518
519     /**
520      * Obtain the XMLCContext for the current application, creating it if
521      * it doesn't exist.
522      *
523      * @param servletContext The servlet context of this application.
524      * @return The XMLC context object for the application.
525      */

526     public static XMLCContext getContext(ServletContext JavaDoc servletContext) {
527         //We used to synchronize here, but that causes problems in some environments (JBoss).
528
//When the application is reloaded the servlet context object changes, but the static
529
//variables used to avoid unnecessary re-checks and synchronization aren't cleared. As
530
//such, after the first initialization (at first use after app server startup), the
531
//XMLCContext object would never be re-set, even if the servlet context object is re-set.
532
//The fix is to forget the initialized check, forget the synchronization, and take the
533
//hit on looking up the XMLCContext object every time this method is called. This is
534
//reasonable and shouldn't cause problems since the only worry about resetting the
535
//XMLCContext object is performance. No real need for synchronization. --Jake
536
XMLCContext context = (XMLCContext)servletContext.getAttribute(CONTEXT_ATTRIBUTE);
537         if (context == null) {
538             context = new XMLCContextInit().createContext(servletContext);
539             servletContext.setAttribute(CONTEXT_ATTRIBUTE, context);
540         }
541         return (XMLCContext)servletContext.getAttribute(CONTEXT_ATTRIBUTE);
542     }
543     
544
545     /** Get the associated servlet context */
546     protected ServletContext JavaDoc getServletContext() { return fServletContext; }
547 }
548
Popular Tags