jakarta.enterprise.concurrent.ContextService Maven / Gradle / Ivy
/*
* 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.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);
/**
* 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
* execute
is 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);
}