Author: remy.maucherat(a)jboss.com
Date: 2009-10-17 06:45:59 -0400 (Sat, 17 Oct 2009)
New Revision: 1204
Modified:
trunk/java/org/apache/catalina/Valve.java
trunk/java/org/apache/catalina/connector/LocalStrings.properties
trunk/java/org/apache/catalina/connector/Request.java
trunk/java/org/apache/catalina/core/StandardHostValve.java
trunk/java/org/apache/catalina/core/StandardWrapperValve.java
trunk/java/org/apache/catalina/valves/ValveBase.java
Log:
- Some valve javadoc update.
- Implement quite a few error conditions.
- Add error page processing to async.
Modified: trunk/java/org/apache/catalina/Valve.java
===================================================================
--- trunk/java/org/apache/catalina/Valve.java 2009-10-16 23:21:50 UTC (rev 1203)
+++ trunk/java/org/apache/catalina/Valve.java 2009-10-17 10:45:59 UTC (rev 1204)
@@ -136,7 +136,8 @@
* Process an event.
*
* @param request The servlet request to be processed
- * @param response The servlet response to be created
+ * @param response The servlet response to be processed
+ * @param event The event to be processed
*
* @exception IOException if an input/output error occurs, or is thrown
* by a subsequently invoked Valve, Filter, or Servlet
Modified: trunk/java/org/apache/catalina/connector/LocalStrings.properties
===================================================================
--- trunk/java/org/apache/catalina/connector/LocalStrings.properties 2009-10-16 23:21:50
UTC (rev 1203)
+++ trunk/java/org/apache/catalina/connector/LocalStrings.properties 2009-10-17 10:45:59
UTC (rev 1204)
@@ -57,6 +57,9 @@
coyoteRequest.servletStack=Current Servlet stack for thread {0}
coyoteRequest.closed=Response has been closed already
coyoteRequest.logoutfail=Exception logging out user
+coyoteRequest.cannotStartAsync=Cannot start async
+coyoteRequest.onStartAsyncError=Error invoking onStartAsync on listener of class {0}
+coyoteRequest.dispatchNoServletContext=Could not determine or access context for server
absolute path [{0}]
#
# MapperListener
Modified: trunk/java/org/apache/catalina/connector/Request.java
===================================================================
--- trunk/java/org/apache/catalina/connector/Request.java 2009-10-16 23:21:50 UTC (rev
1203)
+++ trunk/java/org/apache/catalina/connector/Request.java 2009-10-17 10:45:59 UTC (rev
1204)
@@ -454,6 +454,12 @@
*/
protected String localName = null;
+
+ /**
+ * Can call startAsync.
+ */
+ protected boolean canStartAsync = true;
+
// --------------------------------------------------------- Public Methods
@@ -478,6 +484,7 @@
asyncContext = null;
asyncTimeout = 300000;
+ canStartAsync = true;
asyncListeners.clear();
authType = null;
inputBuffer.recycle();
@@ -843,6 +850,14 @@
}
+ public boolean getCanStartAsync() {
+ return canStartAsync;
+ }
+
+ public void setCanStartAsync(boolean canStartAsync) {
+ this.canStartAsync = canStartAsync;
+ }
+
// ------------------------------------------------- Request Public Methods
@@ -2183,6 +2198,9 @@
return (null);
return context.getServletContext();
}
+ public ServletContext getServletContext0() {
+ return getServletContext();
+ }
/**
@@ -3019,7 +3037,16 @@
if (CHECK_ASYNC && !isAsyncSupported()) {
throw new
IllegalStateException(sm.getString("coyoteRequest.noAsync"));
}
- // FIXME: if (asyncContext != null && !processing) { throw ISE }
+ // ISE if response is closed
+ if (response.isClosed()) {
+ throw new
IllegalStateException(sm.getString("coyoteRequest.closed"));
+ }
+ // ISE if this method is called again without any asynchronous dispatch
+ // ISE if called outside of the subsequent dispatch
+ // ISE if called again within the scope of the same dispatch
+ if (!canStartAsync) {
+ throw new
IllegalStateException(sm.getString("coyoteRequest.cannotStartAsync"));
+ }
LinkedHashMap<AsyncListener, AsyncListenerRegistration> localAsyncListeners
= asyncListeners;
asyncListeners = new LinkedHashMap<AsyncListener,
AsyncListenerRegistration>();
for (AsyncListenerRegistration registration : localAsyncListeners.values()) {
@@ -3028,12 +3055,11 @@
try {
asyncListener.onStartAsync(asyncEvent);
} catch (IOException e) {
- // FIXME: error reporting ? throw new IllegalStateException(e);
+ throw new
IllegalStateException(sm.getString("coyoteRequest.onStartAsyncError",
+ asyncListener.getClass().getName()), e);
}
}
- if (response.isClosed()) {
- throw new
IllegalStateException(sm.getString("coyoteRequest.closed"));
- }
+ canStartAsync = false;
if (asyncContext == null) {
asyncContext = new AsyncContextImpl();
eventMode = true;
@@ -3104,13 +3130,7 @@
if (parts == null) {
parseMultipart();
}
- Part result = parts.get(name);
- if (result == null) {
- // FIXME: error message
- throw new ServletException();
- } else {
- return result;
- }
+ return parts.get(name);
}
@@ -3184,22 +3204,25 @@
}
public void dispatch() {
+ this.servletContext = null;
if (servletRequest == getRequestFacade()) {
// Get the path directly
path = getRequestPathMB().toString();
} else if (servletRequest instanceof HttpServletRequest) {
- // Rebuild the path
- path = ((HttpServletRequest) servletRequest).getRequestURI();
+ // Remap the path to the target context
+ String requestURI = ((HttpServletRequest)
servletRequest).getRequestURI();
+ this.servletContext = getServletContext0().getContext(requestURI);
if (servletContext != null) {
- path = path.substring(servletContext.getContextPath().length());
+ path =
requestURI.substring(servletContext.getContextPath().length());
} else {
- path = path.substring(context.getName().length());
+ throw new
IllegalStateException(sm.getString("coyoteRequest.dispatchNoServletContext",
requestURI));
}
}
resume();
}
public void dispatch(String path) {
+ this.servletContext = null;
this.path = path;
useAttributes = true;
resume();
Modified: trunk/java/org/apache/catalina/core/StandardHostValve.java
===================================================================
--- trunk/java/org/apache/catalina/core/StandardHostValve.java 2009-10-16 23:21:50 UTC
(rev 1203)
+++ trunk/java/org/apache/catalina/core/StandardHostValve.java 2009-10-17 10:45:59 UTC
(rev 1204)
@@ -37,6 +37,7 @@
import org.apache.catalina.valves.ValveBase;
import org.jboss.logging.Logger;
import org.jboss.servlet.http.HttpEvent;
+import org.jboss.servlet.http.HttpEvent.EventType;
/**
@@ -153,8 +154,8 @@
* Process Comet event.
*
* @param request Request to be processed
- * @param response Response to be produced
- * @param valveContext Valve context used to forward to the next Valve
+ * @param response Response to be processed
+ * @param event The event to be processed
*
* @exception IOException if an input/output error occurred
* @exception ServletException if a servlet error occurred
@@ -185,12 +186,27 @@
// Error page processing
response.setSuspended(false);
- Throwable t = (Throwable) request.getAttribute(Globals.EXCEPTION_ATTR);
-
- if (t != null) {
- throwable(request, response, t);
+ if (request.getAsyncContext() == null) {
+ Throwable t = (Throwable) request.getAttribute(Globals.EXCEPTION_ATTR);
+ if (t != null) {
+ throwable(request, response, t);
+ } else {
+ status(request, response);
+ }
} else {
- status(request, response);
+ Request.AsyncContextImpl asyncContext = (Request.AsyncContextImpl)
request.getAsyncContext();
+ if ((event.getType() == EventType.TIMEOUT || event.getType() ==
EventType.ERROR)
+ && request.isEventMode() && asyncContext.getPath() ==
null) {
+ Throwable t = (Throwable) request.getAttribute(Globals.EXCEPTION_ATTR);
+ if (t != null) {
+ throwable(request, response, t);
+ } else {
+ status(request, response);
+ }
+ }
+ if (request.isEventMode() && asyncContext.getPath() == null) {
+ asyncContext.complete();
+ }
}
// Restore the context classloader
Modified: trunk/java/org/apache/catalina/core/StandardWrapperValve.java
===================================================================
--- trunk/java/org/apache/catalina/core/StandardWrapperValve.java 2009-10-16 23:21:50 UTC
(rev 1203)
+++ trunk/java/org/apache/catalina/core/StandardWrapperValve.java 2009-10-17 10:45:59 UTC
(rev 1204)
@@ -52,6 +52,7 @@
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
+import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.Servlet;
@@ -388,6 +389,7 @@
*
* @param request The servlet request to be processed
* @param response The servlet response to be created
+ * @param event The event to be processed
*
* @exception IOException if an input/output error occurs, or is thrown
* by a subsequently invoked Valve, Filter, or Servlet
@@ -398,105 +400,9 @@
throws IOException, ServletException {
// Async processing
- Request.AsyncContextImpl asyncContext = (Request.AsyncContextImpl)
request.getAsyncContext();
+ AsyncContext asyncContext = request.getAsyncContext();
if (asyncContext != null) {
- if (event.getType() == EventType.END || event.getType() == EventType.ERROR
- || event.getType() == EventType.TIMEOUT) {
- // Invoke the listeners with onComplete or onTimeout
- boolean timeout = (event.getType() == EventType.TIMEOUT) ? true : false;
- boolean error = (event.getType() == EventType.ERROR) ? true : false;
- Iterator<AsyncListenerRegistration> asyncListenerRegistrations =
- asyncContext.getAsyncListeners().values().iterator();
- if (timeout && !asyncListenerRegistrations.hasNext()) {
- // FIXME: MUST do an ERROR dispatch to the original URI and MUST set
the response code to 500
- }
- while (asyncListenerRegistrations.hasNext()) {
- AsyncListenerRegistration asyncListenerRegistration =
asyncListenerRegistrations.next();
- AsyncListener asyncListener =
asyncListenerRegistration.getListener();
- try {
- if (timeout) {
- AsyncEvent asyncEvent = new AsyncEvent(asyncContext,
- asyncListenerRegistration.getRequest(),
asyncListenerRegistration.getResponse());
- asyncListener.onTimeout(asyncEvent);
- } else if (error) {
- Throwable t = (Throwable)
request.getAttribute(Globals.EXCEPTION_ATTR);
- AsyncEvent asyncEvent = new AsyncEvent(asyncContext,
- asyncListenerRegistration.getRequest(),
asyncListenerRegistration.getResponse(), t);
- asyncListener.onError(asyncEvent);
- } else {
- AsyncEvent asyncEvent = new AsyncEvent(asyncContext,
- asyncListenerRegistration.getRequest(),
asyncListenerRegistration.getResponse());
- asyncListener.onComplete(asyncEvent);
- }
- } catch (Throwable e) {
-
container.getLogger().error(sm.getString("standardWrapper.async.listenerError",
- getContainer().getName()), e);
- exception(request, response, e);
- }
- }
- } else if (asyncContext.getRunnable() != null) {
- // Execute the runnable
- try {
- asyncContext.getRunnable().run();
- } catch (Throwable e) {
-
container.getLogger().error(sm.getString("standardWrapper.async.runnableError",
- getContainer().getName()), e);
- exception(request, response, e);
- }
- } else if (asyncContext.getPath() != null) {
- // We executed the dispatch
- asyncContext.done();
- // Remap the request, set the dispatch attributes, create the filter
chain
- // and invoke the Servlet
- Context context = (Context) getContainer().getParent();
- ServletContext servletContext = context.getServletContext();
- if (asyncContext.getServletContext() != null) {
- // Cross context
- servletContext = asyncContext.getServletContext();
- }
- ApplicationDispatcher dispatcher =
- (ApplicationDispatcher)
servletContext.getRequestDispatcher(asyncContext.getPath());
- // Invoke the dispatcher async method with the attributes flag
- try {
- dispatcher.async(asyncContext.getRequest(),
asyncContext.getResponse(),
- asyncContext.getUseAttributes());
- } catch (Throwable e) {
-
container.getLogger().error(sm.getString("standardWrapper.async.dispatchError",
- getContainer().getName()), e);
- exception(request, response, e);
- // TODO: the exception must be sent to error
- }
- // If there is no new startAsync, then close the response
- if (!asyncContext.isReady()) {
- // FIXME: see which one is the right one to close the response
- asyncContext.complete();
- /*
- if (asyncContext.getResponse() instanceof ResponseFacade) {
- response.setSuspended(true);
- } else {
- // Close anyway
- try {
- PrintWriter writer = response.getWriter();
- writer.close();
- } catch (IllegalStateException e) {
- try {
- ServletOutputStream stream = response.getOutputStream();
- stream.close();
- } catch (IllegalStateException f) {
- ;
- } catch (IOException f) {
- ;
- }
- } catch (IOException e) {
- ;
- }
- }
- event.close();
- */
- }
- } else {
- throw new
IllegalStateException(sm.getString("standardWrapper.async.invalidContext"));
- }
+ async(request, response, event);
return;
}
@@ -660,6 +566,99 @@
}
+ /**
+ * Process an async dispatch. This is never a direct wrapper invocation.
+ *
+ * @param request The servlet request to be processed
+ * @param response The servlet response to be processed
+ * @param event The event to be processed
+ *
+ * @exception IOException if an input/output error occurs, or is thrown
+ * by a subsequently invoked Valve, Filter, or Servlet
+ * @exception ServletException if a servlet error occurs, or is thrown
+ * by a subsequently invoked Valve, Filter, or Servlet
+ */
+ public void async(Request request, Response response, HttpEvent event)
+ throws IOException, ServletException {
+
+ Request.AsyncContextImpl asyncContext = (Request.AsyncContextImpl)
request.getAsyncContext();
+ if (asyncContext != null) {
+ if (event.getType() == EventType.END || event.getType() == EventType.ERROR
+ || event.getType() == EventType.TIMEOUT) {
+ // Invoke the listeners with onComplete or onTimeout
+ boolean timeout = (event.getType() == EventType.TIMEOUT) ? true : false;
+ boolean error = (event.getType() == EventType.ERROR) ? true : false;
+ Iterator<AsyncListenerRegistration> asyncListenerRegistrations =
+ asyncContext.getAsyncListeners().values().iterator();
+ while (asyncListenerRegistrations.hasNext()) {
+ AsyncListenerRegistration asyncListenerRegistration =
asyncListenerRegistrations.next();
+ AsyncListener asyncListener =
asyncListenerRegistration.getListener();
+ try {
+ if (timeout) {
+ AsyncEvent asyncEvent = new AsyncEvent(asyncContext,
+ asyncListenerRegistration.getRequest(),
asyncListenerRegistration.getResponse());
+ asyncListener.onTimeout(asyncEvent);
+ } else if (error) {
+ Throwable t = (Throwable)
request.getAttribute(Globals.EXCEPTION_ATTR);
+ AsyncEvent asyncEvent = new AsyncEvent(asyncContext,
+ asyncListenerRegistration.getRequest(),
asyncListenerRegistration.getResponse(), t);
+ asyncListener.onError(asyncEvent);
+ } else {
+ AsyncEvent asyncEvent = new AsyncEvent(asyncContext,
+ asyncListenerRegistration.getRequest(),
asyncListenerRegistration.getResponse());
+ asyncListener.onComplete(asyncEvent);
+ }
+ } catch (Throwable e) {
+
container.getLogger().error(sm.getString("standardWrapper.async.listenerError",
+ getContainer().getName()), e);
+ exception(request, response, e);
+ }
+ }
+ } else if (asyncContext.getRunnable() != null) {
+ // Execute the runnable
+ try {
+ asyncContext.getRunnable().run();
+ } catch (Throwable e) {
+
container.getLogger().error(sm.getString("standardWrapper.async.runnableError",
+ getContainer().getName()), e);
+ exception(request, response, e);
+ }
+ } else if (asyncContext.getPath() != null) {
+ // We executed the dispatch
+ asyncContext.done();
+ // Remap the request, set the dispatch attributes, create the filter
chain
+ // and invoke the Servlet
+ Context context = (Context) getContainer().getParent();
+ ServletContext servletContext = context.getServletContext();
+ if (asyncContext.getServletContext() != null) {
+ // Cross context
+ servletContext = asyncContext.getServletContext();
+ }
+ request.setCanStartAsync(true);
+ ApplicationDispatcher dispatcher =
+ (ApplicationDispatcher)
servletContext.getRequestDispatcher(asyncContext.getPath());
+ // Invoke the dispatcher async method with the attributes flag
+ try {
+ dispatcher.async(asyncContext.getRequest(),
asyncContext.getResponse(),
+ asyncContext.getUseAttributes());
+ } catch (Throwable e) {
+
container.getLogger().error(sm.getString("standardWrapper.async.dispatchError",
+ getContainer().getName()), e);
+ exception(request, response, e);
+ }
+ request.setCanStartAsync(false);
+ // If there is no new startAsync, then close the response
+ // Note: The spec uses the same asyncContext instance
+ if (!asyncContext.isReady()) {
+ asyncContext.complete();
+ }
+ } else {
+ throw new
IllegalStateException(sm.getString("standardWrapper.async.invalidContext"));
+ }
+ }
+
+ }
+
// -------------------------------------------------------- Private Methods
Modified: trunk/java/org/apache/catalina/valves/ValveBase.java
===================================================================
--- trunk/java/org/apache/catalina/valves/ValveBase.java 2009-10-16 23:21:50 UTC (rev
1203)
+++ trunk/java/org/apache/catalina/valves/ValveBase.java 2009-10-17 10:45:59 UTC (rev
1204)
@@ -186,7 +186,8 @@
* the thread that is processing the request.
*
* @param request The servlet request to be processed
- * @param response The servlet response to be created
+ * @param response The servlet response to be processed
+ * @param event The event to be processed
*
* @exception IOException if an input/output error occurs, or is thrown
* by a subsequently invoked Valve, Filter, or Servlet