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 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 = 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);
}