/* * 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.Text; using System.Web; using DotCMIS.Binding.Impl; namespace DotCMIS.Binding.AtomPub { internal class LinkCache { private static HashSet KnownLinks = new HashSet(); static LinkCache() { KnownLinks.Add(AtomPubConstants.RelACL); KnownLinks.Add(AtomPubConstants.RelDown); KnownLinks.Add(AtomPubConstants.RelUp); KnownLinks.Add(AtomPubConstants.RelFolderTree); KnownLinks.Add(AtomPubConstants.RelRelationships); KnownLinks.Add(AtomPubConstants.RelSelf); KnownLinks.Add(AtomPubConstants.RelAllowableActions); KnownLinks.Add(AtomPubConstants.RelEditMedia); KnownLinks.Add(AtomPubConstants.RelPolicies); KnownLinks.Add(AtomPubConstants.RelVersionHistory); KnownLinks.Add(AtomPubConstants.RelWorkingCopy); KnownLinks.Add(AtomPubConstants.LinkRelContent); } private const int CacheSizeRepositories = 10; private const int CacheSizeTypes = 100; private const int CacheSizeLinks = 400; private IBindingCache linkCache; private IBindingCache typeLinkCache; private IBindingCache collectionLinkCache; private IBindingCache templateCache; private IBindingCache repositoryLinkCache; public LinkCache(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; } int objCount = session.GetValue(SessionParameter.CacheSizeLinks, CacheSizeLinks); if (objCount < 1) { objCount = CacheSizeLinks; } string dictionaryLevelName = typeof(DictionaryCacheLevel).FullName; string lruLevelName = typeof(LruCacheLevel).FullName; string contentTypeLevelName = typeof(DictionaryCacheLevel).FullName; linkCache = new Cache("Link Cache"); linkCache.Initialize(new string[] { dictionaryLevelName + " " + DictionaryCacheLevel.Capacity + "=" + repCount.ToString(), // repository lruLevelName + " " + LruCacheLevel.MaxEntries + "=" + objCount.ToString(), // id dictionaryLevelName + " " + DictionaryCacheLevel.Capacity + "=16", // rel contentTypeLevelName + " " + DictionaryCacheLevel.Capacity + "=3," + DictionaryCacheLevel.SingleValue + "=true" // type }); typeLinkCache = new Cache("Type Link Cache"); typeLinkCache.Initialize(new string[] { dictionaryLevelName + " " + DictionaryCacheLevel.Capacity + "=" + repCount.ToString(), // repository lruLevelName + " " + LruCacheLevel.MaxEntries + "=" + typeCount.ToString(), // id dictionaryLevelName + " " + DictionaryCacheLevel.Capacity + "=16", // rel contentTypeLevelName + " " + DictionaryCacheLevel.Capacity + "=3," + DictionaryCacheLevel.SingleValue + "=true"// type }); collectionLinkCache = new Cache("Collection Link Cache"); collectionLinkCache.Initialize(new string[] { dictionaryLevelName + " " + DictionaryCacheLevel.Capacity + "=" + repCount.ToString(), // repository dictionaryLevelName + " " + DictionaryCacheLevel.Capacity + "=8" // collection }); templateCache = new Cache("URI Template Cache"); templateCache.Initialize(new string[] { dictionaryLevelName + " " + DictionaryCacheLevel.Capacity + "=" + repCount.ToString(), // repository dictionaryLevelName + " " + DictionaryCacheLevel.Capacity + "=6" // type }); repositoryLinkCache = new Cache("Repository Link Cache"); repositoryLinkCache.Initialize(new string[] { dictionaryLevelName + " " + DictionaryCacheLevel.Capacity + "=" + repCount.ToString(), // repository dictionaryLevelName + " " + DictionaryCacheLevel.Capacity + "=6" // rel }); } // ---- links --- public void AddLink(string repositoryId, string id, string rel, string type, string link) { if (KnownLinks.Contains(rel)) { linkCache.Put(new string[] { repositoryId, id, rel, type }, link); } } public void RemoveLinks(string repositoryId, string id) { linkCache.Remove(new string[] { repositoryId, id }); } public string GetLink(string repositoryId, string id, string rel, string type) { return (string)linkCache.Get(new string[] { repositoryId, id, rel, type }); } public string GetLink(string repositoryId, string id, string rel) { return GetLink(repositoryId, id, rel, null); } public int CheckLink(string repositoryId, string id, string rel, string type) { return linkCache.Check(new string[] { repositoryId, id, rel, type }); } public void LockLinks() { linkCache.Lock(); } public void UnlockLinks() { linkCache.Unlock(); } // ---- type links --- public void AddTypeLink(string repositoryId, string id, string rel, string type, string link) { if (KnownLinks.Contains(rel)) { typeLinkCache.Put(new string[] { repositoryId, id, rel, type }, link); } } public void RemoveTypeLinks(string repositoryId, string id) { typeLinkCache.Remove(new string[] { repositoryId, id }); } public string GetTypeLink(string repositoryId, string id, string rel, string type) { return (string)typeLinkCache.Get(new string[] { repositoryId, id, rel, type }); } public void LockTypeLinks() { typeLinkCache.Lock(); } public void UnlockTypeLinks() { typeLinkCache.Unlock(); } // ---- collections ---- public void AddCollection(string repositoryId, string collection, string link) { collectionLinkCache.Put(new string[] { repositoryId, collection }, link); } public string GetCollection(string repositoryId, string collection) { return (string)collectionLinkCache.Get(new string[] { repositoryId, collection }); } // ---- templates ---- public void AddTemplate(string repositoryId, string type, string link) { templateCache.Put(new string[] { repositoryId, type }, link); } public string GetTemplateLink(string repositoryId, string type, IDictionary parameters) { string template = (string)templateCache.Get(new string[] { repositoryId, type }); if (template == null) { return null; } StringBuilder result = new StringBuilder(); StringBuilder param = new StringBuilder(); bool paramMode = false; for (int i = 0; i < template.Length; i++) { char c = template[i]; if (paramMode) { if (c == '}') { paramMode = false; object paramValue; if (parameters.TryGetValue(param.ToString(), out paramValue)) { result.Append(paramValue == null ? "" : Uri.EscapeDataString(UrlBuilder.NormalizeParameter(paramValue))); } param = new StringBuilder(); } else { param.Append(c); } } else { if (c == '{') { paramMode = true; } else { result.Append(c); } } } return result.ToString(); } // ---- repository links ---- public void AddRepositoryLink(string repositoryId, string rel, string link) { repositoryLinkCache.Put(new string[] { repositoryId, rel }, link); } public string GetRepositoryLink(string repositoryId, string rel) { return (string)repositoryLinkCache.Get(new string[] { repositoryId, rel }); } // ---- clear ---- public void ClearRepository(string repositoryId) { linkCache.Remove(new string[] { repositoryId }); typeLinkCache.Remove(new string[] { repositoryId }); collectionLinkCache.Remove(new string[] { repositoryId }); templateCache.Remove(new string[] { repositoryId }); repositoryLinkCache.Remove(new string[] { repositoryId }); } } internal class ContentTypeCacheLevel : DictionaryCacheLevel { public ContentTypeCacheLevel() { EnableKeyFallback(NullKey); } public override object this[string key] { get { return base[Normalize(key)]; } set { base[Normalize(key)] = value; } } public override void Remove(string key) { base.Remove(Normalize(key)); } private string Normalize(string key) { if (key == null) { return null; } StringBuilder sb = new StringBuilder(); int parameterStart = 0; // first, get the MIME type for (int i = 0; i < key.Length; i++) { char c = key[i]; if (Char.IsWhiteSpace(c)) { continue; } else if (c == ';') { parameterStart = i; break; } sb.Append(Char.ToLower(c)); } // if parameters have been found, gather them if (parameterStart > 0) { SortedList parameter = new SortedList(); StringBuilder ksb = new StringBuilder(); StringBuilder vsb = new StringBuilder(); bool isKey = true; for (int i = parameterStart + 1; i < key.Length; i++) { char c = key[i]; if (Char.IsWhiteSpace(c)) { continue; } if (isKey) { if (c == '=') { // value start isKey = false; continue; } ksb.Append(Char.ToLower(c)); } else { if (c == ';') { // next key isKey = true; parameter.Add(ksb.ToString(), vsb.ToString()); ksb = new StringBuilder(); vsb = new StringBuilder(); continue; } else if (c == '"') { // filter quotes continue; } vsb.Append(Char.ToLower(c)); } } // add last parameter if (ksb.Length > 0) { parameter.Add(ksb.ToString(), vsb.ToString()); } // write parameters sorted by key for (int i = 0; i < parameter.Count; i++) { sb.Append(";"); sb.Append(parameter.Keys[i]); sb.Append("="); sb.Append(parameter.Values[i]); } } return sb.ToString(); } } }