From cc5a6d4d18b929760a8f1b3b86563ca3c3bf8496 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Thu, 29 Jul 2021 15:09:58 +1000 Subject: [PATCH 1/3] Fix #412 AsyncContext execute Fix #412 AsyncContext is an Executor that mutually excludes from other container invocations for the same request. --- .../java/jakarta/servlet/AsyncContext.java | 68 ++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/api/src/main/java/jakarta/servlet/AsyncContext.java b/api/src/main/java/jakarta/servlet/AsyncContext.java index 2f9620040..1e4cc6b0a 100644 --- a/api/src/main/java/jakarta/servlet/AsyncContext.java +++ b/api/src/main/java/jakarta/servlet/AsyncContext.java @@ -17,6 +17,9 @@ package jakarta.servlet; +import java.util.concurrent.CancellationException; +import java.util.concurrent.Executor; + /** * Class representing the execution context for an asynchronous operation that was initiated on a ServletRequest. * @@ -36,9 +39,17 @@ * {@link #dispatch} methods, call {@link #complete}. * * + *

+ * An AsyncContext is an {@link Executor} that provides mutual exclusion between container managed threads for the + * same request. Specifically container managed threads are used for calls to + * {@link Servlet#service(ServletRequest, ServletResponse)}, + * {@link Filter#doFilter(ServletRequest, ServletResponse, FilterChain)}, + * {@link ReadListener#onDataAvailable()}, {@link WriteListener#onWritePossible()} + * and to call the {@link Runnable}s passed to {@link AsyncContext#execute(Runnable)} and {@link AsyncContext#run(Runnable)}. + * * @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 @@ -188,6 +199,11 @@ 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 thread associated with the same request. + * 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 +234,11 @@ public interface AsyncContext { *

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

+ * This method should be called from a container managed thread associated with the same request. + * 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 +273,11 @@ public interface AsyncContext { *

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

+ * This method should be called from a container managed thread associated with the same request. + * 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,11 @@ 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 thread associated with the same request. + * 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 +315,45 @@ 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 #execute(Runnable) + * @see #run(Runnable) * @param run the asynchronous handler */ public void start(Runnable run); + /** + * Causes the container to dispatch a container managed thread, possibly from a managed thread pool, + * to run the passed Runnable. The execution will be mutually excluded from all other container + * managed threads for the same request. + *

+ * If {@link AsyncContext#complete()} is called, or the request lifecycle completes via a dispatch + * prior to the runnable being run, then it will never be run. + *

+ * The container may propagate appropriate contextual information to the Runnable. + * + * @see #start(Runnable) + * @see #run(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 threads for the same request. + *

+ * The container may propagate appropriate contextual information to the Runnable. + * + * @see #start(Runnable) + * @see #execute(Runnable) + * @param run the asynchronous handler + * @throws CancellationException If {@link AsyncContext#complete()} is called, or the request + * lifecycle completes via a dispatch prior to the runnable being run + */ + public void run(Runnable run) throws CancellationException; + /** * 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. From e0413c2c21a6a0bc55867a32df16df8d6c6977a1 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Thu, 29 Jul 2021 15:15:32 +1000 Subject: [PATCH 2/3] Fix #412 AsyncContext execute reformat --- .../java/jakarta/servlet/AsyncContext.java | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/api/src/main/java/jakarta/servlet/AsyncContext.java b/api/src/main/java/jakarta/servlet/AsyncContext.java index 1e4cc6b0a..b224da3aa 100644 --- a/api/src/main/java/jakarta/servlet/AsyncContext.java +++ b/api/src/main/java/jakarta/servlet/AsyncContext.java @@ -40,12 +40,12 @@ * * *

- * An AsyncContext is an {@link Executor} that provides mutual exclusion between container managed threads for the - * same request. Specifically container managed threads are used for calls to + * An AsyncContext is an {@link Executor} that provides mutual exclusion between container managed threads for the same + * request. Specifically container managed threads are used for calls to * {@link Servlet#service(ServletRequest, ServletResponse)}, - * {@link Filter#doFilter(ServletRequest, ServletResponse, FilterChain)}, - * {@link ReadListener#onDataAvailable()}, {@link WriteListener#onWritePossible()} - * and to call the {@link Runnable}s passed to {@link AsyncContext#execute(Runnable)} and {@link AsyncContext#run(Runnable)}. + * {@link Filter#doFilter(ServletRequest, ServletResponse, FilterChain)}, {@link ReadListener#onDataAvailable()}, + * {@link WriteListener#onWritePossible()} and to call the {@link Runnable}s passed to + * {@link AsyncContext#execute(Runnable)} and {@link AsyncContext#run(Runnable)}. * * @since Servlet 3.0 */ @@ -200,9 +200,9 @@ public interface AsyncContext extends Executor { * the dispatched request, then any of the dispatch or {@link #complete} methods may be called. * *

- * This method should be called from a container managed thread associated with the same request. - * 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. + * This method should be called from a container managed thread associated with the same request. 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 @@ -235,9 +235,9 @@ public interface AsyncContext extends Executor { * See {@link #dispatch()} for additional details, including error handling. * *

- * This method should be called from a container managed thread associated with the same request. - * 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. + * This method should be called from a container managed thread associated with the same request. 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 @@ -274,9 +274,9 @@ public interface AsyncContext extends Executor { * See {@link #dispatch()} for additional details, including error handling. * *

- * This method should be called from a container managed thread associated with the same request. - * 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. + * This method should be called from a container managed thread associated with the same request. 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 @@ -305,9 +305,9 @@ public interface AsyncContext extends Executor { * returned to the container. * *

- * This method should be called from a container managed thread associated with the same request. - * 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. + * This method should be called from a container managed thread associated with the same request. 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(); @@ -322,12 +322,12 @@ public interface AsyncContext extends Executor { public void start(Runnable run); /** - * Causes the container to dispatch a container managed thread, possibly from a managed thread pool, - * to run the passed Runnable. The execution will be mutually excluded from all other container - * managed threads for the same request. + * Causes the container to dispatch a container managed thread, possibly from a managed thread pool, to run the passed + * Runnable. The execution will be mutually excluded from all other container managed threads for the same + * request. *

- * If {@link AsyncContext#complete()} is called, or the request lifecycle completes via a dispatch - * prior to the runnable being run, then it will never be run. + * If {@link AsyncContext#complete()} is called, or the request lifecycle completes via a dispatch prior to the runnable + * being run, then it will never be run. *

* The container may propagate appropriate contextual information to the Runnable. * @@ -341,16 +341,16 @@ public default void execute(Runnable run) { } /** - * Executes the given command in the calling thread, mutually excluded from all other container - * managed threads for the same request. + * Executes the given command in the calling thread, mutually excluded from all other container managed threads for the + * same request. *

* The container may propagate appropriate contextual information to the Runnable. * * @see #start(Runnable) * @see #execute(Runnable) * @param run the asynchronous handler - * @throws CancellationException If {@link AsyncContext#complete()} is called, or the request - * lifecycle completes via a dispatch prior to the runnable being run + * @throws CancellationException If {@link AsyncContext#complete()} is called, or the request lifecycle completes via a + * dispatch prior to the runnable being run */ public void run(Runnable run) throws CancellationException; From 161d173daabd30f4f6692866b008003a215a7172 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Thu, 21 Oct 2021 09:04:42 +1100 Subject: [PATCH 3/3] updated and simplified from review --- .../java/jakarta/servlet/AsyncContext.java | 59 +++++++------------ 1 file changed, 20 insertions(+), 39 deletions(-) diff --git a/api/src/main/java/jakarta/servlet/AsyncContext.java b/api/src/main/java/jakarta/servlet/AsyncContext.java index b224da3aa..043052efb 100644 --- a/api/src/main/java/jakarta/servlet/AsyncContext.java +++ b/api/src/main/java/jakarta/servlet/AsyncContext.java @@ -17,7 +17,6 @@ package jakarta.servlet; -import java.util.concurrent.CancellationException; import java.util.concurrent.Executor; /** @@ -41,11 +40,11 @@ * *

* An AsyncContext is an {@link Executor} that provides mutual exclusion between container managed threads for the same - * request. Specifically container managed threads are used for calls to + * request. Specifically container managed threads are mutually excluded from simultaneous calls to * {@link Servlet#service(ServletRequest, ServletResponse)}, * {@link Filter#doFilter(ServletRequest, ServletResponse, FilterChain)}, {@link ReadListener#onDataAvailable()}, - * {@link WriteListener#onWritePossible()} and to call the {@link Runnable}s passed to - * {@link AsyncContext#execute(Runnable)} and {@link AsyncContext#run(Runnable)}. + * {@link WriteListener#onWritePossible()} and to execution of the {@link Runnable}s passed to + * {@link AsyncContext#execute(Runnable)}. * * @since Servlet 3.0 */ @@ -200,9 +199,9 @@ public interface AsyncContext extends Executor { * the dispatched request, then any of the dispatch or {@link #complete} methods may be called. * *

- * This method should be called from a container managed thread associated with the same request. 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. + * Best practise is to call this method in a way that is mutually excluded from other threads active on the same + * request. This can be achieved by only calling the method from a container managed thread. A container may issue a + * warning if a non managed thread is used 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 @@ -235,9 +234,9 @@ public interface AsyncContext extends Executor { * See {@link #dispatch()} for additional details, including error handling. * *

- * This method should be called from a container managed thread associated with the same request. 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. + * Best practise is to call this method in a way that is mutually excluded from other threads active on the same + * request. This can be achieved by only calling the method from a container managed thread. A container may issue a + * warning if a non managed thread is used 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 @@ -274,9 +273,9 @@ public interface AsyncContext extends Executor { * See {@link #dispatch()} for additional details, including error handling. * *

- * This method should be called from a container managed thread associated with the same request. 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. + * Best practise is to call this method in a way that is mutually excluded from other threads active on the same + * request. This can be achieved by only calling the method from a container managed thread. A container may issue a + * warning if a non managed thread is used 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 @@ -305,9 +304,9 @@ public interface AsyncContext extends Executor { * returned to the container. * *

- * This method should be called from a container managed thread associated with the same request. 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. + * Best practise is to call this method in a way that is mutually excluded from other threads active on the same + * request. This can be achieved by only calling the method from a container managed thread. A container may issue a + * warning if a non managed thread is used and future versions of the specification may prohibit such calls. */ public void complete(); @@ -316,15 +315,15 @@ public interface AsyncContext extends Executor { * Runnable. The container may propagate appropriate contextual information to the Runnable. * * @see #execute(Runnable) - * @see #run(Runnable) * @param run the asynchronous handler */ public void start(Runnable run); /** - * Causes the container to dispatch a container managed thread, possibly from a managed thread pool, to run the passed - * Runnable. The execution will be mutually excluded from all other container managed threads for the same - * request. + * Execute the passed {@link Runnable} mutually excluded from all other container managed threads for the same request. + * The execution may be done by the calling thread, which will momentarily be a container managed thread; or by another + * container managed thread, possibly from a managed thread pool. Since execution of the {@link Runnable} will exclude + * other container managed methods, this method is not intended for long-running tasks, nor ones that may block. *

* If {@link AsyncContext#complete()} is called, or the request lifecycle completes via a dispatch prior to the runnable * being run, then it will never be run. @@ -332,27 +331,10 @@ public interface AsyncContext extends Executor { * The container may propagate appropriate contextual information to the Runnable. * * @see #start(Runnable) - * @see #run(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 threads for the - * same request. - *

- * The container may propagate appropriate contextual information to the Runnable. - * - * @see #start(Runnable) - * @see #execute(Runnable) - * @param run the asynchronous handler - * @throws CancellationException If {@link AsyncContext#complete()} is called, or the request lifecycle completes via a - * dispatch prior to the runnable being run - */ - public void run(Runnable run) throws CancellationException; + public void execute(Runnable run); /** * Registers the given {@link AsyncListener} with the most recent asynchronous cycle that was started by a call to one @@ -471,5 +453,4 @@ public default void execute(Runnable run) { * @return the timeout in milliseconds */ public long getTimeout(); - }