All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
tech.rsqn.cacheservice.referencetransparentcache.DefaultTransparentCacheService Maven / Gradle / Ivy
package tech.rsqn.cacheservice.referencetransparentcache;
import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.ReflectionUtils;
import tech.rsqn.cacheservice.CacheService;
import tech.rsqn.cacheservice.TransparentCacheService;
import tech.rsqn.cacheservice.annotations.CacheKey;
import tech.rsqn.cacheservice.annotations.ReadOperation;
import tech.rsqn.cacheservice.exceptions.CacheReflectionRuntimeException;
import tech.rsqn.cacheservice.interceptors.InterceptorMetadata;
import tech.rsqn.cacheservice.interceptors.InterceptorUtil;
import tech.rsqn.cacheservice.support.*;
import tech.rsqn.cacheservice.util.GroupTimer;
import tech.rsqn.reflectionhelpers.ReflectionHelper;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
public class DefaultTransparentCacheService implements TransparentCacheService {
private boolean debugLogging = false;
private Logger log = LoggerFactory.getLogger(getClass());
private String defaultCacheName = "default";
private Map caches;
private List keyGenerators;
private List parameterKeyGenerators;
private boolean cachingDisabled = false;
private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
private ExpressionParser expressionParser = new SpelExpressionParser();
private boolean supportParameterNameDiscovery = false;
private GroupTimer groupTimer;
public DefaultTransparentCacheService() {
groupTimer = new GroupTimer();
}
@Required
public void setCaches(
Map caches) {
this.caches = caches;
}
public Map getCaches() {
return caches;
}
public void setSupportParameterNameDiscovery(
boolean supportParameterNameDiscovery) {
this.supportParameterNameDiscovery = supportParameterNameDiscovery;
}
/**
* For testing and debugging
*/
public void disableCaching() {
this.cachingDisabled = true;
}
/**
* For testing and debugging
*/
public void enableCaching() {
this.cachingDisabled = true;
}
@Required
public void setDefaultCacheName(String defaultCacheName) {
this.defaultCacheName = defaultCacheName;
}
public void setDebugLogging(boolean debugLogging) {
this.debugLogging = debugLogging;
}
@Required
public void setKeyGenerators(List keyGenerators) {
this.keyGenerators = keyGenerators;
}
@Required
public void setParameterKeyGenerators(
List parameterKeyGenerators) {
this.parameterKeyGenerators = parameterKeyGenerators;
}
public long count() {
long ret = 0;
for (CacheService cache : caches.values()) {
ret += cache.count();
}
return ret;
}
public long clear() {
long ret = 0;
for (CacheService cache : caches.values()) {
ret += cache.clear();
}
return ret;
}
public List getSupportedTypes() {
List ret = new ArrayList();
for (CacheKeyGenerator keyGenerator : keyGenerators) {
ret.addAll(keyGenerator.getSupportedClasses());
}
return ret;
}
public String generateParameterKey(Object parameter) {
String key = null;
for (ParameterKeyGenerator generator : parameterKeyGenerators) {
if (parameter != null) {
if (generator.supportsClass(parameter.getClass())) {
key = generator.generateParamKey(parameter);
if (key != null) {
return key;
}
}
}
}
return null;
}
public String generateCacheKey(Class clazz, Object... arguments) {
String key = null;
for (CacheKeyGenerator keyGenerator : keyGenerators) {
if (keyGenerator.supportsClass(clazz)) {
key = keyGenerator.generateKey(this, clazz, arguments);
if (key != null) {
return key;
}
}
}
return null;
}
private String generateKeyFromCacheKeyAnnotation(
MethodInvocation invocation, CacheKey cacheKeyAnnotation) {
String cacheKey;
if (cacheKeyAnnotation.generator() != Object.class) {
try {
CacheKeyGenerator generator = (CacheKeyGenerator) cacheKeyAnnotation.generator()
.newInstance();
if ((invocation.getArguments() != null) &&
(invocation.getArguments().length > 0)) {
for (Object o : invocation.getArguments()) {
if ((o != null) && generator.supportsEntity(o)) {
cacheKey = generator.generateKey(o);
if (cacheKey != null) {
return cacheKey;
}
}
}
}
} catch (InstantiationException e) {
throw new CacheReflectionRuntimeException(
"Exception using Cache Key Generator " +
cacheKeyAnnotation.generator(), e);
} catch (IllegalAccessException e) {
throw new CacheReflectionRuntimeException(
"Exception using Cache Key Generator " +
cacheKeyAnnotation.generator(), e);
}
} else if ((cacheKeyAnnotation.template() != null) &&
(cacheKeyAnnotation.template().length() > 0)) {
return generateCacheKeyFromTemplate(invocation,
cacheKeyAnnotation.template());
}
return null;
}
public String generateCacheKeyFromTemplate(MethodInvocation mi,
String keyTemplate) {
Method method = ReflectionUtils.findMethod(mi.getThis().getClass(),
mi.getMethod().getName(), mi.getMethod().getParameterTypes());
EvaluationContext context = new StandardEvaluationContext();
final Object[] arguments = mi.getArguments();
final String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
if (parameterNames != null) {
for (int i = 0; i < parameterNames.length; i++) {
context.setVariable(parameterNames[i], arguments[i]);
}
}
for (int i = 0; i < arguments.length; i++) {
context.setVariable("arg" + i, arguments[i]);
}
final Expression expression = expressionParser.parseExpression(keyTemplate,
new TemplatedParserContext());
return expression.getValue(context, String.class);
}
public String generateCacheKey(T entity) {
String key = null;
for (CacheKeyGenerator keyGenerator : keyGenerators) {
if (keyGenerator.supportsEntity(entity)) {
key = keyGenerator.generateKey(entity);
if (key != null) {
return key;
}
}
}
return null;
}
public Object aroundWriteMethodInvocation(
final MethodInvocation invocation, InterceptorMetadata meta)
throws Throwable {
groupTimer.start("aroundWriteMethodInvocation");
Object[] args = invocation.getArguments();
final Method targetMethod = invocation.getMethod();
Class> returnType = targetMethod.getReturnType();
final CacheService cache = resolveCacheService(meta);
if (debugLogging) {
log.debug(MessageFormat.format(
"Write Interceptor called for {0}.{1} with return type {2} and {3} arguments",
targetMethod.getDeclaringClass(), targetMethod.getName(),
returnType.getName(), args.length));
}
CacheKey cacheKeyAnnotation = ReflectionHelper.getAnnotationFromInvocation(invocation,
CacheKey.class);
String cacheKey = null;
if (cacheKeyAnnotation != null) {
cacheKey = generateKeyFromCacheKeyAnnotation(invocation,
cacheKeyAnnotation);
}
if (cacheKey != null) {
log.debug("Write Interceptor invalidating key (" + cacheKey + ")");
cache.remove(cacheKey);
}
groupTimer.stopAndReport("aroundWriteMethodInvocation");
Object returnValue = invocation.proceed();
return returnValue;
}
public Object aroundReadMethodInvocation(MethodInvocation invocation,
InterceptorMetadata meta) throws Throwable {
groupTimer.start("aroundReadMethodInvocation-READ");
Object[] args = invocation.getArguments();
Method targetMethod = invocation.getMethod();
Class> returnType = targetMethod.getReturnType();
CacheService cache = resolveCacheService(meta);
if (cachingDisabled) {
log.debug("caching is disabled - executing method ");
groupTimer.stopAndReport("aroundReadMethodInvocation-READ");
return invocation.proceed();
}
if (debugLogging) {
log.debug(MessageFormat.format(
"Read Interceptor called for {0}.{1} with return type {2} and {3} arguments",
targetMethod.getDeclaringClass().getName(),
targetMethod.getName(), returnType.getName(), args.length));
}
CacheKey cacheKeyAnnotation = ReflectionHelper.getAnnotationFromInvocation(invocation,
CacheKey.class);
ReadOperation readOperation = ReflectionHelper.getAnnotationFromInvocation(invocation,
ReadOperation.class);
String entityCacheKey = null;
String methodCacheKey = null;
if ((cacheKeyAnnotation != null) && (args.length > 0)) {
entityCacheKey = generateKeyFromCacheKeyAnnotation(invocation,
cacheKeyAnnotation);
} else {
entityCacheKey = generateCacheKey(returnType, args);
}
if (entityCacheKey == null) {
methodCacheKey = InterceptorUtil.generateCacheKeyBasedOnMethodInvocation(this,
invocation);
}
if (debugLogging) {
log.debug(MessageFormat.format(
"Read Interceptor entityCacheKey ({0}) , methodCacheKey ({1}) ",
entityCacheKey, methodCacheKey));
}
if (entityCacheKey != null) {
Object cachedValue = null;
if (cache.containsKey(entityCacheKey)) {
cachedValue = cache.get(entityCacheKey);
}
if (cachedValue != null) {
if (debugLogging) {
log.debug("Read Interceptor will return item from cache (" +
entityCacheKey + ")");
}
if (TransparentCacheExecutionBehaviour.getBehaviour()
.isClearCacheAfterRead()) {
log.debug("Read Interceptor Clearing cache entity key (" +
entityCacheKey + ") based on " +
TransparentCacheExecutionBehaviour.getBehaviour());
cache.remove(entityCacheKey);
}
if (cachedValue instanceof NonSerializableWrapper) {
NonSerializableWrapper wrapper = (NonSerializableWrapper) cachedValue;
if (wrapper.getValue() != null) {
groupTimer.stopAndReport(
"aroundReadMethodInvocation-READ");
return wrapper.getValue();
}
} else {
groupTimer.stopAndReport("aroundReadMethodInvocation-READ");
return cachedValue;
}
}
if (debugLogging) {
log.debug("Read Interceptor not found in cache (" +
entityCacheKey + ")");
}
}
if ((methodCacheKey != null) && (entityCacheKey == null)) {
Object cachedValue = null;
if (cache.containsKey(methodCacheKey)) {
cachedValue = cache.get(methodCacheKey);
}
if (cachedValue != null) {
if (debugLogging) {
log.debug("Read Interceptor returning item from cache (" +
methodCacheKey + ")");
}
if (TransparentCacheExecutionBehaviour.getBehaviour()
.isClearCacheAfterRead()) {
log.debug("Read Interceptor Clearing cache method key (" +
methodCacheKey + ") based on " +
TransparentCacheExecutionBehaviour.getBehaviour());
cache.remove(methodCacheKey);
}
if (cachedValue instanceof NonSerializableWrapper) {
NonSerializableWrapper wrapper = (NonSerializableWrapper) cachedValue;
if (wrapper.getValue() != null) {
groupTimer.stopAndReport(
"aroundReadMethodInvocation-READ");
return wrapper.getValue();
}
} else {
groupTimer.stopAndReport("aroundReadMethodInvocation-READ");
return cachedValue;
}
}
if (debugLogging) {
log.debug("Read Interceptor not found in cache (" +
methodCacheKey + ")");
}
}
if (TransparentCacheExecutionBehaviour.getBehaviour()
.isReturnIfItemIsNotCached()) {
log.debug("Read Interceptor NOT proceeding with invocation (" +
entityCacheKey + ") (" + methodCacheKey + ") based on " +
TransparentCacheExecutionBehaviour.getBehaviour());
groupTimer.stopAndReport("aroundReadMethodInvocation-READ");
return null;
}
if (debugLogging) {
log.debug("Read Interceptor proceeding with invocation (" +
entityCacheKey + ") (" + methodCacheKey + ")");
}
groupTimer.stopAndReport("aroundReadMethodInvocation-READ");
Object returnValue = invocation.proceed();
Serializable cacheValue = null;
groupTimer.start("aroundReadMethodInvocation-WRITE");
if ((readOperation != null) &&
(readOperation.unlessClass() != Object.class) &&
(returnValue != null)) {
ReadOperationUnlessKludge kludge = (ReadOperationUnlessKludge) readOperation.unlessClass()
.newInstance();
if (!kludge.allowCaching(returnValue)) {
log.info("ReadOperationUnlessKludge did not allow caching of(" +
entityCacheKey + ") (" + methodCacheKey + ")");
groupTimer.stopAndReport("aroundReadMethodInvocation-WRITE");
return returnValue;
}
}
if ((returnValue != null) && !(returnValue instanceof Serializable)) {
if (debugLogging) {
log.debug(
"Return value is not serializable, wrapping in NonSerializableWrapper to cache in memory only (" +
entityCacheKey + ") (" + methodCacheKey + ")");
}
cacheValue = NonSerializableWrapper.with(returnValue);
} else if (returnValue instanceof Serializable) {
cacheValue = (Serializable) returnValue;
}
if ((cacheValue != null) && (entityCacheKey != null)) {
cache.put(entityCacheKey, cacheValue);
if (debugLogging) {
log.debug("Read Interceptor cached single entity (" +
entityCacheKey + ")");
}
}
/** End of support for single entities and begin of support for collections, maps and arrays **/
if ((methodCacheKey != null) && (entityCacheKey == null)) {
if (returnValue instanceof Collection) {
if (debugLogging) {
log.debug("Read Interceptor cached collection under key (" +
methodCacheKey + ")");
}
cache.put(methodCacheKey, cacheValue);
} else if (returnValue instanceof Map) {
if (debugLogging) {
log.debug("Read Interceptor cached map under key (" +
methodCacheKey + ")");
}
cache.put(methodCacheKey, cacheValue);
} else if (returnValue instanceof Object[]) {
if (debugLogging) {
log.debug("Read Interceptor cached array under key (" +
methodCacheKey + ")");
}
cache.put(methodCacheKey, cacheValue);
} else {
if (debugLogging) {
log.debug(
"Read Interceptor cached single object under method key (" +
methodCacheKey + ")");
}
cache.put(methodCacheKey, cacheValue);
}
}
groupTimer.stopAndReport("aroundReadMethodInvocation-WRITE");
return returnValue;
}
public Object aroundInvalidateMethodInvocation(
MethodInvocation invocation, InterceptorMetadata meta)
throws Throwable {
groupTimer.start("aroundInvalidateMethodInvocation");
Object[] args = invocation.getArguments();
Method targetMethod = invocation.getMethod();
Class> returnType = targetMethod.getReturnType();
CacheService cache = resolveCacheService(meta);
if (debugLogging) {
log.debug(MessageFormat.format(
"Invalidating Interceptor called for {0}.{1} with return type {2} and {3} arguments",
targetMethod.getDeclaringClass(), targetMethod.getName(),
returnType.getName(), args.length));
}
CacheKey cacheKeyAnnotation = ReflectionHelper.getAnnotationFromInvocation(invocation,
CacheKey.class);
int nRemoved = 0;
String cacheKey = null;
if (cacheKeyAnnotation != null) {
cacheKey = generateKeyFromCacheKeyAnnotation(invocation,
cacheKeyAnnotation);
}
if (cacheKey != null) {
log.debug("Invalidating Interceptor invalidating key (" + cacheKey +
")");
cache.remove(cacheKey);
nRemoved++;
}
if (cacheKeyAnnotation == null) {
for (Object arg : args) {
if (arg instanceof Serializable) {
String cacheKeyFromArgs = generateCacheKey((Serializable) arg);
if (cacheKeyFromArgs != null) {
log.debug("Invalidating Interceptor invalidating key (" +
cacheKeyFromArgs + ")");
cache.remove(cacheKeyFromArgs);
nRemoved++;
}
}
}
}
String methodCacheKey = null;
if (nRemoved == 0) {
methodCacheKey = InterceptorUtil.generateCacheKeyBasedOnMethodInvocation(this,
invocation);
}
if (methodCacheKey != null) {
log.debug("Invalidating Interceptor invalidating key (" +
methodCacheKey + ")");
cache.remove(methodCacheKey);
}
groupTimer.stopAndReport("aroundInvalidateMethodInvocation");
Object returnValue = invocation.proceed();
return returnValue;
}
private CacheService resolveCacheService(InterceptorMetadata meta) {
if (meta.getTarget() != null) {
return caches.get(meta.getTarget());
}
return caches.get(defaultCacheName);
}
}