or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cache-management.mdcache-operations.mdconfiguration.mdevents.mdindex.mdintegration.mdmanagement.mdspi.md

integration.mddocs/

0

# Integration Features

1

2

The Integration Features system provides support for cache loading, writing, copying, and entry processing to integrate with external data sources and custom business logic. These features enable seamless integration between the cache and external systems like databases, web services, and custom data processing pipelines.

3

4

## Capabilities

5

6

### Cache Loading

7

8

Integration with external data sources through cache loaders for read-through functionality.

9

10

```java { .api }

11

/**

12

* Adapts JCache CacheLoader to Caffeine CacheLoader for integration

13

*/

14

public final class JCacheLoaderAdapter<K, V> implements CacheLoader<K, Expirable<V>> {

15

16

/**

17

* Load a single entry from external data source

18

* @param key the key to load

19

* @return wrapped value with expiration information

20

*/

21

public @Nullable Expirable<V> load(K key);

22

23

/**

24

* Load multiple entries from external data source

25

* @param keys the keys to load

26

* @return map of loaded key-value pairs

27

*/

28

public Map<K, Expirable<V>> loadAll(Set<? extends K> keys);

29

}

30

```

31

32

**Usage Examples:**

33

34

```java

35

// Implement custom cache loader

36

public class DatabaseUserLoader implements CacheLoader<String, User> {

37

private final UserRepository userRepository;

38

39

public DatabaseUserLoader(UserRepository userRepository) {

40

this.userRepository = userRepository;

41

}

42

43

@Override

44

public User load(String userId) throws CacheLoaderException {

45

try {

46

User user = userRepository.findById(userId);

47

if (user == null) {

48

throw new CacheLoaderException("User not found: " + userId);

49

}

50

return user;

51

} catch (Exception e) {

52

throw new CacheLoaderException("Failed to load user: " + userId, e);

53

}

54

}

55

56

@Override

57

public Map<String, User> loadAll(Iterable<? extends String> userIds) throws CacheLoaderException {

58

try {

59

Map<String, User> users = new HashMap<>();

60

for (String userId : userIds) {

61

User user = userRepository.findById(userId);

62

if (user != null) {

63

users.put(userId, user);

64

}

65

}

66

return users;

67

} catch (Exception e) {

68

throw new CacheLoaderException("Failed to load users", e);

69

}

70

}

71

}

72

73

// Configure cache with loader

74

CaffeineConfiguration<String, User> config = new CaffeineConfiguration<String, User>()

75

.setTypes(String.class, User.class)

76

.setCacheLoaderFactory(() -> new DatabaseUserLoader(userRepository))

77

.setReadThrough(true);

78

79

Cache<String, User> userCache = cacheManager.createCache("users", config);

80

81

// Automatic loading on cache miss

82

User user = userCache.get("user123"); // Loads from database if not cached

83

```

84

85

### Cache Writing

86

87

Integration with external data sources through cache writers for write-through functionality.

88

89

```java { .api }

90

/**

91

* Disabled cache writer implementation for caches without write-through

92

*/

93

public enum DisabledCacheWriter implements CacheWriter<Object, Object> {

94

INSTANCE;

95

96

/**

97

* Get singleton instance of disabled cache writer

98

* @return the singleton instance

99

*/

100

public static CacheWriter<Object, Object> get();

101

102

/**

103

* No-op write operation

104

* @param entry the entry to write (ignored)

105

*/

106

public void write(Cache.Entry<? extends Object, ? extends Object> entry);

107

108

/**

109

* No-op write all operation

110

* @param entries the entries to write (ignored)

111

*/

112

public void writeAll(Collection<Cache.Entry<? extends Object, ? extends Object>> entries);

113

114

/**

115

* No-op delete operation

116

* @param key the key to delete (ignored)

117

*/

118

public void delete(Object key);

119

120

/**

121

* No-op delete all operation

122

* @param keys the keys to delete (ignored)

123

*/

124

public void deleteAll(Collection<?> keys);

125

}

126

```

127

128

**Usage Examples:**

129

130

```java

131

// Implement custom cache writer

132

public class DatabaseUserWriter implements CacheWriter<String, User> {

133

private final UserRepository userRepository;

134

135

public DatabaseUserWriter(UserRepository userRepository) {

136

this.userRepository = userRepository;

137

}

138

139

@Override

140

public void write(Cache.Entry<? extends String, ? extends User> entry) throws CacheWriterException {

141

try {

142

userRepository.save(entry.getValue());

143

} catch (Exception e) {

144

throw new CacheWriterException("Failed to write user: " + entry.getKey(), e);

145

}

146

}

147

148

@Override

149

public void writeAll(Collection<Cache.Entry<? extends String, ? extends User>> entries)

150

throws CacheWriterException {

151

try {

152

List<User> users = entries.stream()

153

.map(Cache.Entry::getValue)

154

.collect(Collectors.toList());

155

userRepository.saveAll(users);

156

} catch (Exception e) {

157

throw new CacheWriterException("Failed to write users", e);

158

}

159

}

160

161

@Override

162

public void delete(Object key) throws CacheWriterException {

163

try {

164

userRepository.deleteById((String) key);

165

} catch (Exception e) {

166

throw new CacheWriterException("Failed to delete user: " + key, e);

167

}

168

}

169

170

@Override

171

public void deleteAll(Collection<?> keys) throws CacheWriterException {

172

try {

173

List<String> userIds = keys.stream()

174

.map(String.class::cast)

175

.collect(Collectors.toList());

176

userRepository.deleteAllById(userIds);

177

} catch (Exception e) {

178

throw new CacheWriterException("Failed to delete users", e);

179

}

180

}

181

}

182

183

// Configure cache with writer

184

CaffeineConfiguration<String, User> config = new CaffeineConfiguration<String, User>()

185

.setTypes(String.class, User.class)

186

.setCacheWriterFactory(() -> new DatabaseUserWriter(userRepository))

187

.setWriteThrough(true);

188

189

Cache<String, User> userCache = cacheManager.createCache("users", config);

190

191

// Automatic writing to database

192

userCache.put("user123", new User("John Doe")); // Also saves to database

193

userCache.remove("user123"); // Also deletes from database

194

```

195

196

### Copy Support

197

198

Support for store-by-value semantics through configurable copying strategies.

199

200

```java { .api }

201

/**

202

* Interface for copying objects for store-by-value semantics

203

*/

204

public interface Copier {

205

206

/**

207

* Copy an object using the specified ClassLoader

208

* @param object the object to copy

209

* @param classLoader the ClassLoader for deserialization

210

* @return copied object

211

*/

212

public <T> T copy(T object, ClassLoader classLoader);

213

214

/**

215

* Get identity copier that returns objects unchanged

216

* @return identity copier instance

217

*/

218

public static Copier identity();

219

}

220

```

221

222

### Java Serialization Copier

223

224

Default implementation using Java serialization for copying objects.

225

226

```java { .api }

227

/**

228

* Copier implementation using Java serialization

229

*/

230

public class JavaSerializationCopier extends AbstractCopier<byte[]> {

231

232

/**

233

* Create new Java serialization copier

234

*/

235

public JavaSerializationCopier();

236

237

/**

238

* Serialize object to byte array

239

* @param object the object to serialize

240

* @param classLoader the ClassLoader for serialization

241

* @return serialized byte array

242

*/

243

protected byte[] serialize(Object object, ClassLoader classLoader);

244

245

/**

246

* Deserialize byte array to object

247

* @param data the serialized data

248

* @param classLoader the ClassLoader for deserialization

249

* @return deserialized object

250

*/

251

protected Object deserialize(byte[] data, ClassLoader classLoader);

252

}

253

```

254

255

**Usage Examples:**

256

257

```java

258

// Custom copier implementation using JSON

259

public class JsonCopier implements Copier {

260

private final ObjectMapper objectMapper;

261

262

public JsonCopier() {

263

this.objectMapper = new ObjectMapper();

264

}

265

266

@Override

267

public <T> T copy(T object, ClassLoader classLoader) {

268

if (object == null) {

269

return null;

270

}

271

272

try {

273

// Serialize to JSON and deserialize back

274

String json = objectMapper.writeValueAsString(object);

275

@SuppressWarnings("unchecked")

276

Class<T> clazz = (Class<T>) object.getClass();

277

return objectMapper.readValue(json, clazz);

278

} catch (Exception e) {

279

throw new RuntimeException("Failed to copy object", e);

280

}

281

}

282

}

283

284

// Configure cache with custom copier

285

CaffeineConfiguration<String, User> config = new CaffeineConfiguration<String, User>()

286

.setTypes(String.class, User.class)

287

.setStoreByValue(true)

288

.setCopierFactory(() -> new JsonCopier());

289

290

Cache<String, User> userCache = cacheManager.createCache("users", config);

291

292

// Objects are copied on store and retrieve

293

User original = new User("John Doe");

294

userCache.put("user1", original);

295

User retrieved = userCache.get("user1"); // Different instance, same data

296

assert original != retrieved; // Different object references

297

assert original.equals(retrieved); // Same data

298

```

299

300

### Entry Processing

301

302

Support for atomic entry processing operations with mutable entry interface.

303

304

```java { .api }

305

/**

306

* Mutable entry implementation for entry processors

307

*/

308

public final class EntryProcessorEntry<K, V> implements MutableEntry<K, V> {

309

310

/**

311

* Get the entry key

312

* @return the key of this entry

313

*/

314

public K getKey();

315

316

/**

317

* Get the entry value

318

* @return the value of this entry, or null if not present

319

*/

320

public V getValue();

321

322

/**

323

* Check if the entry exists in the cache

324

* @return true if the entry exists

325

*/

326

public boolean exists();

327

328

/**

329

* Remove this entry from the cache

330

*/

331

public void remove();

332

333

/**

334

* Set the value of this entry

335

* @param value the new value to set

336

*/

337

public void setValue(V value);

338

339

/**

340

* Unwrap this entry to a specific type

341

* @param clazz the class to unwrap to

342

* @return unwrapped instance

343

*/

344

public <T> T unwrap(Class<T> clazz);

345

}

346

347

/**

348

* Entry processor action enumeration

349

*/

350

public enum Action {

351

NONE, // No action performed

352

READ, // Entry was read

353

CREATED, // Entry was created

354

LOADED, // Entry was loaded from external source

355

UPDATED, // Entry was updated

356

DELETED // Entry was deleted

357

}

358

```

359

360

**Usage Examples:**

361

362

```java

363

// Counter increment entry processor

364

EntryProcessor<String, AtomicInteger, Integer> incrementProcessor =

365

(entry, arguments) -> {

366

AtomicInteger counter = entry.getValue();

367

if (counter == null) {

368

counter = new AtomicInteger(0);

369

}

370

int newValue = counter.incrementAndGet();

371

entry.setValue(counter);

372

return newValue;

373

};

374

375

// Conditional update entry processor

376

EntryProcessor<String, User, Boolean> updateEmailProcessor =

377

(entry, arguments) -> {

378

User user = entry.getValue();

379

if (user == null) {

380

return false;

381

}

382

383

String newEmail = (String) arguments[0];

384

if (!isValidEmail(newEmail)) {

385

return false;

386

}

387

388

user.setEmail(newEmail);

389

entry.setValue(user);

390

return true;

391

};

392

393

// Complex business logic processor

394

EntryProcessor<String, Account, TransactionResult> transferProcessor =

395

(entry, arguments) -> {

396

Account account = entry.getValue();

397

BigDecimal amount = (BigDecimal) arguments[0];

398

String transactionId = (String) arguments[1];

399

400

if (account == null) {

401

return new TransactionResult(false, "Account not found");

402

}

403

404

if (account.getBalance().compareTo(amount) < 0) {

405

return new TransactionResult(false, "Insufficient funds");

406

}

407

408

// Perform transfer

409

account.setBalance(account.getBalance().subtract(amount));

410

account.addTransaction(new Transaction(transactionId, amount));

411

entry.setValue(account);

412

413

return new TransactionResult(true, "Transfer completed");

414

};

415

416

// Use entry processors

417

Integer newCount = cache.invoke("counter1", incrementProcessor);

418

Boolean emailUpdated = cache.invoke("user123", updateEmailProcessor, "new@example.com");

419

TransactionResult result = cache.invoke("account456", transferProcessor,

420

new BigDecimal("100.00"), "txn-789");

421

```

422

423

### Integration Patterns

424

425

Common integration patterns for external systems.

426

427

```java { .api }

428

// Database integration pattern

429

public class DatabaseIntegratedCache<K, V> {

430

private final Cache<K, V> cache;

431

private final Repository<K, V> repository;

432

433

public DatabaseIntegratedCache(Cache<K, V> cache, Repository<K, V> repository) {

434

this.cache = cache;

435

this.repository = repository;

436

}

437

438

// Write-behind pattern

439

public void putAsync(K key, V value) {

440

cache.put(key, value);

441

CompletableFuture.runAsync(() -> {

442

try {

443

repository.save(key, value);

444

} catch (Exception e) {

445

// Handle async write failure

446

handleWriteFailure(key, value, e);

447

}

448

});

449

}

450

451

// Cache-aside pattern

452

public V getWithFallback(K key) {

453

V value = cache.get(key);

454

if (value == null) {

455

value = repository.findById(key);

456

if (value != null) {

457

cache.put(key, value);

458

}

459

}

460

return value;

461

}

462

463

private void handleWriteFailure(K key, V value, Exception e) {

464

// Implement failure handling strategy

465

}

466

}

467

468

// Web service integration pattern

469

public class WebServiceCacheLoader implements CacheLoader<String, ApiResponse> {

470

private final WebClient webClient;

471

private final CircuitBreaker circuitBreaker;

472

473

@Override

474

public ApiResponse load(String endpoint) throws CacheLoaderException {

475

return circuitBreaker.executeSupplier(() -> {

476

try {

477

return webClient.get()

478

.uri(endpoint)

479

.retrieve()

480

.bodyToMono(ApiResponse.class)

481

.block(Duration.ofSeconds(10));

482

} catch (Exception e) {

483

throw new CacheLoaderException("Failed to load from web service", e);

484

}

485

});

486

}

487

}

488

489

// Event-driven invalidation pattern

490

public class EventDrivenCacheInvalidation {

491

private final Cache<String, Object> cache;

492

private final EventBus eventBus;

493

494

public EventDrivenCacheInvalidation(Cache<String, Object> cache, EventBus eventBus) {

495

this.cache = cache;

496

this.eventBus = eventBus;

497

498

// Register for invalidation events

499

eventBus.register(this);

500

}

501

502

@Subscribe

503

public void handleInvalidationEvent(CacheInvalidationEvent event) {

504

if (event.isInvalidateAll()) {

505

cache.removeAll();

506

} else {

507

cache.removeAll(event.getKeysToInvalidate());

508

}

509

}

510

}

511

```

512

513

### Bulk Operations Integration

514

515

Efficient bulk operations for external system integration.

516

517

```java { .api }

518

public class BulkOperationHelper {

519

520

// Bulk load with batching

521

public static <K, V> void bulkLoadWithBatching(

522

Cache<K, V> cache,

523

Set<K> keys,

524

CacheLoader<K, V> loader,

525

int batchSize) {

526

527

List<K> keyList = new ArrayList<>(keys);

528

for (int i = 0; i < keyList.size(); i += batchSize) {

529

int endIndex = Math.min(i + batchSize, keyList.size());

530

List<K> batch = keyList.subList(i, endIndex);

531

532

try {

533

Map<K, V> loaded = loader.loadAll(batch);

534

cache.putAll(loaded);

535

} catch (Exception e) {

536

// Handle batch failure - could retry individual items

537

handleBatchFailure(cache, batch, loader, e);

538

}

539

}

540

}

541

542

// Bulk write with error handling

543

public static <K, V> void bulkWriteWithErrorHandling(

544

Cache<K, V> cache,

545

Map<K, V> entries,

546

CacheWriter<K, V> writer) {

547

548

try {

549

List<Cache.Entry<K, V>> entryList = entries.entrySet().stream()

550

.map(e -> new SimpleEntry<>(e.getKey(), e.getValue()))

551

.collect(Collectors.toList());

552

553

writer.writeAll(entryList);

554

cache.putAll(entries);

555

} catch (CacheWriterException e) {

556

// Fall back to individual writes

557

for (Map.Entry<K, V> entry : entries.entrySet()) {

558

try {

559

writer.write(new SimpleEntry<>(entry.getKey(), entry.getValue()));

560

cache.put(entry.getKey(), entry.getValue());

561

} catch (Exception individualError) {

562

// Log individual failure but continue

563

handleIndividualWriteFailure(entry.getKey(), entry.getValue(), individualError);

564

}

565

}

566

}

567

}

568

569

private static <K, V> void handleBatchFailure(Cache<K, V> cache, List<K> batch,

570

CacheLoader<K, V> loader, Exception e) {

571

// Implementation depends on failure handling strategy

572

}

573

574

private static <K, V> void handleIndividualWriteFailure(K key, V value, Exception e) {

575

// Implementation depends on failure handling strategy

576

}

577

}

578

```