From: markt Date: Sat, 28 Aug 2010 11:07:39 +0000 (+0000) Subject: Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=49698 X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=b4a7fa399911a43e005cce861400bee161abb6ba;p=tomcat7.0 Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=49698 Allow listeners to call complete when a async request times out Add a test case based on pero's previous timeout test case git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@990342 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/java/org/apache/catalina/core/AsyncContextImpl.java b/java/org/apache/catalina/core/AsyncContextImpl.java index a15ca27b6..955e77c19 100644 --- a/java/org/apache/catalina/core/AsyncContextImpl.java +++ b/java/org/apache/catalina/core/AsyncContextImpl.java @@ -51,7 +51,7 @@ public class AsyncContextImpl implements AsyncContext { public static enum AsyncState { NOT_STARTED, STARTED, DISPATCHING, DISPATCHED, COMPLETING, TIMING_OUT, - ERROR_DISPATCHING + TIMING_OUT_NEED_COMPLETE, ERROR_DISPATCHING } private static final Log log = LogFactory.getLog(AsyncContextImpl.class); @@ -82,13 +82,19 @@ public class AsyncContextImpl implements AsyncContext { } if (state.get()==AsyncState.COMPLETING) { //do nothing - } else if (state.compareAndSet(AsyncState.DISPATCHED, AsyncState.COMPLETING) || - state.compareAndSet(AsyncState.STARTED, AsyncState.COMPLETING)) { + } else if (state.compareAndSet(AsyncState.DISPATCHED, + AsyncState.COMPLETING) || + state.compareAndSet(AsyncState.STARTED, + AsyncState.COMPLETING) || + state.compareAndSet(AsyncState.TIMING_OUT_NEED_COMPLETE, + AsyncState.COMPLETING)) { AtomicBoolean dispatched = new AtomicBoolean(false); - request.getCoyoteRequest().action(ActionCode.ACTION_ASYNC_COMPLETE,dispatched); + request.getCoyoteRequest().action(ActionCode.ACTION_ASYNC_COMPLETE, + dispatched); if (!dispatched.get()) doInternalComplete(false); } else { - throw new IllegalStateException("Complete not allowed. Invalid state:"+state.get()); + throw new IllegalStateException( + "Complete not allowed. Invalid state:"+state.get()); } } @@ -296,10 +302,14 @@ public class AsyncContextImpl implements AsyncContext { } public void doInternalDispatch() throws ServletException, IOException { - if (this.state.compareAndSet(AsyncState.TIMING_OUT, AsyncState.COMPLETING)) { + if (this.state.compareAndSet(AsyncState.TIMING_OUT, + AsyncState.TIMING_OUT_NEED_COMPLETE)) { log.debug("TIMING OUT!"); boolean listenerInvoked = false; - for (AsyncListenerWrapper listener : listeners) { + List listenersCopy = + new ArrayList(); + listenersCopy.addAll(listeners); + for (AsyncListenerWrapper listener : listenersCopy) { listener.fireOnTimeout(event); listenerInvoked = true; } diff --git a/test/org/apache/catalina/core/TestAsyncContextImpl.java b/test/org/apache/catalina/core/TestAsyncContextImpl.java index cf049faee..e12b6293a 100644 --- a/test/org/apache/catalina/core/TestAsyncContextImpl.java +++ b/test/org/apache/catalina/core/TestAsyncContextImpl.java @@ -17,9 +17,12 @@ package org.apache.catalina.core; +import java.io.File; import java.io.IOException; import javax.servlet.AsyncContext; +import javax.servlet.AsyncEvent; +import javax.servlet.AsyncListener; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; @@ -308,4 +311,70 @@ public class TestAsyncContextImpl extends TomcatBaseTest { } } + public void testTimeout() throws Exception { + // Setup Tomcat instance + Tomcat tomcat = getTomcatInstance(); + + // Must have a real docBase - just use temp + File docBase = new File(System.getProperty("java.io.tmpdir")); + + // Create the folder that will trigger the redirect + File foo = new File(docBase, "async"); + if (!foo.exists() && !foo.mkdirs()) { + fail("Unable to create async directory in docBase"); + } + + Context ctx = tomcat.addContext("/", docBase.getAbsolutePath()); + + TimeoutServlet timeout = new TimeoutServlet(); + + Wrapper wrapper = Tomcat.addServlet(ctx, "time", timeout); + wrapper.setAsyncSupported(true); + ctx.addServletMapping("/async", "time"); + + tomcat.start(); + ByteChunk res = getUrl("http://localhost:" + getPort() + "/async"); + assertEquals("OK", res.toString()); + } + + private static class TimeoutServlet extends HttpServlet { + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + if (req.isAsyncSupported()) { + resp.getWriter().print("OK"); + final AsyncContext ac = req.startAsync(); + ac.setTimeout(2000); + + ac.addListener(new TimeoutListener()); + } else + resp.getWriter().print("FAIL: Async unsupported"); + } + } + + private static class TimeoutListener implements AsyncListener { + + @Override + public void onComplete(AsyncEvent event) throws IOException { + // NO-OP + } + + @Override + public void onTimeout(AsyncEvent event) throws IOException { + event.getAsyncContext().complete(); + } + + @Override + public void onError(AsyncEvent event) throws IOException { + // NOOP + } + + @Override + public void onStartAsync(AsyncEvent event) throws IOException { + // NOOP + } + + } } diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 7b345f89f..1aa146b01 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -52,6 +52,10 @@ refactoring. (markt) + 49698: Allow a listener to complete an asynchronous request + if it times out. (markt) + + 49714: The annotation process of Jar doesn't influence distributable element of web.xml. (kfujino)