diff --git a/api/src/main/java/jakarta/servlet/AsyncContext.java b/api/src/main/java/jakarta/servlet/AsyncContext.java index 2f9620040..8df88bac4 100644 --- a/api/src/main/java/jakarta/servlet/AsyncContext.java +++ b/api/src/main/java/jakarta/servlet/AsyncContext.java @@ -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. * @@ -30,15 +32,15 @@ *
    *
  1. Invoke, at their {@link AsyncListener#onTimeout onTimeout} method, all {@link AsyncListener} instances registered * with the ServletRequest on which the asynchronous operation was initiated.
  2. - *
  3. If none of the listeners called {@link #complete} or any of the {@link #dispatch} methods, perform an error + *
  4. 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 HttpServletResponse.SC_INTERNAL_SERVER_ERROR.
  5. *
  6. 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}.
  7. + * {@link #execute} methods, call {@link #complete}. *
* * @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 @@ -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(); @@ -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(); @@ -175,11 +177,11 @@ public interface AsyncContext { *
  • 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 Throwable available via * {@link AsyncEvent#getThrowable}.
  • - *
  • If none of the listeners called {@link #complete} or any of the {@link #dispatch} methods, perform an error + *
  • 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 HttpServletResponse.SC_INTERNAL_SERVER_ERROR, and make the above * Throwable available as the value of the RequestDispatcher.ERROR_EXCEPTION request attribute.
  • *
  • 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}.
  • + * {@link #execute} methods, call {@link #complete}. * * *

    @@ -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. * + *

    + * 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 * @@ -218,6 +228,14 @@ public interface AsyncContext { *

    * See {@link #dispatch()} for additional details, including error handling. * + *

    + * 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 * @@ -252,6 +270,14 @@ public interface AsyncContext { *

    * See {@link #dispatch()} for additional details, including error handling. * + *

    + * 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 * @@ -277,6 +303,14 @@ public interface AsyncContext { * startAsync 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. + * + *

    + * 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(); @@ -284,10 +318,39 @@ public interface AsyncContext { * Causes the container to dispatch a thread, possibly from a managed thread pool, to run the specified * Runnable. The container may propagate appropriate contextual information to the Runnable. * + * @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 + * Runnable. 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 Runnable. + * + * @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 Runnable. + * + * @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. diff --git a/api/src/main/java/jakarta/servlet/ServletRequest.java b/api/src/main/java/jakarta/servlet/ServletRequest.java index 765955cac..c15612488 100644 --- a/api/src/main/java/jakarta/servlet/ServletRequest.java +++ b/api/src/main/java/jakarta/servlet/ServletRequest.java @@ -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 * @@ -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 * @@ -512,7 +512,7 @@ public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse se * *

    * This method returns false 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 @@ -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 DispatcherType.FORWARD or * DispatcherType.INCLUDE, respectively, while the dispatcher type of an asynchronous request dispatched - * via one of the {@link AsyncContext#dispatch} methods is given as DispatcherType.ASYNC. Finally, the + * via one of the {@link AsyncContext#execute} methods is given as DispatcherType.ASYNC. Finally, the * dispatcher type of a request dispatched to an error page by the container's error handling mechanism is given as * DispatcherType.ERROR. * diff --git a/api/src/main/java/jakarta/servlet/ServletRequestWrapper.java b/api/src/main/java/jakarta/servlet/ServletRequestWrapper.java index 73b94ab3f..93e36bd35 100644 --- a/api/src/main/java/jakarta/servlet/ServletRequestWrapper.java +++ b/api/src/main/java/jakarta/servlet/ServletRequestWrapper.java @@ -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 * @@ -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 *