Polishing cache aspect (logs, exceptions)
This commit is contained in:
@@ -71,9 +71,24 @@ namespace Spring.Aspects.Cache
|
||||
/// Cache instance for the specified <paramref name="name"/> if one
|
||||
/// is registered in the application context, or <c>null</c> if it isn't.
|
||||
/// </returns>
|
||||
/// <exception cref="Spring.Objects.Factory.NoSuchObjectDefinitionException">
|
||||
/// If there's no cache instance registered for the specified <paramref name="name"/>.
|
||||
/// </exception>
|
||||
/// <exception cref="Spring.Objects.ObjectsException">
|
||||
/// If the cache instance could not be created.
|
||||
/// </exception>
|
||||
/// <exception cref="System.ArgumentException">
|
||||
/// If the cache instance registered does not implement the <see cref="Spring.Caching.ICache"/> interface.
|
||||
/// </exception>
|
||||
public ICache GetCache(string name)
|
||||
{
|
||||
return applicationContext.GetObject(name) as ICache;
|
||||
ICache cache = applicationContext.GetObject(name) as ICache;
|
||||
if (cache == null)
|
||||
{
|
||||
throw new ArgumentException(String.Format(
|
||||
"Cache with the specified name [{0}] does not implement the 'Spring.Caching.ICache' interface.", name));
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -52,8 +52,10 @@ namespace Spring.Aspects.Cache
|
||||
/// </remarks>
|
||||
/// <seealso cref="CacheParameterAttribute"/>
|
||||
/// <author>Aleksandar Seovic</author>
|
||||
public class CacheParameterAdvice : BaseCacheAdvice, IAfterReturningAdvice
|
||||
{
|
||||
public class CacheParameterAdvice : BaseCacheAdvice, IAfterReturningAdvice
|
||||
{
|
||||
#region CacheParameterAttribute caching
|
||||
|
||||
private class CacheParameterInfo
|
||||
{
|
||||
public readonly ParameterInfo[] Parameters;
|
||||
@@ -66,7 +68,28 @@ namespace Spring.Aspects.Cache
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Hashtable _cacheParameterInfoCache = new Hashtable();
|
||||
private readonly Hashtable _cacheParameterInfoCache = new Hashtable();
|
||||
|
||||
private CacheParameterInfo GetCacheParameterInfo(MethodInfo method)
|
||||
{
|
||||
CacheParameterInfo cpi = (CacheParameterInfo)_cacheParameterInfoCache[method];
|
||||
if (cpi == null)
|
||||
{
|
||||
ParameterInfo[] parameters = method.GetParameters();
|
||||
CacheParameterAttribute[][] parameterInfos = new CacheParameterAttribute[parameters.Length][];
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
ParameterInfo p = parameters[i];
|
||||
CacheParameterAttribute[] paramInfoArray = (CacheParameterAttribute[])GetCustomAttributes(p, typeof(CacheParameterAttribute));
|
||||
parameterInfos[i] = paramInfoArray;
|
||||
}
|
||||
cpi = new CacheParameterInfo(parameters, parameterInfos);
|
||||
_cacheParameterInfoCache[method] = cpi;
|
||||
}
|
||||
return cpi;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Executes after target <paramref name="method"/>
|
||||
@@ -89,9 +112,11 @@ namespace Spring.Aspects.Cache
|
||||
/// <param name="arguments">The intercepted method's arguments.</param>
|
||||
/// <param name="target">The target object.</param>
|
||||
/// <seealso cref="AopAlliance.Intercept.IMethodInterceptor.Invoke"/>
|
||||
public void AfterReturning(object returnValue, MethodInfo method, object[] arguments, object target)
|
||||
{
|
||||
public void AfterReturning(object returnValue, MethodInfo method, object[] arguments, object target)
|
||||
{
|
||||
#region Instrumentation
|
||||
bool isLogDebugEnabled = logger.IsDebugEnabled;
|
||||
#endregion
|
||||
|
||||
CacheParameterInfo cpi = GetCacheParameterInfo(method);
|
||||
CacheParameterAttribute[][] cacheParameterAttributes = cpi.CacheParameterAttributes;
|
||||
@@ -103,16 +128,13 @@ namespace Spring.Aspects.Cache
|
||||
if (EvalCondition(paramInfo.Condition, paramInfo.ConditionExpression, arguments[i], null))
|
||||
{
|
||||
ICache cache = GetCache(paramInfo.CacheName);
|
||||
if (cache == null)
|
||||
{
|
||||
throw new ArgumentNullException("CacheName", string.Format("Parameter cache with the specified name [{0}] does not exist.", paramInfo.CacheName));
|
||||
}
|
||||
|
||||
object key = paramInfo.KeyExpression.GetValue(arguments[i]);
|
||||
|
||||
#region Instrumentation
|
||||
if (isLogDebugEnabled)
|
||||
{
|
||||
logger.Debug(string.Format("Caching parameter for key [{0}].", key));
|
||||
logger.Debug(string.Format("Caching parameter for key [{0}] into cache [{1}].", key, paramInfo.CacheName));
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -121,24 +143,5 @@ namespace Spring.Aspects.Cache
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CacheParameterInfo GetCacheParameterInfo(MethodInfo method)
|
||||
{
|
||||
CacheParameterInfo cpi = (CacheParameterInfo) _cacheParameterInfoCache[method];
|
||||
if (cpi == null)
|
||||
{
|
||||
ParameterInfo[] parameters = method.GetParameters();
|
||||
CacheParameterAttribute[][] parameterInfos = new CacheParameterAttribute[parameters.Length][];
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
ParameterInfo p = parameters[i];
|
||||
CacheParameterAttribute[] paramInfoArray = (CacheParameterAttribute[])GetCustomAttributes(p, typeof(CacheParameterAttribute));
|
||||
parameterInfos[i] = paramInfoArray;
|
||||
}
|
||||
cpi = new CacheParameterInfo(parameters, parameterInfos);
|
||||
_cacheParameterInfoCache[method] = cpi;
|
||||
}
|
||||
return cpi;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ using Common.Logging;
|
||||
using Spring.Caching;
|
||||
using Spring.Expressions;
|
||||
using Spring.Util;
|
||||
using System;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -50,8 +51,7 @@ namespace Spring.Aspects.Cache
|
||||
/// <author>Aleksandar Seovic</author>
|
||||
public class CacheResultAdvice : BaseCacheAdvice, IMethodInterceptor
|
||||
{
|
||||
// NullValue
|
||||
private static readonly object NullValue = new object();
|
||||
#region CacheResultAttribute & CacheResultItemsAttribute caching
|
||||
|
||||
private class CacheResultInfo
|
||||
{
|
||||
@@ -67,6 +67,26 @@ namespace Spring.Aspects.Cache
|
||||
|
||||
private readonly Hashtable _cacheResultAttributeCache = new Hashtable();
|
||||
|
||||
private CacheResultInfo GetCacheResultInfo(MethodInfo method)
|
||||
{
|
||||
CacheResultInfo cacheResultInfo = (CacheResultInfo)_cacheResultAttributeCache[method];
|
||||
// no need for locking here - last one wins
|
||||
if (cacheResultInfo == null)
|
||||
{
|
||||
CacheResultAttribute resultInfo = (CacheResultAttribute)GetCustomAttribute(method, typeof(CacheResultAttribute));
|
||||
CacheResultItemsAttribute[] itemInfoArray = (CacheResultItemsAttribute[])GetCustomAttributes(method, typeof(CacheResultItemsAttribute));
|
||||
|
||||
cacheResultInfo = new CacheResultInfo(resultInfo, itemInfoArray);
|
||||
_cacheResultAttributeCache[method] = cacheResultInfo;
|
||||
}
|
||||
return cacheResultInfo;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// NullValue
|
||||
private static readonly object NullValue = new object();
|
||||
|
||||
/// <summary>
|
||||
/// Applies caching around a method invocation.
|
||||
/// </summary>
|
||||
@@ -131,8 +151,11 @@ namespace Spring.Aspects.Cache
|
||||
{
|
||||
if (resultInfo != null)
|
||||
{
|
||||
object returnValue = null;
|
||||
#region Instrumentation
|
||||
bool isLogDebugEnabled = logger.IsDebugEnabled;
|
||||
#endregion
|
||||
|
||||
object returnValue = null;
|
||||
|
||||
IDictionary vars = PrepareVariables(invocation.Method, invocation.Arguments);
|
||||
|
||||
@@ -140,33 +163,26 @@ namespace Spring.Aspects.Cache
|
||||
"The cache attribute is missing the key definition.");
|
||||
object resultKey = resultInfo.KeyExpression.GetValue(null, vars);
|
||||
ICache cache = GetCache(resultInfo.CacheName);
|
||||
AssertUtils.ArgumentNotNull(cache, "CacheName",
|
||||
"Result cache with the specified name [" + resultInfo.CacheName +
|
||||
"] does not exist.");
|
||||
|
||||
returnValue = cache.Get(resultKey);
|
||||
cacheHit = (returnValue != null);
|
||||
if (!cacheHit)
|
||||
{
|
||||
#region Instrumentation
|
||||
|
||||
if (isLogDebugEnabled)
|
||||
{
|
||||
logger.Debug("Object for key [" + resultKey + "] was not found in cache. Proceeding...");
|
||||
logger.Debug(String.Format("Object for key [{0}] was not found in cache [{1}]. Proceeding...", resultKey, resultInfo.CacheName));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
returnValue = invocation.Proceed();
|
||||
if (EvalCondition(resultInfo.Condition, resultInfo.ConditionExpression, returnValue, vars))
|
||||
{
|
||||
#region Instrumentation
|
||||
|
||||
if (isLogDebugEnabled)
|
||||
{
|
||||
logger.Debug("Caching object for key [" + resultKey + "].");
|
||||
logger.Debug(String.Format("Caching object for key [{0}] into cache [{1}].", resultKey, resultInfo.CacheName));
|
||||
}
|
||||
|
||||
#endregion
|
||||
cache.Insert(resultKey, (returnValue == null) ? NullValue : returnValue, resultInfo.TimeToLiveTimeSpan);
|
||||
}
|
||||
@@ -174,12 +190,10 @@ namespace Spring.Aspects.Cache
|
||||
else
|
||||
{
|
||||
#region Instrumentation
|
||||
|
||||
if (isLogDebugEnabled)
|
||||
{
|
||||
logger.Debug("Cache hit for [" + resultKey + "]. Aborting invocation...");
|
||||
logger.Debug(String.Format("Object for key [{0}] found in cache [{1}]. Aborting invocation...", resultKey, resultInfo.CacheName));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -203,47 +217,29 @@ namespace Spring.Aspects.Cache
|
||||
{
|
||||
foreach (CacheResultItemsAttribute itemInfo in itemInfoArray)
|
||||
{
|
||||
ICache cache = GetCache(itemInfo.CacheName);
|
||||
AssertUtils.ArgumentNotNull(cache, "CacheName",
|
||||
"Result item cache with the specified name [" + itemInfo.CacheName +
|
||||
"] does not exist.");
|
||||
|
||||
AssertUtils.ArgumentNotNull(itemInfo.KeyExpression, "KeyExpression",
|
||||
"The cache attribute is missing the key definition.");
|
||||
|
||||
ICache cache = GetCache(itemInfo.CacheName);
|
||||
|
||||
#region Instrumentation
|
||||
bool isDebugEnabled = logger.IsDebugEnabled;
|
||||
#endregion
|
||||
foreach (object item in items)
|
||||
{
|
||||
if (EvalCondition(itemInfo.Condition, itemInfo.ConditionExpression, item, null))
|
||||
{
|
||||
object itemKey = itemInfo.KeyExpression.GetValue(item);
|
||||
#region Instrumentation
|
||||
|
||||
if (isDebugEnabled)
|
||||
{
|
||||
logger.Debug("Caching collection item for key [" + itemKey + "].");
|
||||
}
|
||||
|
||||
#endregion
|
||||
cache.Insert(itemKey, (item == null ? NullValue : item), itemInfo.TimeToLiveTimeSpan);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CacheResultInfo GetCacheResultInfo(MethodInfo method)
|
||||
{
|
||||
CacheResultInfo cacheResultInfo = (CacheResultInfo)_cacheResultAttributeCache[method];
|
||||
// no need for locking here - last one wins
|
||||
if (cacheResultInfo == null)
|
||||
{
|
||||
CacheResultAttribute resultInfo = (CacheResultAttribute)GetCustomAttribute(method, typeof(CacheResultAttribute));
|
||||
CacheResultItemsAttribute[] itemInfoArray = (CacheResultItemsAttribute[])GetCustomAttributes(method, typeof(CacheResultItemsAttribute));
|
||||
|
||||
cacheResultInfo = new CacheResultInfo(resultInfo, itemInfoArray);
|
||||
_cacheResultAttributeCache[method] = cacheResultInfo;
|
||||
}
|
||||
return cacheResultInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,10 +53,25 @@ namespace Spring.Aspects.Cache
|
||||
/// </remarks>
|
||||
/// <seealso cref="InvalidateCacheAttribute"/>
|
||||
/// <author>Aleksandar Seovic</author>
|
||||
public class InvalidateCacheAdvice : BaseCacheAdvice, IAfterReturningAdvice
|
||||
{
|
||||
private readonly Hashtable _invalidateCacheAttributeCache = new Hashtable();
|
||||
|
||||
public class InvalidateCacheAdvice : BaseCacheAdvice, IAfterReturningAdvice
|
||||
{
|
||||
#region InvalidateCacheAttribute caching
|
||||
|
||||
private readonly Hashtable _invalidateCacheAttributeCache = new Hashtable();
|
||||
|
||||
private InvalidateCacheAttribute[] GetInvalidateCacheInfo(MethodInfo method)
|
||||
{
|
||||
InvalidateCacheAttribute[] cacheInfoArray = (InvalidateCacheAttribute[])_invalidateCacheAttributeCache[method];
|
||||
if (cacheInfoArray == null)
|
||||
{
|
||||
cacheInfoArray = (InvalidateCacheAttribute[])GetCustomAttributes(method, typeof(InvalidateCacheAttribute));
|
||||
_invalidateCacheAttributeCache[method] = cacheInfoArray;
|
||||
}
|
||||
return cacheInfoArray;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Executes after <paramref name="target"/> <paramref name="method"/>
|
||||
/// returns <b>successfully</b>.
|
||||
@@ -79,7 +94,11 @@ namespace Spring.Aspects.Cache
|
||||
/// <param name="target">The target object.</param>
|
||||
/// <seealso cref="AopAlliance.Intercept.IMethodInterceptor.Invoke"/>
|
||||
public void AfterReturning(object returnValue, MethodInfo method, object[] arguments, object target)
|
||||
{
|
||||
{
|
||||
#region Instrumentation
|
||||
bool isLogDebugEnabled = logger.IsDebugEnabled;
|
||||
#endregion
|
||||
|
||||
InvalidateCacheAttribute[] cacheInfoArray = GetInvalidateCacheInfo(method);
|
||||
|
||||
if (cacheInfoArray.Length > 0)
|
||||
@@ -90,40 +109,44 @@ namespace Spring.Aspects.Cache
|
||||
if (EvalCondition(cacheInfo.Condition, cacheInfo.ConditionExpression, returnValue, vars))
|
||||
{
|
||||
ICache cache = GetCache(cacheInfo.CacheName);
|
||||
AssertUtils.ArgumentNotNull(cache, "CacheName",
|
||||
"Cache with the specified name [" + cacheInfo.CacheName +
|
||||
"] does not exist.");
|
||||
|
||||
if (cacheInfo.KeysExpression != null)
|
||||
{
|
||||
{
|
||||
object keys = cacheInfo.KeysExpression.GetValue(returnValue, vars);
|
||||
if (keys is ICollection)
|
||||
{
|
||||
{
|
||||
#region Instrumentation
|
||||
if (isLogDebugEnabled)
|
||||
{
|
||||
logger.Debug(string.Format("Removing objects for keys [{0}] from cache [{1}].", keys, cacheInfo.CacheName));
|
||||
}
|
||||
#endregion
|
||||
cache.RemoveAll(keys as ICollection);
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
#region Instrumentation
|
||||
if (isLogDebugEnabled)
|
||||
{
|
||||
logger.Debug(string.Format("Removing object for key [{0}] from cache [{1}].", keys, cacheInfo.CacheName));
|
||||
}
|
||||
#endregion
|
||||
cache.Remove(keys);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
#region Instrumentation
|
||||
if (isLogDebugEnabled)
|
||||
{
|
||||
logger.Debug(string.Format("Invalidate cache [{0}].", cacheInfo.CacheName));
|
||||
}
|
||||
#endregion
|
||||
cache.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private InvalidateCacheAttribute[] GetInvalidateCacheInfo(MethodInfo method)
|
||||
{
|
||||
InvalidateCacheAttribute[] cacheInfoArray = (InvalidateCacheAttribute[]) _invalidateCacheAttributeCache[method];
|
||||
if (cacheInfoArray == null)
|
||||
{
|
||||
cacheInfoArray = (InvalidateCacheAttribute[])GetCustomAttributes(method, typeof(InvalidateCacheAttribute));
|
||||
_invalidateCacheAttributeCache[method] = cacheInfoArray;
|
||||
}
|
||||
return cacheInfoArray;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ using Spring.Aop.Framework;
|
||||
using Spring.Caching;
|
||||
using Spring.Context;
|
||||
using Spring.Context.Support;
|
||||
using Spring.Objects.Factory;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -86,7 +87,7 @@ namespace Spring.Aspects.Cache
|
||||
/// http://jira.springframework.org/browse/SPRNET-1226
|
||||
/// </summary>
|
||||
[Test]
|
||||
[ExpectedException(ExpectedException = typeof(ArgumentNullException))]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void NoCacheKeySpecified()
|
||||
{
|
||||
ICache cache = new NonExpiringCache();
|
||||
@@ -100,6 +101,33 @@ namespace Spring.Aspects.Cache
|
||||
Assert.IsNotNull(items);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[ExpectedException(typeof(NoSuchObjectDefinitionException))]
|
||||
public void CacheDoesNotExist()
|
||||
{
|
||||
//ICache cache = new NonExpiringCache();
|
||||
//context.ObjectFactory.RegisterSingleton("losers", cache);
|
||||
|
||||
ProxyFactory pf = new ProxyFactory(new InventorStore());
|
||||
pf.AddAdvisors(cacheAspect);
|
||||
|
||||
IInventorStore store = (IInventorStore)pf.GetProxy();
|
||||
IList items = store.GetAll();
|
||||
}
|
||||
|
||||
[Test]
|
||||
[ExpectedException(typeof(ArgumentException))]
|
||||
public void CacheDoesNotImplementICache()
|
||||
{
|
||||
context.ObjectFactory.RegisterSingleton("inventors", new Object());
|
||||
|
||||
ProxyFactory pf = new ProxyFactory(new InventorStore());
|
||||
pf.AddAdvisors(cacheAspect);
|
||||
|
||||
IInventorStore store = (IInventorStore)pf.GetProxy();
|
||||
IList items = store.GetAll();
|
||||
}
|
||||
|
||||
#if NET_2_0
|
||||
[Test(Description = "http://jira.springframework.org/browse/SPRNET-959")]
|
||||
public void UseMethodInfoForKeyGeneration()
|
||||
|
||||
Reference in New Issue
Block a user