Author: markt Date: Wed Feb 16 17:35:24 2011 New Revision: 1071321 URL: http://svn.apache.org/viewvc?rev=1071321&view=rev Log: Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=50793 Correctly fire request init/destroy events for astnc requests
Modified: tomcat/trunk/java/org/apache/catalina/connector/CoyoteAdapter.java tomcat/trunk/java/org/apache/catalina/core/StandardContext.java tomcat/trunk/java/org/apache/catalina/core/StandardContextValve.java tomcat/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java tomcat/trunk/webapps/docs/changelog.xml Modified: tomcat/trunk/java/org/apache/catalina/connector/CoyoteAdapter.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/connector/CoyoteAdapter.java?rev=1071321&r1=1071320&r2=1071321&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/connector/CoyoteAdapter.java (original) +++ tomcat/trunk/java/org/apache/catalina/connector/CoyoteAdapter.java Wed Feb 16 17:35:24 2011 @@ -268,6 +268,17 @@ public class CoyoteAdapter implements Ad boolean success = true; AsyncContextImpl asyncConImpl = (AsyncContextImpl)request.getAsyncContext(); try { + if (!request.isAsync() && !comet) { + // Error or timeout - need to tell listeners the request is over + // Have to test this first since state may change while in this + // method and this is only required if entering this methos in + // this state + Context ctxt = (Context) request.getMappingData().context; + if (ctxt != null) { + ctxt.fireRequestDestroyEvent(request); + } + } + if (status==SocketStatus.TIMEOUT) { success = true; if (!asyncConImpl.timeout()) { Modified: tomcat/trunk/java/org/apache/catalina/core/StandardContext.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/StandardContext.java?rev=1071321&r1=1071320&r2=1071321&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/core/StandardContext.java (original) +++ tomcat/trunk/java/org/apache/catalina/core/StandardContext.java Wed Feb 16 17:35:24 2011 @@ -50,7 +50,6 @@ import javax.management.NotificationList import javax.management.ObjectName; import javax.naming.NamingException; import javax.naming.directory.DirContext; -import javax.servlet.DispatcherType; import javax.servlet.FilterConfig; import javax.servlet.RequestDispatcher; import javax.servlet.Servlet; @@ -5817,30 +5816,26 @@ public class StandardContext extends Con if ((instances != null) && (instances.length > 0)) { - // Don't fire the listener for async requests - if (!DispatcherType.ASYNC.equals(request.getDispatcherType())) { + ServletRequestEvent event = + new ServletRequestEvent(getServletContext(), request); + + for (int i = 0; i < instances.length; i++) { + if (instances[i] == null) + continue; + if (!(instances[i] instanceof ServletRequestListener)) + continue; + ServletRequestListener listener = + (ServletRequestListener) instances[i]; - ServletRequestEvent event = - new ServletRequestEvent(getServletContext(), request); - - for (int i = 0; i < instances.length; i++) { - if (instances[i] == null) - continue; - if (!(instances[i] instanceof ServletRequestListener)) - continue; - ServletRequestListener listener = - (ServletRequestListener) instances[i]; - - try { - listener.requestInitialized(event); - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - getLogger().error(sm.getString( - "standardContext.requestListener.requestInit", - instances[i].getClass().getName()), t); - request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t); - return false; - } + try { + listener.requestInitialized(event); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + getLogger().error(sm.getString( + "standardContext.requestListener.requestInit", + instances[i].getClass().getName()), t); + request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t); + return false; } } } @@ -5854,31 +5849,27 @@ public class StandardContext extends Con if ((instances != null) && (instances.length > 0)) { - // Don't fire the listener for async requests - if (!DispatcherType.ASYNC.equals(request.getDispatcherType())) { + ServletRequestEvent event = + new ServletRequestEvent(getServletContext(), request); - ServletRequestEvent event = - new ServletRequestEvent(getServletContext(), request); - - for (int i = 0; i < instances.length; i++) { - int j = (instances.length -1) -i; - if (instances[j] == null) - continue; - if (!(instances[j] instanceof ServletRequestListener)) - continue; - ServletRequestListener listener = - (ServletRequestListener) instances[j]; - - try { - listener.requestDestroyed(event); - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); - getLogger().error(sm.getString( - "standardContext.requestListener.requestInit", - instances[j].getClass().getName()), t); - request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t); - return false; - } + for (int i = 0; i < instances.length; i++) { + int j = (instances.length -1) -i; + if (instances[j] == null) + continue; + if (!(instances[j] instanceof ServletRequestListener)) + continue; + ServletRequestListener listener = + (ServletRequestListener) instances[j]; + + try { + listener.requestDestroyed(event); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + getLogger().error(sm.getString( + "standardContext.requestListener.requestInit", + instances[j].getClass().getName()), t); + request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t); + return false; } } } 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=1071321&r1=1071320&r2=1071321&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/core/StandardContextValve.java (original) +++ tomcat/trunk/java/org/apache/catalina/core/StandardContextValve.java Wed Feb 16 17:35:24 2011 @@ -21,6 +21,7 @@ package org.apache.catalina.core; import java.io.IOException; +import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.HttpServletResponse; @@ -152,15 +153,24 @@ final class StandardContextValve } } + // Don't fire listeners during async processing // If a request init listener throws an exception, the request is // aborted - if (context.fireRequestInitEvent(request)) { + boolean asyncAtStart = request.isAsync(); + if (asyncAtStart || context.fireRequestInitEvent(request)) { if (request.isAsyncSupported()) { request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported()); } wrapper.getPipeline().getFirst().invoke(request, response); - context.fireRequestDestroyEvent(request); + // If the request was async at the start and an error occurred then + // the async error handling will kick-in and that will fire the + // request destroyed event *after* the error handling has taken + // place + if (!(request.isAsync() || (asyncAtStart && request.getAttribute( + RequestDispatcher.ERROR_EXCEPTION) != null))) { + context.fireRequestDestroyEvent(request); + } } } Modified: tomcat/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java?rev=1071321&r1=1071320&r2=1071321&view=diff ============================================================================== --- tomcat/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java (original) +++ tomcat/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java Wed Feb 16 17:35:24 2011 @@ -27,6 +27,8 @@ import javax.servlet.AsyncContext; import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; import javax.servlet.ServletException; +import javax.servlet.ServletRequestEvent; +import javax.servlet.ServletRequestListener; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; @@ -34,6 +36,7 @@ import javax.servlet.http.HttpServletRes import org.apache.catalina.Context; import org.apache.catalina.Wrapper; +import org.apache.catalina.connector.Request; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.TomcatBaseTest; import org.apache.tomcat.util.buf.ByteChunk; @@ -358,9 +361,11 @@ public class TestAsyncContextImpl extend ctx.addServletMapping(dispatchUrl, "nonasync"); } + ctx.addApplicationListener(TrackingRequestListener.class.getName()); + tomcat.start(); ByteChunk res = getUrl("http://localhost:" + getPort() + "/async"); - StringBuilder expected = new StringBuilder(); + StringBuilder expected = new StringBuilder("requestInitialized-"); expected.append("TimeoutServletGet-onTimeout-"); if (!completeOnTimeout) { expected.append("onError-"); @@ -370,6 +375,7 @@ public class TestAsyncContextImpl extend } else { expected.append("NonAsyncServletGet-"); } + expected.append("requestDestroyed"); assertEquals(expected.toString(), res.toString()); } @@ -442,6 +448,8 @@ public class TestAsyncContextImpl extend wrapper2.setAsyncSupported(true); ctx.addServletMapping("/stage2", "nonasync"); + ctx.addApplicationListener(TrackingRequestListener.class.getName()); + tomcat.start(); StringBuilder url = new StringBuilder(48); @@ -454,13 +462,14 @@ public class TestAsyncContextImpl extend } ByteChunk res = getUrl(url.toString()); - StringBuilder expected = new StringBuilder(); + StringBuilder expected = new StringBuilder("requestInitialized-"); int loop = iter; while (loop > 0) { expected.append("DispatchingServletGet-"); loop--; } expected.append("NonAsyncServletGet-"); + expected.append("requestDestroyed"); assertEquals(expected.toString(), res.toString()); } @@ -643,6 +652,34 @@ public class TestAsyncContextImpl extend } } + public static class TrackingRequestListener + implements ServletRequestListener { + + @Override + public void requestDestroyed(ServletRequestEvent sre) { + // Need the response and it isn't available via the Servlet API + Request r = (Request) sre.getServletRequest(); + try { + r.getResponse().getWriter().print("requestDestroyed"); + } catch (IOException e) { + // Test will fail if this happens + e.printStackTrace(); + } + } + + @Override + public void requestInitialized(ServletRequestEvent sre) { + // Need the response and it isn't available via the Servlet API + Request r = (Request) sre.getServletRequest(); + try { + r.getResponse().getWriter().print("requestInitialized-"); + } catch (IOException e) { + // Test will fail if this happens + e.printStackTrace(); + } + } + } + public void testDispatchErrorSingle() throws Exception { doTestDispatchError(1, false, false); } @@ -715,6 +752,8 @@ public class TestAsyncContextImpl extend Tomcat.addServlet(ctx, "error", error); ctx.addServletMapping("/stage2", "error"); + ctx.addApplicationListener(TrackingRequestListener.class.getName()); + tomcat.start(); StringBuilder url = new StringBuilder(48); @@ -727,7 +766,7 @@ public class TestAsyncContextImpl extend } ByteChunk res = getUrl(url.toString()); - StringBuilder expected = new StringBuilder(); + StringBuilder expected = new StringBuilder("requestInitialized-"); int loop = iter; while (loop > 0) { expected.append("DispatchingServletGet-"); @@ -736,7 +775,7 @@ public class TestAsyncContextImpl extend } loop--; } - expected.append("ErrorServletGet-onError-onComplete-"); + expected.append("ErrorServletGet-onError-onComplete-requestDestroyed"); assertEquals(expected.toString(), res.toString()); } Modified: tomcat/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1071321&r1=1071320&r2=1071321&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/changelog.xml (original) +++ tomcat/trunk/webapps/docs/changelog.xml Wed Feb 16 17:35:24 2011 @@ -88,6 +88,11 @@ <bug>50752</bug>: Fix typo in debug message in deprecated Embedded class. (markt) </fix> + <fix> + <bug>50793</bug>: When processing Servlet 3.0 async requests, ensure + that the requestInitialized and requestDestroyed events are only fired + once per request at the correct times. (markt) + </fix> </changelog> </subsection> <subsection name="Jasper"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org