You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by mu...@apache.org on 2009/09/28 02:43:39 UTC

svn commit: r819435 [18/23] - in /struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper: ./ compiler/ compiler/tagplugin/ el/ runtime/ security/ servlet/ tagplugins/ tagplugins/jstl/ tagplugins/jstl/core/ util/ xmlparser/

Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/PageContextImpl.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/PageContextImpl.java?rev=819435&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/PageContextImpl.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/PageContextImpl.java Mon Sep 28 00:43:34 2009
@@ -0,0 +1,951 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.runtime;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.Enumeration;
+import java.util.HashMap;
+
+import javax.el.ELContext;
+import javax.el.ExpressionFactory;
+import javax.el.ValueExpression;
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.JspFactory;
+import javax.servlet.jsp.JspWriter;
+import javax.servlet.jsp.PageContext;
+import javax.servlet.jsp.el.ELException;
+import javax.servlet.jsp.el.ExpressionEvaluator;
+import javax.servlet.jsp.el.VariableResolver;
+import javax.servlet.jsp.tagext.BodyContent;
+
+import org.apache.jasper.Constants;
+import org.apache.jasper.compiler.Localizer;
+import org.apache.jasper.el.ELContextImpl;
+import org.apache.jasper.el.ExpressionEvaluatorImpl;
+import org.apache.jasper.el.FunctionMapperImpl;
+import org.apache.jasper.el.VariableResolverImpl;
+import org.apache.jasper.security.SecurityUtil;
+import org.apache.jasper.util.Enumerator;
+
+/**
+ * Implementation of the PageContext class from the JSP spec. Also doubles as a
+ * VariableResolver for the EL.
+ * 
+ * @author Anil K. Vijendran
+ * @author Larry Cable
+ * @author Hans Bergsten
+ * @author Pierre Delisle
+ * @author Mark Roth
+ * @author Jan Luehe
+ * @author Jacob Hookom
+ */
+public class PageContextImpl extends PageContext {
+
+	private static final JspFactory jspf = JspFactory.getDefaultFactory(); 
+
+	private BodyContentImpl[] outs;
+
+	private int depth;
+
+	// per-servlet state
+	private Servlet servlet;
+
+	private ServletConfig config;
+
+	private ServletContext context;
+
+	private JspApplicationContextImpl applicationContext;
+
+	private String errorPageURL;
+
+	// page-scope attributes
+	private transient HashMap<String, Object> attributes;
+
+	// per-request state
+	private transient ServletRequest request;
+
+	private transient ServletResponse response;
+
+	private transient HttpSession session;
+	
+	private transient ELContextImpl elContext;
+
+	private boolean isIncluded;
+	
+	
+	// initial output stream
+	private transient JspWriter out;
+
+	private transient JspWriterImpl baseOut;
+
+	/*
+	 * Constructor.
+	 */
+	PageContextImpl() {
+		this.outs = new BodyContentImpl[0];
+		this.attributes = new HashMap<String, Object>(16);
+		this.depth = -1;
+	}
+
+	public void initialize(Servlet servlet, ServletRequest request,
+			ServletResponse response, String errorPageURL,
+			boolean needsSession, int bufferSize, boolean autoFlush)
+			throws IOException {
+
+		_initialize(servlet, request, response, errorPageURL, needsSession,
+				bufferSize, autoFlush);
+	}
+
+	private void _initialize(Servlet servlet, ServletRequest request,
+			ServletResponse response, String errorPageURL,
+			boolean needsSession, int bufferSize, boolean autoFlush)
+			throws IOException {
+
+		// initialize state
+		this.servlet = servlet;
+		this.config = servlet.getServletConfig();
+		this.context = config.getServletContext();
+		this.errorPageURL = errorPageURL;
+		this.request = request;
+		this.response = response;
+		
+		// initialize application context
+		this.applicationContext = JspApplicationContextImpl.getInstance(context);
+
+		// Setup session (if required)
+		if (request instanceof HttpServletRequest && needsSession)
+			this.session = ((HttpServletRequest) request).getSession();
+		if (needsSession && session == null)
+			throw new IllegalStateException(
+					"Page needs a session and none is available");
+
+		// initialize the initial out ...
+		depth = -1;
+		if (this.baseOut == null) {
+			this.baseOut = new JspWriterImpl(response, bufferSize, autoFlush);
+		} else {
+			this.baseOut.init(response, bufferSize, autoFlush);
+		}
+		this.out = baseOut;
+
+		// register names/values as per spec
+		setAttribute(OUT, this.out);
+		setAttribute(REQUEST, request);
+		setAttribute(RESPONSE, response);
+
+		if (session != null)
+			setAttribute(SESSION, session);
+
+		setAttribute(PAGE, servlet);
+		setAttribute(CONFIG, config);
+		setAttribute(PAGECONTEXT, this);
+		setAttribute(APPLICATION, context);
+
+		isIncluded = request.getAttribute("javax.servlet.include.servlet_path") != null;
+	}
+
+	public void release() {
+		out = baseOut;
+		try {
+			if (isIncluded) {
+				((JspWriterImpl) out).flushBuffer();
+				// push it into the including jspWriter
+			} else {
+				// Old code:
+				// out.flush();
+				// Do not flush the buffer even if we're not included (i.e.
+				// we are the main page. The servlet will flush it and close
+				// the stream.
+				((JspWriterImpl) out).flushBuffer();
+			}
+		} catch (IOException ex) {
+            IllegalStateException ise = new IllegalStateException(Localizer.getMessage("jsp.error.flush"), ex);
+            throw ise;
+		} finally {
+		    servlet = null;
+		    config = null;
+		    context = null;
+		    applicationContext = null;
+		    elContext = null;
+		    errorPageURL = null;
+		    request = null;
+		    response = null;
+		    depth = -1;
+		    baseOut.recycle();
+		    session = null;
+		    attributes.clear();
+        }
+	}
+
+	public Object getAttribute(final String name) {
+
+		if (name == null) {
+			throw new NullPointerException(Localizer
+					.getMessage("jsp.error.attribute.null_name"));
+		}
+
+		if (SecurityUtil.isPackageProtectionEnabled()) {
+			return AccessController.doPrivileged(new PrivilegedAction() {
+				public Object run() {
+					return doGetAttribute(name);
+				}
+			});
+		} else {
+			return doGetAttribute(name);
+		}
+
+	}
+
+	private Object doGetAttribute(String name) {
+		return attributes.get(name);
+	}
+
+	public Object getAttribute(final String name, final int scope) {
+
+		if (name == null) {
+			throw new NullPointerException(Localizer
+					.getMessage("jsp.error.attribute.null_name"));
+		}
+
+		if (SecurityUtil.isPackageProtectionEnabled()) {
+			return AccessController.doPrivileged(new PrivilegedAction() {
+				public Object run() {
+					return doGetAttribute(name, scope);
+				}
+			});
+		} else {
+			return doGetAttribute(name, scope);
+		}
+
+	}
+
+	private Object doGetAttribute(String name, int scope) {
+		switch (scope) {
+		case PAGE_SCOPE:
+			return attributes.get(name);
+
+		case REQUEST_SCOPE:
+			return request.getAttribute(name);
+
+		case SESSION_SCOPE:
+			if (session == null) {
+				throw new IllegalStateException(Localizer
+						.getMessage("jsp.error.page.noSession"));
+			}
+			return session.getAttribute(name);
+
+		case APPLICATION_SCOPE:
+			return context.getAttribute(name);
+
+		default:
+			throw new IllegalArgumentException("Invalid scope");
+		}
+	}
+
+	public void setAttribute(final String name, final Object attribute) {
+
+		if (name == null) {
+			throw new NullPointerException(Localizer
+					.getMessage("jsp.error.attribute.null_name"));
+		}
+
+		if (SecurityUtil.isPackageProtectionEnabled()) {
+			AccessController.doPrivileged(new PrivilegedAction() {
+				public Object run() {
+					doSetAttribute(name, attribute);
+					return null;
+				}
+			});
+		} else {
+			doSetAttribute(name, attribute);
+		}
+	}
+
+	private void doSetAttribute(String name, Object attribute) {
+		if (attribute != null) {
+			attributes.put(name, attribute);
+		} else {
+			removeAttribute(name, PAGE_SCOPE);
+		}
+	}
+
+	public void setAttribute(final String name, final Object o, final int scope) {
+
+		if (name == null) {
+			throw new NullPointerException(Localizer
+					.getMessage("jsp.error.attribute.null_name"));
+		}
+
+		if (SecurityUtil.isPackageProtectionEnabled()) {
+			AccessController.doPrivileged(new PrivilegedAction() {
+				public Object run() {
+					doSetAttribute(name, o, scope);
+					return null;
+				}
+			});
+		} else {
+			doSetAttribute(name, o, scope);
+		}
+
+	}
+
+	private void doSetAttribute(String name, Object o, int scope) {
+		if (o != null) {
+			switch (scope) {
+			case PAGE_SCOPE:
+				attributes.put(name, o);
+				break;
+
+			case REQUEST_SCOPE:
+				request.setAttribute(name, o);
+				break;
+
+			case SESSION_SCOPE:
+				if (session == null) {
+					throw new IllegalStateException(Localizer
+							.getMessage("jsp.error.page.noSession"));
+				}
+				session.setAttribute(name, o);
+				break;
+
+			case APPLICATION_SCOPE:
+				context.setAttribute(name, o);
+				break;
+
+			default:
+				throw new IllegalArgumentException("Invalid scope");
+			}
+		} else {
+			removeAttribute(name, scope);
+		}
+	}
+
+	public void removeAttribute(final String name, final int scope) {
+
+		if (name == null) {
+			throw new NullPointerException(Localizer
+					.getMessage("jsp.error.attribute.null_name"));
+		}
+		if (SecurityUtil.isPackageProtectionEnabled()) {
+			AccessController.doPrivileged(new PrivilegedAction() {
+				public Object run() {
+					doRemoveAttribute(name, scope);
+					return null;
+				}
+			});
+		} else {
+			doRemoveAttribute(name, scope);
+		}
+	}
+
+	private void doRemoveAttribute(String name, int scope) {
+		switch (scope) {
+		case PAGE_SCOPE:
+			attributes.remove(name);
+			break;
+
+		case REQUEST_SCOPE:
+			request.removeAttribute(name);
+			break;
+
+		case SESSION_SCOPE:
+			if (session == null) {
+				throw new IllegalStateException(Localizer
+						.getMessage("jsp.error.page.noSession"));
+			}
+			session.removeAttribute(name);
+			break;
+
+		case APPLICATION_SCOPE:
+			context.removeAttribute(name);
+			break;
+
+		default:
+			throw new IllegalArgumentException("Invalid scope");
+		}
+	}
+
+	public int getAttributesScope(final String name) {
+
+		if (name == null) {
+			throw new NullPointerException(Localizer
+					.getMessage("jsp.error.attribute.null_name"));
+		}
+
+		if (SecurityUtil.isPackageProtectionEnabled()) {
+			return ((Integer) AccessController
+					.doPrivileged(new PrivilegedAction() {
+						public Object run() {
+							return new Integer(doGetAttributeScope(name));
+						}
+					})).intValue();
+		} else {
+			return doGetAttributeScope(name);
+		}
+	}
+
+	private int doGetAttributeScope(String name) {
+		if (attributes.get(name) != null)
+			return PAGE_SCOPE;
+
+		if (request.getAttribute(name) != null)
+			return REQUEST_SCOPE;
+
+		if (session != null) {
+		    try {
+		        if (session.getAttribute(name) != null)
+		            return SESSION_SCOPE;
+	        } catch(IllegalStateException ise) {
+	            // Session has been invalidated.
+		        // Ignore and fall through to application scope.
+		    }
+		}
+
+		if (context.getAttribute(name) != null)
+			return APPLICATION_SCOPE;
+
+		return 0;
+	}
+
+	public Object findAttribute(final String name) {
+		if (SecurityUtil.isPackageProtectionEnabled()) {
+			return AccessController.doPrivileged(new PrivilegedAction() {
+				public Object run() {
+					if (name == null) {
+						throw new NullPointerException(Localizer
+								.getMessage("jsp.error.attribute.null_name"));
+					}
+
+					return doFindAttribute(name);
+				}
+			});
+		} else {
+			if (name == null) {
+				throw new NullPointerException(Localizer
+						.getMessage("jsp.error.attribute.null_name"));
+			}
+
+			return doFindAttribute(name);
+		}
+	}
+
+	private Object doFindAttribute(String name) {
+
+		Object o = attributes.get(name);
+		if (o != null)
+			return o;
+
+		o = request.getAttribute(name);
+		if (o != null)
+			return o;
+
+		if (session != null) {
+		    try {
+		        o = session.getAttribute(name);
+		    } catch(IllegalStateException ise) {
+		        // Session has been invalidated.
+		        // Ignore and fall through to application scope.
+	        }
+			if (o != null)
+				return o;
+		}
+
+		return context.getAttribute(name);
+	}
+
+	public Enumeration<String> getAttributeNamesInScope(final int scope) {
+		if (SecurityUtil.isPackageProtectionEnabled()) {
+			return (Enumeration) AccessController
+					.doPrivileged(new PrivilegedAction() {
+						public Object run() {
+							return doGetAttributeNamesInScope(scope);
+						}
+					});
+		} else {
+			return doGetAttributeNamesInScope(scope);
+		}
+	}
+
+	private Enumeration doGetAttributeNamesInScope(int scope) {
+		switch (scope) {
+		case PAGE_SCOPE:
+			return new Enumerator(attributes.keySet().iterator());
+
+		case REQUEST_SCOPE:
+			return request.getAttributeNames();
+
+		case SESSION_SCOPE:
+			if (session == null) {
+				throw new IllegalStateException(Localizer
+						.getMessage("jsp.error.page.noSession"));
+			}
+			return session.getAttributeNames();
+
+		case APPLICATION_SCOPE:
+			return context.getAttributeNames();
+
+		default:
+			throw new IllegalArgumentException("Invalid scope");
+		}
+	}
+
+	public void removeAttribute(final String name) {
+
+		if (name == null) {
+			throw new NullPointerException(Localizer
+					.getMessage("jsp.error.attribute.null_name"));
+		}
+
+		if (SecurityUtil.isPackageProtectionEnabled()) {
+			AccessController.doPrivileged(new PrivilegedAction() {
+				public Object run() {
+					doRemoveAttribute(name);
+					return null;
+				}
+			});
+		} else {
+			doRemoveAttribute(name);
+		}
+	}
+
+	private void doRemoveAttribute(String name) {
+	    removeAttribute(name, PAGE_SCOPE);
+	    removeAttribute(name, REQUEST_SCOPE);
+	    if( session != null ) {
+	        try {
+	            removeAttribute(name, SESSION_SCOPE);
+	        } catch(IllegalStateException ise) {
+	            // Session has been invalidated.
+	            // Ignore and fall throw to application scope.
+	        }
+	    }
+	    removeAttribute(name, APPLICATION_SCOPE);
+	}
+
+	public JspWriter getOut() {
+		return out;
+	}
+
+	public HttpSession getSession() {
+		return session;
+	}
+
+	public Servlet getServlet() {
+		return servlet;
+	}
+
+	public ServletConfig getServletConfig() {
+		return config;
+	}
+
+	public ServletContext getServletContext() {
+		return config.getServletContext();
+	}
+
+	public ServletRequest getRequest() {
+		return request;
+	}
+
+	public ServletResponse getResponse() {
+		return response;
+	}
+
+	/**
+	 * Returns the exception associated with this page context, if any. <p/>
+	 * Added wrapping for Throwables to avoid ClassCastException: see Bugzilla
+	 * 31171 for details.
+	 * 
+	 * @return The Exception associated with this page context, if any.
+	 */
+	public Exception getException() {
+		Throwable t = JspRuntimeLibrary.getThrowable(request);
+
+		// Only wrap if needed
+		if ((t != null) && (!(t instanceof Exception))) {
+			t = new JspException(t);
+		}
+
+		return (Exception) t;
+	}
+
+	public Object getPage() {
+		return servlet;
+	}
+
+	private final String getAbsolutePathRelativeToContext(String relativeUrlPath) {
+		String path = relativeUrlPath;
+
+		if (!path.startsWith("/")) {
+			String uri = (String) request
+					.getAttribute("javax.servlet.include.servlet_path");
+			if (uri == null)
+				uri = ((HttpServletRequest) request).getServletPath();
+			String baseURI = uri.substring(0, uri.lastIndexOf('/'));
+			path = baseURI + '/' + path;
+		}
+
+		return path;
+	}
+
+	public void include(String relativeUrlPath) throws ServletException,
+			IOException {
+		JspRuntimeLibrary
+				.include(request, response, relativeUrlPath, out, true);
+	}
+
+	public void include(final String relativeUrlPath, final boolean flush)
+			throws ServletException, IOException {
+		if (SecurityUtil.isPackageProtectionEnabled()) {
+			try {
+				AccessController.doPrivileged(new PrivilegedExceptionAction() {
+					public Object run() throws Exception {
+						doInclude(relativeUrlPath, flush);
+						return null;
+					}
+				});
+			} catch (PrivilegedActionException e) {
+				Exception ex = e.getException();
+				if (ex instanceof IOException) {
+					throw (IOException) ex;
+				} else {
+					throw (ServletException) ex;
+				}
+			}
+		} else {
+			doInclude(relativeUrlPath, flush);
+		}
+	}
+
+	private void doInclude(String relativeUrlPath, boolean flush)
+			throws ServletException, IOException {
+		JspRuntimeLibrary.include(request, response, relativeUrlPath, out,
+				flush);
+	}
+
+	public VariableResolver getVariableResolver() {
+		return new VariableResolverImpl(this.getELContext());
+	}
+
+	public void forward(final String relativeUrlPath) throws ServletException,
+			IOException {
+		if (SecurityUtil.isPackageProtectionEnabled()) {
+			try {
+				AccessController.doPrivileged(new PrivilegedExceptionAction() {
+					public Object run() throws Exception {
+						doForward(relativeUrlPath);
+						return null;
+					}
+				});
+			} catch (PrivilegedActionException e) {
+				Exception ex = e.getException();
+				if (ex instanceof IOException) {
+					throw (IOException) ex;
+				} else {
+					throw (ServletException) ex;
+				}
+			}
+		} else {
+			doForward(relativeUrlPath);
+		}
+	}
+
+	private void doForward(String relativeUrlPath) throws ServletException,
+			IOException {
+
+		// JSP.4.5 If the buffer was flushed, throw IllegalStateException
+		try {
+			out.clear();
+		} catch (IOException ex) {
+			IllegalStateException ise = new IllegalStateException(Localizer
+					.getMessage("jsp.error.attempt_to_clear_flushed_buffer"));
+			ise.initCause(ex);
+			throw ise;
+		}
+
+		// Make sure that the response object is not the wrapper for include
+		while (response instanceof ServletResponseWrapperInclude) {
+			response = ((ServletResponseWrapperInclude) response).getResponse();
+		}
+
+		final String path = getAbsolutePathRelativeToContext(relativeUrlPath);
+		String includeUri = (String) request
+				.getAttribute(Constants.INC_SERVLET_PATH);
+
+		if (includeUri != null)
+			request.removeAttribute(Constants.INC_SERVLET_PATH);
+		try {
+			context.getRequestDispatcher(path).forward(request, response);
+		} finally {
+			if (includeUri != null)
+				request.setAttribute(Constants.INC_SERVLET_PATH, includeUri);
+		}
+	}
+
+	public BodyContent pushBody() {
+		return (BodyContent) pushBody(null);
+	}
+
+	public JspWriter pushBody(Writer writer) {
+		depth++;
+		if (depth >= outs.length) {
+			BodyContentImpl[] newOuts = new BodyContentImpl[depth + 1];
+			for (int i = 0; i < outs.length; i++) {
+				newOuts[i] = outs[i];
+			}
+			newOuts[depth] = new BodyContentImpl(out);
+			outs = newOuts;
+		}
+
+		outs[depth].setWriter(writer);
+		out = outs[depth];
+
+		// Update the value of the "out" attribute in the page scope
+		// attribute namespace of this PageContext
+		setAttribute(OUT, out);
+
+		return outs[depth];
+	}
+
+	public JspWriter popBody() {
+		depth--;
+		if (depth >= 0) {
+			out = outs[depth];
+		} else {
+			out = baseOut;
+		}
+
+		// Update the value of the "out" attribute in the page scope
+		// attribute namespace of this PageContext
+		setAttribute(OUT, out);
+
+		return out;
+	}
+
+	/**
+	 * Provides programmatic access to the ExpressionEvaluator. The JSP
+	 * Container must return a valid instance of an ExpressionEvaluator that can
+	 * parse EL expressions.
+	 */
+	public ExpressionEvaluator getExpressionEvaluator() {
+		return new ExpressionEvaluatorImpl(this.applicationContext.getExpressionFactory());
+	}
+
+	public void handlePageException(Exception ex) throws IOException,
+			ServletException {
+		// Should never be called since handleException() called with a
+		// Throwable in the generated servlet.
+		handlePageException((Throwable) ex);
+	}
+
+	public void handlePageException(final Throwable t) throws IOException,
+			ServletException {
+		if (t == null)
+			throw new NullPointerException("null Throwable");
+
+		if (SecurityUtil.isPackageProtectionEnabled()) {
+			try {
+				AccessController.doPrivileged(new PrivilegedExceptionAction() {
+					public Object run() throws Exception {
+						doHandlePageException(t);
+						return null;
+					}
+				});
+			} catch (PrivilegedActionException e) {
+				Exception ex = e.getException();
+				if (ex instanceof IOException) {
+					throw (IOException) ex;
+				} else {
+					throw (ServletException) ex;
+				}
+			}
+		} else {
+			doHandlePageException(t);
+		}
+
+	}
+
+	private void doHandlePageException(Throwable t) throws IOException,
+			ServletException {
+
+		if (errorPageURL != null && !errorPageURL.equals("")) {
+
+			/*
+			 * Set request attributes. Do not set the
+			 * javax.servlet.error.exception attribute here (instead, set in the
+			 * generated servlet code for the error page) in order to prevent
+			 * the ErrorReportValve, which is invoked as part of forwarding the
+			 * request to the error page, from throwing it if the response has
+			 * not been committed (the response will have been committed if the
+			 * error page is a JSP page).
+			 */
+			request.setAttribute("javax.servlet.jsp.jspException", t);
+			request.setAttribute("javax.servlet.error.status_code",
+					new Integer(HttpServletResponse.SC_INTERNAL_SERVER_ERROR));
+			request.setAttribute("javax.servlet.error.request_uri",
+					((HttpServletRequest) request).getRequestURI());
+			request.setAttribute("javax.servlet.error.servlet_name", config
+					.getServletName());
+			try {
+				forward(errorPageURL);
+			} catch (IllegalStateException ise) {
+				include(errorPageURL);
+			}
+
+			// The error page could be inside an include.
+
+			Object newException = request
+					.getAttribute("javax.servlet.error.exception");
+
+			// t==null means the attribute was not set.
+			if ((newException != null) && (newException == t)) {
+				request.removeAttribute("javax.servlet.error.exception");
+			}
+
+			// now clear the error code - to prevent double handling.
+			request.removeAttribute("javax.servlet.error.status_code");
+			request.removeAttribute("javax.servlet.error.request_uri");
+			request.removeAttribute("javax.servlet.error.status_code");
+			request.removeAttribute("javax.servlet.jsp.jspException");
+
+		} else {
+			// Otherwise throw the exception wrapped inside a ServletException.
+			// Set the exception as the root cause in the ServletException
+			// to get a stack trace for the real problem
+			if (t instanceof IOException)
+				throw (IOException) t;
+			if (t instanceof ServletException)
+				throw (ServletException) t;
+			if (t instanceof RuntimeException)
+				throw (RuntimeException) t;
+
+			Throwable rootCause = null;
+			if (t instanceof JspException) {
+				rootCause = ((JspException) t).getRootCause();
+			} else if (t instanceof ELException) {
+				rootCause = ((ELException) t).getRootCause();
+			}
+
+			if (rootCause != null) {
+				throw new ServletException(t.getClass().getName() + ": "
+						+ t.getMessage(), rootCause);
+			}
+
+			throw new ServletException(t);
+		}
+	}
+
+	private static String XmlEscape(String s) {
+		if (s == null)
+			return null;
+		StringBuffer sb = new StringBuffer();
+		for (int i = 0; i < s.length(); i++) {
+			char c = s.charAt(i);
+			if (c == '<') {
+				sb.append("&lt;");
+			} else if (c == '>') {
+				sb.append("&gt;");
+			} else if (c == '\'') {
+				sb.append("&#039;"); // &apos;
+			} else if (c == '&') {
+				sb.append("&amp;");
+			} else if (c == '"') {
+				sb.append("&#034;"); // &quot;
+			} else {
+				sb.append(c);
+			}
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * Proprietary method to evaluate EL expressions. XXX - This method should
+	 * go away once the EL interpreter moves out of JSTL and into its own
+	 * project. For now, this is necessary because the standard machinery is too
+	 * slow.
+	 * 
+	 * @param expression
+	 *            The expression to be evaluated
+	 * @param expectedType
+	 *            The expected resulting type
+	 * @param pageContext
+	 *            The page context
+	 * @param functionMap
+	 *            Maps prefix and name to Method
+	 * @return The result of the evaluation
+	 */
+	public static Object proprietaryEvaluate(final String expression,
+			final Class expectedType, final PageContext pageContext,
+			final ProtectedFunctionMapper functionMap, final boolean escape)
+			throws ELException {
+		Object retValue;
+        final ExpressionFactory exprFactory = jspf.getJspApplicationContext(pageContext.getServletContext()).getExpressionFactory();
+		if (SecurityUtil.isPackageProtectionEnabled()) {
+			try {
+				retValue = AccessController
+						.doPrivileged(new PrivilegedExceptionAction() {
+
+							public Object run() throws Exception {
+                                ELContextImpl ctx = (ELContextImpl) pageContext.getELContext();
+                                ctx.setFunctionMapper(new FunctionMapperImpl(functionMap));
+								ValueExpression ve = exprFactory.createValueExpression(ctx, expression, expectedType);
+                                return ve.getValue(ctx);
+							}
+						});
+			} catch (PrivilegedActionException ex) {
+				Exception realEx = ex.getException();
+				if (realEx instanceof ELException) {
+					throw (ELException) realEx;
+				} else {
+					throw new ELException(realEx);
+				}
+			}
+		} else {
+            ELContextImpl ctx = (ELContextImpl) pageContext.getELContext();
+            ctx.setFunctionMapper(new FunctionMapperImpl(functionMap));
+            ValueExpression ve = exprFactory.createValueExpression(ctx, expression, expectedType);
+            retValue = ve.getValue(ctx);
+		}
+		if (escape && retValue != null) {
+			retValue = XmlEscape(retValue.toString());
+		}
+
+		return retValue;
+	}
+
+	public ELContext getELContext() {
+		if (this.elContext == null) {
+			this.elContext = this.applicationContext.createELContext(this);
+		}
+		return this.elContext;
+	}
+
+}

Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/PerThreadTagHandlerPool.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/PerThreadTagHandlerPool.java?rev=819435&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/PerThreadTagHandlerPool.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/PerThreadTagHandlerPool.java Mon Sep 28 00:43:34 2009
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.runtime;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.Tag;
+
+import org.apache.jasper.Constants;
+
+/**
+ * Thread-local based pool of tag handlers that can be reused.
+ *
+ * @author Jan Luehe
+ * @author Costin Manolache
+ */
+public class PerThreadTagHandlerPool extends TagHandlerPool {
+
+    private int maxSize;
+
+    // For cleanup
+    private Vector perThreadDataVector;
+
+    private ThreadLocal perThread;
+
+    private static class PerThreadData {
+        Tag handlers[];
+        int current;
+    }
+
+    /**
+     * Constructs a tag handler pool with the default capacity.
+     */
+    public PerThreadTagHandlerPool() {
+        super();
+        perThreadDataVector = new Vector();
+    }
+
+    protected void init(ServletConfig config) {
+        maxSize = Constants.MAX_POOL_SIZE;
+        String maxSizeS = getOption(config, OPTION_MAXSIZE, null);
+        if (maxSizeS != null) {
+            maxSize = Integer.parseInt(maxSizeS);
+            if (maxSize < 0) {
+                maxSize = Constants.MAX_POOL_SIZE;
+            }
+        }
+
+        perThread = new ThreadLocal() {
+            protected Object initialValue() {
+                PerThreadData ptd = new PerThreadData();
+                ptd.handlers = new Tag[maxSize];
+                ptd.current = -1;
+                perThreadDataVector.addElement(ptd);
+                return ptd;
+            }
+        };
+    }
+
+    /**
+     * Gets the next available tag handler from this tag handler pool,
+     * instantiating one if this tag handler pool is empty.
+     *
+     * @param handlerClass Tag handler class
+     *
+     * @return Reused or newly instantiated tag handler
+     *
+     * @throws JspException if a tag handler cannot be instantiated
+     */
+    public Tag get(Class handlerClass) throws JspException {
+        PerThreadData ptd = (PerThreadData)perThread.get();
+        if(ptd.current >=0 ) {
+            return ptd.handlers[ptd.current--];
+        } else {
+	    try {
+		return (Tag) handlerClass.newInstance();
+	    } catch (Exception e) {
+		throw new JspException(e.getMessage(), e);
+	    }
+	}
+    }
+
+    /**
+     * Adds the given tag handler to this tag handler pool, unless this tag
+     * handler pool has already reached its capacity, in which case the tag
+     * handler's release() method is called.
+     *
+     * @param handler Tag handler to add to this tag handler pool
+     */
+    public void reuse(Tag handler) {
+        PerThreadData ptd=(PerThreadData)perThread.get();
+	if (ptd.current < (ptd.handlers.length - 1)) {
+	    ptd.handlers[++ptd.current] = handler;
+        } else {
+            handler.release();
+        }
+    }
+
+    /**
+     * Calls the release() method of all tag handlers in this tag handler pool.
+     */
+    public void release() {        
+        Enumeration enumeration = perThreadDataVector.elements();
+        while (enumeration.hasMoreElements()) {
+	    PerThreadData ptd = (PerThreadData)enumeration.nextElement();
+            if (ptd.handlers != null) {
+                for (int i=ptd.current; i>=0; i--) {
+                    if (ptd.handlers[i] != null) {
+                        ptd.handlers[i].release();
+		    }
+                }
+            }
+        }
+    }
+}
+

Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/ProtectedFunctionMapper.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/ProtectedFunctionMapper.java?rev=819435&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/ProtectedFunctionMapper.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/ProtectedFunctionMapper.java Mon Sep 28 00:43:34 2009
@@ -0,0 +1,196 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.runtime;
+
+import java.util.HashMap;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedExceptionAction;
+import java.security.PrivilegedActionException;
+import java.lang.reflect.Method;
+import javax.servlet.jsp.el.FunctionMapper;
+
+import org.apache.jasper.security.SecurityUtil;
+
+/**
+ * Maps EL functions to their Java method counterparts. Keeps the actual Method
+ * objects protected so that JSP pages can't indirectly do reflection.
+ * 
+ * @author Mark Roth
+ * @author Kin-man Chung
+ */
+public final class ProtectedFunctionMapper extends javax.el.FunctionMapper
+        implements FunctionMapper {
+
+    /**
+     * Maps "prefix:name" to java.lang.Method objects.
+     */
+    private HashMap fnmap = null;
+
+    /**
+     * If there is only one function in the map, this is the Method for it.
+     */
+    private Method theMethod = null;
+
+    /**
+     * Constructor has protected access.
+     */
+    private ProtectedFunctionMapper() {
+    }
+
+    /**
+     * Generated Servlet and Tag Handler implementations call this method to
+     * retrieve an instance of the ProtectedFunctionMapper. This is necessary
+     * since generated code does not have access to create instances of classes
+     * in this package.
+     * 
+     * @return A new protected function mapper.
+     */
+    public static ProtectedFunctionMapper getInstance() {
+        ProtectedFunctionMapper funcMapper;
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            funcMapper = (ProtectedFunctionMapper) AccessController
+                    .doPrivileged(new PrivilegedAction() {
+                        public Object run() {
+                            return new ProtectedFunctionMapper();
+                        }
+                    });
+        } else {
+            funcMapper = new ProtectedFunctionMapper();
+        }
+        funcMapper.fnmap = new java.util.HashMap();
+        return funcMapper;
+    }
+
+    /**
+     * Stores a mapping from the given EL function prefix and name to the given
+     * Java method.
+     * 
+     * @param fnQName
+     *            The EL function qualified name (including prefix)
+     * @param c
+     *            The class containing the Java method
+     * @param methodName
+     *            The name of the Java method
+     * @param args
+     *            The arguments of the Java method
+     * @throws RuntimeException
+     *             if no method with the given signature could be found.
+     */
+    public void mapFunction(String fnQName, final Class c,
+            final String methodName, final Class[] args) {
+        java.lang.reflect.Method method;
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            try {
+                method = (java.lang.reflect.Method) AccessController
+                        .doPrivileged(new PrivilegedExceptionAction() {
+
+                            public Object run() throws Exception {
+                                return c.getDeclaredMethod(methodName, args);
+                            }
+                        });
+            } catch (PrivilegedActionException ex) {
+                throw new RuntimeException(
+                        "Invalid function mapping - no such method: "
+                                + ex.getException().getMessage());
+            }
+        } else {
+            try {
+                method = c.getDeclaredMethod(methodName, args);
+            } catch (NoSuchMethodException e) {
+                throw new RuntimeException(
+                        "Invalid function mapping - no such method: "
+                                + e.getMessage());
+            }
+        }
+
+        this.fnmap.put(fnQName, method);
+    }
+
+    /**
+     * Creates an instance for this class, and stores the Method for the given
+     * EL function prefix and name. This method is used for the case when there
+     * is only one function in the EL expression.
+     * 
+     * @param fnQName
+     *            The EL function qualified name (including prefix)
+     * @param c
+     *            The class containing the Java method
+     * @param methodName
+     *            The name of the Java method
+     * @param args
+     *            The arguments of the Java method
+     * @throws RuntimeException
+     *             if no method with the given signature could be found.
+     */
+    public static ProtectedFunctionMapper getMapForFunction(String fnQName,
+            final Class c, final String methodName, final Class[] args) {
+        java.lang.reflect.Method method;
+        ProtectedFunctionMapper funcMapper;
+        if (SecurityUtil.isPackageProtectionEnabled()) {
+            funcMapper = (ProtectedFunctionMapper) AccessController
+                    .doPrivileged(new PrivilegedAction() {
+                        public Object run() {
+                            return new ProtectedFunctionMapper();
+                        }
+                    });
+
+            try {
+                method = (java.lang.reflect.Method) AccessController
+                        .doPrivileged(new PrivilegedExceptionAction() {
+
+                            public Object run() throws Exception {
+                                return c.getDeclaredMethod(methodName, args);
+                            }
+                        });
+            } catch (PrivilegedActionException ex) {
+                throw new RuntimeException(
+                        "Invalid function mapping - no such method: "
+                                + ex.getException().getMessage());
+            }
+        } else {
+            funcMapper = new ProtectedFunctionMapper();
+            try {
+                method = c.getDeclaredMethod(methodName, args);
+            } catch (NoSuchMethodException e) {
+                throw new RuntimeException(
+                        "Invalid function mapping - no such method: "
+                                + e.getMessage());
+            }
+        }
+        funcMapper.theMethod = method;
+        return funcMapper;
+    }
+
+    /**
+     * Resolves the specified local name and prefix into a Java.lang.Method.
+     * Returns null if the prefix and local name are not found.
+     * 
+     * @param prefix
+     *            the prefix of the function
+     * @param localName
+     *            the short name of the function
+     * @return the result of the method mapping. Null means no entry found.
+     */
+    public Method resolveFunction(String prefix, String localName) {
+        if (this.fnmap != null) {
+            return (Method) this.fnmap.get(prefix + ":" + localName);
+        }
+        return theMethod;
+    }
+}

Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/ServletResponseWrapperInclude.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/ServletResponseWrapperInclude.java?rev=819435&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/ServletResponseWrapperInclude.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/ServletResponseWrapperInclude.java Mon Sep 28 00:43:34 2009
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.runtime;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpServletResponseWrapper;
+import javax.servlet.jsp.JspWriter;
+
+/**
+ * ServletResponseWrapper used by the JSP 'include' action.
+ *
+ * This wrapper response object is passed to RequestDispatcher.include(), so
+ * that the output of the included resource is appended to that of the
+ * including page.
+ *
+ * @author Pierre Delisle
+ */
+
+public class ServletResponseWrapperInclude extends HttpServletResponseWrapper {
+
+    /**
+     * PrintWriter which appends to the JspWriter of the including page.
+     */
+    private PrintWriter printWriter;
+
+    private JspWriter jspWriter;
+
+    public ServletResponseWrapperInclude(ServletResponse response, 
+					 JspWriter jspWriter) {
+	super((HttpServletResponse)response);
+	this.printWriter = new PrintWriter(jspWriter);
+	this.jspWriter = jspWriter;
+    }
+
+    /**
+     * Returns a wrapper around the JspWriter of the including page.
+     */
+    public PrintWriter getWriter() throws IOException {
+	return printWriter;
+    }
+
+    public ServletOutputStream getOutputStream() throws IOException {
+	throw new IllegalStateException();
+    }
+
+    /**
+     * Clears the output buffer of the JspWriter associated with the including
+     * page.
+     */
+    public void resetBuffer() {
+	try {
+	    jspWriter.clearBuffer();
+	} catch (IOException ioe) {
+	}
+    }
+}

Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/TagHandlerPool.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/TagHandlerPool.java?rev=819435&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/TagHandlerPool.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/runtime/TagHandlerPool.java Mon Sep 28 00:43:34 2009
@@ -0,0 +1,191 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.runtime;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.jsp.JspException;
+import javax.servlet.jsp.tagext.Tag;
+
+import org.apache.AnnotationProcessor;
+import org.apache.jasper.Constants;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
+/**
+ * Pool of tag handlers that can be reused.
+ *
+ * @author Jan Luehe
+ */
+public class TagHandlerPool {
+
+    private Tag[] handlers;
+
+    public static String OPTION_TAGPOOL="tagpoolClassName";
+    public static String OPTION_MAXSIZE="tagpoolMaxSize";
+
+    private Log log = LogFactory.getLog(TagHandlerPool.class);
+    
+    // index of next available tag handler
+    private int current;
+    protected AnnotationProcessor annotationProcessor = null;
+
+    public static TagHandlerPool getTagHandlerPool( ServletConfig config) {
+        TagHandlerPool result=null;
+
+        String tpClassName=getOption( config, OPTION_TAGPOOL, null);
+        if( tpClassName != null ) {
+            try {
+                Class c=Class.forName( tpClassName );
+                result=(TagHandlerPool)c.newInstance();
+            } catch (Exception e) {
+                e.printStackTrace();
+                result=null;
+            }
+        }
+        if( result==null ) result=new TagHandlerPool();
+        result.init(config);
+
+        return result;
+    }
+
+    protected void init( ServletConfig config ) {
+        int maxSize=-1;
+        String maxSizeS=getOption(config, OPTION_MAXSIZE, null);
+        if( maxSizeS != null ) {
+            try {
+                maxSize=Integer.parseInt(maxSizeS);
+            } catch( Exception ex) {
+                maxSize=-1;
+            }
+        }
+        if( maxSize <0  ) {
+            maxSize=Constants.MAX_POOL_SIZE;
+        }
+        this.handlers = new Tag[maxSize];
+        this.current = -1;
+        this.annotationProcessor = 
+            (AnnotationProcessor) config.getServletContext().getAttribute(AnnotationProcessor.class.getName());
+    }
+
+    /**
+     * Constructs a tag handler pool with the default capacity.
+     */
+    public TagHandlerPool() {
+	// Nothing - jasper generated servlets call the other constructor,
+        // this should be used in future + init .
+    }
+
+    /**
+     * Constructs a tag handler pool with the given capacity.
+     *
+     * @param capacity Tag handler pool capacity
+     * @deprecated Use static getTagHandlerPool
+     */
+    public TagHandlerPool(int capacity) {
+	this.handlers = new Tag[capacity];
+	this.current = -1;
+    }
+
+    /**
+     * Gets the next available tag handler from this tag handler pool,
+     * instantiating one if this tag handler pool is empty.
+     *
+     * @param handlerClass Tag handler class
+     *
+     * @return Reused or newly instantiated tag handler
+     *
+     * @throws JspException if a tag handler cannot be instantiated
+     */
+    public Tag get(Class handlerClass) throws JspException {
+	Tag handler = null;
+        synchronized( this ) {
+            if (current >= 0) {
+                handler = handlers[current--];
+                return handler;
+            }
+        }
+
+        // Out of sync block - there is no need for other threads to
+        // wait for us to construct a tag for this thread.
+        try {
+            Tag instance = (Tag) handlerClass.newInstance();
+            AnnotationHelper.postConstruct(annotationProcessor, instance);
+            return instance;
+        } catch (Exception e) {
+            throw new JspException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Adds the given tag handler to this tag handler pool, unless this tag
+     * handler pool has already reached its capacity, in which case the tag
+     * handler's release() method is called.
+     *
+     * @param handler Tag handler to add to this tag handler pool
+     */
+    public void reuse(Tag handler) {
+        synchronized( this ) {
+            if (current < (handlers.length - 1)) {
+                handlers[++current] = handler;
+                return;
+            }
+        }
+        // There is no need for other threads to wait for us to release
+        handler.release();
+        if (annotationProcessor != null) {
+            try {
+                AnnotationHelper.preDestroy(annotationProcessor, handler);
+            } catch (Exception e) {
+                log.warn("Error processing preDestroy on tag instance of " 
+                        + handler.getClass().getName(), e);
+            }
+        }
+    }
+
+    /**
+     * Calls the release() method of all available tag handlers in this tag
+     * handler pool.
+     */
+    public synchronized void release() {
+        for (int i = current; i >= 0; i--) {
+            handlers[i].release();
+            if (annotationProcessor != null) {
+                try {
+                    AnnotationHelper.preDestroy(annotationProcessor, handlers[i]);
+                } catch (Exception e) {
+                    log.warn("Error processing preDestroy on tag instance of " 
+                            + handlers[i].getClass().getName(), e);
+                }
+            }
+        }
+    }
+
+    protected static String getOption( ServletConfig config, String name, String defaultV) {
+        if( config == null ) return defaultV;
+
+        String value=config.getInitParameter(name);
+        if( value != null ) return value;
+        if( config.getServletContext() ==null )
+            return defaultV;
+        value=config.getServletContext().getInitParameter(name);
+        if( value!=null ) return value;
+        return defaultV;
+    }
+
+}
+

Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/security/SecurityClassLoad.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/security/SecurityClassLoad.java?rev=819435&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/security/SecurityClassLoad.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/security/SecurityClassLoad.java Mon Sep 28 00:43:34 2009
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.jasper.security;
+
+/**
+ * Static class used to preload java classes when using the
+ * Java SecurityManager so that the defineClassInPackage
+ * RuntimePermission does not trigger an AccessControlException.
+ *
+ * @author Jean-Francois Arcand
+ */
+
+public final class SecurityClassLoad {
+
+    private static org.apache.juli.logging.Log log=
+        org.apache.juli.logging.LogFactory.getLog( SecurityClassLoad.class );
+
+    public static void securityClassLoad(ClassLoader loader){
+
+        if( System.getSecurityManager() == null ){
+            return;
+        }
+
+        String basePackage = "org.apache.jasper.";
+        try {
+            loader.loadClass( basePackage +
+                "runtime.JspFactoryImpl$PrivilegedGetPageContext");
+            loader.loadClass( basePackage +
+                "runtime.JspFactoryImpl$PrivilegedReleasePageContext");
+
+            loader.loadClass( basePackage +
+                "runtime.JspRuntimeLibrary");
+            loader.loadClass( basePackage +
+                "runtime.JspRuntimeLibrary$PrivilegedIntrospectHelper");
+            
+            loader.loadClass( basePackage +
+                "runtime.ServletResponseWrapperInclude");
+            loader.loadClass( basePackage +
+                "runtime.TagHandlerPool");
+            loader.loadClass( basePackage +
+                "runtime.JspFragmentHelper");
+
+            loader.loadClass( basePackage +
+                "runtime.ProtectedFunctionMapper");
+            loader.loadClass( basePackage +
+                "runtime.ProtectedFunctionMapper$1");
+            loader.loadClass( basePackage +
+                "runtime.ProtectedFunctionMapper$2"); 
+            loader.loadClass( basePackage +
+                "runtime.ProtectedFunctionMapper$3");
+            loader.loadClass( basePackage +
+                "runtime.ProtectedFunctionMapper$4"); 
+
+            loader.loadClass( basePackage +
+                "runtime.PageContextImpl");      
+            loader.loadClass( basePackage +
+                "runtime.PageContextImpl$1");      
+            loader.loadClass( basePackage +
+                "runtime.PageContextImpl$2");      
+            loader.loadClass( basePackage +
+                "runtime.PageContextImpl$3");      
+            loader.loadClass( basePackage +
+                "runtime.PageContextImpl$4");      
+            loader.loadClass( basePackage +
+                "runtime.PageContextImpl$5");      
+            loader.loadClass( basePackage +
+                "runtime.PageContextImpl$6");      
+            loader.loadClass( basePackage +
+                "runtime.PageContextImpl$7");      
+            loader.loadClass( basePackage +
+                "runtime.PageContextImpl$8");      
+            loader.loadClass( basePackage +
+                "runtime.PageContextImpl$9");      
+            loader.loadClass( basePackage +
+                "runtime.PageContextImpl$10");      
+            loader.loadClass( basePackage +
+                "runtime.PageContextImpl$11");      
+            loader.loadClass( basePackage +
+                "runtime.PageContextImpl$12");      
+            loader.loadClass( basePackage +
+                "runtime.PageContextImpl$13");      
+
+            loader.loadClass( basePackage +
+                "runtime.JspContextWrapper");   
+
+            loader.loadClass( basePackage +
+                "servlet.JspServletWrapper");
+
+            loader.loadClass( basePackage +
+                "runtime.JspWriterImpl$1");
+        } catch (ClassNotFoundException ex) {
+            log.error("SecurityClassLoad", ex);
+        }
+    }
+}

Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/security/SecurityUtil.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/security/SecurityUtil.java?rev=819435&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/security/SecurityUtil.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/security/SecurityUtil.java Mon Sep 28 00:43:34 2009
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jasper.security;
+
+import org.apache.jasper.Constants;
+
+/**
+ * Util class for Security related operations.
+ *
+ * @author Jean-Francois Arcand
+ */
+
+public final class SecurityUtil{
+    
+    private static boolean packageDefinitionEnabled =  
+         System.getProperty("package.definition") == null ? false : true;
+    
+    /**
+     * Return the <code>SecurityManager</code> only if Security is enabled AND
+     * package protection mechanism is enabled.
+     */
+    public static boolean isPackageProtectionEnabled(){
+        if (packageDefinitionEnabled && Constants.IS_SECURITY_ENABLED){
+            return true;
+        }
+        return false;
+    }
+    
+
+    /**
+     * Filter the specified message string for characters that are sensitive
+     * in HTML.  This avoids potential attacks caused by including JavaScript
+     * codes in the request URL that is often reported in error messages.
+     *
+     * @param message The message string to be filtered
+     */
+    public static String filter(String message) {
+
+        if (message == null)
+            return (null);
+
+        char content[] = new char[message.length()];
+        message.getChars(0, message.length(), content, 0);
+        StringBuffer result = new StringBuffer(content.length + 50);
+        for (int i = 0; i < content.length; i++) {
+            switch (content[i]) {
+            case '<':
+                result.append("&lt;");
+                break;
+            case '>':
+                result.append("&gt;");
+                break;
+            case '&':
+                result.append("&amp;");
+                break;
+            case '"':
+                result.append("&quot;");
+                break;
+            default:
+                result.append(content[i]);
+            }
+        }
+        return (result.toString());
+
+    }
+
+}

Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/servlet/JasperLoader.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/servlet/JasperLoader.java?rev=819435&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/servlet/JasperLoader.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/servlet/JasperLoader.java Mon Sep 28 00:43:34 2009
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.servlet;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.CodeSource;
+import java.security.PermissionCollection;
+
+import org.apache.jasper.Constants;
+
+/**
+ * Class loader for loading servlet class files (corresponding to JSP files) 
+ * and tag handler class files (corresponding to tag files).
+ *
+ * @author Anil K. Vijendran
+ * @author Harish Prabandham
+ * @author Jean-Francois Arcand
+ */
+public class JasperLoader extends URLClassLoader {
+
+    private PermissionCollection permissionCollection;
+    private CodeSource codeSource;
+    private String className;
+    private ClassLoader parent;
+    private SecurityManager securityManager;
+
+    public JasperLoader(URL[] urls, ClassLoader parent,
+			PermissionCollection permissionCollection,
+			CodeSource codeSource) {
+	super(urls, parent);
+	this.permissionCollection = permissionCollection;
+	this.codeSource = codeSource;
+	this.parent = parent;
+	this.securityManager = System.getSecurityManager();
+    }
+
+    /**
+     * Load the class with the specified name.  This method searches for
+     * classes in the same manner as <code>loadClass(String, boolean)</code>
+     * with <code>false</code> as the second argument.
+     *
+     * @param name Name of the class to be loaded
+     *
+     * @exception ClassNotFoundException if the class was not found
+     */
+    public Class loadClass(String name) throws ClassNotFoundException {
+
+        return (loadClass(name, false));
+    }
+
+    /**
+     * Load the class with the specified name, searching using the following
+     * algorithm until it finds and returns the class.  If the class cannot
+     * be found, returns <code>ClassNotFoundException</code>.
+     * <ul>
+     * <li>Call <code>findLoadedClass(String)</code> to check if the
+     *     class has already been loaded.  If it has, the same
+     *     <code>Class</code> object is returned.</li>
+     * <li>If the <code>delegate</code> property is set to <code>true</code>,
+     *     call the <code>loadClass()</code> method of the parent class
+     *     loader, if any.</li>            
+     * <li>Call <code>findClass()</code> to find this class in our locally
+     *     defined repositories.</li>      
+     * <li>Call the <code>loadClass()</code> method of our parent
+     *     class loader, if any.</li>      
+     * </ul>
+     * If the class was found using the above steps, and the
+     * <code>resolve</code> flag is <code>true</code>, this method will then
+     * call <code>resolveClass(Class)</code> on the resulting Class object.
+     *                                     
+     * @param name Name of the class to be loaded
+     * @param resolve If <code>true</code> then resolve the class
+     *                                     
+     * @exception ClassNotFoundException if the class was not found
+     */                                    
+    public Class loadClass(final String name, boolean resolve)
+        throws ClassNotFoundException {
+
+        Class clazz = null;                
+                                           
+        // (0) Check our previously loaded class cache
+        clazz = findLoadedClass(name);     
+        if (clazz != null) {               
+            if (resolve)                   
+                resolveClass(clazz);       
+            return (clazz);        
+        }                          
+                          
+        // (.5) Permission to access this class when using a SecurityManager
+        if (securityManager != null) {     
+            int dot = name.lastIndexOf('.');
+            if (dot >= 0) {                
+                try {        
+                    // Do not call the security manager since by default, we grant that package.
+                    if (!"org.apache.jasper.runtime".equalsIgnoreCase(name.substring(0,dot))){
+                        securityManager.checkPackageAccess(name.substring(0,dot));
+                    }
+                } catch (SecurityException se) {
+                    String error = "Security Violation, attempt to use " +
+                        "Restricted Class: " + name;
+                    se.printStackTrace();
+                    throw new ClassNotFoundException(error);
+                }                          
+            }                              
+        }
+
+        if( !name.startsWith(Constants.JSP_PACKAGE_NAME + '.') ) {
+            // Class is not in org.apache.jsp, therefore, have our
+            // parent load it
+            clazz = parent.loadClass(name);            
+	    if( resolve )
+		resolveClass(clazz);
+	    return clazz;
+	}
+
+	return findClass(name);
+    }
+
+    
+    /**
+     * Delegate to parent
+     * 
+     * @see java.lang.ClassLoader#getResourceAsStream(java.lang.String)
+     */
+    public InputStream getResourceAsStream(String name) {
+        InputStream is = parent.getResourceAsStream(name);
+        if (is == null) {
+            URL url = findResource(name);
+            if (url != null) {
+                try {
+                    is = url.openStream();
+                } catch (IOException e) {
+                    is = null;
+                }
+            }
+        }
+        return is;
+    }
+    
+    
+    /**
+     * Get the Permissions for a CodeSource.
+     *
+     * Since this ClassLoader is only used for a JSP page in
+     * a web application context, we just return our preset
+     * PermissionCollection for the web app context.
+     *
+     * @param codeSource Code source where the code was loaded from
+     * @return PermissionCollection for CodeSource
+     */
+    public final PermissionCollection getPermissions(CodeSource codeSource) {
+        return permissionCollection;
+    }
+}
\ No newline at end of file

Added: struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/servlet/JspCServletContext.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/servlet/JspCServletContext.java?rev=819435&view=auto
==============================================================================
--- struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/servlet/JspCServletContext.java (added)
+++ struts/struts2/trunk/plugins/embeddedjsp/src/main/java/org/apache/struts2/jasper/servlet/JspCServletContext.java Mon Sep 28 00:43:34 2009
@@ -0,0 +1,441 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jasper.servlet;
+
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Set;
+import java.util.Vector;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+
+
+/**
+ * Simple <code>ServletContext</code> implementation without
+ * HTTP-specific methods.
+ *
+ * @author Peter Rossbach (pr@webapp.de)
+ */
+
+public class JspCServletContext implements ServletContext {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    /**
+     * Servlet context attributes.
+     */
+    protected Hashtable myAttributes;
+
+
+    /**
+     * The log writer we will write log messages to.
+     */
+    protected PrintWriter myLogWriter;
+
+
+    /**
+     * The base URL (document root) for this context.
+     */
+    protected URL myResourceBaseURL;
+
+
+    // ----------------------------------------------------------- Constructors
+
+
+    /**
+     * Create a new instance of this ServletContext implementation.
+     *
+     * @param aLogWriter PrintWriter which is used for <code>log()</code> calls
+     * @param aResourceBaseURL Resource base URL
+     */
+    public JspCServletContext(PrintWriter aLogWriter, URL aResourceBaseURL) {
+
+        myAttributes = new Hashtable();
+        myLogWriter = aLogWriter;
+        myResourceBaseURL = aResourceBaseURL;
+
+    }
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Return the specified context attribute, if any.
+     *
+     * @param name Name of the requested attribute
+     */
+    public Object getAttribute(String name) {
+
+        return (myAttributes.get(name));
+
+    }
+
+
+    /**
+     * Return an enumeration of context attribute names.
+     */
+    public Enumeration getAttributeNames() {
+
+        return (myAttributes.keys());
+
+    }
+
+
+    /**
+     * Return the servlet context for the specified path.
+     *
+     * @param uripath Server-relative path starting with '/'
+     */
+    public ServletContext getContext(String uripath) {
+
+        return (null);
+
+    }
+
+
+    /**
+     * Return the context path.
+     */
+    public String getContextPath() {
+
+        return (null);
+
+    }
+
+
+    /**
+     * Return the specified context initialization parameter.
+     *
+     * @param name Name of the requested parameter
+     */
+    public String getInitParameter(String name) {
+
+        return (null);
+
+    }
+
+
+    /**
+     * Return an enumeration of the names of context initialization
+     * parameters.
+     */
+    public Enumeration getInitParameterNames() {
+
+        return (new Vector().elements());
+
+    }
+
+
+    /**
+     * Return the Servlet API major version number.
+     */
+    public int getMajorVersion() {
+
+        return (2);
+
+    }
+
+
+    /**
+     * Return the MIME type for the specified filename.
+     *
+     * @param file Filename whose MIME type is requested
+     */
+    public String getMimeType(String file) {
+
+        return (null);
+
+    }
+
+
+    /**
+     * Return the Servlet API minor version number.
+     */
+    public int getMinorVersion() {
+
+        return (3);
+
+    }
+
+
+    /**
+     * Return a request dispatcher for the specified servlet name.
+     *
+     * @param name Name of the requested servlet
+     */
+    public RequestDispatcher getNamedDispatcher(String name) {
+
+        return (null);
+
+    }
+
+
+    /**
+     * Return the real path for the specified context-relative
+     * virtual path.
+     *
+     * @param path The context-relative virtual path to resolve
+     */
+    public String getRealPath(String path) {
+
+        if (!myResourceBaseURL.getProtocol().equals("file"))
+            return (null);
+        if (!path.startsWith("/"))
+            return (null);
+        try {
+            return
+                (getResource(path).getFile().replace('/', File.separatorChar));
+        } catch (Throwable t) {
+            return (null);
+        }
+
+    }
+            
+            
+    /**
+     * Return a request dispatcher for the specified context-relative path.
+     *
+     * @param path Context-relative path for which to acquire a dispatcher
+     */
+    public RequestDispatcher getRequestDispatcher(String path) {
+
+        return (null);
+
+    }
+
+
+    /**
+     * Return a URL object of a resource that is mapped to the
+     * specified context-relative path.
+     *
+     * @param path Context-relative path of the desired resource
+     *
+     * @exception MalformedURLException if the resource path is
+     *  not properly formed
+     */
+    public URL getResource(String path) throws MalformedURLException {
+
+        if (!path.startsWith("/"))
+            throw new MalformedURLException("Path '" + path +
+                                            "' does not start with '/'");
+        URL url = new URL(myResourceBaseURL, path.substring(1));
+        InputStream is = null;
+        try {
+            is = url.openStream();
+        } catch (Throwable t) {
+            url = null;
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (Throwable t2) {
+                    // Ignore
+                }
+            }
+        }
+        return url;
+
+    }
+
+
+    /**
+     * Return an InputStream allowing access to the resource at the
+     * specified context-relative path.
+     *
+     * @param path Context-relative path of the desired resource
+     */
+    public InputStream getResourceAsStream(String path) {
+
+        try {
+            return (getResource(path).openStream());
+        } catch (Throwable t) {
+            return (null);
+        }
+
+    }
+
+
+    /**
+     * Return the set of resource paths for the "directory" at the
+     * specified context path.
+     *
+     * @param path Context-relative base path
+     */
+    public Set getResourcePaths(String path) {
+
+        Set thePaths = new HashSet();
+        if (!path.endsWith("/"))
+            path += "/";
+        String basePath = getRealPath(path);
+        if (basePath == null)
+            return (thePaths);
+        File theBaseDir = new File(basePath);
+        if (!theBaseDir.exists() || !theBaseDir.isDirectory())
+            return (thePaths);
+        String theFiles[] = theBaseDir.list();
+        for (int i = 0; i < theFiles.length; i++) {
+            File testFile = new File(basePath + File.separator + theFiles[i]);
+            if (testFile.isFile())
+                thePaths.add(path + theFiles[i]);
+            else if (testFile.isDirectory())
+                thePaths.add(path + theFiles[i] + "/");
+        }
+        return (thePaths);
+
+    }
+
+
+    /**
+     * Return descriptive information about this server.
+     */
+    public String getServerInfo() {
+
+        return ("JspCServletContext/1.0");
+
+    }
+
+
+    /**
+     * Return a null reference for the specified servlet name.
+     *
+     * @param name Name of the requested servlet
+     *
+     * @deprecated This method has been deprecated with no replacement
+     */
+    public Servlet getServlet(String name) throws ServletException {
+
+        return (null);
+
+    }
+
+
+    /**
+     * Return the name of this servlet context.
+     */
+    public String getServletContextName() {
+
+        return (getServerInfo());
+
+    }
+
+
+    /**
+     * Return an empty enumeration of servlet names.
+     *
+     * @deprecated This method has been deprecated with no replacement
+     */
+    public Enumeration getServletNames() {
+
+        return (new Vector().elements());
+
+    }
+
+
+    /**
+     * Return an empty enumeration of servlets.
+     *
+     * @deprecated This method has been deprecated with no replacement
+     */
+    public Enumeration getServlets() {
+
+        return (new Vector().elements());
+
+    }
+
+
+    /**
+     * Log the specified message.
+     *
+     * @param message The message to be logged
+     */
+    public void log(String message) {
+
+        myLogWriter.println(message);
+
+    }
+
+
+    /**
+     * Log the specified message and exception.
+     *
+     * @param exception The exception to be logged
+     * @param message The message to be logged
+     *
+     * @deprecated Use log(String,Throwable) instead
+     */
+    public void log(Exception exception, String message) {
+
+        log(message, exception);
+
+    }
+
+
+    /**
+     * Log the specified message and exception.
+     *
+     * @param message The message to be logged
+     * @param exception The exception to be logged
+     */
+    public void log(String message, Throwable exception) {
+
+        myLogWriter.println(message);
+        exception.printStackTrace(myLogWriter);
+
+    }
+
+
+    /**
+     * Remove the specified context attribute.
+     *
+     * @param name Name of the attribute to remove
+     */
+    public void removeAttribute(String name) {
+
+        myAttributes.remove(name);
+
+    }
+
+
+    /**
+     * Set or replace the specified context attribute.
+     *
+     * @param name Name of the context attribute to set
+     * @param value Corresponding attribute value
+     */
+    public void setAttribute(String name, Object value) {
+
+        myAttributes.put(name, value);
+
+    }
+
+
+
+}