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

org.eclipse.microprofile.context.ThreadContext Maven / Gradle / Ivy

/*
 * Copyright (c) 2018,2020 Contributors to the Eclipse Foundation
 *
 * See the NOTICE file(s) distributed with this work for additional
 * information regarding copyright ownership.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.eclipse.microprofile.context;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;

import org.eclipse.microprofile.context.spi.ContextManagerProvider;
import org.eclipse.microprofile.context.spi.ContextManager;

/**
 * This interface offers various methods for capturing the context of the current thread
 * and applying it to various interfaces that are commonly used with completion stages
 * and executor services.  This allows you to contextualize specific actions that need
 * access to the context of the creator/submitter of the stage/task.
 *
 * 

Example usage:

*
 * ThreadContext threadContext = ThreadContext.builder()
 *     .propagated(ThreadContext.SECURITY, ThreadContext.APPLICATION)
 *     .cleared(ThreadContext.TRANSACTION)
 *     .unchanged(ThreadContext.ALL_REMAINING)
 *     .build();
 * ...
 * CompletableFuture<Integer> stage2 = stage1.thenApply(threadContext.contextualFunction(function));
 * ...
 * Future<Integer> future = executor.submit(threadContext.contextualCallable(callable));
 * 
 * 
* *

This interface is intentionally kept compatible with ContextService, * with the hope that its methods might one day be contributed to that specification.

*/ public interface ThreadContext { /** * Creates a new {@link Builder} instance. * * @return a new {@link Builder} instance. */ public static Builder builder() { return ContextManagerProvider.instance().getContextManager().newThreadContextBuilder(); } /** *

Builder for {@link ThreadContext} instances.

* *

Example usage:

*
 ThreadContext threadContext = ThreadContext.builder()
     *                                                   .propagated(ThreadContext.APPLICATION, ThreadContext.SECURITY)
     *                                                   .unchanged(ThreadContext.TRANSACTION)
     *                                                   .build();
     * ...
     * 
*/ interface Builder { /** *

Builds a new {@link ThreadContext} instance with the * configuration that this builder represents as of the point in time when * this method is invoked.

* *

After {@link #build} is invoked, the builder instance retains its * configuration and may be further updated to represent different * configurations and build additional ThreadContext * instances.

* *

All created instances of {@link ThreadContext} are destroyed * when the application is stopped. The container automatically shuts down these * {@link ThreadContext} instances and raises IllegalStateException * to reject subsequent attempts to apply previously captured thread context.

* * @return new instance of {@link ThreadContext}. * @throws IllegalStateException for any of the following error conditions *
    *
  • if one or more of the same context types appear in multiple * of the following sets: * ({@link #cleared}, {@link #propagated}, {@link #unchanged})
  • *
  • if a thread context type that is configured to be * {@link #cleared} or {@link #propagated} is unavailable
  • *
  • if context configuration is neither specified on the builder * nor via MicroProfile Config, and the builder implementation lacks * vendor-specific defaults of its own.
  • *
  • if more than one ThreadContextProvider has the * same thread context * {@link org.eclipse.microprofile.context.spi.ThreadContextProvider#getThreadContextType type} *
  • *
*/ ThreadContext build(); /** *

Defines the set of thread context types to clear from the thread * where the action or task executes. The previous context is resumed * on the thread after the action or task ends.

* *

This set replaces the cleared set that was * previously specified on the builder instance, if any.

* *

For example, if the user specifies * {@link ThreadContext#TRANSACTION} in this set, then a transaction * is not active on the thread when the action or task runs, such * that each action or task is able to independently start and end * its own transactional work.

* *

{@link ThreadContext#ALL_REMAINING} is automatically appended to the * set of cleared context if neither the {@link #propagated} set nor the * {@link #unchanged} set include {@link ThreadContext#ALL_REMAINING}.

* *

Constants for specifying some of the core context types are provided * on {@link ThreadContext}. Other thread context types must be defined * by the specification that defines the context type or by a related * MicroProfile specification.

* *

The MicroProfile Config property, mp.context.ThreadContext.cleared, * establishes a default that is used if no value is otherwise specified. * The value of the MicroProfile Config property can be the empty string * or a comma separated list of context type constant values.

* * @param types types of thread context to clear from threads that run * actions and tasks. * @return the same builder instance upon which this method is invoked. */ Builder cleared(String... types); /** *

Defines the set of thread context types to capture from the thread * that contextualizes an action or task. This context is later * re-established on the thread(s) where the action or task executes.

* *

This set replaces the propagated set that was * previously specified on the builder instance, if any.

* *

Constants for specifying some of the core context types are provided * on {@link ThreadContext}. Other thread context types must be defined * by the specification that defines the context type or by a related * MicroProfile specification.

* *

The MicroProfile Config property, mp.context.ThreadContext.propagated, * establishes a default that is used if no value is otherwise specified. * The value of the MicroProfile Config property can be the empty string * or a comma separated list of context type constant values.

* *

Thread context types which are not otherwise included in this set or * in the {@link #unchanged} set are cleared from the thread of execution * for the duration of the action or task.

* * @param types types of thread context to capture and propagate. * @return the same builder instance upon which this method is invoked. */ Builder propagated(String... types); /** *

Defines a set of thread context types that are essentially ignored, * in that they are neither captured nor are they propagated or cleared * from thread(s) that execute the action or task.

* *

This set replaces the unchanged set that was previously * specified on the builder instance.

* *

Constants for specifying some of the core context types are provided * on {@link ThreadContext}. Other thread context types must be defined * by the specification that defines the context type or by a related * MicroProfile specification.

* *

The MicroProfile Config property, mp.context.ThreadContext.unchanged, * establishes a default that is used if no value is otherwise specified. * The value of the MicroProfile Config property can be the empty string * or a comma separated list of context type constant values. If a default * value is not specified by MicroProfile Config, then the default value * is an empty set.

* *

The configuration of unchanged context is provided for * advanced patterns where it is desirable to leave certain context types * on the executing thread.

* *

For example, to run under the transaction of the thread of execution, * with security context cleared and all other thread contexts propagated:

*
 ThreadContext threadContext = ThreadContext.builder()
         *                                                   .unchanged(ThreadContext.TRANSACTION)
         *                                                   .cleared(ThreadContext.SECURITY)
         *                                                   .propagated(ThreadContext.ALL_REMAINING)
         *                                                   .build();
         * ...
         * task = threadContext.contextualRunnable(new MyTransactionlTask());
         * ...
         * // on another thread,
         * tx.begin();
         * ...
         * task.run(); // runs under the transaction due to 'unchanged'
         * tx.commit();
         * 
* * @param types types of thread context to leave unchanged on the thread. * @return the same builder instance upon which this method is invoked. */ Builder unchanged(String... types); } /** *

Identifier for all available thread context types which are * not specified individually under cleared, * propagated, or unchanged.

* *

When using this constant, be aware that bringing in a new * context provider or updating levels of an existing context provider * might change the set of available thread context types.

* * @see ManagedExecutor.Builder#cleared * @see ManagedExecutor.Builder#propagated * @see ThreadContext.Builder */ static final String ALL_REMAINING = "Remaining"; /** * Identifier for application context. Application context controls the * application component that is associated with a thread. * For Jakarta/Java EE applications, application context includes the * thread context class loader as well as the java:comp, java:module, * and java:app name spaces of the application. An empty/default * application context means that the thread is not associated with any * application. * * @see ManagedExecutor.Builder#cleared * @see ManagedExecutor.Builder#propagated * @see ThreadContext.Builder */ static final String APPLICATION = "Application"; /** * Identifier for CDI context. CDI context controls the availability of CDI * scopes. An empty/default CDI context means that the thread does not have * access to the scope of the session, request, and so forth that created the * contextualized action. * * For example, consider the following @RequestScoped resource: * *

    @RequestScoped
    public class MyRequestScopedBean {
        public String getState() {
          // returns some request-specific information to the caller
        }
    }
    
* * CDI context propagation includes request, session and conversation contexts. * When CDI context is propagated, all of the above mentioned contexts that are * currently active will be available to the contextualized task with preserved * state.

    ManagedExecutor exec = ManagedExecutor.builder()
        .propagated(ThreadContext.CDI, ThreadContext.APPLICATION)
        .build();
    
    @Inject
    MyRequestScopedBean requestBean;
    
    @GET
    public void foo() {
        exec.supplyAsync(() -> {
            String state = reqBean.getState();
            // do some work with the request state
        }).thenApply(more -> {
            // request state also available in future stages
        });
    }
    
* * If CDI context is 'cleared', currently active contexts will still be * available to the contextualized task, but their state will be erased. * * If CDI context is 'unchanged', access to CDI bean's contextual state * will be non-deterministic. Namely, context may be missing, or context * from a different task may be applied instead. This option is discouraged, * and only should be used if CDI context is not used in an application. * * @see ManagedExecutor.Builder#cleared * @see ManagedExecutor.Builder#propagated * @see ThreadContext.Builder */ static final String CDI = "CDI"; /** *

An empty array of thread context.

* *

This is provided as a convenience for code that wishes to be more explicit. * For example, you can specify builder.propagated(ThreadContext.NONE) * rather than builder.propagated(new String[0]) * or builder.propagated(), all of which have the same meaning.

* *

When using MicroProfile Config to specify defaults, the value * None indicates an empty array. For example, *

mp.context.ThreadContext.unchanged=None
* or *
mp.context.ManagedExecutor.propagated=None
*/ static final String[] NONE = new String[0]; /** * Identifier for security context. Security context controls the credentials * that are associated with the thread. An empty/default security context * means that the thread is unauthenticated. * * @see ManagedExecutor.Builder#cleared * @see ManagedExecutor.Builder#propagated * @see ThreadContext.Builder */ static final String SECURITY = "Security"; /** * Identifier for transaction context. Transaction context controls the * active transaction scope that is associated with the thread. * Implementations are not expected to propagate transaction context across * threads. Instead, the concept of transaction context is provided for its * cleared context, which means the active transaction on the thread * is suspended such that a new transaction can be started if so desired. * In most cases, the most desirable behavior will be to leave transaction * context defaulted to cleared (suspended), * in order to prevent dependent actions and tasks from accidentally * enlisting in transactions that are on the threads where they happen to * run. * * @see ManagedExecutor.Builder#cleared * @see ManagedExecutor.Builder#propagated * @see ThreadContext.Builder */ static final String TRANSACTION = "Transaction"; /** *

Creates an Executorthat 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 = threadContext.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. */ Executor currentContextExecutor(); /** *

Wraps a Callable with context that is captured from the thread that invokes * contextualCallable.

* *

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. */ Callable contextualCallable(Callable callable); /** *

Wraps a BiConsumer with context that is captured from the thread that invokes * contextualConsumer.

* *

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. */ BiConsumer contextualConsumer(BiConsumer consumer); /** *

Wraps a Consumer with context that is captured from the thread that invokes * contextualConsumer.

* *

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. */ Consumer contextualConsumer(Consumer consumer); /** *

Wraps a BiFunction with context that is captured from the thread that invokes * contextualFunction.

* *

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. */ BiFunction contextualFunction(BiFunction function); /** *

Wraps a Function with context that is captured from the thread that invokes * contextualFunction.

* *

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. */ Function contextualFunction(Function function); /** *

Wraps a Runnable with context that is captured from the thread that invokes * ContextualRunnable.

* *

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. */ Runnable contextualRunnable(Runnable runnable); /** *

Wraps a Supplier with context captured from the thread that invokes * contextualSupplier.

* *

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. */ Supplier contextualSupplier(Supplier supplier); /** *

Returns a new CompletableFuture that is completed by the completion of the * specified stage.

* *

The new completable future will use the same default executor as this ThreadContext, * which can be a ManagedExecutor if this ThreadContext was obtained by {@link ManagedExecutor#getThreadContext()} * or the default executor service if provided by the platform (which can be done via * {@link ContextManager.Builder#withDefaultExecutorService(ExecutorService)}), or otherwise have no default executor.

* *

If this thread context has no default executor, the new stage and all dependent stages created from it, and so forth, * have no default asynchronous execution facility and must raise {@link java.lang.UnsupportedOperationException} * for all *Async methods that do not specify an executor. For example, * {@link java.util.concurrent.CompletionStage#thenRunAsync(Runnable) thenRunAsync(Runnable)}.

* *

When dependent stages are created from the new completable future, thread context is captured * and/or cleared as described in the documentation of the {@link ManagedExecutor} class, except that * this ThreadContext instance takes the place of the default asynchronous execution facility in * supplying the configuration of cleared/propagated context types. 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 supplied * completable future or any dependent stages created from it, other than the new dependent * completable future that is created by 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. */ CompletableFuture withContextCapture(CompletableFuture stage); /** *

Returns a new CompletionStage that is completed by the completion of the * specified stage.

* *

The new completion stage will use the same default executor as this ThreadContext, * which can be a ManagedExecutor if this ThreadContext was obtained by {@link ManagedExecutor#getThreadContext()} * or the default executor service if provided by the platform (which can be done via * {@link ContextManager.Builder#withDefaultExecutorService(ExecutorService)}), or otherwise have no default executor.

* *

If this thread context has no default executor, the new stage and all dependent stages created from it, and so forth, * have no default asynchronous execution facility and must raise {@link java.lang.UnsupportedOperationException} * for all *Async methods that do not specify an executor. For example, * {@link java.util.concurrent.CompletionStage#thenRunAsync(Runnable) thenRunAsync(Runnable)}.

* *

When dependent stages are created from the new completion stage, thread context is captured * and/or cleared as described in the documentation of the {@link ManagedExecutor} class, except that * this ThreadContext instance takes the place of the default asynchronous execution facility in * supplying the configuration of cleared/propagated context types. 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 supplied * stage or any dependent stages created from it, other than the new dependent * completion stage that is created by 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. */ CompletionStage withContextCapture(CompletionStage stage); }