Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/enhydra/xml/xmlc/servlet/XMLCContext.java


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.1.2.8 2001/02/16 20:41:44 markd Exp $
22   */
23  
24  package org.enhydra.xml.xmlc.servlet;
25  
26  import java.io.IOException;
27  import java.io.StringWriter;
28  import java.io.PrintWriter;
29  import java.io.OutputStream;
30  import javax.servlet.ServletContext;
31  import javax.servlet.http.HttpServletRequest;
32  import javax.servlet.http.HttpServletResponse;
33  import javax.servlet.http.HttpServlet;
34  import org.enhydra.xml.io.DOMFormatter;
35  import org.enhydra.xml.io.OutputOptions;
36  import org.enhydra.xml.io.URLRewriter;
37  import org.enhydra.xml.dom.DOMStats;
38  import org.enhydra.xml.xmlc.XMLObject;
39  import org.enhydra.xml.xmlc.XMLCFactory;
40  
41  /**
42   * Class to facility the use of XMLC in a HTTP servlet.  This object contains
43   * a XMLC factory to create instances of XMLC document classes and methods to
44   * output documents.
45   *
46   * <P>
47   * An instance of this object is associated with the ServletContext object
48   * for an application (one per WebApp).  The object is created on first use by
49   * ({@see #getContext getContext}.  It provides a factory for creating XMLC
50   * document class with optional automatic reloading of out of date classes
51   * or recompilation and reloading of classes out-of-date relative to a source
52   * file.  Also provided are methods to output a XMLC document as a HTTP response,
53   * with automatic setting of response content type.
54   * <P>
55   * Instance of this class are configured using the following Servlet
56   * context-wide init parameters:
57   * <UL>
58   * <LI> <tt>xmlcReloading</tt> - Control's the automatic reloading or
59   *      recompilation of XMLC document classes.  The following values are
60   *      recognized.
61   *    <UL>
62   *    <LI> <tt>off</tt> - No automatic reloading or recompilation (default).
63   *    <LI> <tt>reload</tt> - Automatic reloading of modified class files.
64   *    <LI> <tt>recompile</tt> - Automatic recompilation of classes that are 
65   *         out of date relative to their source files and reloading
66   *         of modified class files.
67   *    </UL>
68   * <LI> <tt>xmlcSessionURLEncoding</tt> - Controls automatic encoding of session ID in
69   *      URLs.
70   *    <UL>
71   *    <LI> <tt>auto</tt> - Automatically enable session URL encoding as needed
72   *         (default)
73   *    <LI> <tt>always</tt> - Always enable session URL encoding.
74   *    <LI> <tt>never</tt> - Never enable session URL encoding.
75   *    </UL>
76   * <LI> <tt>xmlcLogging</tt> - Control's what runtime information XMLC logs.
77   *    The value is a space-seperated list of the following values:
78   *    <UL>
79   *    <LI> <tt>INFO</tt> - Log basic information about notable events, such as
80   *         recompiling or reloading classes.
81   *    <LI> <tt>DEBUG</tt> - Log debugging information.  Mostly related to
82   *         recompiling and reloading.
83   *    <LI> <tt>STATS</tt> - Log statistics information useful in debugging
84   *         performance problems.  Currently writes information about each DOM
85   *         that is written.  This is especially useful in looking at the
86   *         how much of a Lazy DOM has been expanded.
87   *    </UL>
88   *  The default is <tt>INFO</tt>.
89   * </UL>
90   *
91   * @see ServletContext
92   */
93  public class XMLCContext {
94      /**
95       * Value indicating that URL session encoding should be detected
96       * automatically based on if it was used for the current requested.
97       */
98      public static final SessionURLEncodingMode URL_ENCODING_AUTO
99          = new SessionURLEncodingMode("auto");
100     
101     /**
102      * Value indicating that URL session encoding should always be used.
103      */
104     public static final SessionURLEncodingMode URL_ENCODING_ALWAYS
105         = new SessionURLEncodingMode("always");
106     
107     /**
108      * Value indicating that URL session encoding should never be used.
109      */
110     public static final SessionURLEncodingMode URL_ENCODING_NEVER
111         = new SessionURLEncodingMode("never");
112 
113     /**
114      * Enumerated constant indicating how URL session encoding is to
115      * be handled.
116      */
117     static public class SessionURLEncodingMode {
118         /** String name of the mode */
119         private final String fModeName;
120         
121         /** Constructor */
122         private SessionURLEncodingMode(String modeName) {
123             fModeName = modeName;
124         }
125 
126         /** Get the string value of the mode */
127         public String toString() {
128             return fModeName;
129         }
130     }
131 
132     /**
133      * Name of ServletContext attribute that contains the instance of this class
134      * for a WebApp.
135      */
136     public static final String CONTEXT_ATTRIBUTE
137         = "http://www.enhydra.org/xmlc/XMLCContext";
138 
139     /**
140      * The servlet context we are associated with.
141      */
142     private ServletContext fServletContext;
143 
144     /*
145      * XMLC factory to use.
146      */
147     private XMLCFactory fFactory;
148 
149     /*
150      * URL encoding of session key configuration. 
151      */
152     private SessionURLEncodingMode fSessionURLEncodingMode;
153 
154     /*
155      * Log various statistics.
156      */
157     private boolean fLogStats;
158 
159     /**
160      * Constructor; instances can only be created through 
161      * ({@see #getContext getContext}.
162      */
163     XMLCContext(ServletContext servletContext,
164                 XMLCFactory factory,
165                 SessionURLEncodingMode sessionURLEncodingMode,
166                 boolean logStats) {
167         fServletContext = servletContext;
168         fFactory = factory;
169         fSessionURLEncodingMode = sessionURLEncodingMode;
170         fLogStats = logStats;
171     }
172 
173     /**
174      * Get the XMLC factory object associated with the context.
175      */
176     public XMLCFactory getXMLCFactory() {
177         return fFactory;
178     }
179 
180     /**
181      * Explictly set XMLC factory.  The XMLC factory is normally defined base
182      * on the servlet configuration parameters. This allows the user to
183      * overide the standard factories.
184      */
185     public void setXMLCFactory(XMLCFactory factory) {
186         fFactory = factory;
187     }
188 
189     /**
190      * Set the session URL encoding mode.  This option is normally set from
191      * the servlet configuration parameters.  This allows the explicit setting
192      * of this option.
193      *
194      * @param mode Constant indicating what mode to use 
195      * @see SessionURLEncodingMode
196      */
197     public void setSessionURLEncoding(SessionURLEncodingMode mode) {
198         fSessionURLEncodingMode = mode;
199     }
200 
201     /*
202      * Get the session URL encoding mode.
203      *
204      * @return The constant indicating what mode should be used for encoding.
205      * @see SessionURLEncodingMode
206      */
207     public SessionURLEncodingMode getSessionURLEncoding() {
208         return fSessionURLEncodingMode;
209     }
210 
211     /**
212      * Determine if a session id should be encoded URLs.
213      */
214     private boolean shouldEncodeSessionId(HttpServletRequest request) {
215         return (((fSessionURLEncodingMode == URL_ENCODING_AUTO)
216                  && request.isRequestedSessionIdFromURL()
217                  && request.isRequestedSessionIdValid())
218                 || (fSessionURLEncodingMode == URL_ENCODING_ALWAYS));
219     }
220 
221     /**
222      * Setup the session URL encoder as required by configuration and
223      * request.
224      */
225     private URLRewriter setupURLRewriter(HttpServletRequest request,
226                                          final HttpServletResponse response) {
227         if (shouldEncodeSessionId(request)) {
228             return new URLRewriter() {
229                     public String rewriteURL(String urlAttrValue) {
230                         return response.encodeURL(urlAttrValue);
231                     }
232                 };
233         } else {
234             return null;
235         }
236     }
237 
238     /**
239      * Set up the encoding using the encoding specified, OutputOptions,
240      * the response, or the XMLC default encoding.  Modifies OutputOptions. 
241      */
242     private void setupEncoding(HttpServletResponse response,
243                                XMLObject document,
244                                OutputOptions options) {
245         
246         // If encoding is already specified, we don't override.
247         if (options.getEncoding() == null) {
248             // Use document default encoding.
249             String encoding = document.getEncoding();
250             if (encoding != null) {
251                 options.setEncoding(encoding);
252             }
253         }
254     }
255 
256     /**
257      * Create an OutputOptions object for a document.  Options are defaulted for
258      * the specified document.  The object maybe then modified as needed to
259      * override the default values.
260      * <P>
261      * The following attributes are set in the object:
262      * <UL>
263      * <LI> encoding
264      * <LI> MIME type - Defaults for document.
265      * <LI> URLRewriter - Set if URL encoding of sessions is enabled.
266      * </UL>
267      *
268      * @param request The servlet request object.  The request is required
269      *  to determine if URL encoding should be done on the document.
270      * @param response The servlet response object.  The response is required
271      *  to set up the URL encoder.
272      * @param document The DOM object to be returned as response.
273      */
274     public OutputOptions createOutputOptions(HttpServletRequest request,
275                                              HttpServletResponse response,
276                                              XMLObject document) {
277         OutputOptions options = DOMFormatter.getDefaultOutputOptions(document);
278 
279         setupEncoding(response, document, options);
280 
281         // Get the default MIME type from object
282         options.setMIMEType(document.getMIMEType());
283 
284         // Enable encoding of session id in URLs, if needed.
285         options.setURLRewriter(setupURLRewriter(request, response));
286         return options;
287     }
288 
289     /**
290      * Write headers, disabling caching if other policy is not already
291      * set.
292      */
293     private void outputHeaders(HttpServletResponse response,
294                                int docLength,
295                                String mimeEncoding,
296                                String mimeType) throws IOException {
297 
298         // Set headers
299         response.setContentLength(docLength);
300         if (mimeType != null) {
301             if (mimeEncoding != null) {
302                 response.setContentType(mimeType + "; charset=" + mimeEncoding);
303             } else {
304                 response.setContentType(mimeType);
305             }
306         }
307         
308         // Disable caching of page unless otherwise specified.
309         if (response.containsHeader("Cache-Control")) {
310             response.setHeader("Cache-Control", "no-cache");
311         }
312         if (response.containsHeader("Expires")) {
313             response.setHeader("Expires", "0");
314         }
315     }
316 
317     /**
318      * Output document statistics.
319      */
320     private void logDocumentStats(XMLObject document) {
321         // Buffer results so it gets logged as a single entry
322         StringWriter buf = new StringWriter(2048);
323         PrintWriter writer = new PrintWriter(buf);
324         DOMStats.printStats("Write " + document.getClass().getName(),
325                             document, 0, writer);
326         writer.flush();
327         fServletContext.log(buf.toString());
328     }
329 
330     /**
331      * Do common operations for writing DOM document.  Sets approriate HTTP
332      * header, including setting of cache-control headers, if not already set.
333      * Handles encoding conversion and output.
334      */
335     private void outputDocument(HttpServletResponse response,
336                                 XMLObject document,
337                                 OutputOptions outputOptions) throws IOException {
338 
339         // Convert to bytes and output
340         DOMFormatter formatter = new DOMFormatter(outputOptions);
341         byte[] docBytes = formatter.toBytes(document);
342 
343         outputHeaders(response, docBytes.length,
344                       outputOptions.getMIMEEncoding(),
345                       outputOptions.getMIMEType());
346 
347         // Log if requested
348         if (fLogStats) {
349             logDocumentStats(document);
350         }
351 
352         // Output
353         OutputStream out = response.getOutputStream();
354         out.write(docBytes);
355         out.flush();
356     }
357 
358     /**
359      * Output an an XMLC document object (DOM). The document is formatted
360      * according to it's type.  The MIME type of the response is automatically
361      * set.
362      *
363      * @param request The servlet request object.  The request is required
364      *  to determine if URL encoding should be done on the document.
365      * @param response The servlet response object.  The 
366      * <tt>Content-Type</tt> and <tt>Content-Length</tt> will be set from
367      * the document..
368      * @param outputFormat Object use to specify options controlling the 
369      *  formatting of the document.
370      * @param document The DOM object to be returned as response.
371      */
372     public void writeDOM(HttpServletRequest request,
373                          HttpServletResponse response,
374                          OutputOptions outputOptions,
375                          XMLObject document) throws IOException {
376         // Copy options so as to not modify when changing encoding.
377         OutputOptions options = new OutputOptions(outputOptions);
378         setupEncoding(response, document, options);
379         outputDocument(response, document, options);
380     }
381 
382     /**
383      * Output an an XMLC document object (DOM). The document is formatted
384      * according to it's type.  The MIME type of the response is automatically
385      * set.
386      *
387      * @param request The servlet request object.  The request is required
388      *  to determine if URL encoding should be done on the document.
389      * @param response The servlet response object.  The 
390      * <tt>Content-Type</tt> and <tt>Content-Length</tt> will be set from
391      * the document..
392      * @param document The DOM object to be returned as response.
393      */
394     public void writeDOM(HttpServletRequest request,
395                          HttpServletResponse response,
396                          XMLObject document) throws IOException {
397         OutputOptions options
398             = createOutputOptions(request, response, document);
399         outputDocument(response, document, options);
400     }
401 
402     /**
403      * Obtain the XMLCContext for the current application, creating it if
404      * it doesn't exist.
405      *
406      * @param servlet A servlet in the application.  The class loader that loaded this
407      *  servlet will be used to load XMLC document classes.
408      * @return The XMLC context object for the application.
409      */
410     public static XMLCContext getContext(HttpServlet servlet) {
411         ServletContext servletContext
412             = servlet.getServletConfig().getServletContext();
413         XMLCContext context = (XMLCContext)servletContext.getAttribute(CONTEXT_ATTRIBUTE);
414         if (context == null) {
415             synchronized (servletContext) {
416                 // Check again now that we are synchronized
417                 context = (XMLCContext)servletContext.getAttribute(CONTEXT_ATTRIBUTE);
418                 if (context == null) {
419                     context = new XMLCContextInit().createContext(servlet);
420                     servletContext.setAttribute(CONTEXT_ATTRIBUTE, context);
421                 }
422             }
423         }
424         return context;
425     }
426 }