package org.apereo.cas.authentication.principal.cache;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import org.apereo.cas.authentication.principal.Principal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
/**
* Wrapper around an attribute repository where attributes cached for a configurable period
* based on google guava's caching library.
* @author Misagh Moayyed
* @since 4.2
*/
public class CachingPrincipalAttributesRepository extends AbstractPrincipalAttributesRepository {
private static final long serialVersionUID = 6350244643948535906L;
private static final long DEFAULT_MAXIMUM_CACHE_SIZE = 1000;
private static final Logger LOGGER = LoggerFactory.getLogger(CachingPrincipalAttributesRepository.class);
private transient Cache<String, Map<String, Object>> cache;
private transient PrincipalAttributesCacheLoader cacheLoader =
new PrincipalAttributesCacheLoader();
private long maxCacheSize = DEFAULT_MAXIMUM_CACHE_SIZE;
/**
* Used for serialization only.
*/
private CachingPrincipalAttributesRepository() {
super();
this.cache = CacheBuilder.newBuilder().maximumSize(this.maxCacheSize)
.expireAfterWrite(getExpiration(), TimeUnit.valueOf(getTimeUnit())).build(this.cacheLoader);
}
/**
* Instantiates a new caching attributes principal factory.
* Sets the default cache size to {@link #DEFAULT_MAXIMUM_CACHE_SIZE}.
* @param timeUnit the time unit
* @param expiryDuration the expiry duration
*/
public CachingPrincipalAttributesRepository(final String timeUnit, final long expiryDuration) {
this(DEFAULT_MAXIMUM_CACHE_SIZE, timeUnit, expiryDuration);
}
/**
* Instantiates a new caching attributes principal factory.
* @param maxCacheSize the max cache size
* @param timeUnit the time unit
* @param expiryDuration the expiry duration
*/
public CachingPrincipalAttributesRepository(final long maxCacheSize,
final String timeUnit,
final long expiryDuration) {
super(expiryDuration, timeUnit);
this.maxCacheSize = maxCacheSize;
this.cache = CacheBuilder.newBuilder().maximumSize(maxCacheSize)
.expireAfterWrite(getExpiration(), TimeUnit.valueOf(getTimeUnit())).build(this.cacheLoader);
}
@Override
protected void addPrincipalAttributes(final String id, final Map<String, Object> attributes) {
this.cache.put(id, attributes);
LOGGER.debug("Cached attributes for [{}]", id);
}
@Override
protected Map<String, Object> getPrincipalAttributes(final Principal p) {
try {
return this.cache.get(p.getId(), () -> {
LOGGER.debug("No cached attributes could be found for [{}]", p.getId());
return new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
});
} catch (final Exception e) {
LOGGER.error(e.getMessage(), e);
}
return null;
}
@Override
public void close() throws IOException {
this.cache.cleanUp();
}
private static class PrincipalAttributesCacheLoader extends CacheLoader<String, Map<String, Object>> {
@Override
public Map<String, Object> load(final String key) throws Exception {
return new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
}
}
}