0
# Function Wrapping
1
2
Function wrapping provides context propagation utilities for various Java functional interfaces, allowing context to be automatically available when lambda expressions and method references are executed.
3
4
## Basic Function Wrapping
5
6
### Runnable Wrapping
7
8
Wraps a Runnable to execute with this context.
9
10
```java { .api }
11
Runnable wrap(Runnable runnable);
12
```
13
14
**Parameters:**
15
- `runnable` - The Runnable to wrap
16
17
**Returns:** A Runnable that executes with the wrapping context
18
19
**Usage Example:**
20
```java
21
Context userContext = Context.current().with(USER_ID_KEY, "user123");
22
23
Runnable task = userContext.wrap(() -> {
24
// This code runs with userContext active
25
String userId = Context.current().get(USER_ID_KEY); // "user123"
26
performUserTask(userId);
27
});
28
29
// Execute later - context is automatically applied
30
task.run();
31
32
// Can also be used with thread creation
33
Thread thread = new Thread(task);
34
thread.start();
35
```
36
37
### Callable Wrapping
38
39
Wraps a Callable to execute with this context.
40
41
```java { .api }
42
<T> Callable<T> wrap(Callable<T> callable);
43
```
44
45
**Parameters:**
46
- `callable` - The Callable to wrap
47
48
**Returns:** A Callable that executes with the wrapping context
49
50
**Usage Example:**
51
```java
52
Context requestContext = Context.current()
53
.with(REQUEST_ID_KEY, "req-456")
54
.with(USER_ID_KEY, "user789");
55
56
Callable<String> operation = requestContext.wrap(() -> {
57
// Both request ID and user ID are available
58
String requestId = Context.current().get(REQUEST_ID_KEY);
59
String userId = Context.current().get(USER_ID_KEY);
60
61
return processRequest(requestId, userId);
62
});
63
64
// Execute with automatic context
65
String result = operation.call();
66
67
// Or submit to executor
68
ExecutorService executor = Executors.newSingleThreadExecutor();
69
Future<String> future = executor.submit(operation);
70
```
71
72
## Functional Interface Wrapping
73
74
### Function Wrapping
75
76
Wraps a Function to execute with this context.
77
78
```java { .api }
79
<T, U> Function<T, U> wrapFunction(Function<T, U> function);
80
```
81
82
**Usage Example:**
83
```java
84
Context processingContext = Context.current().with(PROCESSOR_ID_KEY, "proc-123");
85
86
Function<String, String> processor = processingContext.wrapFunction(input -> {
87
String processorId = Context.current().get(PROCESSOR_ID_KEY);
88
return String.format("[%s] %s", processorId, input.toUpperCase());
89
});
90
91
// Use in stream operations
92
List<String> inputs = Arrays.asList("hello", "world");
93
List<String> results = inputs.stream()
94
.map(processor) // Context automatically applied
95
.collect(Collectors.toList());
96
```
97
98
### BiFunction Wrapping
99
100
Wraps a BiFunction to execute with this context.
101
102
```java { .api }
103
<T, U, V> BiFunction<T, U, V> wrapFunction(BiFunction<T, U, V> function);
104
```
105
106
**Usage Example:**
107
```java
108
Context calculationContext = Context.current().with(PRECISION_KEY, 2);
109
110
BiFunction<Double, Double, String> calculator = calculationContext.wrapFunction((a, b) -> {
111
Integer precision = Context.current().get(PRECISION_KEY);
112
double result = a + b;
113
return String.format("%." + precision + "f", result);
114
});
115
116
String sum = calculator.apply(3.14159, 2.71828); // Context provides precision
117
```
118
119
### Consumer Wrapping
120
121
Wraps a Consumer to execute with this context.
122
123
```java { .api }
124
<T> Consumer<T> wrapConsumer(Consumer<T> consumer);
125
```
126
127
**Usage Example:**
128
```java
129
Context loggingContext = Context.current()
130
.with(LOGGER_NAME_KEY, "UserProcessor")
131
.with(LOG_LEVEL_KEY, "INFO");
132
133
Consumer<String> logger = loggingContext.wrapConsumer(message -> {
134
String loggerName = Context.current().get(LOGGER_NAME_KEY);
135
String logLevel = Context.current().get(LOG_LEVEL_KEY);
136
System.out.printf("[%s] %s: %s%n", logLevel, loggerName, message);
137
});
138
139
// Use with streams
140
List<String> messages = Arrays.asList("Starting process", "Process complete");
141
messages.forEach(logger); // Each call has context
142
```
143
144
### BiConsumer Wrapping
145
146
Wraps a BiConsumer to execute with this context.
147
148
```java { .api }
149
<T, U> BiConsumer<T, U> wrapConsumer(BiConsumer<T, U> consumer);
150
```
151
152
**Usage Example:**
153
```java
154
Context auditContext = Context.current().with(AUDIT_USER_KEY, "admin");
155
156
BiConsumer<String, String> auditor = auditContext.wrapConsumer((action, resource) -> {
157
String auditUser = Context.current().get(AUDIT_USER_KEY);
158
logAuditEvent(auditUser, action, resource);
159
});
160
161
// Use in various contexts
162
auditor.accept("CREATE", "user-record");
163
auditor.accept("DELETE", "temp-file");
164
```
165
166
### Supplier Wrapping
167
168
Wraps a Supplier to execute with this context.
169
170
```java { .api }
171
<T> Supplier<T> wrapSupplier(Supplier<T> supplier);
172
```
173
174
**Usage Example:**
175
```java
176
Context configContext = Context.current().with(CONFIG_SOURCE_KEY, "database");
177
178
Supplier<String> configSupplier = configContext.wrapSupplier(() -> {
179
String source = Context.current().get(CONFIG_SOURCE_KEY);
180
return loadConfiguration(source);
181
});
182
183
// Use with Optional or lazy evaluation
184
Optional<String> config = Optional.of(configSupplier.get());
185
186
// Use with CompletableFuture
187
CompletableFuture<String> futureConfig = CompletableFuture.supplyAsync(configSupplier);
188
```
189
190
## Advanced Function Wrapping Patterns
191
192
### Stream Processing with Context
193
194
```java
195
public class ContextualStreamProcessor {
196
private final Context processingContext;
197
198
public ContextualStreamProcessor(String processorId) {
199
this.processingContext = Context.current().with(PROCESSOR_ID_KEY, processorId);
200
}
201
202
public List<String> processItems(List<String> items) {
203
// All stream operations maintain context
204
Function<String, String> transformer = processingContext.wrapFunction(this::transform);
205
Consumer<String> logger = processingContext.wrapConsumer(this::logItem);
206
207
return items.stream()
208
.peek(logger) // Log with context
209
.map(transformer) // Transform with context
210
.filter(Objects::nonNull)
211
.collect(Collectors.toList());
212
}
213
214
private String transform(String item) {
215
String processorId = Context.current().get(PROCESSOR_ID_KEY);
216
return String.format("[%s] %s", processorId, item.toUpperCase());
217
}
218
219
private void logItem(String item) {
220
String processorId = Context.current().get(PROCESSOR_ID_KEY);
221
logger.debug("Processing item {} with processor {}", item, processorId);
222
}
223
}
224
```
225
226
### Asynchronous Operations with Context
227
228
```java
229
public class AsyncOperationManager {
230
public CompletableFuture<String> processAsync(String input) {
231
Context operationContext = Context.current()
232
.with(OPERATION_ID_KEY, UUID.randomUUID().toString())
233
.with(START_TIME_KEY, System.currentTimeMillis());
234
235
// Wrap suppliers for async execution
236
Supplier<String> processor = operationContext.wrapSupplier(() -> {
237
String operationId = Context.current().get(OPERATION_ID_KEY);
238
Long startTime = Context.current().get(START_TIME_KEY);
239
240
try {
241
String result = performComplexOperation(input);
242
logSuccess(operationId, startTime);
243
return result;
244
} catch (Exception e) {
245
logError(operationId, startTime, e);
246
throw e;
247
}
248
});
249
250
return CompletableFuture.supplyAsync(processor);
251
}
252
}
253
```
254
255
### Event Processing with Context
256
257
```java
258
public class EventProcessor {
259
private final Map<String, Consumer<Event>> handlers = new HashMap<>();
260
261
public void registerHandler(String eventType, Consumer<Event> handler) {
262
// Capture current context when registering
263
Context registrationContext = Context.current();
264
Consumer<Event> contextualHandler = registrationContext.wrapConsumer(handler);
265
handlers.put(eventType, contextualHandler);
266
}
267
268
public void processEvent(Event event) {
269
Consumer<Event> handler = handlers.get(event.getType());
270
if (handler != null) {
271
// Handler executes with registration context
272
handler.accept(event);
273
}
274
}
275
}
276
277
// Usage
278
EventProcessor processor = new EventProcessor();
279
280
// Register handler with specific context
281
Context userContext = Context.current().with(USER_ID_KEY, "admin");
282
try (Scope scope = userContext.makeCurrent()) {
283
processor.registerHandler("USER_ACTION", event -> {
284
// This handler always runs with admin context
285
String userId = Context.current().get(USER_ID_KEY); // "admin"
286
handleUserAction(event, userId);
287
});
288
}
289
290
// Later execution maintains original context
291
processor.processEvent(new Event("USER_ACTION", data));
292
```
293
294
## Function Composition with Context
295
296
```java
297
public class ContextualPipeline {
298
public static <T, U, V> Function<T, V> compose(
299
Context context,
300
Function<T, U> first,
301
Function<U, V> second) {
302
303
Function<T, U> contextFirst = context.wrapFunction(first);
304
Function<U, V> contextSecond = context.wrapFunction(second);
305
306
return contextFirst.andThen(contextSecond);
307
}
308
309
// Usage
310
public String processData(String input) {
311
Context pipelineContext = Context.current().with(PIPELINE_ID_KEY, "pipe-123");
312
313
Function<String, String> pipeline = compose(
314
pipelineContext,
315
this::validateInput,
316
this::transformInput
317
);
318
319
return pipeline.apply(input);
320
}
321
}
322
```
323
324
## Performance Notes
325
326
- Function wrapping has minimal overhead - it captures context at wrap time
327
- Wrapped functions can be reused multiple times efficiently
328
- Context is applied only during function execution, not at wrap time
329
- Consider caching wrapped functions for frequently used operations