net.anotheria.anoprise.cache.RoundRobinSoftReferenceCache Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ano-prise Show documentation
Show all versions of ano-prise Show documentation
Collection of utils for different enterprise class projects. Among other stuff contains
Caches, Mocking, DualCrud, MetaFactory and SessionDistributorService. Visit https://opensource.anotheria.net for details.
package net.anotheria.anoprise.cache;
import net.anotheria.moskito.core.predefined.CacheStats;
import java.lang.ref.SoftReference;
import java.util.HashMap;
/**
* A simple RoundRobin implementation of the cache.
* @author lrosenberg
*/
public class RoundRobinSoftReferenceCache extends AbstractCache implements Cache {
/**
* DEfault start size if no size is specified.
*/
public static final int DEF_START_SIZE = 1000;
/**
* Default max size if no size is specified.
*/
public static final int DEF_MAX_SIZE = 3000;
/**
* Default cache size increment factor.
*/
public static final float DEF_INCREMENT = 0.5F;
/**
* The underlying array of softreferences to cached objects.
*/
private SoftReference[] cache;
/**
* Map for id2index resolution.
*/
private HashMap id2index;
/**
* Map for the reverse resolution (index2id).
*/
private HashMap index2id;
/**
* Max cache size.
*/
private int maxSize;
/**
* Start cache size.
*/
private int startSize;
/**
* Cache size increment.
*/
private float increment;
/**
* Current cache size.
*/
private int currentSize;
/**
* Last written element.
*/
private int lastElement ;
/**
* True if the first cycle is complete and we started rollover.
*/
private boolean firstCycleComplete;
/**
* True if the start size is smaller than max size.
*/
private boolean enlargeable;
/**
* Copy of cache stats object for faster access.
*/
private CacheStats cacheStatsCopy = null;
public RoundRobinSoftReferenceCache(){
this(getUnnamedInstanceName(RoundRobinSoftReferenceCache.class));
}
public RoundRobinSoftReferenceCache(int aStartSize, int aMaxSize){
this(getUnnamedInstanceName(RoundRobinSoftReferenceCache.class), aStartSize, aMaxSize);
}
public RoundRobinSoftReferenceCache(int aStartSize, int aMaxSize, float anIncrement){
this(getUnnamedInstanceName(RoundRobinSoftReferenceCache.class), aStartSize, aMaxSize, anIncrement);
}
public RoundRobinSoftReferenceCache(String name){
this(name, DEF_START_SIZE, DEF_MAX_SIZE, DEF_INCREMENT);
}
public RoundRobinSoftReferenceCache(String name, int aStartSize, int aMaxSize){
this(name, aStartSize, aMaxSize, DEF_INCREMENT);
}
public RoundRobinSoftReferenceCache(String name, int aStartSize, int aMaxSize, float anIncrement){
super(name);
this.startSize = aStartSize;
this.maxSize = aMaxSize;
this.increment = anIncrement;
cacheStatsCopy = getCacheStats();
init();
}
@Override public synchronized void remove(K id){
cacheStatsCopy.addDelete();
Integer index = getCachePosition(id);
if (index==null)
return ;
id2index.remove(id);
index2id.remove(index);
SoftReference ref = cache[index.intValue()];
cache[index.intValue()] = null;
if (ref.get()==null){
cacheStatsCopy.addGarbageCollected();
}else{
ref.clear();
}
}
@Override public synchronized V get(K id){
cacheStatsCopy.addRequest();
Integer index = getCachePosition(id);
if (index==null)
return null;
V toRet = cache[index.intValue()].get();
if (toRet==null){
cacheStatsCopy.addGarbageCollected();
id2index.remove(id);
index2id.remove(index);
}else{
cacheStatsCopy.addHit();
}
return toRet;
}
private Integer getCachePosition(K id){
return id2index.get(id);
}
@SuppressWarnings("unchecked")
@Override public synchronized void put(K id, V cacheable){
SoftReference toPut = new SoftReference(cacheable);
cacheStatsCopy.addWrite();
Integer oldPosition = getCachePosition(id);
if (oldPosition!=null){
//System.out.println("replacing old entry.");
cache[oldPosition.intValue()] = toPut;
return;
}
if (lastElement < currentSize-1 && !firstCycleComplete){
//es ist noch platz im array.
lastElement++;
Integer tmpIndex = Integer.valueOf(lastElement);
index2id.put(tmpIndex, id);
id2index.put(id, tmpIndex);
cache[lastElement] = toPut;
return;
}
if (firstCycleComplete){
//das bedeutet, dass wir schon voll sind, und rotieren muessen.
lastElement++;
if (lastElement==cache.length)
lastElement = 0;
Integer tmp = Integer.valueOf(lastElement);
Object oldId = index2id.get(tmp);
if (oldId!=null){
id2index.remove(oldId);
}
id2index.put(id, tmp);
index2id.put(tmp, id);
cache[lastElement] = toPut;
cacheStatsCopy.addRollover();
return;
}
//wenn wir hier angekommen sind, sind wir full.
if (!firstCycleComplete){
//also lastElement == maxSize ist gegeben, vergroessern
if (enlargeable){
float wishedSize = currentSize*(1+increment);
//System.out.println("wished: "+wishedSize);
int newsize = (int)wishedSize;
if (newsize>maxSize)
newsize = maxSize;
//System.out.println("Enlarging from "+cache.length+" to "+newsize+".");
SoftReference[] oldCache = cache;
cache = new SoftReference[newsize];
System.arraycopy(oldCache, 0, cache, 0, currentSize);
currentSize = newsize;
enlargeable = newsize < maxSize;
//System.out.println("now enlargeable: "+enlargeable);
firstCycleComplete = false;
put(id, cacheable);
return;
}
//wir sind voll und nicht vergroesserbar.
firstCycleComplete = true;
put(id, cacheable);
return;
}
//hier sollten wir nicht ankommen!
throw new AssertionError("You couldn't reach this point in code! ("+this+")");
}
private void init(){
clear();
}
@SuppressWarnings("unchecked")
@Override public synchronized void clear(){
cache = new SoftReference[startSize];
id2index = new HashMap(startSize);
index2id = new HashMap(startSize);
lastElement = -1;
firstCycleComplete = false;
enlargeable = startSize < maxSize;
currentSize = startSize;
}
@Override public String toString(){
if (cache==null)
return "Not initialized cache "+getName();
String ret = getName()+" ";
ret += "CurrentSize: "+cache.length+", MaxSize: "+maxSize;
ret += " Enlargement possible: "+enlargeable+", FirstCycleComplete: "+firstCycleComplete;
ret += " lastElement: "+lastElement;
return ret;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy