SPRNET-1368
Patch Applied. Elegant (if straightforward!) fix. Nice find re: the exposed error condition.
This commit is contained in:
@@ -1,19 +1,19 @@
|
||||
#region License
|
||||
|
||||
/*
|
||||
* Copyright <20> 2002-2006 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
/*
|
||||
* Copyright <20> 2002-2006 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#endregion
|
||||
@@ -84,8 +84,26 @@ namespace Spring.Aspects.Cache
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Inner class to help cache null values.
|
||||
/// </summary>
|
||||
[Serializable] public sealed class NullValueMarkerType
|
||||
{
|
||||
/// <returns>true when other object is of same type.</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is NullValueMarkerType;
|
||||
}
|
||||
|
||||
/// <returns>13</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return 13;
|
||||
}
|
||||
}
|
||||
|
||||
// NullValue
|
||||
private static readonly object NullValue = new object();
|
||||
private static readonly object NullValue = new NullValueMarkerType();
|
||||
|
||||
/// <summary>
|
||||
/// Applies caching around a method invocation.
|
||||
@@ -170,6 +188,26 @@ namespace Spring.Aspects.Cache
|
||||
ICache cache = GetCache(resultInfo.CacheName);
|
||||
returnValue = cache.Get(resultKey);
|
||||
cacheHit = (returnValue != null);
|
||||
|
||||
if (NullValue.Equals(returnValue))
|
||||
{
|
||||
returnValue = null;
|
||||
}
|
||||
|
||||
Type returnType = invocation.Method.ReturnType;
|
||||
if (returnValue != null && !returnType.IsInstanceOfType(returnValue))
|
||||
{
|
||||
#region Instrumentation
|
||||
if (isLogDebugEnabled)
|
||||
{
|
||||
logger.Debug(String.Format("Object for key [{0}] was of type [{1}] which is not compatible with return type [{2}]. Proceeding...", resultKey, returnValue.GetType(), returnType));
|
||||
}
|
||||
#endregion
|
||||
|
||||
cacheHit = false;
|
||||
returnValue = null;
|
||||
}
|
||||
|
||||
if (!cacheHit)
|
||||
{
|
||||
#region Instrumentation
|
||||
@@ -201,7 +239,7 @@ namespace Spring.Aspects.Cache
|
||||
#endregion
|
||||
}
|
||||
|
||||
return (returnValue == NullValue) ? null : returnValue;
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
cacheHit = false;
|
||||
|
||||
@@ -62,14 +62,14 @@ namespace Spring.Aspects.Cache
|
||||
ProxyFactory pf = new ProxyFactory(new InventorStore());
|
||||
pf.AddAdvisors(cacheAspect);
|
||||
|
||||
IInventorStore store = (IInventorStore) pf.GetProxy();
|
||||
IInventorStore store = (IInventorStore)pf.GetProxy();
|
||||
|
||||
Assert.AreEqual(0, cache.Count);
|
||||
|
||||
|
||||
IList inventors = store.GetAll();
|
||||
Assert.AreEqual(2, cache.Count);
|
||||
|
||||
store.Delete((Inventor) inventors[0]);
|
||||
store.Delete((Inventor)inventors[0]);
|
||||
Assert.AreEqual(1, cache.Count);
|
||||
|
||||
Inventor tesla = store.Load("Nikola Tesla");
|
||||
@@ -83,6 +83,24 @@ namespace Spring.Aspects.Cache
|
||||
Assert.AreEqual(0, cache.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCacheResultDoesNotReturnInvalidType()
|
||||
{
|
||||
ICache cache = new NonExpiringCache();
|
||||
context.ObjectFactory.RegisterSingleton("inventors", cache);
|
||||
|
||||
ProxyFactory pf = new ProxyFactory(new InventorStore());
|
||||
pf.AddAdvisors(cacheAspect);
|
||||
|
||||
IInventorStore store = (IInventorStore)pf.GetProxy();
|
||||
|
||||
cache.Insert("Nikola Tesla", "WrongTypeForMethodSignature");
|
||||
|
||||
Inventor value = store.Load("Nikola Tesla");
|
||||
|
||||
Assert.That(value, Is.AssignableTo(typeof(Inventor)), "CacheAspect returned a cached type that is incompatible with the method signature return type");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// http://jira.springframework.org/browse/SPRNET-1226
|
||||
/// </summary>
|
||||
|
||||
@@ -21,7 +21,9 @@
|
||||
#region Imports
|
||||
|
||||
using System.Collections;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using AopAlliance.Intercept;
|
||||
using DotNetMock.Dynamic;
|
||||
using NUnit.Framework;
|
||||
@@ -46,6 +48,7 @@ namespace Spring.Aspects.Cache
|
||||
private CacheResultAdvice advice;
|
||||
private ICache resultCache;
|
||||
private ICache itemCache;
|
||||
private ICache binaryFormatterCache;
|
||||
private CacheResultTarget cacheResultTarget = new CacheResultTarget();
|
||||
|
||||
[SetUp]
|
||||
@@ -59,6 +62,7 @@ namespace Spring.Aspects.Cache
|
||||
|
||||
resultCache = new NonExpiringCache();
|
||||
itemCache = new NonExpiringCache();
|
||||
binaryFormatterCache = new BinaryFormatterCache();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -86,6 +90,36 @@ namespace Spring.Aspects.Cache
|
||||
mockContext.Verify();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CacheResultOfMethodThatReturnsNullWithSerializingCache()
|
||||
{
|
||||
MethodInfo method = new VoidMethod(cacheResultTarget.ReturnsNothing).Method;
|
||||
object expectedReturnValue = null;
|
||||
|
||||
ExpectAttributeRetrieval(method);
|
||||
ExpectCacheKeyGeneration(method, null);
|
||||
ExpectCacheInstanceRetrieval("results", binaryFormatterCache);
|
||||
ExpectCallToProceed(expectedReturnValue);
|
||||
|
||||
// check that the null retVal is cached as well - it might be
|
||||
// the result of an expensive webservice/database call etc.
|
||||
object returnValue = advice.Invoke((IMethodInvocation) mockInvocation.Object);
|
||||
Assert.AreEqual(expectedReturnValue, returnValue);
|
||||
Assert.AreEqual(1, binaryFormatterCache.Count);
|
||||
|
||||
// and again, but without Proceed()...
|
||||
ExpectAttributeRetrieval(method);
|
||||
ExpectCacheKeyGeneration(method, null);
|
||||
ExpectCacheInstanceRetrieval("results", binaryFormatterCache);
|
||||
|
||||
// cached value should be returned
|
||||
object cachedValue = advice.Invoke((IMethodInvocation)mockInvocation.Object);
|
||||
Assert.IsNull(cachedValue, "Should recognize cached value as null-value marker.");
|
||||
|
||||
mockInvocation.Verify();
|
||||
mockContext.Verify();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CacheResultOfMethodThatReturnsObject()
|
||||
{
|
||||
@@ -571,4 +605,33 @@ namespace Spring.Aspects.Cache
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
class BinaryFormatterCache : NonExpiringCache
|
||||
{
|
||||
protected override void DoInsert(object key, object value, System.TimeSpan timeToLive)
|
||||
{
|
||||
BinaryFormatter fmt = new BinaryFormatter();
|
||||
using (MemoryStream stream = new MemoryStream())
|
||||
{
|
||||
fmt.Serialize(stream, value);
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
value = stream.ToArray();
|
||||
}
|
||||
|
||||
base.DoInsert(key, value, timeToLive);
|
||||
}
|
||||
|
||||
public override object Get(object key)
|
||||
{
|
||||
byte[] bytes = (byte[]) base.Get(key);
|
||||
|
||||
if (bytes == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
BinaryFormatter fmt = new BinaryFormatter();
|
||||
return fmt.Deserialize(new MemoryStream(bytes));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user