import java.io.IOException;
+import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.Container;
+import org.apache.catalina.Context;
import org.apache.catalina.Wrapper;
import org.apache.catalina.comet.CometEvent;
+import org.apache.catalina.connector.ClientAbortException;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
+import org.apache.catalina.deploy.ErrorPage;
import org.apache.catalina.valves.ValveBase;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.buf.MessageBytes;
/**
* @version $Id$
*/
-final class StandardContextValve
- extends ValveBase {
+final class StandardContextValve extends ValveBase {
+
+ private static final Log log = LogFactory.getLog(StandardHostValve.class);
//------------------------------------------------------ Constructor
public StandardContextValve() {
// Disallow any direct access to resources under WEB-INF or META-INF
MessageBytes requestPathMB = request.getRequestPathMB();
if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0))
- || (requestPathMB.equalsIgnoreCase("/META-INF"))
- || (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0))
- || (requestPathMB.equalsIgnoreCase("/WEB-INF"))) {
- error(response, HttpServletResponse.SC_NOT_FOUND);
+ || (requestPathMB.equalsIgnoreCase("/META-INF"))
+ || (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0))
+ || (requestPathMB.equalsIgnoreCase("/WEB-INF"))) {
+ error(request, response, HttpServletResponse.SC_NOT_FOUND);
return;
}
// Select the Wrapper to be used for this Request
Wrapper wrapper = request.getWrapper();
if (wrapper == null) {
- error(response, HttpServletResponse.SC_NOT_FOUND);
+ error(request, response, HttpServletResponse.SC_NOT_FOUND);
return;
} else if (wrapper.isUnavailable()) {
// May be as a result of a reload, try and find the new wrapper
wrapper = (Wrapper) container.findChild(wrapper.getName());
if (wrapper == null) {
- error(response, HttpServletResponse.SC_NOT_FOUND);
+ error(request, response, HttpServletResponse.SC_NOT_FOUND);
return;
}
}
container.getLogger().error(sm.getString(
"standardContextValve.acknowledgeException"), ioe);
request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, ioe);
- error(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ error(request, response,
+ HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
// running request.
StandardContext c = context;
if (c != null && c.getState().isAvailable()) {
+ // Error page processing
+ response.setSuspended(false);
+
+ Throwable t = (Throwable) request.getAttribute(
+ RequestDispatcher.ERROR_EXCEPTION);
+
+ if (t != null) {
+ throwable(request, response, t);
+ } else {
+ status(request, response);
+ }
+
context.fireRequestDestroyEvent(request);
}
}
// Select the Wrapper to be used for this Request
Wrapper wrapper = request.getWrapper();
- // Normal request processing
- // FIXME: Firing request listeners could be an addition to the core
- // comet API
-
- //if (context.fireRequestInitEvent(request)) {
- wrapper.getPipeline().getFirst().event(request, response, event);
- // context.fireRequestDestroyEvent(request);
- //}
+ wrapper.getPipeline().getFirst().event(request, response, event);
+
+ // Error page processing
+ response.setSuspended(false);
+
+ Throwable t = (Throwable) request.getAttribute(
+ RequestDispatcher.ERROR_EXCEPTION);
+
+ if (t != null) {
+ throwable(request, response, t);
+ } else {
+ status(request, response);
+ }
}
/**
- * Report an error for the specified resource. FIXME: We
- * should really be using the error reporting settings for this web
- * application, but currently that code runs at the wrapper level rather
- * than the context level.
+ * Report an error for the specified resource.
*
* @param response The response we are creating
*/
- private void error(HttpServletResponse response, int status) {
+ private void error(Request request, Response response, int status) {
+ context.fireRequestInitEvent(request);
+
try {
response.sendError(status);
} catch (IllegalStateException e) {
// Ignore
}
+ response.setSuspended(false);
+ status(request, response);
+
+ context.fireRequestDestroyEvent(request);
+ }
+
+
+ /**
+ * Handle the HTTP status code (and corresponding message) generated
+ * while processing the specified Request to produce the specified
+ * Response. Any exceptions that occur during generation of the error
+ * report are logged and swallowed.
+ *
+ * @param request The request being processed
+ * @param response The response being generated
+ */
+ private void status(Request request, Response response) {
+
+ int statusCode = response.getStatus();
+
+ // Handle a custom error page for this status code
+ Context context = request.getContext();
+ if (context == null)
+ return;
+
+ /* Only look for error pages when isError() is set.
+ * isError() is set when response.sendError() is invoked. This
+ * allows custom error pages without relying on default from
+ * web.xml.
+ */
+ if (!response.isError())
+ return;
+
+ ErrorPage errorPage = context.findErrorPage(statusCode);
+ if (errorPage != null) {
+ response.setAppCommitted(false);
+ request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,
+ Integer.valueOf(statusCode));
+
+ String message = response.getMessage();
+ if (message == null)
+ message = "";
+ request.setAttribute(RequestDispatcher.ERROR_MESSAGE, message);
+ request.setAttribute
+ (ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,
+ errorPage.getLocation());
+ request.setAttribute(ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
+ DispatcherType.ERROR);
+
+
+ Wrapper wrapper = request.getWrapper();
+ if (wrapper != null)
+ request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME,
+ wrapper.getName());
+ request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI,
+ request.getRequestURI());
+ if (custom(request, response, errorPage)) {
+ try {
+ response.flushBuffer();
+ } catch (ClientAbortException e) {
+ // Ignore
+ } catch (IOException e) {
+ container.getLogger().warn("Exception Processing " + errorPage, e);
+ }
+ }
+ }
}
+
+ /**
+ * Handle the specified Throwable encountered while processing
+ * the specified Request to produce the specified Response. Any
+ * exceptions that occur during generation of the exception report are
+ * logged and swallowed.
+ *
+ * @param request The request being processed
+ * @param response The response being generated
+ * @param throwable The exception that occurred (which possibly wraps
+ * a root cause exception
+ */
+ private void throwable(Request request, Response response,
+ Throwable throwable) {
+ Context context = request.getContext();
+ if (context == null)
+ return;
+
+ Throwable realError = throwable;
+
+ if (realError instanceof ServletException) {
+ realError = ((ServletException) realError).getRootCause();
+ if (realError == null) {
+ realError = throwable;
+ }
+ }
+ // If this is an aborted request from a client just log it and return
+ if (realError instanceof ClientAbortException ) {
+ if (log.isDebugEnabled()) {
+ log.debug
+ (sm.getString("standardHost.clientAbort",
+ realError.getCause().getMessage()));
+ }
+ return;
+ }
+
+ ErrorPage errorPage = findErrorPage(context, throwable);
+ if ((errorPage == null) && (realError != throwable)) {
+ errorPage = findErrorPage(context, realError);
+ }
+
+ if (errorPage != null) {
+ response.setAppCommitted(false);
+ request.setAttribute
+ (ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,
+ errorPage.getLocation());
+ request.setAttribute(ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
+ DispatcherType.ERROR);
+ request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,
+ new Integer(HttpServletResponse.SC_INTERNAL_SERVER_ERROR));
+ request.setAttribute(RequestDispatcher.ERROR_MESSAGE,
+ throwable.getMessage());
+ request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,
+ realError);
+ Wrapper wrapper = request.getWrapper();
+ if (wrapper != null)
+ request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME,
+ wrapper.getName());
+ request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI,
+ request.getRequestURI());
+ request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,
+ realError.getClass());
+ if (custom(request, response, errorPage)) {
+ try {
+ response.flushBuffer();
+ } catch (IOException e) {
+ container.getLogger().warn("Exception Processing " + errorPage, e);
+ }
+ }
+ } else {
+ // A custom error-page has not been defined for the exception
+ // that was thrown during request processing. Check if an
+ // error-page for error code 500 was specified and if so,
+ // send that page back as the response.
+ response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ // The response is an error
+ response.setError();
+
+ status(request, response);
+ }
+ }
+
+
+ /**
+ * Handle an HTTP status code or Java exception by forwarding control
+ * to the location included in the specified errorPage object. It is
+ * assumed that the caller has already recorded any request attributes
+ * that are to be forwarded to this page. Return <code>true</code> if
+ * we successfully utilized the specified error page location, or
+ * <code>false</code> if the default error report should be rendered.
+ *
+ * @param request The request being processed
+ * @param response The response being generated
+ * @param errorPage The errorPage directive we are obeying
+ */
+ private boolean custom(Request request, Response response,
+ ErrorPage errorPage) {
+
+ if (container.getLogger().isDebugEnabled())
+ container.getLogger().debug("Processing " + errorPage);
+
+ request.setPathInfo(errorPage.getLocation());
+
+ try {
+ // Forward control to the specified location
+ ServletContext servletContext =
+ request.getContext().getServletContext();
+ RequestDispatcher rd =
+ servletContext.getRequestDispatcher(errorPage.getLocation());
+
+ if (response.isCommitted()) {
+ // Response is committed - including the error page is the
+ // best we can do
+ rd.include(request.getRequest(), response.getResponse());
+ } else {
+ // Reset the response (keeping the real error code and message)
+ response.resetBuffer(true);
+
+ rd.forward(request.getRequest(), response.getResponse());
+
+ // If we forward, the response is suspended again
+ response.setSuspended(false);
+ }
+
+ // Indicate that we have successfully processed this custom page
+ return (true);
+
+ } catch (Throwable t) {
+ ExceptionUtils.handleThrowable(t);
+ // Report our failure to process this custom page
+ container.getLogger().error("Exception Processing " + errorPage, t);
+ return (false);
+
+ }
+ }
+
+
+ /**
+ * Find and return the ErrorPage instance for the specified exception's
+ * class, or an ErrorPage instance for the closest superclass for which
+ * there is such a definition. If no associated ErrorPage instance is
+ * found, return <code>null</code>.
+ *
+ * @param context The Context in which to search
+ * @param exception The exception for which to find an ErrorPage
+ */
+ private static ErrorPage findErrorPage
+ (Context context, Throwable exception) {
+
+ if (exception == null)
+ return (null);
+ Class<?> clazz = exception.getClass();
+ String name = clazz.getName();
+ while (!Object.class.equals(clazz)) {
+ ErrorPage errorPage = context.findErrorPage(name);
+ if (errorPage != null)
+ return (errorPage);
+ clazz = clazz.getSuperclass();
+ if (clazz == null)
+ break;
+ name = clazz.getName();
+ }
+ return (null);
+
+ }
}
import java.security.AccessController;
import java.security.PrivilegedAction;
-import javax.servlet.DispatcherType;
-import javax.servlet.RequestDispatcher;
-import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.Context;
import org.apache.catalina.Globals;
-import org.apache.catalina.Wrapper;
import org.apache.catalina.comet.CometEvent;
-import org.apache.catalina.connector.ClientAbortException;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
-import org.apache.catalina.deploy.ErrorPage;
import org.apache.catalina.valves.ValveBase;
-import org.apache.juli.logging.Log;
-import org.apache.juli.logging.LogFactory;
-import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.res.StringManager;
* @version $Id$
*/
-final class StandardHostValve
- extends ValveBase {
-
-
- private static final Log log = LogFactory.getLog(StandardHostValve.class);
+final class StandardHostValve extends ValveBase {
protected static final boolean STRICT_SERVLET_COMPLIANCE;
request.getSession(false);
}
- // Error page processing
- response.setSuspended(false);
-
- Throwable t = (Throwable) request.getAttribute(
- RequestDispatcher.ERROR_EXCEPTION);
-
- if (t != null) {
- throwable(request, response, t);
- } else {
- status(request, response);
- }
-
// Restore the context classloader
if (Globals.IS_SECURITY_ENABLED) {
PrivilegedAction<Void> pa = new PrivilegedSetTccl(
request.getSession(false);
}
- // Error page processing
- response.setSuspended(false);
-
- Throwable t = (Throwable) request.getAttribute(
- RequestDispatcher.ERROR_EXCEPTION);
-
- if (t != null) {
- throwable(request, response, t);
- } else {
- status(request, response);
- }
-
// Restore the context classloader
Thread.currentThread().setContextClassLoader
(StandardHostValve.class.getClassLoader());
}
- // ------------------------------------------------------ Protected Methods
-
-
- /**
- * Handle the specified Throwable encountered while processing
- * the specified Request to produce the specified Response. Any
- * exceptions that occur during generation of the exception report are
- * logged and swallowed.
- *
- * @param request The request being processed
- * @param response The response being generated
- * @param throwable The exception that occurred (which possibly wraps
- * a root cause exception
- */
- protected void throwable(Request request, Response response,
- Throwable throwable) {
- Context context = request.getContext();
- if (context == null)
- return;
-
- Throwable realError = throwable;
-
- if (realError instanceof ServletException) {
- realError = ((ServletException) realError).getRootCause();
- if (realError == null) {
- realError = throwable;
- }
- }
-
- // If this is an aborted request from a client just log it and return
- if (realError instanceof ClientAbortException ) {
- if (log.isDebugEnabled()) {
- log.debug
- (sm.getString("standardHost.clientAbort",
- realError.getCause().getMessage()));
- }
- return;
- }
-
- ErrorPage errorPage = findErrorPage(context, throwable);
- if ((errorPage == null) && (realError != throwable)) {
- errorPage = findErrorPage(context, realError);
- }
-
- if (errorPage != null) {
- response.setAppCommitted(false);
- request.setAttribute
- (ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,
- errorPage.getLocation());
- request.setAttribute(ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
- DispatcherType.ERROR);
- request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,
- new Integer(HttpServletResponse.SC_INTERNAL_SERVER_ERROR));
- request.setAttribute(RequestDispatcher.ERROR_MESSAGE,
- throwable.getMessage());
- request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,
- realError);
- Wrapper wrapper = request.getWrapper();
- if (wrapper != null)
- request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME,
- wrapper.getName());
- request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI,
- request.getRequestURI());
- request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,
- realError.getClass());
- if (custom(request, response, errorPage)) {
- try {
- response.flushBuffer();
- } catch (IOException e) {
- container.getLogger().warn("Exception Processing " + errorPage, e);
- }
- }
- } else {
- // A custom error-page has not been defined for the exception
- // that was thrown during request processing. Check if an
- // error-page for error code 500 was specified and if so,
- // send that page back as the response.
- response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
- // The response is an error
- response.setError();
-
- status(request, response);
- }
-
-
- }
-
-
- /**
- * Handle the HTTP status code (and corresponding message) generated
- * while processing the specified Request to produce the specified
- * Response. Any exceptions that occur during generation of the error
- * report are logged and swallowed.
- *
- * @param request The request being processed
- * @param response The response being generated
- */
- protected void status(Request request, Response response) {
-
- int statusCode = response.getStatus();
-
- // Handle a custom error page for this status code
- Context context = request.getContext();
- if (context == null)
- return;
-
- /* Only look for error pages when isError() is set.
- * isError() is set when response.sendError() is invoked. This
- * allows custom error pages without relying on default from
- * web.xml.
- */
- if (!response.isError())
- return;
-
- ErrorPage errorPage = context.findErrorPage(statusCode);
- if (errorPage != null) {
- response.setAppCommitted(false);
- request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,
- Integer.valueOf(statusCode));
-
- String message = response.getMessage();
- if (message == null)
- message = "";
- request.setAttribute(RequestDispatcher.ERROR_MESSAGE, message);
- request.setAttribute
- (ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,
- errorPage.getLocation());
- request.setAttribute(ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
- DispatcherType.ERROR);
-
-
- Wrapper wrapper = request.getWrapper();
- if (wrapper != null)
- request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME,
- wrapper.getName());
- request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI,
- request.getRequestURI());
- if (custom(request, response, errorPage)) {
- try {
- response.flushBuffer();
- } catch (ClientAbortException e) {
- // Ignore
- } catch (IOException e) {
- container.getLogger().warn("Exception Processing " + errorPage, e);
- }
- }
- }
-
- }
-
-
- /**
- * Find and return the ErrorPage instance for the specified exception's
- * class, or an ErrorPage instance for the closest superclass for which
- * there is such a definition. If no associated ErrorPage instance is
- * found, return <code>null</code>.
- *
- * @param context The Context in which to search
- * @param exception The exception for which to find an ErrorPage
- */
- protected static ErrorPage findErrorPage
- (Context context, Throwable exception) {
-
- if (exception == null)
- return (null);
- Class<?> clazz = exception.getClass();
- String name = clazz.getName();
- while (!Object.class.equals(clazz)) {
- ErrorPage errorPage = context.findErrorPage(name);
- if (errorPage != null)
- return (errorPage);
- clazz = clazz.getSuperclass();
- if (clazz == null)
- break;
- name = clazz.getName();
- }
- return (null);
-
- }
-
-
- /**
- * Handle an HTTP status code or Java exception by forwarding control
- * to the location included in the specified errorPage object. It is
- * assumed that the caller has already recorded any request attributes
- * that are to be forwarded to this page. Return <code>true</code> if
- * we successfully utilized the specified error page location, or
- * <code>false</code> if the default error report should be rendered.
- *
- * @param request The request being processed
- * @param response The response being generated
- * @param errorPage The errorPage directive we are obeying
- */
- protected boolean custom(Request request, Response response,
- ErrorPage errorPage) {
-
- if (container.getLogger().isDebugEnabled())
- container.getLogger().debug("Processing " + errorPage);
-
- request.setPathInfo(errorPage.getLocation());
-
- try {
- // Forward control to the specified location
- ServletContext servletContext =
- request.getContext().getServletContext();
- RequestDispatcher rd =
- servletContext.getRequestDispatcher(errorPage.getLocation());
-
- if (response.isCommitted()) {
- // Response is committed - including the error page is the
- // best we can do
- rd.include(request.getRequest(), response.getResponse());
- } else {
- // Reset the response (keeping the real error code and message)
- response.resetBuffer(true);
-
- rd.forward(request.getRequest(), response.getResponse());
-
- // If we forward, the response is suspended again
- response.setSuspended(false);
- }
-
- // Indicate that we have successfully processed this custom page
- return (true);
-
- } catch (Throwable t) {
- ExceptionUtils.handleThrowable(t);
- // Report our failure to process this custom page
- container.getLogger().error("Exception Processing " + errorPage, t);
- return (false);
-
- }
-
- }
-
-
private static class PrivilegedSetTccl implements PrivilegedAction<Void> {
private ClassLoader cl;
--- /dev/null
+/*
+ * 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.catalina.core;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequestEvent;
+import javax.servlet.ServletRequestListener;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.deploy.ErrorPage;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TomcatBaseTest;
+import org.apache.tomcat.util.buf.ByteChunk;
+
+public class TestStandardContextValve extends TomcatBaseTest {
+
+ @Test
+ public void testBug51653a() throws Exception {
+ // Set up a container
+ Tomcat tomcat = getTomcatInstance();
+
+ // Must have a real docBase - just use temp
+ File docBase = new File(System.getProperty("java.io.tmpdir"));
+ Context ctx = tomcat.addContext("", docBase.getAbsolutePath());
+
+ // Traces order of events across multiple components
+ StringBuilder trace = new StringBuilder();
+
+ //Add the error page
+ Tomcat.addServlet(ctx, "errorPage", new Bug51653ErrorPage(trace));
+ ctx.addServletMapping("/error", "errorPage");
+ // And the handling for 404 responses
+ ErrorPage errorPage = new ErrorPage();
+ errorPage.setErrorCode(Response.SC_NOT_FOUND);
+ errorPage.setLocation("/error");
+ ctx.addErrorPage(errorPage);
+
+ // Add the request listener
+ Bug51653RequestListener reqListener =
+ new Bug51653RequestListener(trace);
+ ((StandardContext) ctx).addApplicationEventListener(reqListener);
+
+ tomcat.start();
+
+ // Request a page that does not exist
+ int rc = getUrl("http://localhost:" + getPort() + "/invalid",
+ new ByteChunk(), null);
+
+ assertEquals(Response.SC_NOT_FOUND, rc);
+ assertEquals("InitErrorDestroy", trace.toString());
+ }
+
+ @Test
+ public void testBug51653b() throws Exception {
+ // Set up a container
+ Tomcat tomcat = getTomcatInstance();
+
+ // Must have a real docBase - just use temp
+ File docBase = new File(System.getProperty("java.io.tmpdir"));
+ Context ctx = tomcat.addContext("", docBase.getAbsolutePath());
+
+ // Traces order of events across multiple components
+ StringBuilder trace = new StringBuilder();
+
+ // Add the page that generates the error
+ Tomcat.addServlet(ctx, "test", new Bug51653ErrorTrigger());
+ ctx.addServletMapping("/test", "test");
+
+ // Add the error page
+ Tomcat.addServlet(ctx, "errorPage", new Bug51653ErrorPage(trace));
+ ctx.addServletMapping("/error", "errorPage");
+ // And the handling for 404 responses
+ ErrorPage errorPage = new ErrorPage();
+ errorPage.setErrorCode(Response.SC_NOT_FOUND);
+ errorPage.setLocation("/error");
+ ctx.addErrorPage(errorPage);
+
+ // Add the request listener
+ Bug51653RequestListener reqListener =
+ new Bug51653RequestListener(trace);
+ ((StandardContext) ctx).addApplicationEventListener(reqListener);
+
+ tomcat.start();
+
+ // Request a page that does not exist
+ int rc = getUrl("http://localhost:" + getPort() + "/test",
+ new ByteChunk(), null);
+
+ assertEquals(Response.SC_NOT_FOUND, rc);
+ assertEquals("InitErrorDestroy", trace.toString());
+ }
+
+ private static class Bug51653ErrorTrigger extends HttpServlet {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ resp.sendError(Response.SC_NOT_FOUND);
+ }
+ }
+
+ private static class Bug51653ErrorPage extends HttpServlet {
+ private static final long serialVersionUID = 1L;
+
+ private StringBuilder sb;
+
+ public Bug51653ErrorPage(StringBuilder sb) {
+ this.sb = sb;
+ }
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ sb.append("Error");
+
+ resp.setContentType("text/plain");
+ resp.getWriter().write("Error");
+ }
+ }
+
+ private static class Bug51653RequestListener
+ implements ServletRequestListener {
+
+ private StringBuilder sb;
+
+ public Bug51653RequestListener(StringBuilder sb) {
+ this.sb = sb;
+ }
+
+ @Override
+ public void requestInitialized(ServletRequestEvent sre) {
+ sb.append("Init");
+ }
+
+ @Override
+ public void requestDestroyed(ServletRequestEvent sre) {
+ sb.append("Destroy");
+ }
+
+ }
+}