The Jakarta EE Product Provider runs the method on a ManagedExecutorService
and returns to the caller a CompletableFuture
that is backed by the same ManagedExecutorService
to represent the execution of the method. The ManagedExecutorService
is the default asynchronous execution facility for the CompletableFuture
and and all dependent stages that are created from those, and so on,
as defined by the ManagedExecutorService
JavaDoc API.
The Jakarta EE Product Provider makes this CompletableFuture
available
to the asynchronous method implementation via the
Asynchronous.Result.getFuture
and
Asynchronous.Result.complete
methods.
For example,
@Asynchronous public CompletableFuture<Double> hoursWorked(LocalDate from, LocalDate to) { // Application component's context is made available to the async method, try (Connection con = ((DataSource) InitialContext.doLookup( "java:comp/env/jdbc/timesheetDB")).getConnection()) { ... return Asynchronous.Result.complete(total); } catch (NamingException | SQLException x) { throw new CompletionException(x); } }with usage,
hoursWorked(mon, fri).thenAccept(total -> { // Application component's context is made available to dependent stage actions, DataSource ds = InitialContext.doLookup( "java:comp/env/jdbc/payrollDB"); ... });When the asynchronous method implementation returns a different
CompletableFuture
instance, the Jakarta EE Product Provider
uses the completion of that instance to complete the CompletableFuture
that the Jakarta EE Product Provider returns to the caller,
completing it with the same result or exception.
For example,
@Asynchronous public CompletableFuture<List<Itinerary>> findSingleLayoverFlights(Location source, Location dest) { try { ManagedExecutorService executor = InitialContext.doLookup( "java:comp/DefaultManagedExecutorService"); return executor.supplyAsync(source::flightsFrom) .thenCombine(executor.completedFuture(dest.flightsTo()), Itinerary::sourceMatchingDest); } catch (NamingException x) { throw new CompletionException(x); } }with usage,
findSingleLayoverFlights(RST, DEN).thenApply(Itinerary::sortByPrice);
Methods with the following return types can be annotated to be asynchronous methods:
The Jakarta EE Product Provider raises
UnsupportedOperationException
if other return types are used or if the annotation is placed at the class
level. The injection target of ElementType.TYPE
is to be used only
by the CDI extension that is implemented by the Jakarta EE Product Provider to
register the asynchronous method interceptor. Applications must only use the
asynchronous method annotation at method level.
Exceptions that are raised by asynchronous methods are not raised directly
to the caller because the method runs asynchronously to the caller.
Instead, the CompletableFuture
that represents the result
is completed with the raised exception. Asynchronous methods are
discouraged from raising checked exceptions because checked exceptions
force the caller to write exception handling code that is unreachable.
When a checked exception occurs, the asynchronous method implementation
can flow the exception back to the resulting CompletableFuture
either by raising a
CompletionException
with the original exception as the cause, or it can take the equivalent
approach of exceptionally completing the CompletableFuture
, using
completeExceptionally
to supply the original exception as the cause.
Except where otherwise stated, the Jakarta EE Product Provider raises
RejectedExecutionException
upon invocation of the asynchronous method if evident upfront that it cannot
be accepted, for example if the JNDI name is not valid or points to something
other than a managed executor resource. If determined at a later point that the
asynchronous method cannot run (for example, if unable to establish thread context),
then the Jakarta EE Product Provider completes the CompletableFuture
exceptionally with CancellationException
,
and chains a cause exception if there is any.
The Jakarta EE Product Provider must assign the interceptor for asynchronous methods
to have priority of Interceptor.Priority.PLATFORM_BEFORE + 5
.
Interceptors with a lower priority, such as Transactional
, must run on
the thread where the asynchronous method executes, rather than on the submitting thread.
When an asynchronous method is annotated as Transactional
,
the transactional types which can be used are:
TxType.REQUIRES_NEW
, which causes the method to run in a new transaction, and
TxType.NOT_SUPPORTED
, which causes the method to run with no transaction.
All other transaction attributes must result in
UnsupportedOperationException
upon invocation of the asynchronous method.
- Since:
- 3.0
-
Nested Class Summary
Modifier and TypeClassDescriptionstatic final class
Mechanism by which the Jakarta EE Product Provider makes available to the asynchronous method implementation the sameCompletableFuture
instance that the Jakarta EE Product Provider supplies to the caller of the asynchronous method. -
Optional Element Summary
Modifier and TypeOptional ElementDescriptionJNDI name of aManagedExecutorService
orManagedScheduledExecutorService
upon which to run the asynchronous method.Schedule[]
Establishes a schedule for repeated execution of the method.
-
Element Details
-
executor
String executorJNDI name of aManagedExecutorService
orManagedScheduledExecutorService
upon which to run the asynchronous method.The default value is the JNDI name of the built-in
ManagedExecutorService
that is provided by the Jakarta EE platform provider:
java:comp/DefaultManagedExecutorService
- Returns:
- managed executor service JNDI name.
- Default:
- "java:comp/DefaultManagedExecutorService"
-
runAt
Schedule[] runAtEstablishes a schedule for repeated execution of the method. A single future represents the completion of all executions in the schedule. The Jakarta EE product attempts to run the method at the scheduled times until its future is completed or the method returns a non-null result value or raises an exception. The future is always accessible from within the method via
Asynchronous.Result
which returns a typedCompletableFuture
which matches the return type of the method. If the method return type isvoid
thenAsynchronous.Result
will return aCompletableFuture<Void>
.Computation of the start time for the next execution occurs after the completion of the current execution. This prevents overlap of executions from the same asynchronous method request. Scheduled execution times that overlap a prior execution that is still running are skipped. For example, if an asynchronous method is scheduled to run every minute on the minute and execution of the method starts at 8:00 AM, lasting for 2 minutes and 10 seconds, then the Jakarta EE product attempts to start the next execution of the method at 8:03 AM.
Scheduled asynchronous methods are treated similar to other scheduled tasks in that they are not subject to
max-async
constraints ofmanaged-scheduled-executor-definition
andmanaged-executor-definition
and the correspondingManagedScheduledExecutorDefinition.maxAsync()
andManagedExecutorDefinition.maxAsync()
.When a list of multiple
Schedule
annotations is specified, the next execution time is computed according to each, choosing the closest future time after the current time. This allows composite schedules such as,@Asynchronous(runAt = { @Schedule(daysOfWeek = { DayOfWeek.TUESDAY, DayOfWeek.THURSDAY }, hours = 8), @Schedule(daysOfweek = DayOfWeek.WEDNESDAY, hours = 10, minutes = 30) }) public CompletableFuture<String> attendLectureAndLab(String course) { ... if (endOfSemester) return Asynchronous.Result.complete(courseRecord); else return null; // continue at next scheduled time } ... student.attendLectureAndLab(courseName).thenApply(this::assignGrade);
The default value of empty array indicates that the task does not run on a schedule and instead runs one time.
- Returns:
- a schedule for the task or an empty array, where the latter indicates to run once without a schedule.
- Default:
- {}
-