The caffeinated
library provides an asynchronous loading cache for a caching problem
where multiple keys point to the same cached value (via same shared derived key) and are
treated as a single entity for the purpose of expiration or eviction. In effect, it
implements a caching cascade of K1,K2,K3->DK->V
based on key mappers (Function<K,DK>
)
and value loaders (BiFunction<K,DK,V
).
Given a cache instance one can retrieve values via the get
methods, that exist in two
variants:
-
with default loaders registered on the cached during construction (none are registered by default and the future will complete exceptionally):
CompletableFuture<V> get(K key)
-
with explicit loaders:
CompletableFuture<V> get(K key, Function<K,DK> keyMapper, BiFunction<K,DK,V> valueLoader)
The underlying cache is based on AsyncLoadingCache from caffeine
and can be configured
via the Caffeine
builder, e.g.
Caffeine caffeine = Caffeine.newBuilder()
.expireAfterAccess(5, TimeUnit.SECONDS);
AsyncMultikeyCache.<String, String, Session> sessionStore =
AsyncMultikeyCache.<String, String, Session>newBuilder(caffeine)
.removalListener((keys, session, reason) -> session.expire())
.buildAsync();
For this sort of session store the cache usage may look like this:
private final AuthenticationDAO authDao;
@Override
public CompletableFuture<String> authenticate(String username, String password) {
String key = Base64.getEncoder().encodeToString((username + ":" + password).getBytes());
return sessionStore
.get(key, $ -> {
// make sure users with wrong passwords cannot hijack sessions of correctly
// authenticated users as shared key is just username
authDao.validateUsernameAuth(username, password);
return username;
}, ($, $$) -> authDao.authByUsername(username, password))
.thenCompose(session -> CompletableFuture.completedFuture(session.sessionId));
}
A complete example can be found in the SessionStoreExample test class.
Copyright (c) 2018. Oleg Sklyar and teris.io. All rights reserved. MIT license applies