0
# Execution Utilities
1
2
Utilities for wrapping Runnables, Callables, and Executors to automatically propagate context across thread boundaries and asynchronous operations.
3
4
## Capabilities
5
6
### Direct Execution
7
8
Execute code immediately within a specific context scope. These methods handle attach/detach automatically.
9
10
```java { .api }
11
/**
12
* Immediately run a Runnable with this context as the current context.
13
* @param r Runnable to run
14
*/
15
public void run(Runnable r);
16
17
/**
18
* Immediately call a Callable with this context as the current context.
19
* @param c Callable to call
20
* @return Result of the callable
21
* @throws Exception If the callable throws an exception
22
*/
23
public <V> V call(Callable<V> c) throws Exception;
24
```
25
26
**Usage Examples:**
27
28
```java
29
Context.Key<String> USER_KEY = Context.key("user");
30
Context withUser = Context.current().withValue(USER_KEY, "alice");
31
32
// Run a task with the context
33
withUser.run(() -> {
34
String user = USER_KEY.get(); // "alice"
35
processUserRequest(user);
36
});
37
38
// Call a task that returns a value
39
String result = withUser.call(() -> {
40
String user = USER_KEY.get(); // "alice"
41
return fetchUserData(user);
42
});
43
44
// Exception handling with call()
45
try {
46
Integer count = withUser.call(() -> {
47
return performCalculation();
48
});
49
} catch (Exception e) {
50
System.out.println("Calculation failed: " + e.getMessage());
51
}
52
```
53
54
### Runnable and Callable Wrapping
55
56
Wrap Runnables and Callables so they execute with a specific context, useful for passing tasks to other threads or executors.
57
58
```java { .api }
59
/**
60
* Wrap a Runnable so that it executes with this context as the current context.
61
* @param r Runnable to wrap
62
* @return Wrapped Runnable that propagates this context
63
*/
64
public Runnable wrap(Runnable r);
65
66
/**
67
* Wrap a Callable so that it executes with this context as the current context.
68
* @param c Callable to wrap
69
* @return Wrapped Callable that propagates this context
70
*/
71
public <C> Callable<C> wrap(Callable<C> c);
72
```
73
74
**Usage Examples:**
75
76
```java
77
Context.Key<String> REQUEST_ID_KEY = Context.key("requestId");
78
Context withRequestId = Context.current().withValue(REQUEST_ID_KEY, "req-123");
79
80
ExecutorService executor = Executors.newFixedThreadPool(4);
81
82
// Wrap a Runnable for execution in another thread
83
Runnable task = withRequestId.wrap(() -> {
84
String requestId = REQUEST_ID_KEY.get(); // "req-123"
85
processRequest(requestId);
86
});
87
88
// Submit to executor - context will be propagated
89
executor.submit(task);
90
91
// Wrap a Callable for execution in another thread
92
Callable<String> computation = withRequestId.wrap(() -> {
93
String requestId = REQUEST_ID_KEY.get(); // "req-123"
94
return performComputation(requestId);
95
});
96
97
// Submit and get result - context was propagated
98
Future<String> result = executor.submit(computation);
99
String computationResult = result.get();
100
```
101
102
### Fixed Context Executor
103
104
Wrap an Executor so that all tasks submitted to it execute with a specific context. The context is fixed at creation time.
105
106
```java { .api }
107
/**
108
* Wrap an Executor so that it always executes with this context as the current context.
109
* @param e Executor to wrap
110
* @return Wrapped Executor that propagates this context for all tasks
111
*/
112
public Executor fixedContextExecutor(Executor e);
113
```
114
115
**Usage Example:**
116
117
```java
118
Context.Key<String> TENANT_KEY = Context.key("tenant");
119
Context withTenant = Context.current().withValue(TENANT_KEY, "tenant-abc");
120
121
ExecutorService baseExecutor = Executors.newFixedThreadPool(4);
122
123
// Create executor that always uses the tenant context
124
Executor tenantExecutor = withTenant.fixedContextExecutor(baseExecutor);
125
126
// All tasks submitted to this executor will have the tenant context
127
tenantExecutor.execute(() -> {
128
String tenant = TENANT_KEY.get(); // "tenant-abc"
129
processTenantRequest(tenant);
130
});
131
132
tenantExecutor.execute(() -> {
133
String tenant = TENANT_KEY.get(); // "tenant-abc"
134
performTenantMaintenance(tenant);
135
});
136
137
// Even if current context changes, the executor still uses the fixed context
138
Context.current().withValue(TENANT_KEY, "different-tenant").run(() -> {
139
tenantExecutor.execute(() -> {
140
String tenant = TENANT_KEY.get(); // Still "tenant-abc"!
141
handleTenantData(tenant);
142
});
143
});
144
```
145
146
### Current Context Executor
147
148
Create an Executor that propagates whatever the current context is at the time each task is submitted. This is a static method that captures context dynamically.
149
150
```java { .api }
151
/**
152
* Create an executor that propagates the current context when execute() is called.
153
* The context is captured at submission time, not creation time.
154
* @param e Base executor to wrap
155
* @return Wrapped Executor that propagates the current context for each task
156
*/
157
public static Executor currentContextExecutor(Executor e);
158
```
159
160
**Usage Example:**
161
162
```java
163
Context.Key<String> USER_KEY = Context.key("user");
164
ExecutorService baseExecutor = Executors.newFixedThreadPool(4);
165
166
// Create executor that captures current context for each task
167
Executor contextExecutor = Context.currentContextExecutor(baseExecutor);
168
169
// Task submitted with user "alice" context
170
Context.current().withValue(USER_KEY, "alice").run(() -> {
171
contextExecutor.execute(() -> {
172
String user = USER_KEY.get(); // "alice"
173
processUserTask(user);
174
});
175
});
176
177
// Different task submitted with user "bob" context
178
Context.current().withValue(USER_KEY, "bob").run(() -> {
179
contextExecutor.execute(() -> {
180
String user = USER_KEY.get(); // "bob"
181
processUserTask(user);
182
});
183
});
184
185
// Tasks submitted without user context
186
contextExecutor.execute(() -> {
187
String user = USER_KEY.get(); // null (no user in context)
188
processGuestTask();
189
});
190
```
191
192
### Context Propagation Patterns
193
194
Common patterns for propagating context across asynchronous operations.
195
196
**CompletableFuture with Context:**
197
198
```java
199
Context.Key<String> TRACE_ID_KEY = Context.key("traceId");
200
Context withTrace = Context.current().withValue(TRACE_ID_KEY, "trace-456");
201
202
ExecutorService executor = Executors.newFixedThreadPool(4);
203
Executor contextExecutor = Context.currentContextExecutor(executor);
204
205
CompletableFuture<String> future = withTrace.call(() -> {
206
return CompletableFuture
207
.supplyAsync(() -> {
208
String traceId = TRACE_ID_KEY.get(); // "trace-456"
209
return fetchData(traceId);
210
}, contextExecutor)
211
.thenApplyAsync(data -> {
212
String traceId = TRACE_ID_KEY.get(); // "trace-456"
213
return processData(data, traceId);
214
}, contextExecutor);
215
});
216
```
217
218
**Thread Pool with Shared Context:**
219
220
```java
221
Context.Key<String> SERVICE_KEY = Context.key("service");
222
Context serviceContext = Context.current().withValue(SERVICE_KEY, "payment-service");
223
224
ExecutorService pool = Executors.newFixedThreadPool(10);
225
Executor serviceExecutor = serviceContext.fixedContextExecutor(pool);
226
227
// All tasks in this pool will have service context
228
for (int i = 0; i < 100; i++) {
229
final int taskId = i;
230
serviceExecutor.execute(() -> {
231
String service = SERVICE_KEY.get(); // "payment-service"
232
processPaymentTask(taskId, service);
233
});
234
}
235
```
236
237
**Mixed Context Propagation:**
238
239
```java
240
Context.Key<String> REQUEST_KEY = Context.key("request");
241
Context.Key<String> SESSION_KEY = Context.key("session");
242
243
ExecutorService executor = Executors.newFixedThreadPool(4);
244
Executor currentContextExecutor = Context.currentContextExecutor(executor);
245
246
// Base context with session
247
Context sessionContext = Context.current().withValue(SESSION_KEY, "session-789");
248
249
sessionContext.run(() -> {
250
// Each request gets its own context but inherits session
251
for (int i = 0; i < 5; i++) {
252
final String requestId = "req-" + i;
253
254
Context.current().withValue(REQUEST_KEY, requestId).run(() -> {
255
// This task will have both session and request context
256
currentContextExecutor.execute(() -> {
257
String session = SESSION_KEY.get(); // "session-789"
258
String request = REQUEST_KEY.get(); // "req-0", "req-1", etc.
259
handleRequest(request, session);
260
});
261
});
262
}
263
});
264
```