Author: markt
Date: Tue Nov 13 23:44:15 2012
New Revision: 1409030

URL: http://svn.apache.org/viewvc?rev=1409030&view=rev
Log:
More updates to the async error handling triggered by kkolinko's review
- simplify check to see if listeners changed async state during onTimeout
- add option to control if onError fires during error handling (it doesn't if 
called from the timeout code since that has its own event)
- add check to see if listeners changed state during onError
- add check to see if state changed during "error dispatch"
- remove AsyncState.ERROR as a valid start state for asyncPostProcess()
- aligned the unit tests (one change) with the new behaviour

Not tested with the TCK.

Modified:
    tomcat/trunk/java/org/apache/catalina/connector/CoyoteAdapter.java
    tomcat/trunk/java/org/apache/catalina/core/AsyncContextImpl.java
    tomcat/trunk/java/org/apache/coyote/ActionCode.java
    tomcat/trunk/java/org/apache/coyote/AsyncStateMachine.java
    tomcat/trunk/java/org/apache/coyote/ajp/AbstractAjpProcessor.java
    tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Processor.java
    tomcat/trunk/java/org/apache/coyote/spdy/SpdyProcessor.java
    tomcat/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java

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=1409030&r1=1409029&r2=1409030&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/connector/CoyoteAdapter.java 
(original)
+++ tomcat/trunk/java/org/apache/catalina/connector/CoyoteAdapter.java Tue Nov 
13 23:44:15 2012
@@ -300,7 +300,7 @@ public class CoyoteAdapter implements Ad
             if (status==SocketStatus.TIMEOUT) {
                 success = true;
                 if (!asyncConImpl.timeout()) {
-                    asyncConImpl.setErrorState(null);
+                    asyncConImpl.setErrorState(null, false);
                 }
             } else if (status==SocketStatus.ASYNC_READ_ERROR) {
                 success = true;
@@ -308,7 +308,7 @@ public class CoyoteAdapter implements Ad
                 req.getAttributes().remove(RequestDispatcher.ERROR_EXCEPTION);
                 asyncConImpl.notifyReadError(t);
                 if (t != null) {
-                    asyncConImpl.setErrorState(t);
+                    asyncConImpl.setErrorState(t, true);
                 }
             } else if (status==SocketStatus.ASYNC_WRITE_ERROR) {
                 success = true;
@@ -316,7 +316,7 @@ public class CoyoteAdapter implements Ad
                 req.getAttributes().remove(RequestDispatcher.ERROR_EXCEPTION);
                 asyncConImpl.notifyWriteError(t);
                 if (t != null) {
-                    asyncConImpl.setErrorState(t);
+                    asyncConImpl.setErrorState(t, true);
                 }
             }
 
@@ -338,7 +338,7 @@ public class CoyoteAdapter implements Ad
                 Throwable t = (Throwable) request.getAttribute(
                         RequestDispatcher.ERROR_EXCEPTION);
                 if (t != null) {
-                    asyncConImpl.setErrorState(t);
+                    asyncConImpl.setErrorState(t, true);
                 }
             }
 

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=1409030&r1=1409029&r2=1409030&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/core/AsyncContextImpl.java (original)
+++ tomcat/trunk/java/org/apache/catalina/core/AsyncContextImpl.java Tue Nov 13 
23:44:15 2012
@@ -171,7 +171,7 @@ public class AsyncContextImpl implements
         }
     }
 
-    public boolean timeout() throws IOException {
+    public boolean timeout() {
         AtomicBoolean result = new AtomicBoolean();
         request.getCoyoteRequest().action(ActionCode.ASYNC_TIMEOUT, result);
 
@@ -181,22 +181,19 @@ public class AsyncContextImpl implements
             ClassLoader newCL = 
request.getContext().getLoader().getClassLoader();
             try {
                 Thread.currentThread().setContextClassLoader(newCL);
-                boolean listenerInvoked = false;
                 List<AsyncListenerWrapper> listenersCopy = new ArrayList<>();
                 listenersCopy.addAll(listeners);
                 for (AsyncListenerWrapper listener : listenersCopy) {
-                    listener.fireOnTimeout(event);
-                    listenerInvoked = true;
+                    try {
+                        listener.fireOnTimeout(event);
+                    } catch (IOException ioe) {
+                        log.warn("onTimeout() failed for listener of type [" +
+                                listener.getClass().getName() + "]", ioe);
+                    }
                 }
-                if (listenerInvoked) {
-                    request.getCoyoteRequest().action(
-                            ActionCode.ASYNC_IS_TIMINGOUT, result);
-                    return !result.get();
-                } else {
-                    // No listeners, trigger error handling
-                    return false;
-                }
-
+                request.getCoyoteRequest().action(
+                        ActionCode.ASYNC_IS_TIMINGOUT, result);
+                return !result.get();
             } finally {
                 Thread.currentThread().setContextClassLoader(oldCL);
             }
@@ -420,37 +417,50 @@ public class AsyncContextImpl implements
     }
 
 
-    public void setErrorState(Throwable t) {
+    public void setErrorState(Throwable t, boolean fireOnError) {
         if (t!=null) request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, 
t);
         request.getCoyoteRequest().action(ActionCode.ASYNC_ERROR, null);
-        AsyncEvent errorEvent = new AsyncEvent(event.getAsyncContext(),
-                event.getSuppliedRequest(), event.getSuppliedResponse(), t);
-        List<AsyncListenerWrapper> listenersCopy = new ArrayList<>();
-        listenersCopy.addAll(listeners);
-        for (AsyncListenerWrapper listener : listenersCopy) {
-            try {
-                listener.fireOnError(errorEvent);
-            } catch (IOException ioe) {
-                log.warn("onError() failed for listener of type [" +
-                        listener.getClass().getName() + "]", ioe);
+
+        if (fireOnError) {
+            AsyncEvent errorEvent = new AsyncEvent(event.getAsyncContext(),
+                    event.getSuppliedRequest(), event.getSuppliedResponse(), 
t);
+            List<AsyncListenerWrapper> listenersCopy = new ArrayList<>();
+            listenersCopy.addAll(listeners);
+            for (AsyncListenerWrapper listener : listenersCopy) {
+                try {
+                    listener.fireOnError(errorEvent);
+                } catch (IOException ioe) {
+                    log.warn("onError() failed for listener of type [" +
+                            listener.getClass().getName() + "]", ioe);
+                }
             }
         }
 
-        // SRV.2.3.3.3 (search for "error dispatch")
-        if (servletResponse instanceof HttpServletResponse) {
-            ((HttpServletResponse) servletResponse).setStatus(
-                    HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
-        }
 
-        Host host = (Host) context.getParent();
-        Valve stdHostValve = host.getPipeline().getBasic();
-        if (stdHostValve instanceof StandardHostValve) {
-            ((StandardHostValve) stdHostValve).throwable(request,
-                    request.getResponse(), t);
-        }
-
-        if (isStarted() && !request.isAsyncDispatching()) {
-            complete();
+        AtomicBoolean result = new AtomicBoolean();
+        request.getCoyoteRequest().action(ActionCode.ASYNC_IS_ERROR, result);
+        if (result.get()) {
+            // No listener called dispatch() or complete(). This is an error.
+            // SRV.2.3.3.3 (search for "error dispatch")
+            if (servletResponse instanceof HttpServletResponse) {
+                ((HttpServletResponse) servletResponse).setStatus(
+                        HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+            }
+
+            Host host = (Host) context.getParent();
+            Valve stdHostValve = host.getPipeline().getBasic();
+            if (stdHostValve instanceof StandardHostValve) {
+                ((StandardHostValve) stdHostValve).throwable(request,
+                        request.getResponse(), t);
+            }
+
+            request.getCoyoteRequest().action(
+                    ActionCode.ASYNC_IS_ERROR, result);
+            if (result.get()) {
+                // Still in the error state. The error page did not call
+                // complete() or dispatch(). Complete the async processing.
+                complete();
+            }
         }
     }
 

Modified: tomcat/trunk/java/org/apache/coyote/ActionCode.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/ActionCode.java?rev=1409030&r1=1409029&r2=1409030&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/ActionCode.java (original)
+++ tomcat/trunk/java/org/apache/coyote/ActionCode.java Tue Nov 13 23:44:15 2012
@@ -203,6 +203,11 @@ public enum ActionCode {
     ASYNC_IS_TIMINGOUT,
 
     /**
+    * Callback to determine if async is in error
+    */
+    ASYNC_IS_ERROR,
+
+    /**
      * Callback to trigger the HTTP upgrade process.
      */
     UPGRADE,

Modified: tomcat/trunk/java/org/apache/coyote/AsyncStateMachine.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/AsyncStateMachine.java?rev=1409030&r1=1409029&r2=1409030&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/AsyncStateMachine.java (original)
+++ tomcat/trunk/java/org/apache/coyote/AsyncStateMachine.java Tue Nov 13 
23:44:15 2012
@@ -52,41 +52,45 @@ import org.apache.tomcat.util.res.String
  *
  * |----------------->--------------|
  * |                               \|/
- * |   
|----------<---------------ERROR-----------------------<----------------------------------|
- * |   |      complete()         /|\ |                                         
                  |
- * |   |                          |  |postProcess()                            
                  |
- * |   |                   error()|  |                                         
                  |
- * |   |                          |  |  |--|timeout()                          
                  |
- * |   |           postProcess()  | \|/ | \|/            auto                  
                  |
- * |   |         
|--------------->DISPATCHED<-----------------------COMPLETING<-----|            
|
- * |   |         |               /|\  |                               | /|\    
     |            |
- * |   |         |    |--->-------|   |                               |--|     
     |            |
- * |   |         ^    |               |startAsync()                 timeout()  
     |            |
- * |   |         |    |               |                                        
     |            |
- * |  \|/        |    |  complete()  \|/        postProcess()                  
     |            |
- * | MUST_COMPLETE-<- | ----<------STARTING-->--------------------|            
     ^            |
- * |  /|\    /|\      |               |                           |      
complete() |            |
- * |   |      |       |               |                           |     
/-----------|            |
- * |   |      |       ^               |dispatch()                 |    /       
                  |
- * |   |      |       |               |                           |   /        
                  |
- * |   |      |       |              \|/                         \|/ /         
                  |
- * |   |      |       |         MUST_DISPATCH                  STARTED         
                  |
- * |   |      |       |           |                            /|  \ \         
                  |
- * |   |      |       |           |postProcess()              / |   \ \        
                  |
- * ^   |      ^       |           |              dispatch()  /  |    \ \       
                  |
- * |   |      |       |           |                         /   |     \ \    
postProcess()       |
- * |   |      |       |           |    |-------------------/    |auto  \ 
\----<--------------|   |
- * |   |      |       | auto     \|/  \|/                      \|/      \      
              |   |
- * |   |      |       |---<----DISPATCHING<-----------------TIMING_OUT   \     
              |   |
- * |   |      |                               dispatch()      |   |      
|asyncOperation()   ^   |
- * |   |      |                                               |   |     \|/    
              |   ^
- * |   |      |-------<----------------------------------<----|   |      
READ_WRITE_OP->-----|   |
- * |   |                          complete()                      |         |  
    |             |
- * |   |                                                          |         |  
    |  error()    |
- * |<- | ----<-------------------<-----------------------------<--|         |  
    |->-----------|
- *     |                           error()                                  |
- *     |                                                  complete()        |
- *     |--------------------------------------------------------------------|
+ * |   
|----------<---------------ERROR---------------------------<-------------------------------|
+ * |   |      complete()         /|\ | \                                       
                   |
+ * |   |                          |  |  \---------------|                      
                   |
+ * |   |                          |  |                  |dispatch()            
                   |
+ * |   |                          |  |postProcess()    \|/                     
                   |
+ * |   |                   error()|  |                  |                      
                   |
+ * |   |                          |  |  |--|timeout()   |                      
                   |
+ * |   |           postProcess()  | \|/ | \|/           |         auto         
                   |
+ * |   |         |--------------->DISPATCHED<---------- | 
--------------COMPLETING<-----|         |
+ * |   |         |               /|\  |                 |                 | 
/|\         |         |
+ * |   |         |    |--->-------|   |                 |                 |--| 
         |         |
+ * |   |         ^    |               |startAsync()     |               
timeout()       |         |
+ * |   |         |    |               |                 |                      
         |         |
+ * |  \|/        |    |  complete()  \|/  postProcess() |                      
         |         |
+ * | MUST_COMPLETE-<- | ----<------STARTING-->--------- | ------------|        
         ^         |
+ * |  /|\    /|\      |               |                 |             |      
complete() |         |
+ * |   |      |       |               |                 |             |     
/-----------|         |
+ * |   |      |       ^               |dispatch()       |             |    /   
                   |
+ * |   |      |       |               |                 |             |   /    
                   |
+ * |   |      |       |              \|/                /            \|/ /     
                   |
+ * |   |      |       |         MUST_DISPATCH          /           STARTED     
                   |
+ * |   |      |       |           |                   /            /|  \ \     
                   |
+ * |   |      |       |           |postProcess()     /            / |   \ \    
                   |
+ * ^   |      ^       |           |                 /  dispatch()/  |    \ \   
                   |
+ * |   |      |       |           |                /            /   |     \ \  
 postProcess()     |
+ * |   |      |       |           |   |---------- / -----------/    |auto  \ 
\----<-----------|   |
+ * |   |      |       |           |   |          /                  |       \  
               |   |
+ * |   |      |       |           |   |   |-----/                   |       |  
               |   |
+ * |   |      |       | auto     \|/ \|/ \|/                       \|/      |  
               |   |
+ * |   |      |       |---<------DISPATCHING<-----------------TIMING_OUT    |  
               |   |
+ * |   |      |                               dispatch()        |   |       
|asyncOperation() ^   |
+ * |   |      |                                                 |   |      \|/ 
               |   ^
+ * |   |      |-------<----------------------------------<------|   |     
READ_WRITE_OP->-----|   |
+ * |   |                          complete()                        |        | 
     |             |
+ * |   |                                                            |        | 
     |  error()    |
+ * |<- | ----<-------------------<-------------------------------<--|        | 
     |->-----------|
+ *     |                           error()                                   |
+ *     |                                                  complete()         |
+ *     |---------------------------------------------------------------------|
  * </pre>
  */
 public class AsyncStateMachine<S> {
@@ -165,6 +169,9 @@ public class AsyncStateMachine<S> {
         return state == AsyncState.TIMING_OUT;
     }
 
+    public boolean isAsyncError() {
+        return state == AsyncState.ERROR;
+    }
 
     public synchronized void asyncStart(AsyncContextCallback asyncCtxt) {
         if (state == AsyncState.DISPATCHED) {
@@ -211,13 +218,6 @@ public class AsyncStateMachine<S> {
         } else if (state == AsyncState.DISPATCHING) {
             state = AsyncState.DISPATCHED;
             return SocketState.ASYNC_END;
-        } else if (state == AsyncState.ERROR) {
-            asyncCtxt.fireOnComplete();
-            state = AsyncState.DISPATCHED;
-            return SocketState.ASYNC_END;
-        //} else if (state == AsyncState.DISPATCHED) {
-        //    // No state change
-        //    return SocketState.OPEN;
         } else {
             throw new IllegalStateException(
                     sm.getString("asyncStateMachine.invalidAsyncState",

Modified: tomcat/trunk/java/org/apache/coyote/ajp/AbstractAjpProcessor.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/ajp/AbstractAjpProcessor.java?rev=1409030&r1=1409029&r2=1409030&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/ajp/AbstractAjpProcessor.java (original)
+++ tomcat/trunk/java/org/apache/coyote/ajp/AbstractAjpProcessor.java Tue Nov 
13 23:44:15 2012
@@ -459,6 +459,8 @@ public abstract class AbstractAjpProcess
             ((AtomicBoolean) param).set(asyncStateMachine.isAsyncOperation());
         } else if (actionCode == ActionCode.ASYNC_IS_TIMINGOUT) {
             ((AtomicBoolean) param).set(asyncStateMachine.isAsyncTimingOut());
+        } else if (actionCode == ActionCode.ASYNC_IS_ERROR) {
+            ((AtomicBoolean) param).set(asyncStateMachine.isAsyncError());
         } else if (actionCode == ActionCode.UPGRADE) {
             // HTTP connections only. Unsupported for AJP.
             // NOOP

Modified: 
tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Processor.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Processor.java?rev=1409030&r1=1409029&r2=1409030&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Processor.java 
(original)
+++ tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Processor.java Tue 
Nov 13 23:44:15 2012
@@ -800,6 +800,8 @@ public abstract class AbstractHttp11Proc
             ((AtomicBoolean) param).set(asyncStateMachine.isAsyncOperation());
         } else if (actionCode == ActionCode.ASYNC_IS_TIMINGOUT) {
             ((AtomicBoolean) param).set(asyncStateMachine.isAsyncTimingOut());
+        } else if (actionCode == ActionCode.ASYNC_IS_ERROR) {
+            ((AtomicBoolean) param).set(asyncStateMachine.isAsyncError());
         } else if (actionCode == ActionCode.UPGRADE) {
             upgradeInbound = (UpgradeInbound) param;
             // Stop further HTTP output

Modified: tomcat/trunk/java/org/apache/coyote/spdy/SpdyProcessor.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/spdy/SpdyProcessor.java?rev=1409030&r1=1409029&r2=1409030&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/spdy/SpdyProcessor.java (original)
+++ tomcat/trunk/java/org/apache/coyote/spdy/SpdyProcessor.java Tue Nov 13 
23:44:15 2012
@@ -375,6 +375,8 @@ public class SpdyProcessor extends Abstr
             ((AtomicBoolean) param).set(asyncStateMachine.isAsyncOperation());
         } else if (actionCode == ActionCode.ASYNC_IS_TIMINGOUT) {
             ((AtomicBoolean) param).set(asyncStateMachine.isAsyncTimingOut());
+        } else if (actionCode == ActionCode.ASYNC_IS_ERROR) {
+            ((AtomicBoolean) param).set(asyncStateMachine.isAsyncError());
         } else {
             // TODO:
             // actionInternal(actionCode, param);

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=1409030&r1=1409029&r2=1409030&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java 
(original)
+++ tomcat/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java Tue 
Nov 13 23:44:15 2012
@@ -472,9 +472,7 @@ public class TestAsyncContextImpl extend
             }
         } else {
             expected.append("onTimeout-");
-            if (dispatchUrl == null) {
-                expected.append("onError-");
-            } else {
+            if (dispatchUrl != null) {
                 expected.append("NonAsyncServletGet-");
             }
             expected.append("onComplete-");



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

Reply via email to