Author: markt Date: Mon Aug 15 12:29:58 2011 New Revision: 1157810 URL: http://svn.apache.org/viewvc?rev=1157810&view=rev Log: Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=51653 Move custom error handling from Host to Context.
Added: tomcat/trunk/test/org/apache/catalina/core/TestStandardContextValve.java (with props) Modified: tomcat/trunk/java/org/apache/catalina/core/StandardContextValve.java tomcat/trunk/java/org/apache/catalina/core/StandardHostValve.java Modified: tomcat/trunk/java/org/apache/catalina/core/StandardContextValve.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/StandardContextValve.java?rev=1157810&r1=1157809&r2=1157810&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/core/StandardContextValve.java (original) +++ tomcat/trunk/java/org/apache/catalina/core/StandardContextValve.java Mon Aug 15 12:29:58 2011 @@ -21,16 +21,24 @@ package org.apache.catalina.core; 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; /** @@ -44,8 +52,9 @@ import org.apache.tomcat.util.buf.Messag * @version $Id$ */ -final class StandardContextValve - extends ValveBase { +final class StandardContextValve extends ValveBase { + + private static final Log log = LogFactory.getLog(StandardHostValve.class); //------------------------------------------------------ Constructor public StandardContextValve() { @@ -112,10 +121,10 @@ final class 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; } @@ -142,13 +151,13 @@ final class StandardContextValve // 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; } } @@ -160,7 +169,8 @@ final class StandardContextValve 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; } @@ -184,6 +194,18 @@ final class StandardContextValve // 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); } } @@ -210,14 +232,19 @@ final class StandardContextValve // 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); + } } @@ -225,15 +252,14 @@ final class StandardContextValve /** - * 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) { @@ -242,7 +268,238 @@ final class StandardContextValve // 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); + + } } Modified: tomcat/trunk/java/org/apache/catalina/core/StandardHostValve.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/StandardHostValve.java?rev=1157810&r1=1157809&r2=1157810&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/core/StandardHostValve.java (original) +++ tomcat/trunk/java/org/apache/catalina/core/StandardHostValve.java Mon Aug 15 12:29:58 2011 @@ -23,24 +23,15 @@ import java.io.IOException; 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; @@ -56,11 +47,7 @@ import org.apache.tomcat.util.res.String * @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; @@ -169,18 +156,6 @@ final class StandardHostValve 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( @@ -228,18 +203,6 @@ final class StandardHostValve 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()); @@ -247,242 +210,6 @@ final class StandardHostValve } - // ------------------------------------------------------ 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; Added: tomcat/trunk/test/org/apache/catalina/core/TestStandardContextValve.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/core/TestStandardContextValve.java?rev=1157810&view=auto ============================================================================== --- tomcat/trunk/test/org/apache/catalina/core/TestStandardContextValve.java (added) +++ tomcat/trunk/test/org/apache/catalina/core/TestStandardContextValve.java Mon Aug 15 12:29:58 2011 @@ -0,0 +1,166 @@ +/* + * 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"); + } + + } +} Propchange: tomcat/trunk/test/org/apache/catalina/core/TestStandardContextValve.java ------------------------------------------------------------------------------ svn:eol-style = native --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org