Skip to content

Commit

Permalink
Fix jakartaee#412 AsyncContext execute
Browse files Browse the repository at this point in the history
Fix jakartaee#412 AsyncContext is an Executor that mutually excludes from other container invocations for the same request.
  • Loading branch information
gregw committed Jul 29, 2021
1 parent 54c8edb commit e6dccb7
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 13 deletions.
77 changes: 70 additions & 7 deletions api/src/main/java/jakarta/servlet/AsyncContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

package jakarta.servlet;

import java.util.concurrent.Executor;

/**
* Class representing the execution context for an asynchronous operation that was initiated on a ServletRequest.
*
Expand All @@ -30,15 +32,15 @@
* <ol>
* <li>Invoke, at their {@link AsyncListener#onTimeout onTimeout} method, all {@link AsyncListener} instances registered
* with the ServletRequest on which the asynchronous operation was initiated.</li>
* <li>If none of the listeners called {@link #complete} or any of the {@link #dispatch} methods, perform an error
* <li>If none of the listeners called {@link #complete} or any of the {@link #execute} methods, perform an error
* dispatch with a status code equal to <tt>HttpServletResponse.SC_INTERNAL_SERVER_ERROR</tt>.</li>
* <li>If no matching error page was found, or the error page did not call {@link #complete} or any of the
* {@link #dispatch} methods, call {@link #complete}.</li>
* {@link #execute} methods, call {@link #complete}.</li>
* </ol>
*
* @since Servlet 3.0
*/
public interface AsyncContext {
public interface AsyncContext extends Executor {

/**
* The name of the request attribute under which the original request URI is made available to the target of a
Expand Down Expand Up @@ -82,7 +84,7 @@ public interface AsyncContext {
*
* @return the request that was used to initialize this AsyncContext
*
* @exception IllegalStateException if {@link #complete} or any of the {@link #dispatch} methods has been called in the
* @exception IllegalStateException if {@link #complete} or any of the {@link #execute} methods has been called in the
* asynchronous cycle
*/
public ServletRequest getRequest();
Expand All @@ -93,7 +95,7 @@ public interface AsyncContext {
*
* @return the response that was used to initialize this AsyncContext
*
* @exception IllegalStateException if {@link #complete} or any of the {@link #dispatch} methods has been called in the
* @exception IllegalStateException if {@link #complete} or any of the {@link #execute} methods has been called in the
* asynchronous cycle
*/
public ServletResponse getResponse();
Expand Down Expand Up @@ -175,11 +177,11 @@ public interface AsyncContext {
* <li>Invoke, at their {@link AsyncListener#onError onError} method, all {@link AsyncListener} instances registered
* with the ServletRequest for which this AsyncContext was created, and make the caught <tt>Throwable</tt> available via
* {@link AsyncEvent#getThrowable}.</li>
* <li>If none of the listeners called {@link #complete} or any of the {@link #dispatch} methods, perform an error
* <li>If none of the listeners called {@link #complete} or any of the {@link #execute} methods, perform an error
* dispatch with a status code equal to <tt>HttpServletResponse.SC_INTERNAL_SERVER_ERROR</tt>, and make the above
* <tt>Throwable</tt> available as the value of the <tt>RequestDispatcher.ERROR_EXCEPTION</tt> request attribute.</li>
* <li>If no matching error page was found, or the error page did not call {@link #complete} or any of the
* {@link #dispatch} methods, call {@link #complete}.</li>
* {@link #execute} methods, call {@link #complete}.</li>
* </ol>
*
* <p>
Expand All @@ -188,6 +190,14 @@ public interface AsyncContext {
* within the same asynchronous cycle will result in an IllegalStateException. If startAsync is subsequently called on
* the dispatched request, then any of the dispatch or {@link #complete} methods may be called.
*
* <p>
* This method should be called from a container managed invocation associated with the same request, such as
* {@link Servlet#service(ServletRequest, ServletResponse)},
* {@link Filter#doFilter(ServletRequest, ServletResponse, FilterChain)}, {@link ReadListener#onDataAvailable()},
* {@link WriteListener#onWritePossible()}, {@link AsyncContext#execute(Runnable)}, {@link AsyncContext#run(Runnable)},
* etc. A container may log a warning the first time this method is called from a non container managed thread and
* future versions of the specification may prohibit such calls.
*
* @throws IllegalStateException if one of the dispatch methods has been called and the startAsync method has not been
* called during the resulting dispatch, or if {@link #complete} was called
*
Expand Down Expand Up @@ -218,6 +228,14 @@ public interface AsyncContext {
* <p>
* See {@link #dispatch()} for additional details, including error handling.
*
* <p>
* This method should be called from a container managed invocation associated with the same request, such as
* {@link Servlet#service(ServletRequest, ServletResponse)},
* {@link Filter#doFilter(ServletRequest, ServletResponse, FilterChain)}, {@link ReadListener#onDataAvailable()},
* {@link WriteListener#onWritePossible()}, {@link AsyncContext#execute(Runnable)}, {@link AsyncContext#run(Runnable)},
* etc. A container may log a warning the first time this method is called from a non container managed thread and
* future versions of the specification may prohibit such calls.
*
* @param path the path of the dispatch target, scoped to the ServletContext from which this AsyncContext was
* initialized
*
Expand Down Expand Up @@ -252,6 +270,14 @@ public interface AsyncContext {
* <p>
* See {@link #dispatch()} for additional details, including error handling.
*
* <p>
* This method should be called from a container managed invocation associated with the same request, such as
* {@link Servlet#service(ServletRequest, ServletResponse)},
* {@link Filter#doFilter(ServletRequest, ServletResponse, FilterChain)}, {@link ReadListener#onDataAvailable()},
* {@link WriteListener#onWritePossible()}, {@link AsyncContext#execute(Runnable)}, {@link AsyncContext#run(Runnable)},
* etc. A container may log a warning the first time this method is called from a non container managed thread and
* future versions of the specification may prohibit such calls.
*
* @param context the ServletContext of the dispatch target
* @param path the path of the dispatch target, scoped to the given ServletContext
*
Expand All @@ -277,17 +303,54 @@ public interface AsyncContext {
* <tt>startAsync</tt> has returned to the container, then the call will not take effect (and any invocations of
* {@link AsyncListener#onComplete(AsyncEvent)} will be delayed) until after the container-initiated dispatch has
* returned to the container.
*
* <p>
* This method should be called from a container managed invocation associated with the same request, such as
* {@link Servlet#service(ServletRequest, ServletResponse)},
* {@link Filter#doFilter(ServletRequest, ServletResponse, FilterChain)}, {@link ReadListener#onDataAvailable()},
* {@link WriteListener#onWritePossible()}, {@link AsyncContext#execute(Runnable)}, {@link AsyncContext#run(Runnable)},
* etc. A container may log a warning the first time this method is called from a non container managed thread and
* future versions of the specification may prohibit such calls.
*/
public void complete();

/**
* Causes the container to dispatch a thread, possibly from a managed thread pool, to run the specified
* <tt>Runnable</tt>. The container may propagate appropriate contextual information to the <tt>Runnable</tt>.
*
* @see #dispatch()
* @param run the asynchronous handler
*/
public void start(Runnable run);

/**
* Causes the container to dispatch a thread, possibly from a managed thread pool, to run the specified
* <tt>Runnable</tt>. The execution will be mutually excluded from all other container managed invocations for the same
* request, such as {@link Servlet#service(ServletRequest, ServletResponse)},
* {@link Filter#doFilter(ServletRequest, ServletResponse, FilterChain)}, {@link ReadListener#onDataAvailable()},
* {@link WriteListener#onWritePossible()}, {@link AsyncContext#execute(Runnable)}, {@link AsyncContext#run(Runnable)},
* etc. The container may propagate appropriate contextual information to the <tt>Runnable</tt>.
*
* @see #start(Runnable)
* @param run the asynchronous handler
*/
@Override
public default void execute(Runnable run) {
start(() -> run(run));
}

/**
* Executes the given command in the calling thread, mutually excluded from all other container managed invocations for
* the same request, such as {@link Servlet#service(ServletRequest, ServletResponse)},
* {@link Filter#doFilter(ServletRequest, ServletResponse, FilterChain)}, {@link ReadListener#onDataAvailable()},
* {@link WriteListener#onWritePossible()}, {@link AsyncContext#execute(Runnable)}, {@link AsyncContext#run(Runnable)},
* etc. The container may propagate appropriate contextual information to the <tt>Runnable</tt>.
*
* @see #start(Runnable)
* @param run the asynchronous handler
*/
public void run(Runnable run);

/**
* Registers the given {@link AsyncListener} with the most recent asynchronous cycle that was started by a call to one
* of the {@link ServletRequest#startAsync} methods.
Expand Down
8 changes: 4 additions & 4 deletions api/src/main/java/jakarta/servlet/ServletRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ public interface ServletRequest {
*
* @throws IllegalStateException if this request is within the scope of a filter or servlet that does not support
* asynchronous operations (that is, {@link #isAsyncSupported} returns false), or if this method is called again without
* any asynchronous dispatch (resulting from one of the {@link AsyncContext#dispatch} methods), is called outside the
* any asynchronous dispatch (resulting from one of the {@link AsyncContext#execute} methods), is called outside the
* scope of any such dispatch, or is called again within the scope of the same dispatch, or if the response has already
* been closed
*
Expand Down Expand Up @@ -494,7 +494,7 @@ public interface ServletRequest {
*
* @throws IllegalStateException if this request is within the scope of a filter or servlet that does not support
* asynchronous operations (that is, {@link #isAsyncSupported} returns false), or if this method is called again without
* any asynchronous dispatch (resulting from one of the {@link AsyncContext#dispatch} methods), is called outside the
* any asynchronous dispatch (resulting from one of the {@link AsyncContext#execute} methods), is called outside the
* scope of any such dispatch, or is called again within the scope of the same dispatch, or if the response has already
* been closed
*
Expand All @@ -512,7 +512,7 @@ public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse se
*
* <p>
* This method returns <tt>false</tt> if this request was put into asynchronous mode, but has since been dispatched
* using one of the {@link AsyncContext#dispatch} methods or released from asynchronous mode via a call to
* using one of the {@link AsyncContext#execute} methods or released from asynchronous mode via a call to
* {@link AsyncContext#complete}.
*
* @return true if this request has been put into asynchronous mode, false otherwise
Expand Down Expand Up @@ -564,7 +564,7 @@ public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse se
* request dispatched via {@link RequestDispatcher#forward(ServletRequest, ServletResponse)} or
* {@link RequestDispatcher#include(ServletRequest, ServletResponse)} is given as <code>DispatcherType.FORWARD</code> or
* <code>DispatcherType.INCLUDE</code>, respectively, while the dispatcher type of an asynchronous request dispatched
* via one of the {@link AsyncContext#dispatch} methods is given as <code>DispatcherType.ASYNC</code>. Finally, the
* via one of the {@link AsyncContext#execute} methods is given as <code>DispatcherType.ASYNC</code>. Finally, the
* dispatcher type of a request dispatched to an error page by the container's error handling mechanism is given as
* <code>DispatcherType.ERROR</code>.
*
Expand Down
4 changes: 2 additions & 2 deletions api/src/main/java/jakarta/servlet/ServletRequestWrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ public ServletContext getServletContext() {
*
* @throws IllegalStateException if the request is within the scope of a filter or servlet that does not support
* asynchronous operations (that is, {@link #isAsyncSupported} returns false), or if this method is called again without
* any asynchronous dispatch (resulting from one of the {@link AsyncContext#dispatch} methods), is called outside the
* any asynchronous dispatch (resulting from one of the {@link AsyncContext#execute} methods), is called outside the
* scope of any such dispatch, or is called again within the scope of the same dispatch, or if the response has already
* been closed
*
Expand All @@ -372,7 +372,7 @@ public AsyncContext startAsync() throws IllegalStateException {
*
* @throws IllegalStateException if the request is within the scope of a filter or servlet that does not support
* asynchronous operations (that is, {@link #isAsyncSupported} returns false), or if this method is called again without
* any asynchronous dispatch (resulting from one of the {@link AsyncContext#dispatch} methods), is called outside the
* any asynchronous dispatch (resulting from one of the {@link AsyncContext#execute} methods), is called outside the
* scope of any such dispatch, or is called again within the scope of the same dispatch, or if the response has already
* been closed
*
Expand Down

0 comments on commit e6dccb7

Please sign in to comment.