All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jakarta.enterprise.concurrent.Asynchronous Maven / Gradle / Ivy

Go to download

Jakarta Concurrency provides a specification for using concurrency from application components without compromising container integrity while still preserving the Jakarta EE platform’s fundamental benefits.

The newest version!
/*
 * Copyright (c) 2021,2023 Contributors to the Eclipse Foundation
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package jakarta.enterprise.concurrent;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.CompletableFuture;

import jakarta.enterprise.util.Nonbinding;
import jakarta.interceptor.InterceptorBinding;

/**
 * Annotates a CDI managed bean method to run asynchronously.
 * The CDI managed bean must not be a Jakarta Enterprise Bean,
 * and neither the method nor its class can be annotated with
 * the MicroProfile Asynchronous annotation.
 * 

* The Jakarta EE Product Provider runs the method on a {@link ManagedExecutorService} * and returns to the caller a {@link java.util.concurrent.CompletableFuture 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 * {@link Result#getFuture Asynchronous.Result.getFuture} and * {@link Result#complete Asynchronous.Result.complete} methods. *

* For example, * *

 * {@literal @}Asynchronous
 * public CompletableFuture{@literal } 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 {@literal ->} {
 *     // 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, * *

 * {@literal @}Asynchronous
 * public CompletableFuture{@literal >} 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: *

    *
  • {@link java.util.concurrent.CompletableFuture CompletableFuture}
  • *
  • {@link java.util.concurrent.CompletionStage CompletionStage}
  • *
  • void
  • *
*

* The Jakarta EE Product Provider raises * {@link java.lang.UnsupportedOperationException 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 * {@link java.util.concurrent.CompletionException CompletionException} * with the original exception as the cause, or it can take the equivalent * approach of exceptionally completing the CompletableFuture, using * {@link java.util.concurrent.CompletableFuture#completeExceptionally completeExceptionally} * to supply the original exception as the cause. *

* Except where otherwise stated, the Jakarta EE Product Provider raises * {@link java.util.concurrent.RejectedExecutionException 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 {@link java.util.concurrent.CancellationException 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 * {@link java.lang.UnsupportedOperationException UnsupportedOperationException} * upon invocation of the asynchronous method. * * @since 3.0 */ // TODO the above restrictions on Transactional interceptors could be eliminated // if transaction context propagation is later added to the spec. @Documented @Inherited @InterceptorBinding @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD, ElementType.TYPE }) public @interface Asynchronous { /** * JNDI name of a {@link ManagedExecutorService} or {@link ManagedScheduledExecutorService} * 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 * * @return managed executor service JNDI name. */ @Nonbinding String executor() default "java:comp/DefaultManagedExecutorService"; /** *

Establishes 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 * {@code Asynchronous.Result} which returns a typed {@code CompletableFuture} which matches the return * type of the method. If the method return type is {@code void} then {@code Asynchronous.Result} * will return a {@code CompletableFuture}.

* *

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 {@code max-async} constraints of * {@code managed-scheduled-executor-definition} and * {@code managed-executor-definition} and the corresponding * {@link ManagedScheduledExecutorDefinition#maxAsync()} and * {@link ManagedExecutorDefinition#maxAsync()}.

* *

When a list of multiple {@link 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, *

* *
     * {@literal @}Asynchronous(runAt = {
     *     {@literal @}Schedule(daysOfWeek = { DayOfWeek.TUESDAY, DayOfWeek.THURSDAY }, hours = 8),
     *     {@literal @}Schedule(daysOfweek = DayOfWeek.WEDNESDAY, hours = 10, minutes = 30)
     * })
     * public CompletableFuture{@literal } 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.

* * @return a schedule for the task or an empty array, where the latter indicates * to run once without a schedule. */ @Nonbinding Schedule[] runAt() default {}; /** * Mechanism by which the Jakarta EE Product Provider makes available * to the asynchronous method implementation the same * {@link java.util.concurrent.CompletableFuture CompletableFuture} * instance that the Jakarta EE Product Provider supplies to the caller * of the asynchronous method. *

* Before invoking the asynchronous method implementation on a thread, * the Jakarta EE Product Provider invokes the {@link #setFuture} method * which makes available to the asynchronous method implementation * the same CompletableFuture that the Jakarta EE Product Provider * returns to the caller. *

* The asynchronous method implementation invokes the {@link #getFuture} method * to obtain the same CompletableFuture that the * Jakarta EE Product Provider returns to the caller. * The asynchronous method implementation can choose to complete * this future (normally or exceptionally) or otherwise arrange for its * completion, for example upon completion of a pipeline of completion stages. * Having this same CompletableFuture also enables the asynchronous * method implementation to determine if the caller has forcibly completed * (such as by cancellation or any other means) the CompletableFuture, * in which case the asynchronous method implementation could decide to end * immediately rather than continue processing. *

* For example, * *

     * {@literal @}Asynchronous
     * public CompletableFuture{@literal } hoursWorked(LocalDateTime from, LocalDateTime to) {
     *     CompletableFuture{@literal } future = Asynchronous.Result.getFuture();
     *     if (future.isDone())
     *         return future;
     *
     *     try (Connection con = ((DataSource) InitialContext.doLookup(
     *         "java:comp/env/jdbc/timesheetDB")).getConnection()) {
     *         ...
     *         for (ResultSet result = stmt.executeQuery(); result.next() {@literal &&} !future.isDone(); )
     *             ...
     *         future.complete(total);
     *     } catch (NamingException | SQLException x) {
     *         future.completeExceptionally(x);
     *     }
     *     return future;
     * }
     * 
* * After the asynchronous method completes, the Jakarta EE Product Provider * invokes the {@link #setFuture} method with a null value * to clear it from the thread. * * @since 3.0 */ public static final class Result { private static final ThreadLocal> FUTURES = new ThreadLocal>(); // Prevent instantiation private Result() { } /** * Completes the {@link java.util.concurrent.CompletableFuture CompletableFuture} * instance that the Jakarta EE Product Provider supplies to the caller of the * asynchronous method. *

* This method must only be invoked by the asynchronous method implementation. * * @param type of result returned by the asynchronous method's CompletableFuture. * @param result result with which to complete the asynchronous method's CompletableFuture. * @return the same CompletableFuture that the container returns to the caller. * @throws IllegalStateException if the CompletableFuture for an asynchronous * method is not present on the thread. */ public static CompletableFuture complete(final T result) { @SuppressWarnings("unchecked") CompletableFuture future = (CompletableFuture) FUTURES.get(); if (future == null) { throw new IllegalStateException(); } future.complete(result); return future; } /** * Obtains the same {@link java.util.concurrent.CompletableFuture CompletableFuture} * instance that the Jakarta EE Product Provider supplies to the caller of the * asynchronous method. *

* This method must only be invoked by the asynchronous method implementation. * * @param type of result returned by the asynchronous method's CompletableFuture. * @return the same CompletableFuture that the container returns to the caller. * @throws IllegalStateException if the CompletableFuture for an asynchronous * method is not present on the thread. */ public static CompletableFuture getFuture() { @SuppressWarnings("unchecked") CompletableFuture future = (CompletableFuture) FUTURES.get(); if (future == null) { throw new IllegalStateException(); } return future; } /** * Before invoking the asynchronous method implementation on a thread, * the Jakarta EE Product Provider invokes this method to make available * to the asynchronous method implementation the same CompletableFuture * that the Jakarta EE Product Provider returns to the caller. *

* After the asynchronous method completes, the Jakarta EE Product Provider * invokes this method with a null value * to clear it from the thread. *

* This method must only be invoked by the Jakarta EE Product Provider. * * @param type of result returned by the asynchronous method's CompletableFuture. * @param future CompletableFuture that the container returns to the caller, * or null to clear it. */ public static void setFuture(final CompletableFuture future) { if (future == null) { FUTURES.remove(); } else { FUTURES.set(future); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy