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

jakarta.enterprise.concurrent.ContextService 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) 2010, 2021 Oracle and/or its affiliates. All rights reserved.
 *
 * 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.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Executor;
import java.util.concurrent.Flow;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * The ContextService provides methods for creating dynamic proxy objects
 * (as defined by {@link java.lang.reflect.Proxy java.lang.reflect.Proxy}).
 * ContextService also creates proxy objects for functional interfaces
 * (such as {@link java.util.function.Function}) that can be used as
 * completion stage actions. Proxy objects run with
 * the addition of context typically associated with applications executing in a
 * Jakarta™ EE environment.
 * Examples of such context are classloading, namespace, security, etc.
 * 

* * The proxy objects follow the same rules as defined for the * {@link java.lang.reflect.Proxy java.lang.reflect.Proxy} class with the following additions: *

    *
  • The proxy instance will retain the context of the creator's * thread. *
  • The proxy instance will implement all of the interfaces specified on the * {@code createContextualProxy} methods. *
  • The object to have a proxy instance created for should not be a * component managed by the Jakarta EE Product Provider, such as a web * component or a Jakarta Enterprise Bean. *
  • All interface method invocations on a proxy instance run in the * creator's context with the exception of {@code hashCode}, * {@code equals}, {@code toString} and all other methods declared in * {@link java.lang.Object}. *
  • The proxy instance must implement {@link java.io.Serializable} * if the proxied object instance is serializable. *
  • The proxied object instance must implement * {@link java.io.Serializable} if the proxy instance is serialized. *
  • Execution properties can be stored with the proxy instance. Custom * property keys must not begin with "jakarta.enterprise.concurrent.". *
  • Execution properties are to be used for controlling how various contextual * information is retrieved and applied to the thread. Although application * components can store arbitrary property keys and values, it is not * recommended. Jakarta EE product providers may impose limits to the * size of the keys and values. *
*

* For example, to contextualize a single completion stage action * such that it is able to access the namespace of the application component, *

 * contextSvc = InitialContext.doLookup("java:comp/DefaultContextService");
 * stage2 = stage1.thenApply(contextSvc.contextualFunction(i -> {
 *     DataSource ds = InitialContext.doLookup("java:comp/env/dsRef");
 *     try (Connection con = ds.getConnection()) {
 *         PreparedStatement stmt = con.prepareStatement(sql);
 *         stmt.setInt(1, i);
 *         ResultSet result = stmt.executeQuery();
 *         return result.next() ? result.getInt(1) : 0;
 *     }
 * }));
 * 
*

* * @since 1.0 */ public interface ContextService { /** *

Wraps a {@link java.util.concurrent.Callable} with context * that is captured from the thread that invokes * contextualCallable. Context is captured at the time contextualCallable is invoked.

* *

When call is invoked on the proxy instance, * context is first established on the thread that will run the call method, * then the call method of the provided Callable is invoked. * Finally, the previous context is restored on the thread, and the result of the * Callable is returned to the invoker.

* * @param callable result type. * @param callable instance to contextualize. * @return contextualized proxy instance that wraps execution of the call method with context. * @throws IllegalArgumentException if an already-contextualized Callable is supplied to this method. * @since 3.0 */ public Callable contextualCallable(Callable callable); /** *

Wraps a {@link java.util.function.BiConsumer} with context * that is captured from the thread that invokes * contextualConsumer. Context is captured at the time contextualConsumer is invoked.

* *

When accept is invoked on the proxy instance, * context is first established on the thread that will run the accept method, * then the accept method of the provided BiConsumer is invoked. * Finally, the previous context is restored on the thread, and control is returned to the invoker.

* * @param type of first parameter to consumer. * @param type of second parameter to consumer. * @param consumer instance to contextualize. * @return contextualized proxy instance that wraps execution of the accept method with context. * @throws IllegalArgumentException if an already-contextualized BiConsumer is supplied to this method. * @since 3.0 */ public BiConsumer contextualConsumer(BiConsumer consumer); /** *

Wraps a {@link java.util.function.Consumer} with context * that is captured from the thread that invokes * contextualConsumer. Context is captured at the time contextualConsumer is invoked.

* *

When accept is invoked on the proxy instance, * context is first established on the thread that will run the accept method, * then the accept method of the provided Consumer is invoked. * Finally, the previous context is restored on the thread, and control is returned to the invoker.

* * @param type of parameter to consumer. * @param consumer instance to contextualize. * @return contextualized proxy instance that wraps execution of the accept method with context. * @throws IllegalArgumentException if an already-contextualized Consumer is supplied to this method. * @since 3.0 */ public Consumer contextualConsumer(Consumer consumer); /** *

Wraps a {@link java.util.function.BiFunction} with context * that is captured from the thread that invokes * contextualFunction. Context is captured at the time contextualFunction is invoked.

* *

When apply is invoked on the proxy instance, * context is first established on the thread that will run the apply method, * then the apply method of the provided BiFunction is invoked. * Finally, the previous context is restored on the thread, and the result of the * BiFunction is returned to the invoker.

* * @param type of first parameter to function. * @param type of second parameter to function. * @param function result type. * @param function instance to contextualize. * @return contextualized proxy instance that wraps execution of the apply method with context. * @throws IllegalArgumentException if an already-contextualized BiFunction is supplied to this method. * @since 3.0 */ public BiFunction contextualFunction(BiFunction function); /** *

Wraps a {@link java.util.function.BiFunction} with context * that is captured from the thread that invokes * contextualFunction. Context is captured at the time contextualFunction is invoked.

* *

When apply is invoked on the proxy instance, * context is first established on the thread that will run the apply method, * then the apply method of the provided Function is invoked. * Finally, the previous context is restored on the thread, and the result of the * Function is returned to the invoker.

* * @param type of parameter to function. * @param function result type. * @param function instance to contextualize. * @return contextualized proxy instance that wraps execution of the apply method with context. * @throws IllegalArgumentException if an already-contextualized Function is supplied to this method. * @since 3.0 */ public Function contextualFunction(Function function); /** *

Wraps a {@link java.lang.Runnable} with context * that is captured from the thread that invokes * contextualRunnable. Context is captured at the time contextualRunnable is invoked.

* *

When run is invoked on the proxy instance, * context is first established on the thread that will run the run method, * then the run method of the provided Runnable is invoked. * Finally, the previous context is restored on the thread, and control is returned to the invoker.

* * @param runnable instance to contextualize. * @return contextualized proxy instance that wraps execution of the run method with context. * @throws IllegalArgumentException if an already-contextualized Runnable is supplied to this method. * @since 3.0 */ public Runnable contextualRunnable(Runnable runnable); /** *

Wraps a {@link java.util.function.Supplier} with context captured from the thread that invokes * contextualSupplier. Context is captured at the time contextualSupplier is invoked.

* *

When supply is invoked on the proxy instance, * context is first established on the thread that will run the supply method, * then the supply method of the provided Supplier is invoked. * Finally, the previous context is restored on the thread, and the result of the * Supplier is returned to the invoker.

* * @param supplier result type. * @param supplier instance to contextualize. * @return contextualized proxy instance that wraps execution of the supply method with context. * @throws IllegalArgumentException if an already-contextualized Supplier is supplied to this method. * @since 3.0 */ public Supplier contextualSupplier(Supplier supplier); /** *

Wraps a {@link java.util.concurrent.Flow.Subscriber} with context captured from the thread that invokes * contextualSubscriber. Context is captured at the time contextualSubscriber is invoked.

* *

Whenever one of the methods of the {@link java.util.concurrent.Flow.Subscriber} interface (such as onSubscribe, * onNext, onError, or onComplete) is invoked on the proxy instance, * context is first established on the thread that will run the method, * then the method of the provided Flow.Subscriber is invoked. * Finally, the previous context is restored on the thread, and the result of the * Flow.Subscriber method is returned to the invoker.

* * @param the subscribed item type * @param subscriber instance to contextualize * @return contextualized proxy instance that wraps execution of all Subscriber methods. * @since 3.1 */ public Flow.Subscriber contextualSubscriber(Flow.Subscriber subscriber); /** *

Wraps a {@link java.util.concurrent.Flow.Processor} with context captured from the thread that invokes * contextualProcessor. Context is captured at the time contextualProcessor is invoked.

* *

Whenever one of the methods of the {@link java.util.concurrent.Flow.Subscriber} interface (such as onSubscribe, * onNext, onError, or onComplete) from which {@link java.util.concurrent.Flow.Processor} * extends is invoked on the proxy instance, context is first established on the thread that will run the method, * then the method of the provided Flow.Processor is invoked. * Finally, the previous context is restored on the thread, and the result of the * Flow.Processor method is returned to the invoker.

* * @param the subscribed item type * @param the published item type * @param processor instance to contextualize * @return contextualized proxy instance that wraps execution of all methods that are inherited from Subscriber. * @since 3.1 */ public Flow.Processor contextualProcessor(Flow.Processor processor); /** * Creates a new contextual object proxy for the input object instance. *

* Each method invocation will have the context of the application component * instance that created the contextual object proxy. *

* The contextual object is useful when developing or using Java™ SE * threading mechanisms propagating events to other component instances. *

* If the application component that created the proxy is not started or * deployed, all methods on reflected interfaces may throw an * {@link java.lang.IllegalStateException}. *

* For example, to execute a Runnable which is contextualized with the * creator's context using a Java™ SE ExecutorService: * *

   *  public class MyRunnable implements Runnable {
   *      public void run() {
   *          System.out.println("MyRunnable.run with Jakarta EE Context available.");
   *      }
   *  }
   *
   *  InitialContext ctx = new InitialContext();
   *  ThreadFactory threadFactory = (ThreadFactory) ctx
   *           .lookup("java:comp/env/concurrent/ThreadFactory");
   *
   *  ContextService ctxService = (ContextService) ctx
   *           .lookup("java:comp/env/concurrent/ContextService");
   *
   *  MyRunnable myRunnableInstance = ...;
   *
   *  Runnable rProxy = ctxService.createContextualProxy(myRunnableInstance, Runnable.class);
   *
   *  ExecutorService exSvc = Executors.newThreadPool(10, threadFactory);
   *
   *  Future f = exSvc.submit(rProxy);
   * 
*

* * @param instance the instance of the object to proxy. * @param intf the interface that the proxy should implement. * @param the type of the instance to proxy * @return a proxy for the input object that implements the specified interface. * @throws java.lang.IllegalArgumentException - if the {@code intf} argument * is null or the instance does not implement the specified * interface. * @throws java.lang.UnsupportedOperationException - if the {@code intf} * interface is {@link java.io.Serializable serializable} * but a thread context type does not support serialization. */ public T createContextualProxy(T instance, Class intf); /** * Creates a new contextual object proxy for the input object instance. *

* This method is similar to {@code T createContextualProxy(T instance, Class intf)} * except that this method can be used if the proxy has to support multiple * interfaces. *

* Example: * *
   *  public class MyRunnableWork implements Runnable, SomeWorkInterface {
   *      public void run() {
   *          System.out.println("MyRunnableWork.run with Jakarta EE Context available.");
   *      }
   *      public void someWorkInterfaceMethod() {
   *          ...
   *      }
   *  }
   *
   *  ThreadFactory threadFactory = ...;
   *
   *  ContextService ctxService = ...;
   *
   *  MyRunnableWork myRunnableWorkInstance = ...;
   *
   *  Object proxy = ctxService.createContextualProxy(myRunnableWorkInstance,
   *                                   Runnable.class, SomeWorkInterface.class);
   *
   *  // call SomeWorkInterface method on the proxy
   *  ((SomeWorkInterface) proxy).someWorkInterfaceMethod();
   *
   *  ExecutorService exSvc = Executors.newThreadPool(10, threadFactory);
   *
   *  // submit the proxy as a Runnable to the ExecutorService
   *  Future f = exSvc.submit( (Runnable)proxy);
   * 
* * * @param instance the instance of the object to proxy. * @param interfaces the interfaces that the proxy should implement. * @return a proxy for the input object that implements all of the specified * interfaces. * @throws java.lang.IllegalArgumentException - if the {@code interfaces} * argument is null or the instance does not implement * all the specified interfaces. * @throws java.lang.UnsupportedOperationException - if any of the {@code interfaces} * are {@link java.io.Serializable serializable} but a thread context type * does not support serialization. */ public Object createContextualProxy(Object instance, Class... interfaces); /** * Creates a new contextual object proxy for the input object instance. *

* The contextual object is useful when developing or using Java™ SE * threading mechanisms propagating events to other component instances. *

* If the application component that created the proxy is not started or * deployed, all methods on reflected interfaces may throw an * {@link java.lang.IllegalStateException}. *

* This method accepts a {@code Map} object which allows the * contextual object creator to define what contexts or behaviors to capture * when creating the contextual object. The specified properties will remain * with the contextual object. *

* * For example, to call a Message Driven Bean (MDB) with the sender's * context, but within the MDB's transaction: * *

   * public class MyServlet ... {
   *     public void doPost() throws NamingException, JMSException {
   *        InitialContext ctx = new InitialContext();
   *
   *        // Get the ContextService that only propagates
   *        // security context.
   *        ContextService ctxSvc = (ContextService)
   *            ctx.lookup("java:comp/env/SecurityContext");
   *
   *        // Set any custom context data through execution properties
   *        Map<String, String> execProps = new HashMap<>();
   *        execProps.put("vendor_a.security.tokenexpiration", "15000");
   *        // Specify that contextual object should run inside the current
   *        // transaction.  If we have a failure, we don't want to consume
   *        // the message.
   *        execProps.put(ManagedTask.TRANSACTION, "USE_TRANSACTION_OF_EXECUTION_THREAD");
   *
   *        ProcessMessage msgProcessor =
   *            ctxSvc.createContextualProxy(new MessageProcessor(), execProps,
   *            ProcessMessage.class);
   *
   *        ConnectionFactory cf = (ConnectionFactory)
   *             ctx.lookup("java:comp/env/MyTopicConnectionFactory");
   *        Destination dest = (Destination) ctx.lookup("java:comp/env/MyTopic");
   *        Connection con = cf.createConnection();
   *
   *        Session session = con.createSession(true, Session.AUTO_ACKNOWLEDGE);
   *        MessageProducer producer = session.createProducer(dest);
   *
   *        Message msg = session.createObjectMessage((Serializable)msgProcessor);
   *        producer.send(dest, msg);
   *        ...
   *
   *    }
   *
   *  public class MyMDB ... {
   *    public void onMessage(Message msg) {
   *        // Get the ProcessMessage contextual object from the message.
   *        ObjectMessage omsg = (ObjectMessage)msg;
   *        ProcessMessage msgProcessor = (ProcessMessage)omsg.getObject();
   *
   *        // Process the message in the specified context.
   *        msgProcessor.processMessage(msg);
   *    }
   *  }
   *
   *  public interface  ProcessMessage {
   *      public void processMessage(Message msg);
   *  }
   *
   *  public class MessageProcessor implements ProcessMessage, Serializable {
   *      public void processMessage(Message msg) {
   *          // Process the message with the application container
   *          // context that sent the message.
   *
   *      }
   *  }
   *
* * * @param instance the instance of the object to proxy. * @param executionProperties the properties to use when creating and running the context * object. * @param intf the interface that the proxy should implement. * @param the type of the interface * @return a proxy for the input object that implements the specified interface. * * @throws java.lang.IllegalArgumentException - if the {@code intf} argument * null or the instance does not implement the specified interface. * @throws java.lang.UnsupportedOperationException - if the {@code intf} * interface is {@link java.io.Serializable serializable} * but a thread context type does not support serialization. */ public T createContextualProxy(T instance, Map executionProperties, Class intf); /** * Creates a new contextual object proxy for the input object instance. *

* This method is similar to * {@code T createContextualProxy(T instance, Map executionProperties, Class intf)} * except that this method can be used if the proxy has to support multiple * interfaces. * * @param instance the instance of the object to proxy. * @param executionProperties the properties to use when creating and running the context * object. * @param interfaces the interfaces that the proxy should implement. * @return a proxy for the input object that implements all of the specified * interfaces. * * @throws java.lang.IllegalArgumentException - if the {@code interfaces} * argument is null or the instance does not implement all the specified * interfaces. * @throws java.lang.UnsupportedOperationException - if any of the {@code interfaces} * are {@link java.io.Serializable serializable} but a thread context type * does not support serialization. */ public Object createContextualProxy(Object instance, Map executionProperties, Class... interfaces); /** *

Captures thread context as an {@link java.util.concurrent.Executor} * that runs tasks on the same thread from which * executeis invoked but with context that is captured from the thread * that invokes currentContextExecutor.

* *

Example usage:

*
   * Executor contextSnapshot = contextSvc.currentContextExecutor();
   * ...
   * // from another thread, or after thread context has changed,
   * contextSnapshot.execute(() -> obj.doSomethingThatNeedsContext());
   * contextSnapshot.execute(() -> doSomethingElseThatNeedsContext(x, y));
   * 
* *

The returned Executor must raise IllegalArgumentException * if an already-contextualized Runnable is supplied to its * execute method.

* * @return an executor that wraps the execute method with context. * @since 3.0 */ public Executor currentContextExecutor(); /** * Gets the current execution properties on the context proxy instance. * * @param contextualProxy the contextual proxy instance to retrieve the execution properties. * @return A copy of the current contextual object execution properties, or null if * the contextualProxy is created without specifying any execution properties. * * @throws java.lang.IllegalArgumentException thrown if the input contextualProxy is not a valid * contextual object proxy created with the * {@code createContextualProxy} method. */ public Map getExecutionProperties(Object contextualProxy); /** *

Returns a new {@link java.util.concurrent.CompletableFuture} that is completed by the completion of the * specified stage.

* *

The new completable future gets its default asynchronous execution facility * from this ContextService, * using the same {@link ManagedExecutorService} if this ContextService * was obtained by {@link ManagedExecutorService#getContextService()}.

* *

When dependent stages are created from the new completable future, * and from the dependent stages of those stages, and so on, thread context is captured * and/or cleared by the ContextService. This guarantees that the action * performed by each stage always runs under the thread context of the code that creates the stage, * unless the user explicitly overrides by supplying a pre-contextualized action.

* *

Invocation of this method does not impact thread context propagation for the originally supplied * completable future or any other dependent stages directly created from it (not using this method).

* * @param completable future result type. * @param stage a completable future whose completion triggers completion of the new completable * future that is created by this method. * @return the new completable future. * @since 3.0 */ public CompletableFuture withContextCapture(CompletableFuture stage); /** *

Returns a new {@link java.util.concurrent.CompletionStage} that is completed by the completion of the * specified stage.

* *

The new completion stage gets its default asynchronous execution facility from this ContextService, * using the same {@link ManagedExecutorService} if this ContextService * was obtained by {@link ManagedExecutorService#getContextService()}, * otherwise using the DefaultManagedExecutorService.

* *

When dependent stages are created from the new completion stage, * and from the dependent stages of those stages, and so on, thread context is captured * and/or cleared by the ContextService. This guarantees that the action * performed by each stage always runs under the thread context of the code that creates the stage, * unless the user explicitly overrides by supplying a pre-contextualized action.

* *

Invocation of this method does not impact thread context propagation for the originally supplied * stage or any other dependent stages directly created from it (not using this method).

* * @param completion stage result type. * @param stage a completion stage whose completion triggers completion of the new stage * that is created by this method. * @return the new completion stage. * @since 3.0 */ public CompletionStage withContextCapture(CompletionStage stage); }