package rfx.server.util.cache;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.reflections.Reflections;
import rfx.server.util.StringPool;
import rfx.server.util.StringUtil;
import com.google.common.cache.LoadingCache;
/**
* How to use Spring AOP http://www.journaldev.com/2583/spring-aop-example-tutorial-aspect-advice-pointcut-joinpoint-annotations-xml-configuration
*
* @author Trieu.nguyen
*
*/
@Aspect
public class CacheManagerForAllDAO {
final static String daoClasspath = "sample.pollapp.business.dao";
final static String withinClasspath = "within("+daoClasspath+".*)";
final static Map<String, CachePool> signatureConfigCache = new HashMap<>();
final static Map<String, Boolean> globalCachableMethods = new HashMap<>();
//TODO use Memcache here
static boolean cacheAllMethodsInDAO = true;
public CacheManagerForAllDAO() {
System.out.println("---CacheManagerForAllDAO---");
}
@Around(withinClasspath)
public Object process(ProceedingJoinPoint pJoinPoint){
try {
String className = pJoinPoint.getTarget().getClass().getName();
CachePool cachePool = signatureConfigCache.get(className);
if(cachePool != null){
Object value = null;
// System.out.println(" ---------Before invoking ---------- ");
//String key = pJoinPoint.getSignature().getName() + HashUtil.hashUrlCrc64(Arrays.toString(pJoinPoint.getArgs()));
Signature method = pJoinPoint.getSignature();
String methodName = method.getName();
Object[] args = pJoinPoint.getArgs();
long expireAfter = cachePool.getExpireAfter(methodName);
if(expireAfter > 0){
System.out.println(className +" " +methodName + " " + cachePool);
String key = cachePool.buildKey(methodName, args);
LoadingCache<String, Object> cache = cachePool.getCache();
value = cache.get(key);
System.out.println("++ Target: "+pJoinPoint.getTarget().getClass().getName());
System.out.println("++ Signature: "+pJoinPoint.getSignature().getName());
System.out.println("++ call method=" + pJoinPoint.getSignature().getName());
System.out.println("++ Agruments Passed=" + Arrays.toString(pJoinPoint.getArgs()));
if(StringUtil.isEmpty(value)){
value = pJoinPoint.proceed();
cache.put(key, value);
} else {
System.out.println("Hit cache by key: " + key );
}
} else {
value = pJoinPoint.proceed();
}
//System.out.println(" value: "+value);
return value;
}
return pJoinPoint.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
@Before(withinClasspath)
public void logStringArguments(JoinPoint joinPoint) {
System.out.println("Before call method= " + joinPoint.toString());
//System.out.println("Agruments Passed=" + Arrays.toString(joinPoint.getArgs()));
}
static class CachePool {
LoadingCache<String, Object> cache;
String keyPrefix;
Map<String, Long> cachableMethods;
long defaultExpire = 1;
public CachePool(LoadingCache<String, Object> cache, String keyPrefix, Map<String, Long> cachableMethods, long defaultExpire) {
super();
this.cache = cache;
this.keyPrefix = keyPrefix;
this.cachableMethods = cachableMethods;
this.defaultExpire = defaultExpire;
}
public LoadingCache<String, Object> getCache() {
return cache;
}
public void setCache(LoadingCache<String, Object> cache) {
this.cache = cache;
}
public String getKeyPrefix() {
return keyPrefix;
}
public void setKeyPrefix(String keyPrefix) {
this.keyPrefix = keyPrefix;
}
public String buildKey(String signatureName, Object[] args){
if(StringUtil.isEmpty(keyPrefix)){
return StringUtil.toString(signatureName, StringPool.UNDERLINE, StringUtil.join(args,StringPool.UNDERLINE));
}
return StringUtil.toString(keyPrefix, signatureName, StringPool.UNDERLINE, StringUtil.join(args,StringPool.UNDERLINE));
}
public long getExpireAfter(String methodName){
if(cachableMethods != null){
return cachableMethods.getOrDefault(methodName, 0L);
}
return defaultExpire;
}
@Override
public String toString() {
return StringUtil.convertObjectToJson(this);
}
}
public static void init() throws Exception{
Reflections reflections = new Reflections(daoClasspath);
Set<Class<?>> classes = reflections.getTypesAnnotatedWith(CacheConfig.class);
for (Class<?> clazz : classes) {
String className = clazz.getName();
if (clazz.isAnnotationPresent(CacheConfig.class) ) {
Method[] methods = clazz.getMethods();
Map<String, Long> cachableMethods = new HashMap<>(methods.length);
for (Method method : methods) {
if(method.isAnnotationPresent(Cachable.class)){
Annotation am = method.getAnnotation(Cachable.class);
Cachable cachable = (Cachable) am;
String mkey;
if( cachable.keyFormat().isEmpty() ){
mkey = method.getName();
} else {
mkey = cachable.keyFormat();//TODO
}
if(cachableMethods.containsKey(mkey)){
throw new IllegalArgumentException("duplicated cachable method key at class:"+className + " method:" + mkey);
}
cachableMethods.put(mkey, cachable.expireAfter());
}
}
Annotation annotation = clazz.getAnnotation(CacheConfig.class);
CacheConfig cacheConfig = (CacheConfig) annotation;
long maximumSize = cacheConfig.maximumSize() > 0 ? cacheConfig.maximumSize() : 1000000;
long expireAfter = cacheConfig.expireAfter() > 0 ? cacheConfig.expireAfter() : 10;
String keyPrefix = cacheConfig.keyPrefix();
int type = cacheConfig.type();
if(type == CacheConfig.LOCAL_CACHE_ENGINE){
LoadingCache<String, Object> cacheImpl = GuavaCacheUtil.getLoadingCache(className, maximumSize, expireAfter );
signatureConfigCache.put(className, new CachePool(cacheImpl, keyPrefix, cachableMethods, expireAfter));
} else if(type == CacheConfig.MEMCACHE_CACHE_ENGINE){
LoadingCache<String, Object> cacheImpl = new MemcacheLoadingImpl();
signatureConfigCache.put(className, new CachePool(cacheImpl, keyPrefix, cachableMethods, expireAfter));
}
System.out.println("...registered signatureConfigCache:" + className);
}
}
}
public static void main(String[] args) throws Exception {
init();
System.out.println(signatureConfigCache.get("sample.pollapp.business.dao.PollAppDAOImpl"));
}
}