You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@chemistry.apache.org by fm...@apache.org on 2011/02/11 18:29:32 UTC

svn commit: r1069900 - in /incubator/chemistry/dotcmis/trunk: ./ DotCMIS/ DotCMIS/binding/ DotCMIS/client/ DotCMISUnitTest/

Author: fmui
Date: Fri Feb 11 17:29:32 2011
New Revision: 1069900

URL: http://svn.apache.org/viewvc?rev=1069900&view=rev
Log:
- added object cache implementation
- bug fixes

Added:
    incubator/chemistry/dotcmis/trunk/DotCMIS/util.cs
    incubator/chemistry/dotcmis/trunk/DotCMISUnitTest/CacheTest.cs
Removed:
    incubator/chemistry/dotcmis/trunk/DotCMIS.suo
Modified:
    incubator/chemistry/dotcmis/trunk/   (props changed)
    incubator/chemistry/dotcmis/trunk/DotCMIS/DotCMIS.csproj
    incubator/chemistry/dotcmis/trunk/DotCMIS/binding/binding-caches.cs
    incubator/chemistry/dotcmis/trunk/DotCMIS/client/client-caches.cs
    incubator/chemistry/dotcmis/trunk/DotCMIS/client/client-impl.cs
    incubator/chemistry/dotcmis/trunk/DotCMIS/client/client-intf.cs
    incubator/chemistry/dotcmis/trunk/DotCMIS/client/client-objectfactory.cs
    incubator/chemistry/dotcmis/trunk/DotCMIS/const.cs
    incubator/chemistry/dotcmis/trunk/DotCMISUnitTest/DotCMISUnitTest.csproj
    incubator/chemistry/dotcmis/trunk/DotCMISUnitTest/SmokeTest.cs

Propchange: incubator/chemistry/dotcmis/trunk/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Fri Feb 11 17:29:32 2011
@@ -0,0 +1 @@
+*.suo

Modified: incubator/chemistry/dotcmis/trunk/DotCMIS/DotCMIS.csproj
URL: http://svn.apache.org/viewvc/incubator/chemistry/dotcmis/trunk/DotCMIS/DotCMIS.csproj?rev=1069900&r1=1069899&r2=1069900&view=diff
==============================================================================
--- incubator/chemistry/dotcmis/trunk/DotCMIS/DotCMIS.csproj (original)
+++ incubator/chemistry/dotcmis/trunk/DotCMIS/DotCMIS.csproj Fri Feb 11 17:29:32 2011
@@ -76,6 +76,7 @@
       <DesignTime>True</DesignTime>
       <DependentUpon>Reference.svcmap</DependentUpon>
     </Compile>
+    <Compile Include="util.cs" />
   </ItemGroup>
   <ItemGroup />
   <ItemGroup>

Modified: incubator/chemistry/dotcmis/trunk/DotCMIS/binding/binding-caches.cs
URL: http://svn.apache.org/viewvc/incubator/chemistry/dotcmis/trunk/DotCMIS/binding/binding-caches.cs?rev=1069900&r1=1069899&r2=1069900&view=diff
==============================================================================
--- incubator/chemistry/dotcmis/trunk/DotCMIS/binding/binding-caches.cs (original)
+++ incubator/chemistry/dotcmis/trunk/DotCMIS/binding/binding-caches.cs Fri Feb 11 17:29:32 2011
@@ -24,6 +24,7 @@ using System.Text;
 using System.Threading;
 using DotCMIS.Binding.Impl;
 using DotCMIS.Data;
+using DotCMIS.Util;
 
 namespace DotCMIS.Binding
 {
@@ -350,11 +351,10 @@ namespace DotCMIS.Binding
         }
     }
 
-    internal abstract class AbstractDictionaryCacheLevel : IBindingCacheLevel
+    internal abstract class AbstractCacheLevel : IBindingCacheLevel
     {
         protected static string NullKey = "";
 
-        private IDictionary<string, object> dict;
         private bool fallbackEnabled = false;
         private string fallbackKey = null;
         private bool singleValueEnabled = false;
@@ -365,42 +365,48 @@ namespace DotCMIS.Binding
         {
             get
             {
-                object value = null;
-                if (dict.TryGetValue(key == null ? NullKey : key, out value))
+                object value = GetValue(key == null ? NullKey : key);
+                if (value != null)
                 {
                     return value;
                 }
 
-                if (fallbackEnabled && dict.TryGetValue(fallbackKey, out value))
+                if (fallbackEnabled)
                 {
-                    return value;
+                    value = GetValue(fallbackKey);
+                    if (value != null)
+                    {
+                        return value;
+                    }
                 }
 
-                if (singleValueEnabled && dict.Count == 1)
+                if (singleValueEnabled)
                 {
-                    value = dict.Values.First();
+                    value = GetSingleValue();
+                    if (value != null)
+                    {
+                        return value;
+                    }
                 }
 
-                return value;
+                return null;
             }
             set
             {
                 if (value != null)
                 {
-                    dict[key == null ? NullKey : key] = value;
+                    AddValue(key == null ? NullKey : key, value);
                 }
             }
         }
 
-        public virtual void Remove(string key)
-        {
-            dict.Remove(key);
-        }
+        public abstract void Remove(string key);
 
-        public void SetDictionary(IDictionary<string, object> dict)
-        {
-            this.dict = dict;
-        }
+        protected abstract object GetValue(string key);
+
+        protected abstract object GetSingleValue();
+
+        protected abstract void AddValue(string key, object value);
 
         protected void EnableKeyFallback(string key)
         {
@@ -470,91 +476,93 @@ namespace DotCMIS.Binding
         }
     }
 
-    internal class DictionaryCacheLevel : AbstractDictionaryCacheLevel
+    internal class DictionaryCacheLevel : AbstractCacheLevel
     {
         public const string Capacity = "capacity";
         public const string SingleValue = "singleValue";
 
+        private IDictionary<string, object> dict;
+
         public override void Initialize(IDictionary<string, string> parameters)
         {
             int initialCapacity = GetIntParameter(parameters, Capacity, 32);
             bool singleValue = GetBooleanParameter(parameters, SingleValue, false);
 
-            SetDictionary(new Dictionary<string, object>(initialCapacity));
+            dict = new Dictionary<string, object>(initialCapacity);
             if (singleValue)
             {
                 EnableSingeValueFallback();
             }
         }
+
+        public override void Remove(string key)
+        {
+            dict.Remove(key);
+        }
+
+        protected override object GetValue(string key)
+        {
+            object value;
+            if (dict.TryGetValue(key, out value))
+            {
+                return value;
+            }
+
+            return null;
+        }
+
+        protected override object GetSingleValue()
+        {
+            if (dict.Count == 1)
+            {
+                return dict.Values.First();
+            }
+
+            return null;
+        }
+
+        protected override void AddValue(string key, object value)
+        {
+            dict[key] = value;
+        }
     }
 
-    internal class LruCacheLevel : AbstractDictionaryCacheLevel
+    internal class LruCacheLevel : AbstractCacheLevel
     {
         public const string MaxEntries = "maxEntries";
 
-        private LinkedList<string> keyList;
-        private int maxEntries;
+        private LRUCache<string, object> cache;
 
         public override void Initialize(IDictionary<string, string> parameters)
         {
-            maxEntries = GetIntParameter(parameters, MaxEntries, 100);
-            keyList = new LinkedList<string>();
-            SetDictionary(new Dictionary<string, object>(maxEntries + 1));
+            int maxEntries = GetIntParameter(parameters, MaxEntries, 100);
+
+            cache = new LRUCache<string, object>(maxEntries, TimeSpan.FromDays(1));
         }
 
-        public override object this[string key]
+        public override void Remove(string key)
         {
-            get
-            {
-                object value = base[key];
-                if (value != null)
-                {
-                    LinkedListNode<string> node = keyList.Find(key);
-                    if (node == null)
-                    {
-                        throw new ApplicationException("Cache error!");
-                    }
-                    else
-                    {
-                        keyList.Remove(node);
-                        keyList.AddFirst(node);
-                    }
-                }
-
-                return value;
-            }
-            set
-            {
-                if (value == null)
-                {
-                    return;
-                }
+            cache.Remove(key);
+        }
 
-                LinkedListNode<string> node = keyList.Find(key);
-                if (node == null)
-                {
-                    keyList.AddFirst(key);
-                    while (keyList.Count > maxEntries)
-                    {
-                        LinkedListNode<string> lastNode = keyList.Last;
-                        base.Remove(lastNode.Value);
-                        keyList.RemoveLast();
-                    }
-                }
-                else
-                {
-                    keyList.Remove(node);
-                    keyList.AddFirst(node);
-                }
+        protected override object GetValue(string key)
+        {
+            return cache.Get(key);
+        }
 
-                base[key] = value;
+        protected override object GetSingleValue()
+        {
+            if (cache.Count == 1)
+            {
+                return cache.GetLatest();
             }
+
+            return null;
         }
 
-        public override void Remove(string key)
+        protected override void AddValue(string key, object value)
         {
-            keyList.Remove(key);
-            base.Remove(key);
+            cache.Add(key, value);
         }
     }
 

Modified: incubator/chemistry/dotcmis/trunk/DotCMIS/client/client-caches.cs
URL: http://svn.apache.org/viewvc/incubator/chemistry/dotcmis/trunk/DotCMIS/client/client-caches.cs?rev=1069900&r1=1069899&r2=1069900&view=diff
==============================================================================
--- incubator/chemistry/dotcmis/trunk/DotCMIS/client/client-caches.cs (original)
+++ incubator/chemistry/dotcmis/trunk/DotCMIS/client/client-caches.cs Fri Feb 11 17:29:32 2011
@@ -20,6 +20,8 @@ using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
+using System.Threading;
+using DotCMIS.Util;
 
 namespace DotCMIS.Client.Impl.Cache
 {
@@ -54,4 +56,253 @@ namespace DotCMIS.Client.Impl.Cache
         public void Clear() { }
         public int CacheSize { get { return 0; } }
     }
+
+    public class CmisObjectCache : ICache
+    {
+        private int cacheSize;
+        private int cacheTtl;
+        private int pathToIdSize;
+        private int pathToIdTtl;
+
+        private LRUCache<string, IDictionary<string, ICmisObject>> objectCache;
+        private LRUCache<string, string> pathToIdCache;
+
+        private object cacheLock = new object();
+
+        public CmisObjectCache() { }
+
+        public void Initialize(ISession session, IDictionary<string, string> parameters)
+        {
+            Lock();
+            try
+            {
+                // cache size
+                cacheSize = 1000;
+                try
+                {
+                    string cacheSizeStr;
+                    if (parameters.TryGetValue(SessionParameter.CacheSizeObjects, out cacheSizeStr))
+                    {
+                        cacheSize = Int32.Parse(cacheSizeStr);
+                        if (cacheSize < 0)
+                        {
+                            cacheSize = 0;
+                        }
+                    }
+                }
+                catch (Exception) { }
+
+                // cache time-to-live
+                cacheTtl = 2 * 60 * 60 * 1000;
+                try
+                {
+                    string cacheTtlStr;
+                    if (parameters.TryGetValue(SessionParameter.CacheTTLObjects, out cacheTtlStr))
+                    {
+                        cacheTtl = Int32.Parse(cacheTtlStr);
+                        if (cacheTtl < 0)
+                        {
+                            cacheTtl = 2 * 60 * 60 * 1000;
+                        }
+                    }
+                }
+                catch (Exception) { }
+
+                // path-to-id size
+                pathToIdSize = 1000;
+                try
+                {
+                    string pathToIdSizeStr;
+                    if (parameters.TryGetValue(SessionParameter.CacheSizePathToId, out pathToIdSizeStr))
+                    {
+                        pathToIdSize = Int32.Parse(pathToIdSizeStr);
+                        if (pathToIdSize < 0)
+                        {
+                            pathToIdSize = 0;
+                        }
+                    }
+                }
+                catch (Exception) { }
+
+                // path-to-id time-to-live
+                pathToIdTtl = 30 * 60 * 1000;
+                try
+                {
+                    string pathToIdTtlStr;
+                    if (parameters.TryGetValue(SessionParameter.CacheTTLPathToId, out pathToIdTtlStr))
+                    {
+                        pathToIdTtl = Int32.Parse(pathToIdTtlStr);
+                        if (pathToIdTtl < 0)
+                        {
+                            pathToIdTtl = 30 * 60 * 1000;
+                        }
+                    }
+                }
+                catch (Exception) { }
+
+                InitializeInternals();
+            }
+            finally
+            {
+                Unlock();
+            }
+        }
+
+        private void InitializeInternals()
+        {
+            Lock();
+            try
+            {
+                objectCache = new LRUCache<string, IDictionary<string, ICmisObject>>(cacheSize, TimeSpan.FromMilliseconds(cacheTtl));
+                pathToIdCache = new LRUCache<string, string>(pathToIdSize, TimeSpan.FromMilliseconds(pathToIdTtl));
+            }
+            finally
+            {
+                Unlock();
+            }
+        }
+
+        public void Clear()
+        {
+            InitializeInternals();
+        }
+
+        public bool ContainsId(string objectId, string cacheKey)
+        {
+            Lock();
+            try
+            {
+                return objectCache.Get(objectId) != null;
+            }
+            finally
+            {
+                Unlock();
+            }
+        }
+
+        public bool ContainsPath(string path, string cacheKey)
+        {
+            Lock();
+            try
+            {
+                return pathToIdCache.Get(path) != null;
+            }
+            finally
+            {
+                Unlock();
+            }
+        }
+
+        public ICmisObject GetById(string objectId, string cacheKey)
+        {
+            Lock();
+            try
+            {
+                IDictionary<string, ICmisObject> cacheKeyDict = objectCache.Get(objectId);
+                if (cacheKeyDict == null)
+                {
+                    return null;
+                }
+
+                ICmisObject cmisObject;
+                if (cacheKeyDict.TryGetValue(cacheKey, out cmisObject))
+                {
+                    return cmisObject;
+                }
+
+                return null;
+            }
+            finally
+            {
+                Unlock();
+            }
+        }
+
+        public ICmisObject GetByPath(string path, string cacheKey)
+        {
+            Lock();
+            try
+            {
+                string id = pathToIdCache.Get(path);
+                if (id == null)
+                {
+                    return null;
+                }
+
+                return GetById(id, cacheKey);
+            }
+            finally
+            {
+                Unlock();
+            }
+        }
+
+        public void Put(ICmisObject cmisObject, string cacheKey)
+        {
+            // no object, no id, no cache key - no cache
+            if (cmisObject == null || cmisObject.Id == null || cacheKey == null)
+            {
+                return;
+            }
+
+            Lock();
+            try
+            {
+                IDictionary<string, ICmisObject> cacheKeyDict = objectCache.Get(cmisObject.Id);
+                if (cacheKeyDict == null)
+                {
+                    cacheKeyDict = new Dictionary<string, ICmisObject>();
+                    objectCache.Add(cmisObject.Id, cacheKeyDict);
+                }
+
+                cacheKeyDict[cacheKey] = cmisObject;
+
+                // folders may have a path, use it!
+                string path = cmisObject.GetPropertyValue(PropertyIds.Path) as string;
+                if (path != null)
+                {
+                    pathToIdCache.Add(path, cmisObject.Id);
+                }
+            }
+            finally
+            {
+                Unlock();
+            }
+        }
+
+        public void PutPath(string path, ICmisObject cmisObject, string cacheKey)
+        {
+            // no path, no object, no id, no cache key - no cache
+            if (path ==null || cmisObject == null || cmisObject.Id == null || cacheKey == null)
+            {
+                return;
+            }
+
+            Lock();
+            try
+            {
+                Put(cmisObject, cacheKey);
+                pathToIdCache.Add(path, cmisObject.Id);
+            }
+            finally
+            {
+                Unlock();
+            }
+        }
+
+        public int CacheSize
+        {
+            get { return cacheSize; }
+        }
+
+        protected void Lock()
+        {
+            Monitor.Enter(cacheLock);
+        }
+
+        protected void Unlock()
+        {
+            Monitor.Exit(cacheLock);
+        }
+    }
 }

Modified: incubator/chemistry/dotcmis/trunk/DotCMIS/client/client-impl.cs
URL: http://svn.apache.org/viewvc/incubator/chemistry/dotcmis/trunk/DotCMIS/client/client-impl.cs?rev=1069900&r1=1069899&r2=1069900&view=diff
==============================================================================
--- incubator/chemistry/dotcmis/trunk/DotCMIS/client/client-impl.cs (original)
+++ incubator/chemistry/dotcmis/trunk/DotCMIS/client/client-impl.cs Fri Feb 11 17:29:32 2011
@@ -257,7 +257,7 @@ namespace DotCMIS.Client.Impl
                 }
                 else
                 {
-                    cacheType = typeof(NoCache);
+                    cacheType = typeof(CmisObjectCache);
                 }
 
                 ICache cacheObject = Activator.CreateInstance(cacheType) as ICache;

Modified: incubator/chemistry/dotcmis/trunk/DotCMIS/client/client-intf.cs
URL: http://svn.apache.org/viewvc/incubator/chemistry/dotcmis/trunk/DotCMIS/client/client-intf.cs?rev=1069900&r1=1069899&r2=1069900&view=diff
==============================================================================
--- incubator/chemistry/dotcmis/trunk/DotCMIS/client/client-intf.cs (original)
+++ incubator/chemistry/dotcmis/trunk/DotCMIS/client/client-intf.cs Fri Feb 11 17:29:32 2011
@@ -99,7 +99,21 @@ namespace DotCMIS.Client
     }
 
     /// <summary>
-    /// Session interface.
+    /// A session is a connection to a CMIS repository with a specific user.
+    /// <para>
+    /// Not all operations might be supported by the connected repository. Either DotCMIS or the repository will 
+    /// throw an exception if an unsupported operation is called. 
+    /// The capabilities of the repository can be discovered by evaluating the repository info 
+    /// (see <see cref="RepositoryInfo"/>).
+    /// </para>
+    /// <para>
+    /// Almost all methods might throw exceptions derived from <see cref="DotCMIS.Exceptions.CmisBaseException"/>!
+    /// </para>
+    /// <para>
+    /// (Please refer to the <a href="http://docs.oasis-open.org/cmis/CMIS/v1.0/os/">CMIS specification</a>
+    /// for details about the domain model, terms, concepts, base types, properties, ids and query names, 
+    /// query language, etc.)
+    /// </para>
     /// </summary>
     public interface ISession
     {
@@ -336,17 +350,71 @@ namespace DotCMIS.Client
     /// </summary>
     public interface IProperty
     {
+        /// <summary>
+        /// Gets the property id.
+        /// </summary>
         string Id { get; }
+
+        /// <summary>
+        /// Gets the property local name.
+        /// </summary>
         string LocalName { get; }
+
+        /// <summary>
+        /// Gets the property display name.
+        /// </summary>
         string DisplayName { get; }
+
+        /// <summary>
+        /// Gets the property query name.
+        /// </summary>
         string QueryName { get; }
+
+        /// <summary>
+        /// Gets if the property is a multi-value proprty.
+        /// </summary>
         bool IsMultiValued { get; }
+
+        /// <summary>
+        /// Gets the proprty type.
+        /// </summary>
         PropertyType? PropertyType { get; }
+
+        /// <summary>
+        /// Gets the property defintion.
+        /// </summary>
         IPropertyDefinition PropertyDefinition { get; }
+
+        /// <summary>
+        /// Gets the value of the property.
+        /// </summary>
+        /// <remarks>
+        /// If the property is a single-value property the single value is returned.
+        /// If the property is a multi-value property a IList&lt;object&gt; is returned.
+        /// </remarks>
         object Value { get; }
+
+        /// <summary>
+        /// Gets the value list of the property.
+        /// </summary>
+        /// <remarks>
+        /// If the property is a single-value property a list with one or no items is returend.
+        /// </remarks>
         IList<object> Values { get; }
+
+        /// <summary>
+        /// Gets the first value of the value list or <c>null</c> if the list has no values.
+        /// </summary>
         object FirstValue { get; }
+
+        /// <summary>
+        /// Gets a string representation of the first value of the value list.
+        /// </summary>
         string ValueAsString { get; }
+
+        /// <summary>
+        /// Gets a string representation of the value list.
+        /// </summary>
         string ValuesAsString { get; }
     }
 
@@ -430,17 +498,45 @@ namespace DotCMIS.Client
     /// </summary>
     public interface ICmisObject : IObjectId, ICmisObjectProperties
     {
-        // object
+        /// <summary>
+        /// Gets the allowable actions if they have been fetched for this object.
+        /// </summary>
         IAllowableActions AllowableActions { get; }
+
+        /// <summary>
+        /// Gets the relationships if they have been fetched for this object.
+        /// </summary>
         IList<IRelationship> Relationships { get; }
+
+        /// <summary>
+        /// Gets the ACL if it has been fetched for this object.
+        /// </summary>
         IAcl Acl { get; }
 
-        // object service
+        /// <summary>
+        /// Deletes this object.
+        /// </summary>
+        /// <param name="allVersions">if this object is a document this parameter defines if just this version or all versions should be deleted</param>
         void Delete(bool allVersions);
+
+        /// <summary>
+        /// Updates the properties that are provided.
+        /// </summary>
+        /// <param name="properties">the properties to update</param>
+        /// <returns>the updated object (a repository might have created a new object)</returns>
         ICmisObject UpdateProperties(IDictionary<string, object> properties);
+
+        /// <summary>
+        /// Updates the properties that are provided.
+        /// </summary>
+        /// <param name="properties">the properties to update</param>
+        /// <param name="refresh">indicates if the object should be refresh after the update</param>
+        /// <returns>the object id of the updated object (a repository might have created a new object)</returns>
         IObjectId UpdateProperties(IDictionary<string, object> properties, bool refresh);
 
-        // renditions
+        /// <summary>
+        /// Gets the renditions if they have been fetched for this object.
+        /// </summary>
         IList<IRendition> Renditions { get; }
 
         // policy service
@@ -456,8 +552,19 @@ namespace DotCMIS.Client
         // extensions
         IList<ICmisExtensionElement> GetExtensions(ExtensionLevel level);
 
+        /// <summary>
+        /// Gets the timestamp of the last refresh.
+        /// </summary>
         DateTime RefreshTimestamp { get; }
+
+        /// <summary>
+        /// Reloads the data from the repository.
+        /// </summary>
         void Refresh();
+
+        /// <summary>
+        /// Reloads the data from the repository if the last refresh did not occur within <c>durationInMillis</c>.
+        /// </summary>
         void RefreshIfOld(long durationInMillis);
     }
 

Modified: incubator/chemistry/dotcmis/trunk/DotCMIS/client/client-objectfactory.cs
URL: http://svn.apache.org/viewvc/incubator/chemistry/dotcmis/trunk/DotCMIS/client/client-objectfactory.cs?rev=1069900&r1=1069899&r2=1069900&view=diff
==============================================================================
--- incubator/chemistry/dotcmis/trunk/DotCMIS/client/client-objectfactory.cs (original)
+++ incubator/chemistry/dotcmis/trunk/DotCMIS/client/client-objectfactory.cs Fri Feb 11 17:29:32 2011
@@ -210,7 +210,13 @@ namespace DotCMIS.Client.Impl
             // get the type
             if (type == null)
             {
-                string typeId = properties[PropertyIds.ObjectTypeId] as string;
+                object typeIdObject;
+                if (!properties.TryGetValue(PropertyIds.ObjectTypeId, out typeIdObject))
+                {
+                    throw new ArgumentException("Type or type property must be set!");
+                }
+
+                string typeId = typeIdObject as string;
                 if (typeId == null)
                 {
                     throw new ArgumentException("Type or type property must be set!");

Modified: incubator/chemistry/dotcmis/trunk/DotCMIS/const.cs
URL: http://svn.apache.org/viewvc/incubator/chemistry/dotcmis/trunk/DotCMIS/const.cs?rev=1069900&r1=1069899&r2=1069900&view=diff
==============================================================================
--- incubator/chemistry/dotcmis/trunk/DotCMIS/const.cs (original)
+++ incubator/chemistry/dotcmis/trunk/DotCMIS/const.cs Fri Feb 11 17:29:32 2011
@@ -66,6 +66,10 @@ namespace DotCMIS
         public const string CacheClass = "org.apache.chemistry.dotcmis.cache.classname";
         public const string RepositoryId = "org.apache.chemistry.dotcmis.session.repository.id";
 
+        public const string CacheSizeObjects = "org.apache.chemistry.dotcmis.cache.objects.size";
+        public const string CacheTTLObjects = "org.apache.chemistry.dotcmis.cache.objects.ttl";
+        public const string CacheSizePathToId = "org.apache.chemistry.dotcmis.cache.pathtoid.size";
+        public const string CacheTTLPathToId = "org.apache.chemistry.dotcmis.cache.pathtoid.ttl";
         public const string CachePathOmit = "org.apache.chemistry.dotcmis.cache.path.omit";
     }
 

Added: incubator/chemistry/dotcmis/trunk/DotCMIS/util.cs
URL: http://svn.apache.org/viewvc/incubator/chemistry/dotcmis/trunk/DotCMIS/util.cs?rev=1069900&view=auto
==============================================================================
--- incubator/chemistry/dotcmis/trunk/DotCMIS/util.cs (added)
+++ incubator/chemistry/dotcmis/trunk/DotCMIS/util.cs Fri Feb 11 17:29:32 2011
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace DotCMIS.Util
+{
+    /// <summary>
+    /// LRU cache implementation. Not thread safe!
+    /// </summary>
+    internal class LRUCache<K, V>
+    {
+        private int capacity;
+        private TimeSpan ttl;
+        private Dictionary<K, LinkedListNode<LRUCacheItem<K, V>>> cacheDict = new Dictionary<K, LinkedListNode<LRUCacheItem<K, V>>>();
+        private LinkedList<LRUCacheItem<K, V>> lruList = new LinkedList<LRUCacheItem<K, V>>();
+
+        public LRUCache(int capacity, TimeSpan ttl)
+        {
+            this.capacity = capacity;
+            this.ttl = ttl;
+        }
+
+        public V Get(K key)
+        {
+            LinkedListNode<LRUCacheItem<K, V>> node;
+            if (cacheDict.TryGetValue(key, out node))
+            {
+                lruList.Remove(node);
+
+                if (node.Value.IsExpired)
+                {
+                    cacheDict.Remove(node.Value.Key);
+                    return default(V);
+                }
+
+                lruList.AddLast(node);
+
+                return node.Value.Value;
+            }
+
+            return default(V);
+        }
+
+        public V GetLatest()
+        {
+            if (lruList.Count == 0)
+            {
+                return default(V);
+            }
+
+            return lruList.First().Value;
+        }
+
+        public void Add(K key, V val)
+        {
+            Remove(key);
+
+            if (cacheDict.Count >= capacity)
+            {
+                RemoveFirst();
+            }
+
+            LRUCacheItem<K, V> cacheItem = new LRUCacheItem<K, V>(key, val, DateTime.UtcNow + ttl);
+            LinkedListNode<LRUCacheItem<K, V>> node = new LinkedListNode<LRUCacheItem<K, V>>(cacheItem);
+
+            lruList.AddLast(node);
+            cacheDict.Add(key, node);
+        }
+
+        protected void RemoveFirst()
+        {
+            LinkedListNode<LRUCacheItem<K, V>> node = lruList.First;
+            lruList.RemoveFirst();
+            cacheDict.Remove(node.Value.Key);
+        }
+
+        public void Remove(K key)
+        {
+            LinkedListNode<LRUCacheItem<K, V>> node;
+            if (cacheDict.TryGetValue(key, out node))
+            {
+                lruList.Remove(node);
+                cacheDict.Remove(node.Value.Key);
+            }
+        }
+
+        public int Count
+        {
+            get { return lruList.Count; }
+        }
+    }
+
+    internal class LRUCacheItem<K, V>
+    {
+        public LRUCacheItem(K key, V value, DateTime expiration)
+        {
+            Key = key;
+            Value = value;
+            Expiration = expiration;
+        }
+
+        public K Key { get; private set; }
+
+        public V Value { get; private set; }
+
+        public DateTime Expiration { get; private set; }
+
+        public bool IsExpired
+        {
+            get { return Value == null || DateTime.UtcNow > Expiration; }
+        }
+    }
+}

Added: incubator/chemistry/dotcmis/trunk/DotCMISUnitTest/CacheTest.cs
URL: http://svn.apache.org/viewvc/incubator/chemistry/dotcmis/trunk/DotCMISUnitTest/CacheTest.cs?rev=1069900&view=auto
==============================================================================
--- incubator/chemistry/dotcmis/trunk/DotCMISUnitTest/CacheTest.cs (added)
+++ incubator/chemistry/dotcmis/trunk/DotCMISUnitTest/CacheTest.cs Fri Feb 11 17:29:32 2011
@@ -0,0 +1,208 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+using System;
+using System.Collections.Generic;
+using DotCMIS.Client;
+using DotCMIS.Client.Impl.Cache;
+using DotCMIS.Data;
+using DotCMIS.Data.Extensions;
+using DotCMIS.Enums;
+using NUnit.Framework;
+using DotCMIS;
+using System.Threading;
+
+namespace DotCMISUnitTest
+{
+    [TestFixture]
+    class CacheTest
+    {
+        [Test]
+        public void TestCache()
+        {
+            IDictionary<string, string> parameters = new Dictionary<string, string>();
+
+            CmisObjectCache cache = new CmisObjectCache();
+            cache.Initialize(null, parameters);
+
+            string cacheKey1 = "ck1";
+            string cacheKey2 = "ck2";
+
+            // add first object
+            MockObject mock1 = new MockObject("1");
+            cache.Put(mock1, cacheKey1);
+
+            ICmisObject o1 = cache.GetById(mock1.Id, cacheKey1);
+            Assert.NotNull(o1);
+            Assert.AreEqual(mock1.Id, o1.Id);
+            Assert.Null(cache.GetById(mock1.Id, cacheKey2));
+            Assert.Null(cache.GetById("- some id -", cacheKey1));
+
+            // add second object - same id
+            MockObject mock2 = new MockObject("1");
+            cache.Put(mock2, cacheKey2);
+
+            o1 = cache.GetById(mock1.Id, cacheKey1);
+            Assert.NotNull(o1);
+            Assert.AreEqual(mock1.Id, o1.Id);
+
+            ICmisObject o2 = cache.GetById(mock2.Id, cacheKey2);
+            Assert.NotNull(o2);
+            Assert.AreEqual(mock2.Id, o2.Id);
+
+            // add third object - other id
+
+            MockObject mock3 = new MockObject("3");
+            cache.Put(mock3, cacheKey1);
+
+            o1 = cache.GetById(mock1.Id, cacheKey1);
+            Assert.NotNull(o1);
+            Assert.AreEqual(mock1.Id, o1.Id);
+
+            o2 = cache.GetById(mock2.Id, cacheKey2);
+            Assert.NotNull(o2);
+            Assert.AreEqual(mock2.Id, o2.Id);
+
+            ICmisObject o3 = cache.GetById(mock3.Id, cacheKey1);
+            Assert.NotNull(o3);
+            Assert.AreEqual(mock3.Id, o3.Id);
+        }
+
+        [Test]
+        public void TestLRU()
+        {
+            IDictionary<string, string> parameters = new Dictionary<string, string>();
+            parameters[SessionParameter.CacheSizeObjects] = "10";
+
+            CmisObjectCache cache = new CmisObjectCache();
+            cache.Initialize(null, parameters);
+
+            string cacheKey1 = "ck1";
+
+            MockObject[] mocks = new MockObject[10];
+            for (int i = 0; i < 10; i++)
+            {
+                mocks[i] = new MockObject("m" + i);
+                cache.Put(mocks[i], cacheKey1);
+            }
+
+            for (int i = 0; i < 10; i++)
+            {
+                mocks[i] = new MockObject("m" + i);
+                Assert.NotNull(cache.GetById("m" + i, cacheKey1));
+            }
+
+            MockObject newMock = new MockObject("new");
+            cache.Put(newMock, cacheKey1);
+            Assert.NotNull(cache.GetById(newMock.Id, cacheKey1));
+
+            for (int i = 1; i < 10; i++)
+            {
+                mocks[i] = new MockObject("m" + i);
+                Assert.NotNull(cache.GetById("m" + i, cacheKey1));
+            }
+
+            Assert.Null(cache.GetById("m0", cacheKey1));
+        }
+
+        [Test]
+        public void TestExpiration()
+        {
+            IDictionary<string, string> parameters = new Dictionary<string, string>();
+            parameters[SessionParameter.CacheTTLObjects] = "500";
+
+            CmisObjectCache cache = new CmisObjectCache();
+            cache.Initialize(null, parameters);
+
+            string cacheKey1 = "ck1";
+
+            MockObject mock1 = new MockObject("1");
+            cache.Put(mock1, cacheKey1);
+
+            Assert.NotNull(cache.GetById(mock1.Id, cacheKey1));
+
+            Thread.Sleep(TimeSpan.FromSeconds(1));
+
+            Assert.Null(cache.GetById(mock1.Id, cacheKey1));
+        }
+
+        [Test]
+        public void TestPath()
+        {
+            IDictionary<string, string> parameters = new Dictionary<string, string>();
+
+            CmisObjectCache cache = new CmisObjectCache();
+            cache.Initialize(null, parameters);
+
+            string cacheKey1 = "ck1";
+            string path = "/test/path";
+
+            MockObject mock1 = new MockObject("1");
+            cache.PutPath(path, mock1, cacheKey1);
+
+            ICmisObject o1 = cache.GetById(mock1.Id, cacheKey1);
+            Assert.NotNull(o1);
+            Assert.AreEqual(mock1.Id, o1.Id);
+
+            ICmisObject o2 = cache.GetByPath(path, cacheKey1);
+            Assert.NotNull(o2);
+            Assert.AreEqual(mock1.Id, o2.Id);
+
+            Assert.Null(cache.GetByPath("/some/other/path/", cacheKey1));
+        }
+    }
+
+    class MockObject : ICmisObject
+    {
+        public MockObject(string id)
+        {
+            Id = id;
+        }
+
+        public string Id { get; private set; }
+        public IList<IProperty> Properties { get { return null; } }
+        public IProperty this[string propertyId] { get { return null; } }
+        public object GetPropertyValue(string propertyId) { return null; }
+        public string Name { get { return null; } }
+        public string CreatedBy { get { return null; } }
+        public DateTime? CreationDate { get { return null; } }
+        public string LastModifiedBy { get { return null; } }
+        public DateTime? LastModificationDate { get { return null; } }
+        public BaseTypeId BaseTypeId { get { return BaseTypeId.CmisDocument; } }
+        public IObjectType BaseType { get { return null; } }
+        public IObjectType ObjectType { get { return null; } }
+        public string ChangeToken { get { return null; } }
+        public IAllowableActions AllowableActions { get { return null; } }
+        public IList<IRelationship> Relationships { get { return null; } }
+        public IAcl Acl { get { return null; } }
+        public void Delete(bool allVersions) { }
+        public ICmisObject UpdateProperties(IDictionary<string, object> properties) { return null; }
+        public IObjectId UpdateProperties(IDictionary<string, object> properties, bool refresh) { return null; }
+        public IList<IRendition> Renditions { get { return null; } }
+        public void ApplyPolicy(params IObjectId[] policyId) { }
+        public void RemovePolicy(params IObjectId[] policyId) { }
+        public IList<IPolicy> Policies { get { return null; } }
+        public IAcl ApplyAcl(IList<IAce> AddAces, IList<IAce> removeAces, AclPropagation? aclPropagation) { return null; }
+        public IAcl AddAcl(IList<IAce> AddAces, AclPropagation? aclPropagation) { return null; }
+        public IAcl RemoveAcl(IList<IAce> RemoveAces, AclPropagation? aclPropagation) { return null; }
+        public IList<ICmisExtensionElement> GetExtensions(ExtensionLevel level) { return null; }
+        public DateTime RefreshTimestamp { get { return DateTime.Now; } }
+        public void Refresh() { }
+        public void RefreshIfOld(long durationInMillis) { }
+    }
+}

Modified: incubator/chemistry/dotcmis/trunk/DotCMISUnitTest/DotCMISUnitTest.csproj
URL: http://svn.apache.org/viewvc/incubator/chemistry/dotcmis/trunk/DotCMISUnitTest/DotCMISUnitTest.csproj?rev=1069900&r1=1069899&r2=1069900&view=diff
==============================================================================
--- incubator/chemistry/dotcmis/trunk/DotCMISUnitTest/DotCMISUnitTest.csproj (original)
+++ incubator/chemistry/dotcmis/trunk/DotCMISUnitTest/DotCMISUnitTest.csproj Fri Feb 11 17:29:32 2011
@@ -43,6 +43,7 @@
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="CacheTest.cs" />
     <Compile Include="CRUDTest.cs" />
     <Compile Include="EnumeratorTest.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />

Modified: incubator/chemistry/dotcmis/trunk/DotCMISUnitTest/SmokeTest.cs
URL: http://svn.apache.org/viewvc/incubator/chemistry/dotcmis/trunk/DotCMISUnitTest/SmokeTest.cs?rev=1069900&r1=1069899&r2=1069900&view=diff
==============================================================================
--- incubator/chemistry/dotcmis/trunk/DotCMISUnitTest/SmokeTest.cs (original)
+++ incubator/chemistry/dotcmis/trunk/DotCMISUnitTest/SmokeTest.cs Fri Feb 11 17:29:32 2011
@@ -23,6 +23,11 @@ using DotCMIS.Client.Impl;
 using DotCMIS.Enums;
 using NUnit.Framework;
 using System;
+using DotCMIS.Data;
+using DotCMIS.Data.Impl;
+using System.Text;
+using System.IO;
+using DotCMIS.Exceptions;
 
 namespace DotCMISUnitTest
 {
@@ -30,7 +35,7 @@ namespace DotCMISUnitTest
     class SmokeTest : TestFramework
     {
         [Test]
-        public void TestSession()
+        public void SmokeTestSession()
         {
             Assert.NotNull(Session);
             Assert.NotNull(Session.Binding);
@@ -168,5 +173,80 @@ namespace DotCMISUnitTest
                 Console.WriteLine(hit.GetPropertyValueById(PropertyIds.Name) + " (" + hit.GetPropertyValueById(PropertyIds.ObjectId) + ")");
             }
         }
+
+        [Test]
+        public void SmokeTestCreateDocument()
+        {
+            IFolder rootFolder = Session.GetRootFolder();
+
+            IDictionary<string, object> properties = new Dictionary<string, object>();
+            properties[PropertyIds.Name] = "test-smoke.txt";
+            properties[PropertyIds.ObjectTypeId] = "cmis:document";
+
+            byte[] content = UTF8Encoding.UTF8.GetBytes("Hello World!");
+
+            ContentStream contentStream = new ContentStream();
+            contentStream.FileName = properties[PropertyIds.Name] as string;
+            contentStream.MimeType = "text/plain";
+            contentStream.Length = content.Length;
+            contentStream.Stream = new MemoryStream(content);
+
+            IDocument doc = rootFolder.CreateDocument(properties, contentStream, null);
+
+            // check doc
+            Assert.NotNull(doc);
+            Assert.NotNull(doc.Id);
+
+            // check versions
+            IList<IDocument> versions = doc.GetAllVersions();
+            Assert.NotNull(versions);
+            Assert.AreEqual(1, versions.Count);
+            Assert.AreEqual(doc.Id, versions[0].Id);
+
+            // check content
+            IContentStream retrievedContentStream = doc.GetContentStream();
+            Assert.NotNull(retrievedContentStream);
+            Assert.NotNull(retrievedContentStream.Stream);
+
+            doc.Delete(true);
+
+            try
+            {
+                doc.Refresh();
+                Assert.Fail("Document shouldn't exist anymore!");
+            }
+            catch (CmisObjectNotFoundException) { }
+        }
+
+        [Test]
+        public void SmokeTestCreateFolder()
+        {
+            IFolder rootFolder = Session.GetRootFolder();
+
+            IDictionary<string, object> properties = new Dictionary<string, object>();
+            properties[PropertyIds.Name] = "test-smoke";
+            properties[PropertyIds.ObjectTypeId] = "cmis:folder";
+
+            IFolder folder = rootFolder.CreateFolder(properties);
+            
+            // check folder
+            Assert.NotNull(folder);
+            Assert.NotNull(folder.Id);
+
+            // check children
+            foreach (ICmisObject cmisObject in folder.GetChildren())
+            {
+                Assert.Fail("Folder shouldn't have children!");
+            }
+
+            folder.Delete(true);
+
+            try
+            {
+                folder.Refresh();
+                Assert.Fail("Folder shouldn't exist anymore!");
+            }
+            catch (CmisObjectNotFoundException) { }
+        }
     }
 }