package com.bazaarvoice.auth.hmac.server;
import com.bazaarvoice.auth.hmac.common.Credentials;
import com.google.common.base.Optional;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
public abstract class AbstractCachingAuthenticator<Principal> extends AbstractAuthenticator<Principal> {
private static final Logger LOG = LoggerFactory.getLogger(AbstractCachingAuthenticator.class);
private final Cache<String, Optional<Principal>> cache;
protected AbstractCachingAuthenticator(long allowedTimestampSlop, long cacheTimeout, TimeUnit timeUnit, long maxCacheElements) {
super(allowedTimestampSlop, timeUnit);
cache = CacheBuilder.newBuilder()
.maximumSize(maxCacheElements)
.expireAfterWrite(cacheTimeout, timeUnit)
.build();
}
/**
* Clear out all elements from the cache.
*/
protected void clearCache() {
cache.invalidateAll();
}
/**
* Do the loading of the Principal based on the Credentials. Note that this will only be called if the Credentials
* object is not found in the in-memory cache.
* <p/>
* Note: it is safe to return null from this method if the Principal is not found for these Credentials, and that will be cached.
*/
protected abstract Principal loadPrincipal(Credentials credentials);
/**
* If the Principal for this Credentials is already cached, return it. Otherwise call {@link #loadPrincipal} and cache the results.
*/
@Override
protected final Principal getPrincipal(final Credentials credentials) {
try {
Optional<Principal> principalOptional = cache.get(credentials.getApiKey(), new Callable<Optional<Principal>>() {
public Optional<Principal> call() throws Exception {
return Optional.fromNullable(loadPrincipal(credentials));
}
});
return principalOptional.orNull();
} catch (ExecutionException e) {
LOG.warn("Exception when loading the cache for credentials with API key " + credentials.getApiKey());
return null;
}
}
/**
* Put this principal directly into cache. This can avoid lookup on
* user request and "prepay" the lookup cost.
*/
protected void cachePrincipal(String apiKey, Principal principal) {
cache.put(apiKey, Optional.fromNullable(principal));
}
}