0
# Executor Integration
1
2
Context executor integration provides automatic context propagation across asynchronous operations by wrapping Java executor services. This ensures that context values remain available in background threads and scheduled tasks.
3
4
## Static Executor Wrapping
5
6
Static methods wrap executors to automatically propagate the current context at the time of task submission.
7
8
### Executor Wrapping
9
10
Wraps an Executor to propagate the current context.
11
12
```java { .api }
13
static Executor taskWrapping(Executor executor);
14
```
15
16
**Parameters:**
17
- `executor` - The executor to wrap
18
19
**Returns:** An Executor that propagates current context to submitted tasks
20
21
**Usage Example:**
22
```java
23
// Create context-aware executor
24
Executor executor = Executors.newFixedThreadPool(4);
25
Executor contextExecutor = Context.taskWrapping(executor);
26
27
// Context at submission time is propagated
28
Context.current().with(USER_ID_KEY, "user123").makeCurrent();
29
30
contextExecutor.execute(() -> {
31
// This runs with the context from submission time
32
String userId = Context.current().get(USER_ID_KEY); // "user123"
33
processUser(userId);
34
});
35
```
36
37
### ExecutorService Wrapping
38
39
Wraps an ExecutorService to propagate current context to all submitted tasks.
40
41
```java { .api }
42
static ExecutorService taskWrapping(ExecutorService executorService);
43
```
44
45
**Parameters:**
46
- `executorService` - The executor service to wrap
47
48
**Returns:** An ExecutorService that propagates current context
49
50
**Usage Example:**
51
```java
52
ExecutorService executorService = Executors.newCachedThreadPool();
53
ExecutorService contextExecutorService = Context.taskWrapping(executorService);
54
55
Context.current().with(REQUEST_ID_KEY, "req-456").makeCurrent();
56
57
// Submit callable with context propagation
58
Future<String> future = contextExecutorService.submit(() -> {
59
String requestId = Context.current().get(REQUEST_ID_KEY); // "req-456"
60
return processRequest(requestId);
61
});
62
63
String result = future.get();
64
```
65
66
### ScheduledExecutorService Wrapping
67
68
Wraps a ScheduledExecutorService to propagate context to scheduled tasks.
69
70
```java { .api }
71
static ScheduledExecutorService taskWrapping(ScheduledExecutorService executorService);
72
```
73
74
**Parameters:**
75
- `executorService` - The scheduled executor service to wrap
76
77
**Returns:** A ScheduledExecutorService that propagates current context
78
79
**Usage Example:**
80
```java
81
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
82
ScheduledExecutorService contextScheduler = Context.taskWrapping(scheduler);
83
84
Context.current().with(JOB_ID_KEY, "job-789").makeCurrent();
85
86
// Schedule task with context propagation
87
ScheduledFuture<?> future = contextScheduler.schedule(() -> {
88
String jobId = Context.current().get(JOB_ID_KEY); // "job-789"
89
executeScheduledJob(jobId);
90
}, 5, TimeUnit.SECONDS);
91
```
92
93
**Note:** Context is NOT propagated for `scheduleAtFixedRate()` and `scheduleWithFixedDelay()` calls due to their repeated execution nature.
94
95
## Instance Executor Wrapping
96
97
Instance methods wrap executors to propagate a specific context rather than the current context.
98
99
### Instance Executor Wrapping
100
101
Wraps an Executor to propagate this specific context.
102
103
```java { .api }
104
Executor wrap(Executor executor);
105
```
106
107
**Usage Example:**
108
```java
109
// Create specific context
110
Context userContext = Context.current().with(USER_ID_KEY, "admin");
111
112
// Wrap executor with specific context
113
Executor executor = Executors.newSingleThreadExecutor();
114
Executor contextExecutor = userContext.wrap(executor);
115
116
// Tasks always run with the admin context, regardless of current context
117
Context.current().with(USER_ID_KEY, "guest").makeCurrent();
118
119
contextExecutor.execute(() -> {
120
String userId = Context.current().get(USER_ID_KEY); // "admin" (not "guest")
121
performAdminTask(userId);
122
});
123
```
124
125
### Instance ExecutorService Wrapping
126
127
Wraps an ExecutorService to propagate this specific context.
128
129
```java { .api }
130
ExecutorService wrap(ExecutorService executorService);
131
```
132
133
**Usage Example:**
134
```java
135
Context backgroundContext = Context.current()
136
.with(USER_ID_KEY, "system")
137
.with(OPERATION_KEY, "background");
138
139
ExecutorService executorService = Executors.newFixedThreadPool(4);
140
ExecutorService contextExecutorService = backgroundContext.wrap(executorService);
141
142
// All submissions use background context
143
List<Future<String>> futures = new ArrayList<>();
144
for (String task : tasks) {
145
Future<String> future = contextExecutorService.submit(() -> {
146
// Always runs with system user context
147
String userId = Context.current().get(USER_ID_KEY); // "system"
148
return processBackgroundTask(task);
149
});
150
futures.add(future);
151
}
152
```
153
154
### Instance ScheduledExecutorService Wrapping
155
156
Wraps a ScheduledExecutorService to propagate this specific context.
157
158
```java { .api }
159
ScheduledExecutorService wrap(ScheduledExecutorService executorService);
160
```
161
162
**Usage Example:**
163
```java
164
Context maintenanceContext = Context.current()
165
.with(USER_ID_KEY, "maintenance")
166
.with(OPERATION_KEY, "cleanup");
167
168
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
169
ScheduledExecutorService contextScheduler = maintenanceContext.wrap(scheduler);
170
171
// Schedule maintenance tasks with specific context
172
contextScheduler.scheduleAtFixedRate(() -> {
173
String userId = Context.current().get(USER_ID_KEY); // "maintenance"
174
performMaintenance();
175
}, 0, 1, TimeUnit.HOURS);
176
```
177
178
## Executor Patterns
179
180
### Mixed Context Usage
181
182
```java
183
public class TaskProcessor {
184
private final ExecutorService backgroundExecutor;
185
private final ExecutorService userExecutor;
186
187
public TaskProcessor() {
188
// Different executors for different contexts
189
ExecutorService baseExecutor = Executors.newFixedThreadPool(8);
190
191
// Background tasks run with system context
192
Context systemContext = Context.current().with(USER_ID_KEY, "system");
193
this.backgroundExecutor = systemContext.wrap(baseExecutor);
194
195
// User tasks propagate current context
196
this.userExecutor = Context.taskWrapping(baseExecutor);
197
}
198
199
public void processUserTask(Runnable task) {
200
// Uses current context
201
userExecutor.execute(task);
202
}
203
204
public void processBackgroundTask(Runnable task) {
205
// Always uses system context
206
backgroundExecutor.execute(task);
207
}
208
}
209
```
210
211
### Context-Aware Thread Pool
212
213
```java
214
public class ContextAwareService {
215
private final ExecutorService executor;
216
217
public ContextAwareService() {
218
ExecutorService baseExecutor = Executors.newWorkStealingPool();
219
this.executor = Context.taskWrapping(baseExecutor);
220
}
221
222
public CompletableFuture<String> processAsync(String input) {
223
// Context from calling thread is automatically propagated
224
return CompletableFuture.supplyAsync(() -> {
225
// Process with propagated context
226
String userId = Context.current().get(USER_ID_KEY);
227
return processWithUser(input, userId);
228
}, executor);
229
}
230
231
public void shutdown() {
232
executor.shutdown();
233
}
234
}
235
```
236
237
### Error Handling with Context Executors
238
239
```java
240
public void handleAsyncErrors() {
241
ExecutorService executor = Context.taskWrapping(Executors.newSingleThreadExecutor());
242
243
Context errorContext = Context.current().with(OPERATION_KEY, "error-prone");
244
245
try (Scope scope = errorContext.makeCurrent()) {
246
Future<String> future = executor.submit(() -> {
247
try {
248
// Context available in error handling
249
String operation = Context.current().get(OPERATION_KEY);
250
return performRiskyOperation();
251
} catch (Exception e) {
252
// Log with context information
253
String operation = Context.current().get(OPERATION_KEY);
254
logger.error("Operation {} failed", operation, e);
255
throw e;
256
}
257
});
258
259
try {
260
String result = future.get();
261
} catch (ExecutionException e) {
262
// Handle wrapped exception
263
handleError(e.getCause());
264
}
265
}
266
}
267
```
268
269
## Performance Considerations
270
271
- Wrapped executors have minimal overhead - they simply wrap submitted tasks
272
- Context propagation happens at task submission time, not execution time
273
- Avoid creating multiple wrappers around the same executor
274
- Reuse wrapped executors when possible
275
276
```java
277
// Good: Create wrapper once, reuse
278
private static final ExecutorService CONTEXT_EXECUTOR =
279
Context.taskWrapping(Executors.newFixedThreadPool(10));
280
281
// Avoid: Creating wrapper for each use
282
public void badPattern() {
283
ExecutorService wrapped = Context.taskWrapping(someExecutor); // Don't do this repeatedly
284
wrapped.execute(task);
285
}
286
```