or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

context-keys-scoping.mdcontext-propagation.mdcore-context.mdexecutor-integration.mdfunction-wrapping.mdimplicit-context-values.mdindex.mdstorage-customization.md

function-wrapping.mddocs/

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