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
```