Author: markt
Date: Sat Aug 28 11:07:39 2010
New Revision: 990342

URL: http://svn.apache.org/viewvc?rev=990342&view=rev
Log:
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

Modified:
    tomcat/trunk/java/org/apache/catalina/core/AsyncContextImpl.java
    tomcat/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java
    tomcat/trunk/webapps/docs/changelog.xml

Modified: tomcat/trunk/java/org/apache/catalina/core/AsyncContextImpl.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/AsyncContextImpl.java?rev=990342&r1=990341&r2=990342&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/core/AsyncContextImpl.java (original)
+++ tomcat/trunk/java/org/apache/catalina/core/AsyncContextImpl.java Sat Aug 28 
11:07:39 2010
@@ -51,7 +51,7 @@ public class AsyncContextImpl implements
     
     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
         }
         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
     }
     
     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<AsyncListenerWrapper> listenersCopy =
+                new ArrayList<AsyncListenerWrapper>();
+            listenersCopy.addAll(listeners);
+            for (AsyncListenerWrapper listener : listenersCopy) {
                 listener.fireOnTimeout(event);
                 listenerInvoked = true;
             }

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=990342&r1=990341&r2=990342&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java 
(original)
+++ tomcat/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java Sat 
Aug 28 11:07:39 2010
@@ -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 extend
         }
     }
 
+    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
+        }
+        
+    }
 }

Modified: tomcat/trunk/webapps/docs/changelog.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=990342&r1=990341&r2=990342&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Sat Aug 28 11:07:39 2010
@@ -52,6 +52,10 @@
         refactoring. (markt)
       </fix>
       <fix>
+        <bug>49698</bug>: Allow a listener to complete an asynchronous request
+        if it times out. (markt)
+      </fix>
+      <fix>
         <bug>49714</bug>: The annotation process of Jar doesn't influence 
         distributable element of web.xml. (kfujino)
       </fix>



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to