Important
Library to capture a snapshot of one or more ThreadLocal
values and reactivate them in another thread.
The library automatically detects supported ThreadLocal
values to be captured.
It uses a ContextManager
Service Provider Interface (SPI) for this using the Java ServiceLoader.
The core module provides several utility classes to safely capture a context snapshot in the calling thread and reactivating it in another thread and ensuring proper cleanup after its execution finishes. This reduces the chance of 'leaking' thread-local values.
A brief explanation of the core concepts from the api:
Captures active ThreadLocal values from all detected ContextManager implementations.
These values can be reactivated in another thread.
Reactivated snapshots must be closed to avoid leaking context.
All context aware utility classes in this library are tested to make sure they reactivate and close snapshots in a safe way.
Manages a context by providing a standard way of interacting with ThreadLocal
values.
Thread-local values can be accessed via a ContextManager by:
- Calling
getActiveContextValue()
which gets the current thread-local value. - Calling
activate(value)
which sets the given value untilclose()
is called on the resultingContext
. - Calling
clear()
which removes the thread-local value.
Abstraction for an activated thread-local value.
When the context manager activates a value, a new Context
is returned.
Closing this context will undo this activated value again.
Important
It is the responsibility of the one activating a new Context to also close it from the same thread. Using every activated context in a 'try-with-resources' block of code is a recommended and safe way to do this.
The context-propagation-core
module contains various utility classes
that make it easier to capture context snapshots and reactivate them safely in other threads.
Examples include:
ContextAwareExecutorService
, wrapping any existingExecutorService
, automatically capturing and reactivating context snapshots.ContextAwareCompletableFuture
, propagating context snapshots into each successiveCompletionStage
.- Variants of java standard
java.util.function
implementations, executing within a context snapshot. - Base class
AbstractThreadLocalContext
that features nesting active values and predictable behaviour for out-of-order closing.
If your background threads are managed by an ExecutorService, you can use our context aware ExecutorService to wrap your usual threadpool.
The ContextAwareExcutorService
automatically captures context snapshots before submitting work.
This snapshot is then reactivated (and closed) in the submitted background thread.
It can wrap any ExecutorService for the actual thread execution:
private static final ExecutorService THREADPOOL =
ContextAwareExecutorService.wrap(Executors.newCachedThreadpool());
Just before creating a new thread, capture a snapshot of all ThreadLocal context values:
final ContextSnapshot snapshot = ContextSnapshot.capture();
In the code of your background thread, reactivate the snapshot to have all ThreadLocal context values set as they were captured:
try (ContextSnapshot.Reactivation reactivation = snapshot.reactivate()) {
// All ThreadLocal values from the snapshot are available within this block
}
// or, using a Runnable lambda:
snapshot.wrap(() -> {
// All ThreadLocal values from the snapshot are available within this block
}).run();
The following ThreadLocal
-based contexts are currently supported
out of the box by this context-propagation library:
- Spring Security Context
- OpenTelemetry Context
- SLF4J MDC (Mapped Diagnostic Context)
- Log4j 2 Thread Context
- Locale context
- ServletRequest contexts
- OpenTracing Span contexts
- .. Yours? Feel free to create an issue or pull-request if you know of a ThreadLocal context that should also be included in a context snapshot.
Adding your own Context
type is possible
by creating your own context manager.
When using a build tool or plugin to create an 'uber-jar', i.e. a jar file with all
the classes of its dependencies included, you have to make sure that the service
provider configuration files under META-INF/services
are either preserved or
merged. Otherwise Java's ServiceLoader
will not be able to find the context
implementations of this library.
In case you are using the Maven Shade Plugin, you can use the
ServicesResourceTransformer
for this task.
No library is 'free' with regards to performance.
Capturing a context snapshot and reactivating it in another thread is no different.
For insight, the library tracks the overall time used creating and reactivating
context snapshots along with time spent in each individual ContextManager
.
On a development machine, you can get timing for each snapshot by turning on logging
for nl.talsmasoftware.context.api.ContextTimer
at FINEST
or TRACE
level
(depending on your logger of choice).
Please do not turn this on in production as the logging overhead will most likely
have a noticeable impact on your application.
The context propagation metrics module uses the excellent dropwizard metrics library to instrument Timers for context propagation.
Similarly, the context propagation Micrometer module adds Micrometer instrumentation Timers for the context propagation.
Adding either of these modules to your classpath will automatically configure various timers in the global default metric registry of your application.
Purpose of 'v2' of this library has been simplification of both the API and the structure of the repository.
- Minimum Java version bumped to 8.
- Repository module restructuring.
- Separate API module containing only the minimum API.
- Separate core module for all utility classes.
- All provided manager implementations moved to
managers
subdirectory. - All context timer implementations moved to
timers
subdirectory.
- API simplification.
- Static
ContextSnapshot.capture()
captures a new snapshot andContextSnapshot.reactivate()
reactivates it. ContextManager.initializeNewContext(value)
was renamed toactivate(value)
.ContextManager.getActiveContext()
was replaced bygetActiveContextValue()
.ContextManager.clear()
must be implemented, but is allowed to be a 'no-op' empty implementation. TheClearable
interface was removed.
- Static
- All
@Deprecated(forRemoval=true)
items from v1 were removed.