Skip to content

Commit 725f158

Browse files
authored
Capture context snapshots as part of the API (#527)
* Replace core ContextManagers.createContextSnapshot by api ContextSnapshot.capture. Signed-off-by: Sjoerd Talsma <sjoerdtalsma@users.noreply.github.com> * Move javadoc for ContextManager.clearAll. Signed-off-by: Sjoerd Talsma <sjoerdtalsma@users.noreply.github.com> * Rename loggers in snapshot implementation appropriately. Signed-off-by: Sjoerd Talsma <sjoerdtalsma@users.noreply.github.com> * Move some tests to api package. Signed-off-by: Sjoerd Talsma <sjoerdtalsma@users.noreply.github.com> * Replace ContextManagers.createContextSnapshot() with ContextSnapshot.capture(). Signed-off-by: Sjoerd Talsma <sjoerdtalsma@users.noreply.github.com> * Replace ContextManagers.createContextSnapshot() with ContextSnapshot.capture() in documentation. Signed-off-by: Sjoerd Talsma <sjoerdtalsma@users.noreply.github.com> * Test Context Snapshot Signed-off-by: Sjoerd Talsma <sjoerdtalsma@users.noreply.github.com> * Remove more ContextManagers references. Signed-off-by: Sjoerd Talsma <sjoerdtalsma@users.noreply.github.com> * Remove yet more ContextManagers references. Signed-off-by: Sjoerd Talsma <sjoerdtalsma@users.noreply.github.com> * Update context-propagation-api/src/main/java/nl/talsmasoftware/context/api/ContextSnapshot.java * Update context-propagation-api/src/main/java/nl/talsmasoftware/context/api/ContextSnapshot.java * Clear service cache when capture exception occurs. Signed-off-by: Sjoerd Talsma <sjoerdtalsma@users.noreply.github.com> * Remove replaced ContextManagers utility class. Signed-off-by: Sjoerd Talsma <sjoerdtalsma@users.noreply.github.com> --------- Signed-off-by: Sjoerd Talsma <sjoerdtalsma@users.noreply.github.com>
1 parent 5a7704d commit 725f158

File tree

68 files changed

+882
-652
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+882
-652
lines changed

context-propagation-api/README.md

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,15 @@ although technically these use cases are not appropriate.
3535
Manages contexts by initializing and maintaining an active context value.
3636

3737
Normally it is not necessary to interact directly with individual context managers.
38-
The `ContextManagers` utility class detects available context managers and lets
39-
you take a [_snapshot_](#context-snapshot) of **all** active contexts at once.
38+
The api detects available context managers and lets
39+
you capture a [_snapshot_](#context-snapshot) of **all** active contexts at once.
4040

4141
- [ContextManager javadoc][contextmanager]
42-
- [ContextManagers javadoc][contextmanagers]
42+
- [ContextSnapshot javadoc][contextsnapshot]
4343

4444
### Context Snapshot
4545

46-
A context snapshot is created by the [ContextManagers]' `createContextSnapshot()` method.
46+
A context snapshot is captured by the [ContextSnapshot]' `capture()` method.
4747
The snapshot contains active context values from all known [ContextManager] implementations.
4848
Once created, the captured _values_ in such context snapshot will not change anymore,
4949
even when the active context is later modified.
@@ -52,7 +52,6 @@ They stay active until the reactivation is closed again (or are overwritten by n
5252
Closing the reactivated object is mandatory (from the thread where the reactivation was called).
5353

5454
- [ContextSnapshot javadoc](https://javadoc.io/page/nl.talsmasoftware.context/context-propagation/latest/nl/talsmasoftware/context/ContextSnapshot.html)
55-
- [ContextManagers javadoc](https://javadoc.io/page/nl.talsmasoftware.context/context-propagation/latest/nl/talsmasoftware/context/ContextManagers.html)
5655

5756
## Creating your own context manager
5857

@@ -68,23 +67,24 @@ It should contain the fully qualified classname of your implementation.
6867

6968
```java
7069
public class DummyContextManager implements ContextManager<String> {
71-
public Context<String> initializeNewContext(String value) {
72-
return new DummyContext(value);
70+
public Context<String> initializeNewContext(String value) {
71+
return new DummyContext(value);
72+
}
73+
74+
public Context<String> getActiveContextValue() {
75+
DummyContext current = DummyContext.current();
76+
return current != null ? current.getValue() : null;
77+
}
78+
79+
private static final class DummyContext extends AbstractThreadLocalContext<String> {
80+
private DummyContext(String newValue) {
81+
super(newValue);
7382
}
7483

75-
public Context<String> getActiveContext() {
76-
return DummyContext.current();
77-
}
78-
79-
private static final class DummyContext extends AbstractThreadLocalContext<String> {
80-
private DummyContext(String newValue) {
81-
super(newValue);
82-
}
83-
84-
private static Context<String> current() {
85-
return AbstractThreadLocalContext.current(DummyContext.class);
86-
}
84+
private static Context<String> current() {
85+
return AbstractThreadLocalContext.current(DummyContext.class);
8786
}
87+
}
8888
}
8989
```
9090

@@ -95,7 +95,6 @@ public class DummyContextManager implements ContextManager<String> {
9595
[javadoc]: https://www.javadoc.io/doc/nl.talsmasoftware.context/context-propagation
9696

9797
[threadlocal]: https://docs.oracle.com/javase/8/docs/api/java/lang/ThreadLocal.html
98-
[context]: https://javadoc.io/page/nl.talsmasoftware.context/context-propagation/latest/nl/talsmasoftware/context/Context.html
99-
[contextsnapshot]: https://javadoc.io/page/nl.talsmasoftware.context/context-propagation/latest/nl/talsmasoftware/context/ContextSnapshot.html
100-
[contextmanager]: https://javadoc.io/page/nl.talsmasoftware.context/context-propagation/latest/nl/talsmasoftware/context/ContextManager.html
101-
[contextmanagers]: https://javadoc.io/page/nl.talsmasoftware.context/context-propagation/latest/nl/talsmasoftware/context/ContextManagers.html
98+
[context]: https://javadoc.io/page/nl.talsmasoftware.context/context-propagation/latest/nl/talsmasoftware/context/api/Context.html
99+
[contextsnapshot]: https://javadoc.io/page/nl.talsmasoftware.context/context-propagation/latest/nl/talsmasoftware/context/api/ContextSnapshot.html
100+
[contextmanager]: https://javadoc.io/page/nl.talsmasoftware.context/context-propagation/latest/nl/talsmasoftware/context/api/ContextManager.html

context-propagation-api/src/main/java/nl/talsmasoftware/context/api/Context.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@
4646
*/
4747
public interface Context<T> extends Closeable {
4848

49+
// TODO think about removing Context.getValue as ContextManager.getActiveContextValue() should suffice.
50+
4951
/**
5052
* The value associated with this context.
5153
*

context-propagation-api/src/main/java/nl/talsmasoftware/context/api/ContextManager.java

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,62 @@ public interface ContextManager<T> {
6262
* to clear all contexts before returning threads to the pool.
6363
*
6464
* <p>
65-
* This method normally should only get called by {@code ContextManagers.clearActiveContexts()}.
65+
* This method normally should only get called by {@linkplain #clearAll()}.
6666
*
6767
* @since 2.0.0
6868
*/
6969
void clear();
7070

71+
/**
72+
* Clears all active contexts from the current thread.
73+
*
74+
* <p>
75+
* Contexts that are 'stacked' (i.e. restore the previous state upon close)
76+
* should be closed in a way that includes all 'parent' contexts as well.
77+
*
78+
* <p>
79+
* This operation is not intended to be used by general application code
80+
* as it likely breaks any 'stacked' active context that surrounding code may depend upon.
81+
*
82+
* <p>
83+
* Appropriate use includes thread management, where threads are reused by some pooling mechanism.<br>
84+
* For example, it is considered safe to clear the context when obtaining a 'fresh' thread from a
85+
* thread pool (as no context expectations should exist at that point).<br>
86+
* An even better strategy would be to clear the context right before returning a used thread
87+
* back to the pool as this will allow any unclosed contexts to be garbage collected.<br>
88+
* Besides preventing contextual issues, this reduces the risk of memory leaks by unbalanced context calls.
89+
*
90+
* @since 2.0.0
91+
*/
92+
static void clearAll() {
93+
ContextSnapshotImpl.clearActiveContexts();
94+
}
95+
96+
/**
97+
* Override the {@linkplain ClassLoader} used to lookup {@linkplain ContextManager context managers}.
98+
*
99+
* <p>
100+
* Normally, capturing a snapshot uses the {@linkplain Thread#getContextClassLoader() Context ClassLoader} from the
101+
* {@linkplain Thread#currentThread() current thread} to look up all {@linkplain ContextManager context managers}.
102+
* It is possible to configure a fixed, single classloader in your application for these lookups.
103+
*
104+
* <p>
105+
* Using this method to specify a fixed classloader will only impact
106+
* <strong>new</strong> {@linkplain ContextSnapshot context snapshots}.<br>
107+
* Existing snapshots will <strong>not</strong> be impacted.
108+
*
109+
* <p>
110+
* <strong>Notes:</strong><br>
111+
* <ul>
112+
* <li>Please be aware that this configuration is global!
113+
* <li>This will also affect the lookup of {@linkplain ContextTimer context timers}
114+
* </ul>
115+
*
116+
* @param classLoader The single, fixed ClassLoader to use for finding context managers.
117+
* Specify {@code null} to restore the default behaviour.
118+
* @since 2.0.0
119+
*/
120+
static void useClassLoader(ClassLoader classLoader) {
121+
ServiceCache.useClassLoader(classLoader);
122+
}
71123
}

context-propagation-api/src/main/java/nl/talsmasoftware/context/api/ContextSnapshot.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@
2525
* ensuring that all context values are set in that other thread.
2626
*
2727
* <p>
28-
* A snapshot can be obtained from the {@code ContextManagers} utility class for interaction with all registered
29-
* {@link ContextManager} implementations.
28+
* A snapshot can be obtained from the {@linkplain #capture()} method.
3029
*
3130
* <p>
3231
* This library contains several utility classes named {@code ContextAware...} or {...WithContext} that will
@@ -41,6 +40,23 @@
4140
* @since 2.0.0
4241
*/
4342
public interface ContextSnapshot {
43+
/**
44+
* Captures a snapshot of the current
45+
* {@link ContextManager#getActiveContextValue() active context value}
46+
* from <em>all known {@link ContextManager}</em> implementations.
47+
*
48+
* <p>
49+
* This snapshot with context values is returned as a single object and can be temporarily
50+
* {@link ContextSnapshot#reactivate() reactivated}.
51+
* Remember to {@link Context#close() close} the reactivated context once you're done,
52+
* preferably in a <code>try-with-resources</code> construct.
53+
*
54+
* @return A new context snapshot that can be reactivated elsewhere (e.g. a background thread)
55+
* within a try-with-resources construct.
56+
*/
57+
static ContextSnapshot capture() {
58+
return ContextSnapshotImpl.capture();
59+
}
4460

4561
/**
4662
* Temporarily reactivates all captured context values that are in the snapshot.

0 commit comments

Comments
 (0)