0
# Implicit Context Values
1
2
Implicit context values allow objects to store themselves in context without exposing explicit context keys. This pattern is useful for objects that need to be context-aware but want to hide their internal storage mechanism from users.
3
4
## ImplicitContextKeyed Interface
5
6
Objects implementing this interface can be stored in context and automatically manage their own context keys.
7
8
```java { .api }
9
interface ImplicitContextKeyed {
10
Scope makeCurrent();
11
Context storeInContext(Context context);
12
}
13
```
14
15
## Interface Methods
16
17
### Make Current
18
19
Adds this value to the current context and makes the new context current.
20
21
```java { .api }
22
Scope makeCurrent();
23
```
24
25
**Returns:** A Scope that must be closed to restore the previous context
26
27
This method is equivalent to `Context.current().with(this).makeCurrent()`.
28
29
**Usage Example:**
30
```java
31
// Assuming Span implements ImplicitContextKeyed
32
Span span = tracer.spanBuilder("operation").startSpan();
33
34
try (Scope scope = span.makeCurrent()) {
35
// Span is now available in current context
36
performOperation();
37
} // Previous context restored
38
```
39
40
### Store in Context
41
42
Returns a new context with this value stored in it.
43
44
```java { .api }
45
Context storeInContext(Context context);
46
```
47
48
**Parameters:**
49
- `context` - The context to store this value in
50
51
**Returns:** A new Context containing this value
52
53
It's generally recommended to use `Context.with(ImplicitContextKeyed)` instead of calling this method directly.
54
55
**Usage Example:**
56
```java
57
Span span = tracer.spanBuilder("operation").startSpan();
58
59
// These are equivalent:
60
Context contextWithSpan1 = Context.current().with(span);
61
Context contextWithSpan2 = span.storeInContext(Context.current());
62
```
63
64
## Implementation Examples
65
66
### Trace Span Implementation
67
68
```java
69
public class TraceSpan implements ImplicitContextKeyed {
70
private static final ContextKey<TraceSpan> SPAN_KEY = ContextKey.named("span");
71
72
private final String spanId;
73
private final String operationName;
74
75
public TraceSpan(String spanId, String operationName) {
76
this.spanId = spanId;
77
this.operationName = operationName;
78
}
79
80
@Override
81
public Scope makeCurrent() {
82
return Context.current().with(this).makeCurrent();
83
}
84
85
@Override
86
public Context storeInContext(Context context) {
87
return context.with(SPAN_KEY, this);
88
}
89
90
// Static method to get current span
91
public static TraceSpan current() {
92
return Context.current().get(SPAN_KEY);
93
}
94
95
// Span-specific methods
96
public void setTag(String key, String value) {
97
// Implementation
98
}
99
100
public void end() {
101
// Implementation
102
}
103
}
104
105
// Usage
106
TraceSpan span = new TraceSpan("span-123", "database-query");
107
try (Scope scope = span.makeCurrent()) {
108
span.setTag("query", "SELECT * FROM users");
109
performDatabaseQuery();
110
span.end();
111
}
112
```
113
114
### Request Context Implementation
115
116
```java
117
public class RequestContext implements ImplicitContextKeyed {
118
private static final ContextKey<RequestContext> REQUEST_KEY = ContextKey.named("request");
119
120
private final String requestId;
121
private final String userId;
122
private final Map<String, String> attributes;
123
124
public RequestContext(String requestId, String userId) {
125
this.requestId = requestId;
126
this.userId = userId;
127
this.attributes = new ConcurrentHashMap<>();
128
}
129
130
@Override
131
public Scope makeCurrent() {
132
return Context.current().with(this).makeCurrent();
133
}
134
135
@Override
136
public Context storeInContext(Context context) {
137
return context.with(REQUEST_KEY, this);
138
}
139
140
public static RequestContext current() {
141
return Context.current().get(REQUEST_KEY);
142
}
143
144
// Request-specific methods
145
public String getRequestId() { return requestId; }
146
public String getUserId() { return userId; }
147
148
public void setAttribute(String key, String value) {
149
attributes.put(key, value);
150
}
151
152
public String getAttribute(String key) {
153
return attributes.get(key);
154
}
155
}
156
157
// Usage
158
RequestContext request = new RequestContext("req-456", "user-789");
159
try (Scope scope = request.makeCurrent()) {
160
request.setAttribute("action", "login");
161
processRequest();
162
}
163
```
164
165
### Baggage Implementation
166
167
```java
168
public class Baggage implements ImplicitContextKeyed {
169
private static final ContextKey<Baggage> BAGGAGE_KEY = ContextKey.named("baggage");
170
171
private final Map<String, String> values;
172
173
private Baggage(Map<String, String> values) {
174
this.values = Collections.unmodifiableMap(new HashMap<>(values));
175
}
176
177
public static Baggage empty() {
178
return new Baggage(Collections.emptyMap());
179
}
180
181
public static Baggage current() {
182
Baggage baggage = Context.current().get(BAGGAGE_KEY);
183
return baggage != null ? baggage : empty();
184
}
185
186
@Override
187
public Scope makeCurrent() {
188
return Context.current().with(this).makeCurrent();
189
}
190
191
@Override
192
public Context storeInContext(Context context) {
193
return context.with(BAGGAGE_KEY, this);
194
}
195
196
public Baggage put(String key, String value) {
197
Map<String, String> newValues = new HashMap<>(values);
198
newValues.put(key, value);
199
return new Baggage(newValues);
200
}
201
202
public String get(String key) {
203
return values.get(key);
204
}
205
206
public Set<String> keys() {
207
return values.keySet();
208
}
209
}
210
211
// Usage
212
Baggage baggage = Baggage.current()
213
.put("user-type", "premium")
214
.put("region", "us-west");
215
216
try (Scope scope = baggage.makeCurrent()) {
217
// Baggage values available throughout call chain
218
processWithBaggage();
219
}
220
```
221
222
## Usage Patterns
223
224
### Builder Pattern with Context
225
226
```java
227
public class OperationBuilder {
228
private String operationName;
229
private Map<String, String> tags = new HashMap<>();
230
231
public OperationBuilder setName(String name) {
232
this.operationName = name;
233
return this;
234
}
235
236
public OperationBuilder addTag(String key, String value) {
237
tags.put(key, value);
238
return this;
239
}
240
241
public Operation build() {
242
return new Operation(operationName, tags);
243
}
244
245
public Scope buildAndMakeCurrent() {
246
return build().makeCurrent();
247
}
248
}
249
250
// Usage
251
try (Scope scope = new OperationBuilder()
252
.setName("user-registration")
253
.addTag("method", "email")
254
.buildAndMakeCurrent()) {
255
256
registerUser();
257
}
258
```
259
260
### Context Chaining with Implicit Values
261
262
```java
263
public void processUserRequest(String userId, String requestId) {
264
// Chain multiple implicit context values
265
RequestContext request = new RequestContext(requestId, userId);
266
TraceSpan span = new TraceSpan("span-123", "process-request");
267
268
Context enrichedContext = Context.current()
269
.with(request) // Uses ImplicitContextKeyed.storeInContext()
270
.with(span); // Uses ImplicitContextKeyed.storeInContext()
271
272
try (Scope scope = enrichedContext.makeCurrent()) {
273
// Both request and span are available
274
RequestContext currentRequest = RequestContext.current();
275
TraceSpan currentSpan = TraceSpan.current();
276
277
performOperation();
278
}
279
}
280
```
281
282
### Conditional Context Application
283
284
```java
285
public class ConditionalContextManager {
286
public static Scope applyUserContext(String userId) {
287
if (userId != null && !userId.isEmpty()) {
288
UserContext userContext = new UserContext(userId);
289
return userContext.makeCurrent();
290
} else {
291
return Scope.noop();
292
}
293
}
294
}
295
296
// Usage
297
try (Scope scope = ConditionalContextManager.applyUserContext(getUserId())) {
298
// User context applied only if user ID is present
299
performOperation();
300
}
301
```
302
303
## Advanced Patterns
304
305
### Hierarchical Context Values
306
307
```java
308
public class HierarchicalContext implements ImplicitContextKeyed {
309
private static final ContextKey<HierarchicalContext> KEY = ContextKey.named("hierarchical");
310
311
private final String level;
312
private final HierarchicalContext parent;
313
314
private HierarchicalContext(String level, HierarchicalContext parent) {
315
this.level = level;
316
this.parent = parent;
317
}
318
319
public static HierarchicalContext root(String level) {
320
return new HierarchicalContext(level, null);
321
}
322
323
public HierarchicalContext child(String childLevel) {
324
return new HierarchicalContext(childLevel, this);
325
}
326
327
public static HierarchicalContext current() {
328
return Context.current().get(KEY);
329
}
330
331
@Override
332
public Scope makeCurrent() {
333
return Context.current().with(this).makeCurrent();
334
}
335
336
@Override
337
public Context storeInContext(Context context) {
338
return context.with(KEY, this);
339
}
340
341
public List<String> getHierarchy() {
342
List<String> hierarchy = new ArrayList<>();
343
HierarchicalContext current = this;
344
while (current != null) {
345
hierarchy.add(0, current.level); // Add to front
346
current = current.parent;
347
}
348
return hierarchy;
349
}
350
}
351
352
// Usage
353
HierarchicalContext root = HierarchicalContext.root("application");
354
try (Scope rootScope = root.makeCurrent()) {
355
356
HierarchicalContext service = HierarchicalContext.current().child("user-service");
357
try (Scope serviceScope = service.makeCurrent()) {
358
359
HierarchicalContext operation = HierarchicalContext.current().child("get-user");
360
try (Scope operationScope = operation.makeCurrent()) {
361
362
List<String> hierarchy = HierarchicalContext.current().getHierarchy();
363
// hierarchy = ["application", "user-service", "get-user"]
364
}
365
}
366
}
367
```
368
369
### Context Value Composition
370
371
```java
372
public class CompositeContext implements ImplicitContextKeyed {
373
private static final ContextKey<CompositeContext> KEY = ContextKey.named("composite");
374
375
private final Map<String, Object> values;
376
377
private CompositeContext(Map<String, Object> values) {
378
this.values = Collections.unmodifiableMap(new HashMap<>(values));
379
}
380
381
public static CompositeContext empty() {
382
return new CompositeContext(Collections.emptyMap());
383
}
384
385
public static CompositeContext current() {
386
CompositeContext context = Context.current().get(KEY);
387
return context != null ? context : empty();
388
}
389
390
@Override
391
public Scope makeCurrent() {
392
return Context.current().with(this).makeCurrent();
393
}
394
395
@Override
396
public Context storeInContext(Context context) {
397
return context.with(KEY, this);
398
}
399
400
public <T> CompositeContext with(String key, T value) {
401
Map<String, Object> newValues = new HashMap<>(values);
402
newValues.put(key, value);
403
return new CompositeContext(newValues);
404
}
405
406
@SuppressWarnings("unchecked")
407
public <T> T get(String key, Class<T> type) {
408
Object value = values.get(key);
409
return type.isInstance(value) ? (T) value : null;
410
}
411
}
412
413
// Usage
414
CompositeContext composite = CompositeContext.current()
415
.with("requestId", "req-123")
416
.with("timestamp", System.currentTimeMillis())
417
.with("feature-flags", Set.of("feature-a", "feature-b"));
418
419
try (Scope scope = composite.makeCurrent()) {
420
String requestId = CompositeContext.current().get("requestId", String.class);
421
Long timestamp = CompositeContext.current().get("timestamp", Long.class);
422
Set<String> flags = CompositeContext.current().get("feature-flags", Set.class);
423
}
424
```
425
426
## Best Practices
427
428
1. **Use private context keys** - Hide implementation details from users
429
2. **Provide static current() methods** - Make it easy to access current instance
430
3. **Make objects immutable** - Follow context immutability patterns
431
4. **Use descriptive key names** - Aid in debugging and logging
432
5. **Handle null contexts gracefully** - Provide sensible defaults
433
6. **Document thread safety** - Clarify concurrency expectations
434
435
```java
436
// Good implementation example
437
public class WellDesignedContext implements ImplicitContextKeyed {
438
private static final ContextKey<WellDesignedContext> KEY =
439
ContextKey.named("well-designed-context");
440
441
// Immutable fields
442
private final String id;
443
private final long timestamp;
444
445
public WellDesignedContext(String id) {
446
this.id = Objects.requireNonNull(id, "id must not be null");
447
this.timestamp = System.currentTimeMillis();
448
}
449
450
// Thread-safe static access
451
public static WellDesignedContext current() {
452
WellDesignedContext context = Context.current().get(KEY);
453
if (context == null) {
454
throw new IllegalStateException("No WellDesignedContext in current context");
455
}
456
return context;
457
}
458
459
// Safe static access with default
460
public static WellDesignedContext currentOrDefault() {
461
WellDesignedContext context = Context.current().get(KEY);
462
return context != null ? context : new WellDesignedContext("default");
463
}
464
465
@Override
466
public Scope makeCurrent() {
467
return Context.current().with(this).makeCurrent();
468
}
469
470
@Override
471
public Context storeInContext(Context context) {
472
return context.with(KEY, this);
473
}
474
475
// Immutable accessors
476
public String getId() { return id; }
477
public long getTimestamp() { return timestamp; }
478
}
479
```