All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.enhydra.xml.xmlc.servlet.XMLCContext Maven / Gradle / Ivy

The newest version!
/*
 * Enhydra Java Application Server Project
 * 
 * The contents of this file are subject to the Enhydra Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License on
 * the Enhydra web site ( http://www.enhydra.org/ ).
 * 
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 
 * the License for the specific terms governing rights and limitations
 * under the License.
 * 
 * The Initial Developer of the Enhydra Application Server is Lutris
 * Technologies, Inc. The Enhydra Application Server and portions created
 * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
 * All Rights Reserved.
 * 
 * Contributor(s):
 * 
 * $Id: XMLCContext.java,v 1.7 2005/09/03 05:05:56 jkjome Exp $
 */

package org.enhydra.xml.xmlc.servlet;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.zip.GZIPOutputStream;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.enhydra.xml.dom.DOMStats;
import org.enhydra.xml.io.DOMFormatter;
import org.enhydra.xml.io.OutputOptions;
import org.enhydra.xml.io.URLRewriter;
import org.enhydra.xml.xmlc.XMLCFactory;
import org.enhydra.xml.xmlc.XMLObject;

/**
 * Class to facility the use of XMLC in a HTTP servlet.  This object contains
 * a XMLC factory to create instances of XMLC document classes and methods to
 * output documents.
 *
 * 

* An instance of this object is associated with the ServletContext object * for an application (one per WebApp). The object is created on first use by * {@link #getContext getContext}. It provides a factory for creating XMLC * document class with optional automatic reloading of out of date classes * or recompilation and reloading of classes out-of-date relative to a source * file. Also provided are methods to output a XMLC document as a HTTP response, * with automatic setting of response content type. *

* Instance of this class are configured using the following Servlet * context-wide init parameters: *

    *
  • xmlcReloading - Control's the automatic reloading or * recompilation of XMLC document classes. The following values are * recognized. Note: 'reload' and 'recompile' are only * supported under Enhydra Application Server because the options are * server-specific and the supporting classes no longer exist under XMLC * proper. Even under Enhydra, it is recommended that you use the * server-agnostic deferred parsing by using the "reparse" option. *
      *
    • off - No automatic reloading or recompilation (default). *
    • reload - Automatic reloading of modified class files. *
    • recompile - Automatic recompilation of classes that are * out of date relative to their source files and reloading * of modified class files. *
    • reparse - reparse the ML files when updated. No class reloading * or recompilation is necessary. This requires configuring * xmlcReparseResourceDirs and related params as needed. *
    *
  • xmlcReparseResourceDirs - A list of additional directories to * load the html and meta data files, separated by File.pathSeparator, such * as "/my/project/resources:/my/project/otherresources" on Unix or * "C:\my\project\resources;C:\my\project\otherresources" on Windows. * This is only used if xmlcReloading is set to reparse. *
  • xmlcReparsePackagePrefixes - A list of package prefixes to be * removed while searching for html and meta data files, separated by * File.pathSeparator, such as "my/package/path:my/other/package/path" on * Unix or "my\package\path;my\other\package\path" on Windows. * This is only used if xmlcReloading is set to reparse. *
  • xmlcReparseDefaultMetaDataPath - A path to the default meta data * file relative to the resource dirs. Generally, this would just be * "options.xmlc". Deeper paths would be specified just as in * xmlcReparsePackagePrefixes, only with a single path directly * to the options.xmlc file. * This is only used if xmlcReloading is set to reparse. *
  • xmlcRecompilationHandler - Defines the class of the * recompilation handler. This is only used if xmlcReloading * is set to recompile. Defaults to * org.enhydra.xml.xmlc.reloading.StandardRecompilationHandler * if not set. The specified class must implement the * org.enhydra.xml.xmlc.reloading.RecompilationHandler interface. *
  • xmlcLogger - Defines the logger class. Defaults to * org.enhydra.xml.xmlc.servlet.ServletXMLCLogger if not * set. The specified class must implement * {@link org.enhydra.xml.xmlc.XMLCLogger} and * define a constructor with a signature of ({@link * ServletContext} context, boolean enableInfo, boolean * enableDebug) (same as {@link ServletXMLCLogger}). *
  • xmlcSessionURLEncoding - Controls automatic encoding of session ID in * URLs. *
      *
    • auto - Automatically enable session URL encoding as needed * (default) *
    • always - Always enable session URL encoding. This does not * work on most servers, as they will always try to send a cookie and * not encode a URL if the session is from a cookie. *
    • never - Never enable session URL encoding. *
    *
  • xmlcLogging - Control's what runtime information XMLC logs. * The value is a space-seperated list of the following values: *
      *
    • INFO - Log basic information about notable events, such as * recompiling or reloading classes. *
    • DEBUG - Log debugging information. Mostly related to * recompiling and reloading. *
    • STATS - Log statistics information useful in debugging * performance problems. Currently writes information about each DOM * that is written. This is especially useful in looking at the * how much of a Lazy DOM has been expanded. *
    * The default is INFO. *
  • xmlcCompression - Controls compression of the response * sent to the client. The following values are recognized: *
      *
    • none - Responses are never compressed *
    • gzip - Responses are compressed with gzip if the client * inidcates it can handle gzip compressed content by sending an appropriate * Accept-Encoding HTTP header. *
    * The default is none *
* * @see ServletContext */ public class XMLCContext { /** * Value indicating that URL session encoding should be detected * automatically based on if it was used for the current requested. */ public static final SessionURLEncodingMode URL_ENCODING_AUTO = new SessionURLEncodingMode("auto"); /** * Value indicating that URL session encoding should always be used. */ public static final SessionURLEncodingMode URL_ENCODING_ALWAYS = new SessionURLEncodingMode("always"); /** * Value indicating that URL session encoding should never be used. */ public static final SessionURLEncodingMode URL_ENCODING_NEVER = new SessionURLEncodingMode("never"); /** * Enumerated constant indicating how URL session encoding is to * be handled. */ static public class SessionURLEncodingMode { /** String name of the mode */ private final String fModeName; /** Constructor */ private SessionURLEncodingMode(String modeName) { fModeName = modeName; } /** Get the string value of the mode */ public String toString() { return fModeName; } } /** * Name of ServletContext attribute that contains the instance of this class * for a WebApp. */ public static final String CONTEXT_ATTRIBUTE = "http://www.enhydra.org/xmlc/XMLCContext"; /** * The servlet context we are associated with. */ private ServletContext fServletContext; /* * XMLC factory to use. */ private XMLCFactory fFactory; /* * URL encoding of session key configuration. */ private SessionURLEncodingMode fSessionURLEncodingMode; /* * Log various statistics. */ private boolean fLogStats; /* * Allowed compression types */ private int fCompressionType; /** * Definition of xmlcCompression parameter values as a bit set. */ static final int XMLC_COMPRESS_NONE = 0x00; static final int XMLC_COMPRESS_GZIP = 0x01; /** * Constructor. Must be public because it is accessed via * reflection, but instances should only be created through * {@link #getContext getContext}. */ public XMLCContext(ServletContext servletContext, XMLCFactory factory, SessionURLEncodingMode sessionURLEncodingMode, boolean logStats, int compressionType) { fServletContext = servletContext; fFactory = factory; fSessionURLEncodingMode = sessionURLEncodingMode; fLogStats = logStats; fCompressionType = compressionType; } /** * Get the XMLC factory object associated with the context. */ public XMLCFactory getXMLCFactory() { return fFactory; } /** * Explictly set XMLC factory. The XMLC factory is normally defined base * on the servlet configuration parameters. This allows the user to * overide the standard factories. */ public void setXMLCFactory(XMLCFactory factory) { fFactory = factory; } /** * Set the session URL encoding mode. This option is normally set from * the servlet configuration parameters. This allows the explicit setting * of this option. * * @param mode Constant indicating what mode to use * @see SessionURLEncodingMode */ public void setSessionURLEncoding(SessionURLEncodingMode mode) { fSessionURLEncodingMode = mode; } /* * Get the session URL encoding mode. * * @return The constant indicating what mode should be used for encoding. * @see SessionURLEncodingMode */ public SessionURLEncodingMode getSessionURLEncoding() { return fSessionURLEncodingMode; } /** * Determine if a session id should be encoded URLs. */ protected boolean shouldEncodeSessionId(HttpServletRequest request) { // This checks if the session is from a cookie. This causes these // session id to always be encoded on the first request, as well as // sent in a cookied. If it doesn't come back in a cookie, we keep // encoding it. return (((fSessionURLEncodingMode == URL_ENCODING_AUTO) && request.isRequestedSessionIdValid() && !request.isRequestedSessionIdFromCookie()) || (fSessionURLEncodingMode == URL_ENCODING_ALWAYS)); } /** * Setup the session URL encoder as required by configuration and * request. */ protected URLRewriter setupURLRewriter(HttpServletRequest request, final HttpServletResponse response) { if (shouldEncodeSessionId(request)) { return new URLRewriter() { public String rewriteURL(String urlAttrValue) { return response.encodeURL(urlAttrValue); } }; } else { return null; } } /** * Set up the encoding using the encoding specified, OutputOptions, * the response, or the XMLC default encoding. Modifies OutputOptions. */ private void setupEncoding(HttpServletResponse response, XMLObject document, OutputOptions options) { // If encoding is already specified, we don't override. if (options.getEncoding() == null) { // Use document default encoding. String encoding = document.getEncoding(); if (encoding != null) { options.setEncoding(encoding); } } } /** * Create an OutputOptions object for a document. Options are defaulted for * the specified document. The object maybe then modified as needed to * override the default values. *

* The following attributes are set in the object: *

    *
  • encoding *
  • MIME type - Defaults for document. *
  • URLRewriter - Set if URL encoding of sessions is enabled. *
* * @param request The servlet request object. The request is required * to determine if URL encoding should be done on the document. * @param response The servlet response object. The response is required * to set up the URL encoder. * @param document The DOM object to be returned as response. */ public OutputOptions createOutputOptions(HttpServletRequest request, HttpServletResponse response, XMLObject document) { OutputOptions options = DOMFormatter.getDefaultOutputOptions(document); setupEncoding(response, document, options); // Get the default MIME type from object options.setMIMEType(document.getMIMEType()); // Enable encoding of session id in URLs, if needed. options.setURLRewriter(setupURLRewriter(request, response)); return options; } /** * Write headers, disabling caching if other policy is not already * set. */ private void outputHeaders(HttpServletResponse response, int docLength, String mimeEncoding, String mimeType, boolean compressed) throws IOException { // Set headers response.setContentLength(docLength); if (mimeType != null) { if (mimeEncoding != null) { response.setContentType(mimeType + "; charset=" + mimeEncoding); } else { response.setContentType(mimeType); } } if (compressed) { response.setHeader("Content-Encoding","gzip"); } // Disable caching of page unless otherwise specified. if (response.containsHeader("Cache-Control")) { response.setHeader("Cache-Control", "no-cache"); } if (response.containsHeader("Expires")) { response.setHeader("Expires", "0"); } } /** * Output document statistics. */ private void logDocumentStats(XMLObject document) { // Buffer results so it gets logged as a single entry StringWriter buf = new StringWriter(2048); PrintWriter writer = new PrintWriter(buf); DOMStats.printStats("Write " + document.getClass().getName(), document, 0, writer); writer.flush(); fServletContext.log(buf.toString()); } /** * Do common operations for writing DOM document. Sets approriate HTTP * header, including setting of cache-control headers, if not already set. * Handles encoding conversion and output. */ private void outputDocument(HttpServletResponse response, XMLObject document, OutputOptions outputOptions, boolean compress) throws IOException { // Convert to bytes and output DOMFormatter formatter = new DOMFormatter(outputOptions); byte[] docBytes = null; if (compress) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); GZIPOutputStream os = new GZIPOutputStream(bos); formatter.write(document, os); os.finish(); docBytes = bos.toByteArray(); } else { docBytes = formatter.toBytes(document); } outputHeaders(response, docBytes.length, outputOptions.getMIMEEncoding(), outputOptions.getMIMEType(), compress); // Log if requested if (fLogStats) { logDocumentStats(document); } // Output OutputStream out = response.getOutputStream(); out.write(docBytes); out.flush(); } /** * Output an an XMLC document object (DOM). The document is formatted * according to it's type. The MIME type of the response is automatically * set. * * @param request The servlet request object. The request is required * to determine if URL encoding should be done on the document. * @param response The servlet response object. The * Content-Type and Content-Length will be set from * the document.. * @param outputFormat Object use to specify options controlling the * formatting of the document. * @param document The DOM object to be returned as response. */ public void writeDOM(HttpServletRequest request, HttpServletResponse response, OutputOptions outputOptions, XMLObject document) throws IOException { // Copy options so as to not modify when changing encoding. OutputOptions options = new OutputOptions(outputOptions); setupEncoding(response, document, options); // Get the default MIME type from object if no mimetype is set if (options.getMIMEType() == null) { options.setMIMEType(document.getMIMEType()); } // Enable encoding of session id in URLs, if needed and no URL // rewriter has been specified in the options if (options.getURLRewriter() == null) { options.setURLRewriter(setupURLRewriter(request, response)); } outputDocument(response, document, options, useCompression(request)); } /** * Check if the output should be compressed for this particular request */ private boolean useCompression(HttpServletRequest request) { // check for output compression if it is enabled boolean compress = false; if ((fCompressionType & XMLC_COMPRESS_GZIP) != 0) { String enc = request.getHeader("Accept-Encoding"); if (enc != null) { int pos = enc.indexOf("gzip"); // FIXME: check for a qvalue of 0 and disable compression if found. // The current implementation violates the HTTP 1.1 protocol // by sending gzip compressed content when the client explicitly // *excluded* gzip compression :-( // See RFC 2616, section 14.3 for details compress = (pos > -1); } } return compress; } /** * Output an an XMLC document object (DOM). The document is formatted * according to it's type. The MIME type of the response is automatically * set. * * @param request The servlet request object. The request is required * to determine if URL encoding should be done on the document. * @param response The servlet response object. The * Content-Type and Content-Length will be set from * the document.. * @param document The DOM object to be returned as response. */ public void writeDOM(HttpServletRequest request, HttpServletResponse response, XMLObject document) throws IOException { OutputOptions options = createOutputOptions(request, response, document); outputDocument(response, document, options, useCompression(request)); } /** * Obtain the XMLCContext for the current application, creating it if * it doesn't exist. * * @param servlet A servlet in the application. Used to obtain the servlet context. * @return The XMLC context object for the application. * @see #getContext(ServletContext) */ public static XMLCContext getContext(HttpServlet servlet) { ServletContext servletContext = servlet.getServletContext(); return getContext(servletContext); } /** * Obtain the XMLCContext for the current application, creating it if * it doesn't exist. * * @param servletContext The servlet context of this application. * @return The XMLC context object for the application. */ public static XMLCContext getContext(ServletContext servletContext) { //We used to synchronize here, but that causes problems in some environments (JBoss). //When the application is reloaded the servlet context object changes, but the static //variables used to avoid unnecessary re-checks and synchronization aren't cleared. As //such, after the first initialization (at first use after app server startup), the //XMLCContext object would never be re-set, even if the servlet context object is re-set. //The fix is to forget the initialized check, forget the synchronization, and take the //hit on looking up the XMLCContext object every time this method is called. This is //reasonable and shouldn't cause problems since the only worry about resetting the //XMLCContext object is performance. No real need for synchronization. --Jake XMLCContext context = (XMLCContext)servletContext.getAttribute(CONTEXT_ATTRIBUTE); if (context != null) { return context; } context = new XMLCContextInit().createContext(servletContext); servletContext.setAttribute(CONTEXT_ATTRIBUTE, context); return context; } /** Get the associated servlet context */ protected ServletContext getServletContext() { return fServletContext; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy