Added: 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=1061233&view=auto ============================================================================== --- incubator/chemistry/dotcmis/trunk/DotCMIS/binding/binding-caches.cs (added) +++ incubator/chemistry/dotcmis/trunk/DotCMIS/binding/binding-caches.cs Thu Jan 20 11:38:48 2011 @@ -0,0 +1,662 @@ +/* + * 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.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading; +using DotCMIS.Binding.Impl; +using DotCMIS.Data; + +namespace DotCMIS.Binding +{ + // --- base cache implementation --- + + internal interface ICache + { + void Initialize(string[] cacheLevelConfig); + + void Put(string[] keys, object value); + + object Get(string[] keys); + + void Remove(string[] keys); + + int Check(string[] keys); + + void Lock(); + + void Unlock(); + } + + internal interface ICacheLevel + { + void Initialize(IDictionary<string, string> cacheLevelConfig); + + object this[string key] { get; set; } + + void Remove(string key); + } + + internal class Cache : ICache + { + private IList<Type> cacheLevels; + private IList<IDictionary<string, string>> cacheLevelParameters; + private ICacheLevel root; + private string name; + private object cacheLock = new object(); + + public Cache() + : this("Cache") + { + } + + public Cache(string name) + { + this.name = name; + } + + public void Initialize(string[] cacheLevelConfig) + { + if (cacheLevels != null) + { + throw new ApplicationException("Cache already initialize!"); + } + + if ((cacheLevelConfig == null) || (cacheLevelConfig.Length == 0)) + { + throw new ArgumentException("Cache config must not be empty!"); + } + + Lock(); + try + { + cacheLevels = new List<Type>(); + cacheLevelParameters = new List<IDictionary<string, string>>(); + + // build level lists + foreach (string config in cacheLevelConfig) + { + int x = config.IndexOf(' '); + if (x == -1) + { + AddLevel(config, null); + } + else + { + AddLevel(config.Substring(0, x), config.Substring(x + 1)); + } + } + + root = CreateCacheLevel(0); + } + finally + { + Unlock(); + } + } + + public void Put(string[] keys, object value) + { + if (keys == null) { return; } + + if (keys.Length != cacheLevels.Count) + { + throw new ArgumentException("Wrong number of keys!"); + } + + Lock(); + try + { + ICacheLevel cacheLevel = root; + + // follow the branch + for (int i = 0; i < keys.Length - 1; i++) + { + object level = cacheLevel[keys[i]]; + + // does the branch exist? + if (level == null) + { + level = CreateCacheLevel(i + 1); + cacheLevel[keys[i]] = level; + } + + // next level + cacheLevel = (ICacheLevel)level; + } + + cacheLevel[keys[keys.Length - 1]] = value; + + Trace.WriteLine(name + ": put [" + GetFormattedKeys(keys) + "] = " + value); + } + finally + { + Unlock(); + } + } + + public object Get(string[] keys) + { + if (keys == null) { return null; } + + if (keys.Length != cacheLevels.Count) + { + throw new ArgumentException("Wrong number of keys!"); + } + + object result = null; + + Lock(); + try + { + ICacheLevel cacheLevel = root; + + // follow the branch + for (int i = 0; i < keys.Length - 1; i++) + { + object level = cacheLevel[keys[i]]; + + // does the branch exist? + if (level == null) { return null; } + + // next level + cacheLevel = (ICacheLevel)level; + } + + // get the value + result = cacheLevel[keys[keys.Length - 1]]; + } + finally + { + Unlock(); + } + + return result; + } + + public void Remove(string[] keys) + { + if (keys == null) { return; } + + Lock(); + try + { + ICacheLevel cacheLevel = root; + + // follow the branch + for (int i = 0; i < keys.Length - 1; i++) + { + object level = cacheLevel[keys[i]]; + + // does the branch exist? + if (level == null) { return; } + + // next level + cacheLevel = (ICacheLevel)level; + } + + cacheLevel.Remove(keys[keys.Length - 1]); + + Trace.WriteLine(name + ": removed [" + GetFormattedKeys(keys) + "]"); + } + finally + { + Unlock(); + } + } + + public int Check(string[] keys) + { + if (keys == null) { return -1; } + + Lock(); + try + { + ICacheLevel cacheLevel = root; + + // follow the branch + for (int i = 0; i < keys.Length - 1; i++) + { + object level = cacheLevel[keys[i]]; + + // does the branch exist? + if (level == null) { return i; } + + // next level + cacheLevel = (ICacheLevel)level; + } + + return keys.Length; + } + finally + { + Unlock(); + } + } + + public void Lock() + { + Monitor.Enter(cacheLock); + } + + public void Unlock() + { + Monitor.Exit(cacheLock); + } + + // --- internal --- + + private void AddLevel(string typeName, string parameters) + { + Type levelType; + + try + { + levelType = Type.GetType(typeName); + } + catch (Exception e) + { + throw new ArgumentException("Class '" + typeName + "' not found!", e); + } + + if (!typeof(ICacheLevel).IsAssignableFrom(levelType)) + { + throw new ArgumentException("Class '" + typeName + "' does not implement the ICacheLevel interface!"); + } + + cacheLevels.Add(levelType); + + // process parameters + if (parameters == null) + { + cacheLevelParameters.Add(null); + } + else + { + Dictionary<string, string> parameterDict = new Dictionary<string, string>(); + cacheLevelParameters.Add(parameterDict); + + foreach (string pair in parameters.Split(',')) + { + string[] keyValue = pair.Split('='); + if (keyValue.Length == 1) + { + parameterDict[keyValue[0]] = ""; + } + else + { + parameterDict[keyValue[0]] = keyValue[1]; + } + } + } + } + + private ICacheLevel CreateCacheLevel(int level) + { + if ((level < 0) || (level >= cacheLevels.Count)) + { + throw new ArgumentException("Cache level doesn't fit the configuration!"); + } + + // get the class and create an instance + Type levelType = cacheLevels[level]; + ICacheLevel cacheLevel = null; + try + { + cacheLevel = (ICacheLevel)Activator.CreateInstance(levelType); + } + catch (Exception e) + { + throw new ArgumentException("Cache level problem?!", e); + } + + // initialize it + cacheLevel.Initialize(cacheLevelParameters[level]); + + return cacheLevel; + } + + private string GetFormattedKeys(string[] keys) + { + StringBuilder sb = new StringBuilder(); + foreach (string k in keys) + { + if (sb.Length > 0) + { + sb.Append(", "); + } + sb.Append(k); + } + + return sb.ToString(); + } + } + + internal abstract class AbstractDictionaryCacheLevel : ICacheLevel + { + private static string NullKey = ""; + + private IDictionary<string, object> dict; + private bool fallbackEnabled = false; + private string fallbackKey = null; + private bool singleValueEnabled = false; + + public abstract void Initialize(IDictionary<string, string> cacheLevelConfig); + + public virtual object this[string key] + { + get + { + object value = null; + if (dict.TryGetValue(key == null ? NullKey : key, out value)) + { + return value; + } + + if (fallbackEnabled && dict.TryGetValue(fallbackKey, out value)) + { + return value; + } + + if (singleValueEnabled && dict.Count == 1) + { + value = dict.Values.First(); + } + + return value; + } + set + { + if (value != null) + { + dict[key == null ? NullKey : key] = value; + } + } + } + + public virtual void Remove(string key) + { + dict.Remove(key); + } + + public void SetDictionary(IDictionary<string, object> dict) + { + this.dict = dict; + } + + protected void EnableKeyFallback(string key) + { + fallbackKey = key; + fallbackEnabled = true; + } + + protected void DisableKeyFallback() + { + fallbackEnabled = false; + } + + protected void EnableSingeValueFallback() + { + singleValueEnabled = true; + } + + protected void DisableSingeValueFallback() + { + singleValueEnabled = false; + } + + protected int GetIntParameter(IDictionary<string, string> parameters, string name, int defValue) + { + if (parameters == null) + { + return defValue; + } + + string value; + if (!parameters.TryGetValue(name, out value)) + { + return defValue; + } + + try + { + return Int32.Parse(value); + } + catch (Exception) + { + return defValue; + } + } + + protected bool GetBooleanParameter(IDictionary<string, string> parameters, string name, bool defValue) + { + if (parameters == null) + { + return defValue; + } + + string value; + if (!parameters.TryGetValue(name, out value)) + { + return defValue; + } + + try + { + return Boolean.Parse(value); + } + catch (Exception) + { + return defValue; + } + } + } + + internal class DictionaryCacheLevel : AbstractDictionaryCacheLevel + { + public const string Capacity = "capacity"; + public const string SingleValue = "singleValue"; + + 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)); + if (singleValue) + { + EnableSingeValueFallback(); + } + } + } + + internal class LruCacheLevel : AbstractDictionaryCacheLevel + { + public const string MaxEntries = "maxEntries"; + + private LinkedList<string> keyList; + private int maxEntries; + + public override void Initialize(IDictionary<string, string> parameters) + { + maxEntries = GetIntParameter(parameters, MaxEntries, 100); + keyList = new LinkedList<string>(); + SetDictionary(new Dictionary<string, object>(maxEntries + 1)); + } + + public override object this[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; + } + + 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); + } + + base[key] = value; + } + } + + public override void Remove(string key) + { + keyList.Remove(key); + base.Remove(key); + } + } + + // ---- Caches ---- + + /// <summary> + /// Repository Info cache. + /// </summary> + internal class RepositoryInfoCache + { + private const int CacheSizeRepositories = 10; + + private ICache cache; + + public RepositoryInfoCache(BindingSession session) + { + int repCount = session.GetValue(SessionParameter.CacheSizeRepositories, CacheSizeRepositories); + if (repCount < 1) + { + repCount = CacheSizeRepositories; + } + + cache = new Cache("Repository Info Cache"); + cache.Initialize(new string[] { + typeof(DictionaryCacheLevel).FullName + " " + DictionaryCacheLevel.Capacity + "=" + repCount }); + } + + public void Put(IRepositoryInfo repositoryInfo) + { + if ((repositoryInfo == null) || (repositoryInfo.Id == null)) + { + return; + } + + cache.Put(new string[] { repositoryInfo.Id }, repositoryInfo); + } + + public IRepositoryInfo Get(string repositoryId) + { + return (IRepositoryInfo)cache.Get(new string[] { repositoryId }); + } + + public void Remove(string repositoryId) + { + cache.Remove(new string[] { repositoryId }); + } + } + + /// <summary> + /// Type Definition cache. + /// </summary> + internal class TypeDefinitionCache + { + private const int CacheSizeRepositories = 10; + private const int CacheSizeTypes = 100; + + private ICache cache; + + public TypeDefinitionCache(BindingSession session) + { + int repCount = session.GetValue(SessionParameter.CacheSizeRepositories, CacheSizeRepositories); + if (repCount < 1) + { + repCount = CacheSizeRepositories; + } + + int typeCount = session.GetValue(SessionParameter.CacheSizeTypes, CacheSizeTypes); + if (typeCount < 1) + { + typeCount = CacheSizeTypes; + } + + cache = new Cache("Type Definition Cache"); + cache.Initialize(new string[] { + typeof(DictionaryCacheLevel).FullName + " " + DictionaryCacheLevel.Capacity + "=" + repCount, // repository + typeof(LruCacheLevel).FullName + " " + LruCacheLevel.MaxEntries + "=" + typeCount // type + }); + } + + public void Put(string repositoryId, ITypeDefinition typeDefinition) + { + if ((typeDefinition == null) || (typeDefinition.Id == null)) + { + return; + } + + cache.Put(new string[] { repositoryId, typeDefinition.Id }, typeDefinition); + } + + public ITypeDefinition Get(string repositoryId, string typeId) + { + return (ITypeDefinition)cache.Get(new string[] { repositoryId, typeId }); + } + + public void Remove(string repositoryId, string typeId) + { + cache.Remove(new string[] { repositoryId, typeId }); + } + + public void Remove(string repositoryId) + { + cache.Remove(new string[] { repositoryId }); + } + } +}
Added: incubator/chemistry/dotcmis/trunk/DotCMIS/binding/binding-impl.cs URL: http://svn.apache.org/viewvc/incubator/chemistry/dotcmis/trunk/DotCMIS/binding/binding-impl.cs?rev=1061233&view=auto ============================================================================== --- incubator/chemistry/dotcmis/trunk/DotCMIS/binding/binding-impl.cs (added) +++ incubator/chemistry/dotcmis/trunk/DotCMIS/binding/binding-impl.cs Thu Jan 20 11:38:48 2011 @@ -0,0 +1,604 @@ +/* + * 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; +using DotCMIS.Binding; +using DotCMIS.Binding.Services; +using System.Threading; +using DotCMIS.Exceptions; +using DotCMIS.Data; +using DotCMIS.Data.Extensions; + +namespace DotCMIS.Binding.Impl +{ + /// <summary> + /// Binding layer implementation. + /// </summary> + internal class CmisBinding : ICmisBinding + { + private BindingSession session; + private BindingsObjectFactory objectFactory; + private BindingRepositoryService repositoryServiceWrapper; + + public CmisBinding(IDictionary<string, string> sessionParameters) + { + if (sessionParameters == null) + { + throw new ArgumentNullException("sessionParameters"); + } + + if (!sessionParameters.ContainsKey(SessionParameter.BindingSpiClass)) + { + throw new ArgumentException("Session parameters do not contain a SPI class name!"); + } + + // initialize session + session = new BindingSession(); + foreach (string key in sessionParameters.Keys) + { + session.PutValue(key, sessionParameters[key]); + } + + // set up object factory + objectFactory = new BindingsObjectFactory(); + + // set up authentication provider + string authenticationProviderClass; + if (sessionParameters.TryGetValue(SessionParameter.AuthenticationProviderClass, out authenticationProviderClass)) + { + try + { + Type authProvType = Type.GetType(authenticationProviderClass); + AbstractAuthenticationProvider authenticationProvider = (AbstractAuthenticationProvider)Activator.CreateInstance(authProvType); + authenticationProvider.Session = session; + session.PutValue(BindingSession.AuthenticationProvider, authenticationProvider); + } + catch (Exception e) + { + throw new CmisRuntimeException("Could not load authentictaion provider: " + e.Message, e); + } + } + + // initialize the SPI + GetSpi(); + + // set up caches + ClearAllCaches(); + + // set up repository service + repositoryServiceWrapper = new BindingRepositoryService(session); + } + + public IRepositoryService GetRepositoryService() + { + CheckSession(); + return repositoryServiceWrapper; + } + + public INavigationService GetNavigationService() + { + CheckSession(); + ICmisSpi spi = GetSpi(); + return spi.GetNavigationService(); + } + + public IObjectService GetObjectService() + { + CheckSession(); + ICmisSpi spi = GetSpi(); + return spi.GetObjectService(); + } + + public IVersioningService GetVersioningService() + { + CheckSession(); + ICmisSpi spi = GetSpi(); + return spi.GetVersioningService(); + } + + public IRelationshipService GetRelationshipService() + { + CheckSession(); + ICmisSpi spi = GetSpi(); + return spi.GetRelationshipService(); + } + + public IDiscoveryService GetDiscoveryService() + { + CheckSession(); + ICmisSpi spi = GetSpi(); + return spi.GetDiscoveryService(); + } + + public IMultiFilingService GetMultiFilingService() + { + CheckSession(); + ICmisSpi spi = GetSpi(); + return spi.GetMultiFilingService(); + } + + public IAclService GetAclService() + { + CheckSession(); + ICmisSpi spi = GetSpi(); + return spi.GetAclService(); + } + + public IPolicyService GetPolicyService() + { + CheckSession(); + ICmisSpi spi = GetSpi(); + return spi.GetPolicyService(); + } + + public IBindingsObjectFactory GetObjectFactory() + { + return objectFactory; + } + + public void ClearAllCaches() + { + CheckSession(); + + session.Lock(); + try + { + session.PutValue(BindingSession.RepositoryInfoCache, new RepositoryInfoCache(session)); + session.PutValue(BindingSession.TypeDefinitionCache, new TypeDefinitionCache(session)); + + ICmisSpi spi = GetSpi(); + spi.ClearAllCaches(); + } + finally + { + session.Unlock(); + } + } + + public void ClearRepositoryCache(string repositoryId) + { + CheckSession(); + + if (repositoryId == null) + { + return; + } + + session.Lock(); + try + { + RepositoryInfoCache repInfoCache = session.GetRepositoryInfoCache(); + repInfoCache.Remove(repositoryId); + + TypeDefinitionCache typeDefCache = session.GetTypeDefinitionCache(); + typeDefCache.Remove(repositoryId); + + ICmisSpi spi = GetSpi(); + spi.ClearRepositoryCache(repositoryId); + } + finally + { + session.Unlock(); + } + } + + public void Close() + { + CheckSession(); + + session.Lock(); + try + { + GetSpi().Close(); + } + finally + { + session.Unlock(); + session = null; + } + } + + private void CheckSession() + { + if (session == null) + { + throw new ApplicationException("Already closed."); + } + } + + private ICmisSpi GetSpi() + { + return session.GetSpi(); + } + } + + /// <summary> + /// Session object implementation of the binding layer. + /// </summary> + internal class BindingSession : IBindingSession + { + public const string RepositoryInfoCache = "org.apache.chemistry.dotcmis.bindings.repositoryInfoCache"; + public const string TypeDefinitionCache = "org.apache.chemistry.dotcmis.bindings.typeDefintionCache"; + public const string AuthenticationProvider = "org.apache.chemistry.dotcmis.bindings.authenticationProvider"; + public const string SpiObject = "org.apache.chemistry.dotcmis.bindings.spi.object"; + + private Dictionary<string, object> data; + private object sessionLock = new object(); + + public BindingSession() + { + data = new Dictionary<string, object>(); + } + + public object GetValue(string key) + { + return GetValue(key, null); + } + + public object GetValue(string key, object defValue) + { + object result = null; + + Lock(); + try + { + if (data.TryGetValue(key, out result)) + { + return result; + } + else + { + return defValue; + } + } + finally + { + Unlock(); + } + } + + public int GetValue(string key, int defValue) + { + object value = GetValue(key); + + try + { + if (value is string) + { + return Int32.Parse((string)value); + } + else if (value is int) + { + return (int)value; + } + } + catch (Exception) + { + } + + return defValue; + } + + public void PutValue(string key, object value) + { + Lock(); + try + { + data[key] = value; + } + finally + { + Unlock(); + } + } + + public void RemoveValue(string key) + { + Lock(); + try + { + data.Remove(key); + } + finally + { + Unlock(); + } + } + + public ICmisSpi GetSpi() + { + Lock(); + try + { + ICmisSpi spi = GetValue(SpiObject) as ICmisSpi; + if (spi != null) + { + return spi; + } + + + // ok, we have to create it... + try + { + object spiObject; + if (data.TryGetValue(SessionParameter.BindingSpiClass, out spiObject)) + { + string spiClassName = spiObject as string; + if (spiClassName != null) + { + Type spiClass = Type.GetType(spiClassName); + spi = (ICmisSpi)Activator.CreateInstance(spiClass); + spi.initialize(this); + } + else + { + throw new CmisRuntimeException("SPI class is not set!"); + } + } + else + { + throw new CmisRuntimeException("SPI class is not set!"); + } + } + catch (CmisBaseException ce) + { + throw ce; + } + catch (Exception e) + { + throw new CmisRuntimeException("SPI cannot be initialized: " + e.Message, e); + } + + // we have a SPI object -> put it into the session + data[SpiObject] = spi; + + return spi; + } + finally + { + Unlock(); + } + } + + public RepositoryInfoCache GetRepositoryInfoCache() + { + return GetValue(RepositoryInfoCache) as RepositoryInfoCache; + } + + public TypeDefinitionCache GetTypeDefinitionCache() + { + return GetValue(TypeDefinitionCache) as TypeDefinitionCache; + } + + public AbstractAuthenticationProvider GetAuthenticationProvider() + { + return GetValue(AuthenticationProvider) as AbstractAuthenticationProvider; + } + + public void Lock() + { + Monitor.Enter(sessionLock); + } + + public void Unlock() + { + Monitor.Exit(sessionLock); + } + + public override string ToString() + { + return data.ToString(); + } + } + + /// <summary> + /// Repository service proxy that caches repository infos and type defintions. + /// </summary> + internal class BindingRepositoryService : IRepositoryService + { + private BindingSession session; + + public BindingRepositoryService(BindingSession session) + { + this.session = session; + } + + public IList<IRepositoryInfo> GetRepositoryInfos(IExtensionsData extension) + { + IList<IRepositoryInfo> result = null; + bool hasExtension = (extension != null) && (extension.Extensions != null) && (extension.Extensions.Count > 0); + + // get the SPI and fetch the repository infos + ICmisSpi spi = session.GetSpi(); + result = spi.GetRepositoryService().GetRepositoryInfos(extension); + + // put it into the cache + if (!hasExtension && (result != null)) + { + RepositoryInfoCache cache = session.GetRepositoryInfoCache(); + foreach (RepositoryInfo rid in result) + { + cache.Put(rid); + } + } + + return result; + } + + public IRepositoryInfo GetRepositoryInfo(string repositoryId, IExtensionsData extension) + { + IRepositoryInfo result = null; + bool hasExtension = (extension != null) && (extension.Extensions != null) && (extension.Extensions.Count > 0); + + RepositoryInfoCache cache = session.GetRepositoryInfoCache(); + + // if extension is not set, check the cache first + if (!hasExtension) + { + result = cache.Get(repositoryId); + if (result != null) + { + return result; + } + } + + // it was not in the cache -> get the SPI and fetch the repository info + ICmisSpi spi = session.GetSpi(); + result = spi.GetRepositoryService().GetRepositoryInfo(repositoryId, extension); + + // put it into the cache + if (!hasExtension) + { + cache.Put(result); + } + + return result; + } + + public ITypeDefinitionList GetTypeChildren(string repositoryId, string typeId, bool? includePropertyDefinitions, + long? maxItems, long? skipCount, IExtensionsData extension) + { + ITypeDefinitionList result = null; + bool hasExtension = (extension != null) && (extension.Extensions != null) && (extension.Extensions.Count > 0); + + // get the SPI and fetch the type definitions + ICmisSpi spi = session.GetSpi(); + result = spi.GetRepositoryService().GetTypeChildren(repositoryId, typeId, includePropertyDefinitions, maxItems, + skipCount, extension); + + // put it into the cache + if (!hasExtension && (includePropertyDefinitions ?? false) && (result != null)) + { + TypeDefinitionCache cache = session.GetTypeDefinitionCache(); + foreach (ITypeDefinition tdd in result.List) + { + cache.Put(repositoryId, tdd); + } + } + + return result; + } + + public IList<ITypeDefinitionContainer> GetTypeDescendants(string repositoryId, string typeId, long? depth, + bool? includePropertyDefinitions, IExtensionsData extension) + { + IList<ITypeDefinitionContainer> result = null; + bool hasExtension = (extension != null) && (extension.Extensions != null) && (extension.Extensions.Count > 0); + + // get the SPI and fetch the type definitions + ICmisSpi spi = session.GetSpi(); + result = spi.GetRepositoryService().GetTypeDescendants(repositoryId, typeId, depth, includePropertyDefinitions, + extension); + + // put it into the cache + if (!hasExtension && (includePropertyDefinitions ?? false) && (result != null)) + { + TypeDefinitionCache cache = session.GetTypeDefinitionCache(); + AddToTypeCache(cache, repositoryId, result); + } + + return result; + } + + private void AddToTypeCache(TypeDefinitionCache cache, string repositoryId, IList<ITypeDefinitionContainer> containers) + { + if (containers == null) + { + return; + } + + foreach (ITypeDefinitionContainer container in containers) + { + cache.Put(repositoryId, container.TypeDefinition); + AddToTypeCache(cache, repositoryId, container.Children); + } + } + + public ITypeDefinition GetTypeDefinition(string repositoryId, string typeId, IExtensionsData extension) + { + ITypeDefinition result = null; + bool hasExtension = (extension != null) && (extension.Extensions != null) && (extension.Extensions.Count > 0); + + TypeDefinitionCache cache = session.GetTypeDefinitionCache(); + + // if extension is not set, check the cache first + if (!hasExtension) + { + result = cache.Get(repositoryId, typeId); + if (result != null) + { + return result; + } + } + + // it was not in the cache -> get the SPI and fetch the type definition + ICmisSpi spi = session.GetSpi(); + result = spi.GetRepositoryService().GetTypeDefinition(repositoryId, typeId, extension); + + // put it into the cache + if (!hasExtension && (result != null)) + { + cache.Put(repositoryId, result); + } + + return result; + } + } + + /// <summary> + /// Object factory implementation. + /// </summary> + internal class BindingsObjectFactory : IBindingsObjectFactory + { + } + + /// <summary> + /// SPI interface. + /// </summary> + internal interface ICmisSpi + { + void initialize(BindingSession session); + + IRepositoryService GetRepositoryService(); + + INavigationService GetNavigationService(); + + IObjectService GetObjectService(); + + IVersioningService GetVersioningService(); + + IRelationshipService GetRelationshipService(); + + IDiscoveryService GetDiscoveryService(); + + IMultiFilingService GetMultiFilingService(); + + IAclService GetAclService(); + + IPolicyService GetPolicyService(); + + void ClearAllCaches(); + + void ClearRepositoryCache(string repositoryId); + + void Close(); + } +} Added: incubator/chemistry/dotcmis/trunk/DotCMIS/binding/binding-intf.cs URL: http://svn.apache.org/viewvc/incubator/chemistry/dotcmis/trunk/DotCMIS/binding/binding-intf.cs?rev=1061233&view=auto ============================================================================== --- incubator/chemistry/dotcmis/trunk/DotCMIS/binding/binding-intf.cs (added) +++ incubator/chemistry/dotcmis/trunk/DotCMIS/binding/binding-intf.cs Thu Jan 20 11:38:48 2011 @@ -0,0 +1,296 @@ +/* + * 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; +using DotCMIS.Binding.Services; +using DotCMIS.Binding.Impl; +using DotCMIS.CMISWebServicesReference; +using System.Net; + +namespace DotCMIS.Binding +{ + public interface ICmisBinding + { + IRepositoryService GetRepositoryService(); + + INavigationService GetNavigationService(); + + IObjectService GetObjectService(); + + IVersioningService GetVersioningService(); + + IRelationshipService GetRelationshipService(); + + IDiscoveryService GetDiscoveryService(); + + IMultiFilingService GetMultiFilingService(); + + IAclService GetAclService(); + + IPolicyService GetPolicyService(); + + IBindingsObjectFactory GetObjectFactory(); + + void ClearAllCaches(); + + void ClearRepositoryCache(string repositoryId); + + void Close(); + } + + public interface IBindingSession + { + object GetValue(string key); + + object GetValue(string key, object defValue); + + int GetValue(string key, int defValue); + } + + public abstract class AbstractAuthenticationProvider + { + public IBindingSession Session { get; set; } + + public abstract void Authenticate(object connection); + + public string GetUser() + { + return Session.GetValue(SessionParameter.User) as string; + } + + public string GetPassword() + { + return Session.GetValue(SessionParameter.Password) as string; + } + } + + public class StandardAuthenticationProvider : AbstractAuthenticationProvider + { + public override void Authenticate(object connection) + { + string user = GetUser(); + string password = GetPassword(); + if (user == null || password == null) + { + return; + } + + if (connection is RepositoryServicePortClient) + { + ((RepositoryServicePortClient)connection).ClientCredentials.UserName.UserName = user; + ((RepositoryServicePortClient)connection).ClientCredentials.UserName.Password = password; + } + else if (connection is NavigationServicePortClient) + { + ((NavigationServicePortClient)connection).ClientCredentials.UserName.UserName = user; + ((NavigationServicePortClient)connection).ClientCredentials.UserName.Password = password; + } + else if (connection is ObjectServicePortClient) + { + ((ObjectServicePortClient)connection).ClientCredentials.UserName.UserName = user; + ((ObjectServicePortClient)connection).ClientCredentials.UserName.Password = password; + } + else if (connection is VersioningServicePortClient) + { + ((VersioningServicePortClient)connection).ClientCredentials.UserName.UserName = user; + ((VersioningServicePortClient)connection).ClientCredentials.UserName.Password = password; + } + else if (connection is DiscoveryServicePortClient) + { + ((DiscoveryServicePortClient)connection).ClientCredentials.UserName.UserName = user; + ((DiscoveryServicePortClient)connection).ClientCredentials.UserName.Password = password; + } + else if (connection is RelationshipServicePortClient) + { + ((RelationshipServicePortClient)connection).ClientCredentials.UserName.UserName = user; + ((RelationshipServicePortClient)connection).ClientCredentials.UserName.Password = password; + } + else if (connection is MultiFilingServicePortClient) + { + ((MultiFilingServicePortClient)connection).ClientCredentials.UserName.UserName = user; + ((MultiFilingServicePortClient)connection).ClientCredentials.UserName.Password = password; + } + else if (connection is PolicyServicePortClient) + { + ((PolicyServicePortClient)connection).ClientCredentials.UserName.UserName = user; + ((PolicyServicePortClient)connection).ClientCredentials.UserName.Password = password; + } + else if (connection is ACLServicePortClient) + { + ((ACLServicePortClient)connection).ClientCredentials.UserName.UserName = user; + ((ACLServicePortClient)connection).ClientCredentials.UserName.Password = password; + } + else if (connection is WebRequest) + { + ((WebRequest)connection).Credentials = new NetworkCredential(user, password); + } + } + } + + + public interface IBindingsObjectFactory + { + } + + public class CmisBindingFactory + { + // Default CMIS AtomPub binding SPI implementation + public const string BindingSpiAtomPub = "DotCMIS.Binding.AtomPub.CmisAtomPubSpi"; + // Default CMIS Web Services binding SPI implementation + public const string BindingSpiWebServices = "DotCMIS.Binding.WebServices.CmisWebServicesSpi"; + + public const string StandardAuthenticationProviderClass = "DotCMIS.Binding.StandardAuthenticationProvider"; + + private IDictionary<string, string> defaults; + + private CmisBindingFactory() + { + defaults = CreateNewDefaultParameters(); + } + + public static CmisBindingFactory NewInstance() + { + return new CmisBindingFactory(); + } + + public IDictionary<string, string> GetDefaultSessionParameters() + { + return defaults; + } + + public void SetDefaultSessionParameters(IDictionary<string, string> sessionParameters) + { + if (sessionParameters == null) + { + defaults = CreateNewDefaultParameters(); + } + else + { + defaults = sessionParameters; + } + } + + public ICmisBinding CreateCmisBinding(IDictionary<string, string> sessionParameters) + { + CheckSessionParameters(sessionParameters, true); + AddDefaultParameters(sessionParameters); + + return new CmisBinding(sessionParameters); + } + + public ICmisBinding CreateCmisAtomPubBinding(IDictionary<string, string> sessionParameters) + { + CheckSessionParameters(sessionParameters, false); + sessionParameters[SessionParameter.BindingSpiClass] = BindingSpiAtomPub; + if (!sessionParameters.ContainsKey(SessionParameter.AuthenticationProviderClass)) + { + sessionParameters[SessionParameter.AuthenticationProviderClass] = StandardAuthenticationProviderClass; + } + + AddDefaultParameters(sessionParameters); + + Check(sessionParameters, SessionParameter.AtomPubUrl); + + return new CmisBinding(sessionParameters); + } + + public ICmisBinding CreateCmisWebServicesBinding(IDictionary<string, string> sessionParameters) + { + CheckSessionParameters(sessionParameters, false); + sessionParameters[SessionParameter.BindingSpiClass] = BindingSpiWebServices; + if (!sessionParameters.ContainsKey(SessionParameter.AuthenticationProviderClass)) + { + sessionParameters[SessionParameter.AuthenticationProviderClass] = StandardAuthenticationProviderClass; + } + + AddDefaultParameters(sessionParameters); + + Check(sessionParameters, SessionParameter.WebServicesAclService); + Check(sessionParameters, SessionParameter.WebServicesDiscoveryService); + Check(sessionParameters, SessionParameter.WebServicesMultifilingService); + Check(sessionParameters, SessionParameter.WebServicesNavigationService); + Check(sessionParameters, SessionParameter.WebServicesObjectService); + Check(sessionParameters, SessionParameter.WebServicesPolicyService); + Check(sessionParameters, SessionParameter.WebServicesRelationshipService); + Check(sessionParameters, SessionParameter.WebServicesRepositoryService); + Check(sessionParameters, SessionParameter.WebServicesVersioningService); + + return new CmisBinding(sessionParameters); + } + + // ---- internals ---- + + private void CheckSessionParameters(IDictionary<string, string> sessionParameters, bool mustContainSpi) + { + // don't accept null + if (sessionParameters == null) + { + throw new ArgumentNullException("sessionParameters"); + } + + // check binding entry + if (mustContainSpi) + { + string spiClass; + + if (sessionParameters.TryGetValue(SessionParameter.BindingSpiClass, out spiClass)) + { + throw new ArgumentException("SPI class entry (" + SessionParameter.BindingSpiClass + ") is missing!"); + } + + if ((spiClass == null) || (spiClass.Trim().Length == 0)) + { + throw new ArgumentException("SPI class entry (" + SessionParameter.BindingSpiClass + ") is invalid!"); + } + } + } + + private void Check(IDictionary<string, string> sessionParameters, String parameter) + { + if (!sessionParameters.ContainsKey(parameter)) + { + throw new ArgumentException("Parameter '" + parameter + "' is missing!"); + } + } + + private void AddDefaultParameters(IDictionary<string, string> sessionParameters) + { + foreach (string key in defaults.Keys) + { + if (!sessionParameters.ContainsKey(key)) + { + sessionParameters[key] = defaults[key]; + } + } + } + + private IDictionary<string, string> CreateNewDefaultParameters() + { + IDictionary<string, string> result = new Dictionary<string, string>(); + + result[SessionParameter.CacheSizeRepositories] = "10"; + result[SessionParameter.CacheSizeTypes] = "100"; + result[SessionParameter.CacheSizeLinks] = "400"; + + return result; + } + } +}
