package com.enioka.jqm.webui.shiro;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.util.CollectionUtils;
import org.apache.shiro.util.SoftHashMap;
/**
* Heavy copy/paste from Shiro's MapCache.
*
* This cache is built upon Shiro's {@link SoftHashMap SoftHashMap} which in turn uses soft references to avoid any memory leak. It keeps
* track of the moment the entry was created and declares entries obsolete after a given amount of time.
*
* No housekeeping is done - the time limit is simply checked when the value is asked for. Actual purge of items falls upon the garbage
* collector (thanks to the soft links).
*/
public class UserCache<K, V> implements Cache<K, V>
{
/**
* The name of this cache.
*/
private final String name;
/**
* Real cache
*/
private Map<K, TimeCacheEntry<V>> cache = new SoftHashMap<K, UserCache<K, V>.TimeCacheEntry<V>>();
private class TimeCacheEntry<Z>
{
Z entry;
Calendar lastReset = Calendar.getInstance();
TimeCacheEntry(Z entry)
{
this.entry = entry;
}
boolean isValid()
{
Calendar limit = Calendar.getInstance();
limit.add(Calendar.MINUTE, -10);
return this.lastReset.after(limit);
}
}
public UserCache(String name)
{
this.name = name;
}
public V get(K key) throws CacheException
{
if (cache.containsKey(key) && cache.get(key).isValid())
{
return cache.get(key).entry;
}
return null;
}
public V put(K key, V value) throws CacheException
{
V existing = cache.get(key) != null ? cache.get(key).entry : null;
cache.put(key, new TimeCacheEntry<V>(value));
return existing;
}
public V remove(K key) throws CacheException
{
V existing = cache.get(key) != null ? cache.get(key).entry : null;
cache.remove(key);
return existing;
}
public void clear() throws CacheException
{
cache.clear();
}
public int size()
{
return cache.size();
}
public Set<K> keys()
{
Set<K> keys = new HashSet<K>();
for (Map.Entry<K, TimeCacheEntry<V>> e : cache.entrySet())
{
if (e.getValue().isValid())
{
keys.add(e.getKey());
}
}
if (!keys.isEmpty())
{
return Collections.unmodifiableSet(keys);
}
return Collections.emptySet();
}
public Collection<V> values()
{
Collection<V> values = new ArrayList<V>();
for (TimeCacheEntry<V> e : this.cache.values())
{
if (e.isValid())
{
values.add(e.entry);
}
}
if (!CollectionUtils.isEmpty(values))
{
return Collections.unmodifiableCollection(values);
}
return Collections.emptySet();
}
public String toString()
{
return new StringBuilder("MapCache '").append(name).append("' (").append(cache.size()).append(" entries)").toString();
}
}