Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed NH-1086 on 1.2.x branch

SVN: branches/1.2.x@3102
  • Loading branch information...
commit 6e2ae378942a4ddcedfdc4ba745ff02159ea0ed2 1 parent 7134482
Karl Chu authored
68 src/NHibernate.Caches.MemCache.Tests/NH1086/Fixture.cs
View
@@ -0,0 +1,68 @@
+using System.Collections;
+using log4net.Config;
+using NHibernate.Cache;
+using NHibernate.Cfg;
+using NHibernate.Engine;
+using NHibernate.Metadata;
+using NUnit.Framework;
+
+namespace NHibernate.Caches.MemCache.Tests.NH1086
+{
+ [TestFixture]
+ public class Fixture
+ {
+ private MemCacheProvider provider;
+ private Hashtable props;
+
+ [TestFixtureSetUp]
+ public void FixtureSetup()
+ {
+ XmlConfigurator.Configure();
+ props = new Hashtable();
+ props.Add("compression_enabled", false);
+ props.Add("expiration", 20);
+ provider = new MemCacheProvider();
+ provider.Start(props);
+ }
+
+ [TestFixtureTearDown]
+ public void FixtureStop()
+ {
+ provider.Stop();
+ }
+
+ [Test]
+ public void Test()
+ {
+ Configuration config = new Configuration();
+ config.AddResource("NHibernate.Caches.MemCache.Tests.NH1086.Mappings.hbm.xml",
+ this.GetType().Assembly);
+
+ ISessionFactoryImplementor sessions = config.BuildSessionFactory() as ISessionFactoryImplementor;
+
+ IClassMetadata classMetadata = sessions.GetClassMetadata(typeof (ClassWithCompId));
+
+ ICache cache = provider.BuildCache("NH1086", props);
+ Assert.IsNotNull(cache, "no cache returned");
+
+ // Create the object to be cached
+ ClassWithCompId obj = new ClassWithCompId();
+ obj.ID = new CompId("Test object", 1);
+
+ // Create the CacheKey
+ CacheKey key = new CacheKey(obj.ID,
+ classMetadata.IdentifierType,
+ typeof(ClassWithCompId).FullName,
+ sessions);
+
+ // Put the object into cache; mainly to test Serialization goes okay
+ cache.Put(key, obj);
+
+ // Get the object back from cache
+ ClassWithCompId retrievedFromCache = cache.Get(key) as ClassWithCompId;
+
+ Assert.AreNotSame(obj, retrievedFromCache,
+ "Object put into cache and object retrieved from cache should not be the same");
+ }
+ }
+}
13 src/NHibernate.Caches.MemCache.Tests/NH1086/Mappings.hbm.xml
View
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
+ namespace="NHibernate.Caches.MemCache.Tests.NH1086"
+ assembly="NHibernate.Caches.MemCache.Tests"
+>
+ <class name="ClassWithCompId">
+ <composite-id name="ID" class="CompId">
+ <key-property name="KeyString"/>
+ <key-property name="KeyInt"/>
+ </composite-id>
+ <property name="Name"/>
+ </class>
+</hibernate-mapping>
70 src/NHibernate.Caches.MemCache.Tests/NH1086/Model.cs
View
@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace NHibernate.Caches.MemCache.Tests.NH1086
+{
+ [Serializable]
+ public class CompId
+ {
+ private string keyString;
+ private int keyInt;
+
+ public virtual string KeyString
+ {
+ get { return keyString; }
+ private set { keyString = value; }
+ }
+
+ public virtual int KeyInt
+ {
+ get { return keyInt; }
+ private set { keyInt = value; }
+ }
+
+ private CompId() {}
+
+ public CompId(string keyString, int keyInt)
+ {
+ this.keyString = keyString;
+ this.keyInt = keyInt;
+ }
+
+ protected bool Equals(CompId compId)
+ {
+ if (compId == null) return false;
+ return Equals(keyString, compId.keyString) && keyInt == compId.keyInt;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(this, obj)) return true;
+ return Equals(obj as CompId);
+ }
+
+ public override int GetHashCode()
+ {
+ // Generated using ReSharper
+ return (keyString != null ? keyString.GetHashCode() : 0) + 29*keyInt;
+ }
+ }
+
+ [Serializable]
+ public class ClassWithCompId
+ {
+ private CompId id;
+ private string name;
+
+ public virtual CompId ID
+ {
+ get { return id; }
+ set { id = value; }
+ }
+
+ public virtual string Name
+ {
+ get { return name; }
+ set { name = value; }
+ }
+ }
+}
12 src/NHibernate.Caches.MemCache.Tests/NH1086/TestMemCacheClient.cs
View
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Memcached.ClientLibrary;
+using NMock;
+
+namespace NHibernate.Caches.MemCache.Tests.NH1086
+{
+ public class TestMemCacheClient : MemcachedClient
+ {
+ }
+}
14 src/NHibernate.Caches.MemCache.Tests/NHibernate.Caches.MemCache.Tests-2.0.csproj
View
@@ -32,6 +32,14 @@
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\net\2.0\log4net.dll</HintPath>
</Reference>
+ <Reference Include="Memcached.ClientLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=42c0400964dcc297, processorArchitecture=MSIL">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\lib\net\2.0\Memcached.ClientLibrary.dll</HintPath>
+ </Reference>
+ <Reference Include="nmock, Version=1.0.1536.20687, Culture=neutral">
+ <SpecificVersion>False</SpecificVersion>
+ <HintPath>..\..\lib\net\nmock.dll</HintPath>
+ </Reference>
<Reference Include="nunit.framework, Version=2.2.0.0, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\lib\net\nunit.framework.dll</HintPath>
@@ -45,6 +53,9 @@
<Compile Include="MemCacheFixture.cs" />
<Compile Include="MemCacheProviderFixture.cs" />
<Compile Include="MemCacheSectionHandlerFixture.cs" />
+ <Compile Include="NH1086\Fixture.cs" />
+ <Compile Include="NH1086\Model.cs" />
+ <Compile Include="NH1086\TestMemCacheClient.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
@@ -60,6 +71,9 @@
<Name>NHibernate-2.0</Name>
</ProjectReference>
</ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="NH1086\Mappings.hbm.xml" />
+ </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
62 src/NHibernate.Caches.MemCache/MemCacheClient.cs
View
@@ -40,6 +40,7 @@ namespace NHibernate.Caches.MemCache
public class MemCacheClient : ICache
{
private HashAlgorithm hasher = HashAlgorithm.Create();
+ private MD5 md5 = MD5.Create();
private static readonly ILog _log;
private string _region;
private string _regionPrefix = "";
@@ -117,18 +118,53 @@ public MemCacheClient(string regionName, IDictionary properties)
/// <summary>
/// Turn the key obj into a string, preperably using human readable
- /// string, and if the srtring is too long (>=250) it will be hashed
+ /// string, and if the string is too long (>=250) it will be hashed
/// </summary>
private string KeyAsString(object key)
{
- string fullKey = string.Format("{0}{1}@{2}", _regionPrefix, _region, (key == null ? string.Empty : key.ToString()));
+ string fullKey = FullKeyAsString(key);
if (fullKey.Length >= 250) //max key size for memcache
- {
- byte[] bytes = Encoding.ASCII.GetBytes(fullKey);
- byte[] computedHash = hasher.ComputeHash(bytes);
- return Convert.ToBase64String(computedHash);
- }
- return fullKey.Replace(' ', '-');
+ return ComputeHash(fullKey, hasher);
+ else
+ return fullKey.Replace(' ', '-');
+ }
+
+ /// <summary>
+ /// Turn the key object into a human readable string.
+ /// </summary>
+ /// <param name="key"></param>
+ /// <returns></returns>
+ private string FullKeyAsString(object key)
+ {
+ return string.Format("{0}{1}@{2}", _regionPrefix, _region, (key == null ? string.Empty : key.ToString()));
+ }
+
+ /// <summary>
+ /// Compute the hash of the full key string using the given hash algorithm
+ /// </summary>
+ /// <param name="fullKeyString">The full key return by call FullKeyAsString</param>
+ /// <param name="hashAlgorithm">The hash algorithm used to hash the key</param>
+ /// <returns>The hashed key as a string</returns>
+ private string ComputeHash(string fullKeyString, HashAlgorithm hashAlgorithm)
+ {
+ byte[] bytes = Encoding.ASCII.GetBytes(fullKeyString);
+ byte[] computedHash = hashAlgorithm.ComputeHash(bytes);
+ return Convert.ToBase64String(computedHash);
+ }
+
+ /// <summary>
+ /// Compute an alternate key hash; used as a check that the looked-up value is
+ /// in fact what has been put there in the first place.
+ /// </summary>
+ /// <param name="key"></param>
+ /// <returns>The alternate key hash (using the MD5 algorithm)</returns>
+ private string GetAlternateKeyHash(object key)
+ {
+ string fullKey = FullKeyAsString(key);
+ if (fullKey.Length >= 250)
+ return ComputeHash(fullKey, md5);
+ else
+ return fullKey.Replace(' ', '-');
}
public object Get(object key)
@@ -148,9 +184,11 @@ public object Get(object key)
//the reason is that for long keys, we hash the value, and this mean that we may get
//hash collisions. The chance is very low, but it is better to be safe
DictionaryEntry de = (DictionaryEntry) maybeObj;
- if (key.Equals(de.Key) == false)
+ string checkKeyHash = GetAlternateKeyHash(key);
+ if (checkKeyHash.Equals(de.Key))
+ return de.Value;
+ else
return null;
- return de.Value;
}
public void Put(object key, object value)
@@ -168,7 +206,7 @@ public void Put(object key, object value)
{
_log.DebugFormat("setting value for item {0}", key);
}
- bool returnOK = _client.Set(KeyAsString(key), new DictionaryEntry(key, value), DateTime.Now.AddSeconds(_expiry));
+ bool returnOK = _client.Set(KeyAsString(key), new DictionaryEntry(GetAlternateKeyHash(key), value), DateTime.Now.AddSeconds(_expiry));
if (!returnOK)
{
if (_log.IsWarnEnabled)
@@ -226,4 +264,4 @@ public string RegionName
get { return _region; }
}
}
-}
+}
9 src/NHibernate/Cache/CacheKey.cs
View
@@ -15,6 +15,7 @@ public class CacheKey
private object key;
private IType type;
private string entityOrRoleName;
+ private ISessionFactoryImplementor factory;
private int hashCode;
/// <summary>
@@ -27,13 +28,17 @@ public CacheKey(object id, IType type, string entityOrRoleName, ISessionFactoryI
key = id;
this.type = type;
this.entityOrRoleName = entityOrRoleName;
+ this.factory = factory;
hashCode = type.GetHashCode(key, factory);
}
- //Mainly for SysCache
+ //Mainly for SysCache and Memcache
public override String ToString()
{
- return entityOrRoleName + '#' + key.ToString(); //"CacheKey#" + type.toString(key, sf);
+ if (type is ComponentType)
+ return entityOrRoleName + '#' + type.ToLoggableString(key, factory);
+ else
+ return entityOrRoleName + '#' + key.ToString();
}
public override bool Equals(Object other)
Please sign in to comment.
Something went wrong with that request. Please try again.