org.cesecore.internal.CommonCacheBase Maven / Gradle / Ivy
/*************************************************************************
* *
* CESeCore: CE Security Core *
* *
* This software is free software; you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public *
* License as published by the Free Software Foundation; either *
* version 2.1 of the License, or any later version. *
* *
* See terms of license at gnu.org. *
* *
*************************************************************************/
package org.cesecore.internal;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.log4j.Logger;
/**
* Object and name to id lookup cache base implementation.
*
* Note that this type of cache is not optimized for short-lived objects, but
* will prevent memory leaks to some extent through checking for stale data
* during updates.
*
* @version $Id: CommonCacheBase.java 28332 2018-02-20 14:40:52Z anatom $
*/
public abstract class CommonCacheBase implements CommonCache {
private class CacheEntry {
long lastUpdate;
final int digest;
final String name;
final T object;
CacheEntry(long lastUpdate, int digest, String name, T object) {
this.lastUpdate = lastUpdate;
this.digest = digest;
this.name = name;
this.object = object;
}
}
private final Logger log = Logger.getLogger(CommonCacheBase.class);
private Map cache = new HashMap();
private Map nameToIdMap = new HashMap();
/** @return how long to cache objects in milliseconds. */
protected abstract long getCacheTime();
/** @return the maximum allowed time an object may reside in the cache before it is purged. 0 means live forever. */
protected abstract long getMaxCacheLifeTime();
@Override
public T getEntry(final Integer id) {
final CacheEntry cacheEntry = getCacheEntry(id);
if (cacheEntry == null) {
return null;
}
return cacheEntry.object;
}
@Override
public T getEntry(final int id) {
return getEntry(Integer.valueOf(id));
}
public Set getAllEntries() {
Set result = new HashSet();
for(CacheEntry cacheEntry : cache.values()) {
result.add(cacheEntry.object);
}
return result;
}
@Override
public boolean shouldCheckForUpdates(final int id) {
final long now = System.currentTimeMillis();
final long cacheTime = getCacheTime();
if (cacheTime<0) {
// Cache is disabled, caller should check db
return true;
}
final Integer key = Integer.valueOf(id);
final CacheEntry cacheEntry = cache.get(key);
if (cacheEntry == null) {
// No such object in cache, caller should check db
return true;
}
if (cacheEntry.lastUpdate+cacheTime cacheStage = new HashMap();
final Map nameToIdMapStage = new HashMap();
final long maxCacheLifeTime = getMaxCacheLifeTime();
final long staleCutOffTime = System.currentTimeMillis()-maxCacheLifeTime;
synchronized (this) {
// Process all entries except for the one that will change
for (final Entry entry : cache.entrySet()) {
final Integer currentId = entry.getKey();
if (!key.equals(currentId)) {
final CacheEntry currentCacheEntry = entry.getValue();
// By flushing older entries we at least limit how much
// this registry will grow when used for short-lived objects
// in a clustered environment.
if (maxCacheLifeTime<1 || currentCacheEntry.lastUpdate >= staleCutOffTime) {
// Keep using the current entry in the new cache
cacheStage.put(entry.getKey(), currentCacheEntry);
nameToIdMapStage.put(currentCacheEntry.name, entry.getKey());
}
}
}
// Process the one that will change
if (cacheEntry == null) {
// Don't add if to the new version of the cache if it existed (e.g. remove it)
} else {
cacheStage.put(key, cacheEntry);
nameToIdMapStage.put(cacheEntry.name, key);
}
cache = cacheStage;
nameToIdMap = Collections.unmodifiableMap(nameToIdMapStage);
}
}
@Override
public Map getNameToIdMap() {
return nameToIdMap;
}
@Override
public void flush() {
final Map cacheStage = new HashMap();
final Map nameToIdMapStage = new HashMap();
replaceCache(cacheStage, nameToIdMapStage);
}
@Override
public void replaceCacheWith(List keys) {
Map cacheStage = new HashMap();
Map nameToIdMapStage = new HashMap();
for(Integer key : keys) {
CacheEntry entry = cache.get(key);
cacheStage.put(key, entry);
String name = entry.name;
nameToIdMapStage.put(name, nameToIdMap.get(name));
}
replaceCache(cacheStage, nameToIdMapStage);
}
private void replaceCache(Map cacheStage, Map nameToIdMapStage) {
synchronized (this) {
cache = cacheStage;
nameToIdMap = nameToIdMapStage;
}
}
}