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: [email protected]
For additional commands, e-mail: [email protected]