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()) {
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;
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;
}
}
}
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;
}
}
}
import java.io.IOException;
+import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
}
}
+ // 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);
+ }
}
}
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;
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;
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-");
} else {
expected.append("NonAsyncServletGet-");
}
+ expected.append("requestDestroyed");
assertEquals(expected.toString(), res.toString());
}
wrapper2.setAsyncSupported(true);
ctx.addServletMapping("/stage2", "nonasync");
+ ctx.addApplicationListener(TrackingRequestListener.class.getName());
+
tomcat.start();
StringBuilder url = new StringBuilder(48);
}
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());
}
}
}
+ 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);
}
Tomcat.addServlet(ctx, "error", error);
ctx.addServletMapping("/stage2", "error");
+ ctx.addApplicationListener(TrackingRequestListener.class.getName());
+
tomcat.start();
StringBuilder url = new StringBuilder(48);
}
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("ErrorServletGet-onError-onComplete-");
+ expected.append("ErrorServletGet-onError-onComplete-requestDestroyed");
assertEquals(expected.toString(), res.toString());
}
<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">