
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