Polishing cache aspect (logs, exceptions)

This commit is contained in:
bbaia
2009-12-12 21:03:35 +00:00
parent bfb1b2cf72
commit 6f850cc45c
5 changed files with 156 additions and 91 deletions

View File

@@ -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>

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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()