package cn.org.rapid_framework.util.holder;
import java.io.NotSerializableException;
import java.io.Serializable;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import cn.org.rapid_framework.cache.Cache;
/**
* The CacheHolder. initial with spring or new CacheHolder().setCache(cache);
*/
public class CacheHolder implements InitializingBean{
static Log logger = LogFactory.getLog(CacheHolder.class);
/**
* The underlying cache implementation
*/
private static Cache cache;
public void afterPropertiesSet() throws Exception {
if(cache == null) throw new IllegalStateException("not found 'cache' for CacheHolder ");
}
public void setCache(Cache c) {
if(this.getCache() != null) {
throw new IllegalStateException("CacheHolder already holded 'cache'");
}
this.cache = c;
}
/**
* Add an element only if it doesn't exist.
* @param key Element key
* @param value Element value
* @param expiration Ex: 10s, 3mn, 8h, 1d
*/
public static void add(String key, Object value, String expiration) {
checkSerializable(value);
getCache().add(key, value, parseDuration(expiration));
}
/**
* Add an element only if it doesn't exist, and return only when
* the element is effectivly cached.
* @param key Element key
* @param value Element value
* @param expiration Ex: 10s, 3mn, 8h, 1d
* @return If the element an eventually been cached
*/
public static boolean safeAdd(String key, Object value, String expiration) {
checkSerializable(value);
return getCache().safeAdd(key, value, parseDuration(expiration));
}
/**
* Add an element only if it doesn't exist and store it indefinitly.
* @param key Element key
* @param value Element value
*/
public static void add(String key, Object value) {
checkSerializable(value);
getCache().add(key, value, parseDuration(null));
}
/**
* Set an element.
* @param key Element key
* @param value Element value
* @param expiration Ex: 10s, 3mn, 8h, 1d
*/
public static void set(String key, Object value, String expiration) {
checkSerializable(value);
getCache().set(key, value, parseDuration(expiration));
}
/**
* Set an element and return only when the element is effectivly cached.
* @param key Element key
* @param value Element value
* @param expiration Ex: 10s, 3mn, 8h, 1d
* @return If the element an eventually been cached
*/
public static boolean safeSet(String key, Object value, String expiration) {
checkSerializable(value);
return getCache().safeSet(key, value, parseDuration(expiration));
}
/**
* Set an element and store it indefinitly.
* @param key Element key
* @param value Element value
*/
public static void set(String key, Object value) {
checkSerializable(value);
getCache().set(key, value, parseDuration(null));
}
/**
* Replace an element only if it already exists.
* @param key Element key
* @param value Element value
* @param expiration Ex: 10s, 3mn, 8h, 1d
*/
public static void replace(String key, Object value, String expiration) {
checkSerializable(value);
getCache().replace(key, value, parseDuration(expiration));
}
/**
* Replace an element only if it already exists and return only when the
* element is effectivly cached.
* @param key Element key
* @param value Element value
* @param expiration Ex: 10s, 3mn, 8h, 1d
* @return If the element an eventually been cached
*/
public static boolean safeReplace(String key, Object value, String expiration) {
checkSerializable(value);
return getCache().safeReplace(key, value, parseDuration(expiration));
}
/**
* Replace an element only if it already exists and store it indefinitly.
* @param key Element key
* @param value Element value
*/
public static void replace(String key, Object value) {
checkSerializable(value);
getCache().replace(key, value, parseDuration(null));
}
/**
* Increment the element value (must be a Number).
* @param key Element key
* @param by The incr value
* @return The new value
*/
public static long incr(String key, int by) {
return getCache().incr(key, by);
}
/**
* Increment the element value (must be a Number) by 1.
* @param key Element key
* @return The new value
*/
public static long incr(String key) {
return getCache().incr(key, 1);
}
/**
* Decrement the element value (must be a Number).
* @param key Element key
* @param by The decr value
* @return The new value
*/
public static long decr(String key, int by) {
return getCache().decr(key, by);
}
/**
* Decrement the element value (must be a Number) by 1.
* @param key Element key
* @return The new value
*/
public static long decr(String key) {
return getCache().decr(key, 1);
}
/**
* Retrieve an object.
* @param key The element key
* @return The element value or null
*/
public static Object get(String key) {
return getCache().get(key);
}
/**
* Bulk retrieve.
* @param key List of keys
* @return Map of keys & values
*/
public static Map<String, Object> get(String... key) {
return getCache().get(key);
}
/**
* Delete an element from the cache.
* @param key The element key *
*/
public static void delete(String key) {
getCache().delete(key);
}
/**
* Delete an element from the cache and return only when the
* element is effectivly removed.
* @param key The element key
* @return If the element an eventually been deleted
*/
public static boolean safeDelete(String key) {
return getCache().safeDelete(key);
}
/**
* Clear all data from cache.
*/
public static void clear() {
getCache().clear();
}
/**
* Convenient clazz to get a value a class type;
* @param <T> The needed type
* @param key The element key
* @param clazz The type class
* @return The element value or null
*/
public static <T> T get(String key, Class<T> clazz) {
return (T) getCache().get(key);
}
/**
* Stop the cache system.
*/
public static void stop() {
getCache().stop();
}
public static void cleanHolder() {
CacheHolder.cache = null;
}
private static Cache getCache() {
if(cache == null) throw new IllegalStateException("not found 'cache' for CacheHolder ");
return cache;
}
/**
* Utility that check that an object is serializable.
*/
static void checkSerializable(Object value) {
if(!(value instanceof Serializable)) {
throw new IllegalStateException("Cannot cache a non-serializable value of type " + value.getClass().getName(), new NotSerializableException(value.getClass().getName()));
}
}
static Pattern days = Pattern.compile("^([0-9]+)d$");
static Pattern hours = Pattern.compile("^([0-9]+)h$");
static Pattern minutes = Pattern.compile("^([0-9]+)mn$");
static Pattern seconds = Pattern.compile("^([0-9]+)s$");
/**
* Parse a duration
* @param duration 3h, 2mn, 7s, 1d
* @return The number of seconds
*/
private static int parseDuration(String duration) {
if (duration == null) {
return 60 * 60 * 24 * 30;
}
int toAdd = -1;
if (days.matcher(duration).matches()) {
Matcher matcher = days.matcher(duration);
matcher.matches();
toAdd = Integer.parseInt(matcher.group(1)) * (60 * 60) * 24;
} else if (hours.matcher(duration).matches()) {
Matcher matcher = hours.matcher(duration);
matcher.matches();
toAdd = Integer.parseInt(matcher.group(1)) * (60 * 60);
} else if (minutes.matcher(duration).matches()) {
Matcher matcher = minutes.matcher(duration);
matcher.matches();
toAdd = Integer.parseInt(matcher.group(1)) * (60);
} else if (seconds.matcher(duration).matches()) {
Matcher matcher = seconds.matcher(duration);
matcher.matches();
toAdd = Integer.parseInt(matcher.group(1));
}
if (toAdd == -1) {
throw new IllegalArgumentException("Invalid duration pattern : " + duration);
}
return toAdd;
}
}