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

implicit-context-values.mddocs/

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

```