/**
*
*/
package com.trendrr.oss.cache;
import java.io.UnsupportedEncodingException;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.trendrr.oss.DynMap;
import com.trendrr.oss.StringHelper;
import com.trendrr.oss.TypeCast;
import com.trendrr.oss.concurrent.LazyInit;
import com.trendrr.oss.exceptions.TrendrrException;
import com.trendrr.oss.exceptions.TrendrrParseException;
import com.trendrr.oss.exceptions.TrendrrTimeoutException;
/**
*
* A cache wrapper. should be be easily adapted for in memory caching as
* well as many other datastores.
*
* @author Dustin Norlander
* @created Dec 29, 2011
*
*/
public abstract class TrendrrCache {
protected static Log log = LogFactory.getLog(TrendrrCache.class);
/**
* initialize the connection. called exactly once the first time the cache is accessed.
*/
protected abstract void _init(DynMap config);
/**
* Set a key - value pair
*
* @param key
* @param obj
* @param expires
*/
protected abstract void _set(String key, Object obj, Date expires) throws TrendrrTimeoutException, TrendrrException;
/**
* Deletes the key value pair
* @param key
*/
protected abstract void _del(String key) throws TrendrrTimeoutException, TrendrrException;
/**
* get the value at a key
* @param key
* @return
*/
protected abstract Object _get(String key) throws TrendrrTimeoutException, TrendrrException;
/**
* returns the values at the given set of keys
* @param keys
* @return
*/
protected abstract Map<String, Object> _getMulti(Set<String> keys) throws TrendrrTimeoutException, TrendrrException;
/**
* increment a key.
* @param key
* @param value
* @param expire when this key should expire. null for forever (if available)
*/
protected abstract long _inc(String key, int value, Date expire) throws TrendrrTimeoutException, TrendrrException;
/**
* increments multiple keys in a map.
* @param key
* @param values
* @param expire
*/
protected abstract void _incMulti(String key, Map<String, Integer> values, Date expire) throws TrendrrTimeoutException, TrendrrException;
/**
* gets the map from an incMulti call.
* @param key
* @return
*/
protected abstract Map<String,Long> _getIncMulti(String key) throws TrendrrTimeoutException, TrendrrException;
/**
* Add these items into a set add the given key.
* @param str
* @return
*/
protected abstract Set<String> _addToSet(String key, Collection<String> str, Date expire) throws TrendrrTimeoutException, TrendrrException;
/**
* loads a previous created set.
* @param key
* @return
*/
protected abstract Set<String> _getSet(String key) throws TrendrrTimeoutException, TrendrrException;
/**
* Remove from a set
* @param str
* @return
*/
protected abstract Set<String> _removeFromSet(String key, Collection<String> str) throws TrendrrTimeoutException, TrendrrException;
/**
* should set the key if and only if the value doesn't already exist. Should return whether the item was inserted or not.
* @param key
* @param value
* @param expires
*/
protected abstract boolean _setIfAbsent(String key, Object value, Date expires) throws TrendrrTimeoutException, TrendrrException;
/**
* Shutdown this cache, clean up any resources.
*/
public abstract void shutdown();
/**
* initialize a new cache. should be passed any config params that the specific implementation should need.
*
* @param config
*/
public TrendrrCache(DynMap config) {
this.config = config;
}
protected DynMap config = new DynMap();
private LazyInit initLock = new LazyInit();
/**
* initializes the cache. this is called exactly once. is not required to explicitly call this, as it will be called the
* first time the cache is accessed.
*/
public void init() {
if (initLock.start()) {
try {
this._init(this.config);
} finally {
initLock.end();
}
}
}
/**
* reinitializes.
*/
protected void reinit() {
this.initLock.reset();
this.init();
}
/**
* sets a key given the requested namespace.
* @param namespace
* @param key
* @param obj
* @param expires
*/
public void set(String namespace, String key, Object obj, Date expires) throws TrendrrTimeoutException, TrendrrException{
this.init();
this._set(this.getKey(namespace, key), obj, expires);
}
/**
* atomically adds the values to a Set (a collection with no duplicates).
*
* This is meant for record keeping, for small collections. definitly do not use for
* queues, or any set with many items.
*
* @param namespace
* @param key
* @param values
* @param expires
*/
public void addToSet(String namespace, String key, Collection<String> values, Date expire) throws TrendrrTimeoutException, TrendrrException {
this.init();
this._addToSet(this.getKey(namespace, key), values, expire);
}
/**
* loads a Set
* @param namespace
* @param key
* @return
*/
public Set<String> getSet(String namespace, String key) throws TrendrrTimeoutException, TrendrrException{
this.init();
return this._getSet(this.getKey(namespace, key));
}
public Set<String> getSet(String key) throws TrendrrTimeoutException, TrendrrException{
return this.getSet(null, key);
}
/**
* sets the key with the default namespace
* @param namespace
* @param key
* @param obj
* @param expires
*/
public void set(String key, Object obj, Date expires) throws TrendrrTimeoutException, TrendrrException{
this.set(null, key, obj, expires);
}
protected TrendrrCacheKeyMaker keyMaker = new DefaultTrendrrCacheKeyMaker();
protected String getKey(String namespace, String key){
// log.info("Getting key from: " + namespace + " " + key );
return keyMaker.toKey(namespace, key);
}
public void setKeyMaker(TrendrrCacheKeyMaker keyMaker) {
this.keyMaker = keyMaker;
}
public TrendrrCacheKeyMaker getKeyMaker() {
return this.keyMaker;
}
/**
* sets the key if and only if the key does not already exist
* @param namespace
* @param key
* @param value
* @param expires
* @return
*/
public boolean setIfAbsent(String namespace, String key, Object value, Date expires) throws TrendrrTimeoutException, TrendrrException{
this.init();
return this._setIfAbsent(this.getKey(namespace, key), value, expires);
}
/**
* uses the default namespace
* @param key
* @param value
* @param expires
* @return
*/
public boolean setIfAbsent(String key, Object value, Date expires) throws TrendrrTimeoutException, TrendrrException{
return this.setIfAbsent(null, key, value, expires);
}
/**
* Gets the value at the specified namespace and key.
* @param namespace
* @param key
* @return
*/
public Object get(String namespace, String key) throws TrendrrTimeoutException, TrendrrException {
this.init();
return this._get(this.getKey(namespace, key));
}
/**
* gets the value from the default namespace.
* @param key
* @return
*/
public Object get(String key) throws TrendrrTimeoutException, TrendrrException {
return this.get(null, key);
}
/**
* gets a typed value
* @param cls
* @param namespace
* @param key
* @return
*/
public <T> T get(Class<T> cls, String namespace, String key) throws TrendrrTimeoutException, TrendrrException {
return TypeCast.cast(cls, this.get(namespace, key));
}
public Map<String, Long> getIncMulti(String namespace, String key) throws TrendrrTimeoutException, TrendrrException{
this.init();
return this._getIncMulti(this.getKey(namespace, key));
}
public Map<String, Long> getIncMulti(String key) throws TrendrrTimeoutException, TrendrrException{
return this.getIncMulti(null, key);
}
/**
* returns a map of
* @param namespace
* @param keys
* @return
*/
public DynMap getMulti(String namespace, Collection<String> keys) throws TrendrrTimeoutException, TrendrrException{
this.init();
/*
* does some copying around here in order to keep with our namespaced keys.
*/
HashSet<String> k = new HashSet<String>();
HashMap<String, String> newKeys = new HashMap<String,String>();
for (String key : keys) {
String newKey = this.getKey(namespace, key);
newKeys.put(newKey, key);
k.add(newKey);
}
Map<String, Object> results = this._getMulti(k);
DynMap newResults = new DynMap();
if (results == null){
return newResults;
}
for (String key : results.keySet()) {
newResults.put(newKeys.get(key), results.get(key));
}
return newResults;
}
public Map<String, Object> getMulti(Collection<String> keys) throws TrendrrTimeoutException, TrendrrException{
return this.getMulti(null, keys);
}
/**
* deletes the specified key
* @param namespace
* @param key
*/
public void delete(String namespace, String key) throws TrendrrTimeoutException, TrendrrException{
this.init();
this._del(this.getKey(namespace, key));
}
/**
*
* @param key
*/
public void delete(String key) throws TrendrrTimeoutException, TrendrrException{
this.delete(null, key);
}
/**
* increments the specified key
* @param namespace
* @param key
* @param value
*/
public long inc(String namespace, String key, int value, Date expire) throws TrendrrTimeoutException, TrendrrException {
this.init();
return this._inc(this.getKey(namespace, key), value, expire);
}
/**
*
* @param namespace
* @param key
* @param value
*/
public long inc(String key, int value, Date expire) throws TrendrrTimeoutException, TrendrrException {
return this.inc(null, key, value, expire);
}
/**
* increments multiple keys in a map (ex: a redis hashmap).
* @param namespace
* @param key
* @param values
* @param expire
*/
public void incMulti(String namespace, String key, Map<String, Integer> values, Date expire) throws TrendrrTimeoutException, TrendrrException {
this.init();
this._incMulti(this.getKey(namespace, key), values, expire);
}
/**
* increments multiple keys in a map (ex: a redis hashmap).
* @param namespace
* @param key
* @param values
* @param expire
*/
public void incMulti(String key, Map<String, Integer> values, Date expire) throws TrendrrTimeoutException, TrendrrException {
this.incMulti(null, key, values, expire);
}
}