/* * 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.Threading; using DotCMIS.Binding; using DotCMIS.Binding.Services; using DotCMIS.Client.Impl.Cache; using DotCMIS.Data; using DotCMIS.Data.Impl; using DotCMIS.Enums; using DotCMIS.Exceptions; namespace DotCMIS.Client.Impl { /// /// Session factory implementation. /// public class SessionFactory : ISessionFactory { private SessionFactory() { } public static SessionFactory NewInstance() { return new SessionFactory(); } public ISession CreateSession(IDictionary parameters) { return CreateSession(parameters, null, null, null); } public ISession CreateSession(IDictionary parameters, IObjectFactory objectFactory, AbstractAuthenticationProvider authenticationProvider, ICache cache) { Session session = new Session(parameters, objectFactory, authenticationProvider, cache); session.Connect(); return session; } public IList GetRepositories(IDictionary parameters) { return GetRepositories(parameters, null, null, null); } public IList GetRepositories(IDictionary parameters, IObjectFactory objectFactory, AbstractAuthenticationProvider authenticationProvider, ICache cache) { ICmisBinding binding = CmisBindingHelper.CreateBinding(parameters); IList repositoryInfos = binding.GetRepositoryService().GetRepositoryInfos(null); IList result = new List(); foreach (IRepositoryInfo data in repositoryInfos) { result.Add(new Repository(data, parameters, this, objectFactory, authenticationProvider, cache)); } return result; } } /// /// Binding helper class. /// internal class CmisBindingHelper { public static ICmisBinding CreateBinding(IDictionary parameters) { return CreateBinding(parameters, null); } public static ICmisBinding CreateBinding(IDictionary parameters, AbstractAuthenticationProvider authenticationProvider) { if (parameters == null) { throw new ArgumentNullException("parameters"); } if (!parameters.ContainsKey(SessionParameter.BindingType)) { parameters[SessionParameter.BindingType] = BindingType.Custom; } string bt = parameters[SessionParameter.BindingType]; switch (bt) { case BindingType.AtomPub: return CreateAtomPubBinding(parameters, authenticationProvider); case BindingType.WebServices: return CreateWebServiceBinding(parameters, authenticationProvider); case BindingType.Custom: return CreateCustomBinding(parameters, authenticationProvider); default: throw new CmisRuntimeException("Ambiguous session parameter: " + parameters); } } private static ICmisBinding CreateCustomBinding(IDictionary parameters, AbstractAuthenticationProvider authenticationProvider) { CmisBindingFactory factory = CmisBindingFactory.NewInstance(); ICmisBinding binding = factory.CreateCmisBinding(parameters, authenticationProvider); return binding; } private static ICmisBinding CreateWebServiceBinding(IDictionary parameters, AbstractAuthenticationProvider authenticationProvider) { CmisBindingFactory factory = CmisBindingFactory.NewInstance(); ICmisBinding binding = factory.CreateCmisWebServicesBinding(parameters, authenticationProvider); return binding; } private static ICmisBinding CreateAtomPubBinding(IDictionary parameters, AbstractAuthenticationProvider authenticationProvider) { CmisBindingFactory factory = CmisBindingFactory.NewInstance(); ICmisBinding binding = factory.CreateCmisAtomPubBinding(parameters, authenticationProvider); return binding; } } /// /// Repository implementation. /// public class Repository : RepositoryInfo, IRepository { private IDictionary parameters; private SessionFactory sessionFactory; private IObjectFactory objectFactory; private AbstractAuthenticationProvider authenticationProvider; private ICache cache; public Repository(IRepositoryInfo info, IDictionary parameters, SessionFactory sessionFactory, IObjectFactory objectFactory, AbstractAuthenticationProvider authenticationProvider, ICache cache) : base(info) { this.parameters = new Dictionary(parameters); this.parameters[SessionParameter.RepositoryId] = Id; this.sessionFactory = sessionFactory; this.objectFactory = objectFactory; this.authenticationProvider = authenticationProvider; this.cache = cache; } public ISession CreateSession() { return sessionFactory.CreateSession(parameters, objectFactory, authenticationProvider, cache); } } /// /// Session implementation. /// public class Session : ISession { private static HashSet CreateUpdatability = new HashSet(); static Session() { CreateUpdatability.Add(Updatability.OnCreate); CreateUpdatability.Add(Updatability.ReadWrite); } protected static IOperationContext FallbackContext = new OperationContext(null, false, true, false, IncludeRelationshipsFlag.None, null, true, null, true, 100); protected IDictionary parameters; private object sessionLock = new object(); public ICmisBinding Binding { get; protected set; } public IRepositoryInfo RepositoryInfo { get; protected set; } public string RepositoryId { get { return RepositoryInfo.Id; } } public IObjectFactory ObjectFactory { get; protected set; } protected AbstractAuthenticationProvider AuthenticationProvider { get; set; } protected ICache Cache { get; set; } protected bool cachePathOmit; private IOperationContext context = FallbackContext; public IOperationContext DefaultContext { get { Lock(); try { return context; } finally { Unlock(); } } set { Lock(); try { context = (value == null ? FallbackContext : value); } finally { Unlock(); } } } public Session(IDictionary parameters, IObjectFactory objectFactory, AbstractAuthenticationProvider authenticationProvider, ICache cache) { if (parameters == null) { throw new ArgumentNullException("parameters"); } this.parameters = parameters; ObjectFactory = (objectFactory == null ? CreateObjectFactory() : objectFactory); AuthenticationProvider = authenticationProvider; Cache = (cache == null ? CreateCache() : cache); string cachePathOmitStr; if (parameters.TryGetValue(SessionParameter.CachePathOmit, out cachePathOmitStr)) { cachePathOmit = cachePathOmitStr.ToLower() == "true"; } else { cachePathOmit = false; } } public void Connect() { Lock(); try { Binding = CmisBindingHelper.CreateBinding(parameters, AuthenticationProvider); string repositoryId; if (!parameters.TryGetValue(SessionParameter.RepositoryId, out repositoryId)) { throw new ArgumentException("Repository Id is not set!"); } RepositoryInfo = Binding.GetRepositoryService().GetRepositoryInfo(repositoryId, null); } finally { Unlock(); } } protected ICache CreateCache() { try { string typeName; Type cacheType; if (parameters.TryGetValue(SessionParameter.CacheClass, out typeName)) { cacheType = Type.GetType(typeName); } else { cacheType = typeof(CmisObjectCache); } ICache cacheObject = Activator.CreateInstance(cacheType) as ICache; if (cacheObject == null) { throw new Exception("Class does not implement ICache!"); } cacheObject.Initialize(this, parameters); return cacheObject; } catch (Exception e) { throw new ArgumentException("Unable to create cache: " + e, e); } } protected IObjectFactory CreateObjectFactory() { try { string ofName; Type ofType; if (parameters.TryGetValue(SessionParameter.ObjectFactoryClass, out ofName)) { ofType = Type.GetType(ofName); } else { ofType = typeof(ObjectFactory); } IObjectFactory ofObject = Activator.CreateInstance(ofType) as IObjectFactory; if (ofObject == null) { throw new Exception("Class does not implement IObjectFactory!"); } ofObject.Initialize(this, parameters); return ofObject; } catch (Exception e) { throw new ArgumentException("Unable to create object factory: " + e, e); } } public void Clear() { Lock(); try { Cache = CreateCache(); Binding.ClearAllCaches(); } finally { Unlock(); } } // session context public IOperationContext CreateOperationContext() { return new OperationContext(); } public IOperationContext CreateOperationContext(HashSet filter, bool includeAcls, bool includeAllowableActions, bool includePolicies, IncludeRelationshipsFlag includeRelationships, HashSet renditionFilter, bool includePathSegments, string orderBy, bool cacheEnabled, int maxItemsPerPage) { return new OperationContext(filter, includeAcls, includeAllowableActions, includePolicies, includeRelationships, renditionFilter, includePathSegments, orderBy, cacheEnabled, maxItemsPerPage); } public IObjectId CreateObjectId(string id) { return new ObjectId(id); } // types public IObjectType GetTypeDefinition(string typeId) { ITypeDefinition typeDefinition = Binding.GetRepositoryService().GetTypeDefinition(RepositoryId, typeId, null); return ObjectFactory.ConvertTypeDefinition(typeDefinition); } public IItemEnumerable GetTypeChildren(string typeId, bool includePropertyDefinitions) { IRepositoryService service = Binding.GetRepositoryService(); PageFetcher.FetchPage fetchPageDelegate = delegate(long maxNumItems, long skipCount) { // fetch the data ITypeDefinitionList tdl = service.GetTypeChildren(RepositoryId, typeId, includePropertyDefinitions, maxNumItems, skipCount, null); // convert type definitions int count = (tdl != null && tdl.List != null ? tdl.List.Count : 0); IList page = new List(count); if (count > 0) { foreach (ITypeDefinition typeDefinition in tdl.List) { page.Add(ObjectFactory.ConvertTypeDefinition(typeDefinition)); } } return new PageFetcher.Page(page, tdl.NumItems, tdl.HasMoreItems); }; return new CollectionEnumerable(new PageFetcher(DefaultContext.MaxItemsPerPage, fetchPageDelegate)); } public IList> GetTypeDescendants(string typeId, int depth, bool includePropertyDefinitions) { IList descendants = Binding.GetRepositoryService().GetTypeDescendants( RepositoryId, typeId, depth, includePropertyDefinitions, null); return ConvertTypeDescendants(descendants); } private IList> ConvertTypeDescendants(IList descendantsList) { if (descendantsList == null || descendantsList.Count == 0) { return null; } IList> result = new List>(); foreach (ITypeDefinitionContainer container in descendantsList) { Tree tree = new Tree(); tree.Item = ObjectFactory.ConvertTypeDefinition(container.TypeDefinition); tree.Children = ConvertTypeDescendants(container.Children); result.Add(tree); } return result; } // navigation public IFolder GetRootFolder() { return GetRootFolder(DefaultContext); } public IFolder GetRootFolder(IOperationContext context) { IFolder rootFolder = GetObject(CreateObjectId(RepositoryInfo.RootFolderId), context) as IFolder; if (rootFolder == null) { throw new CmisRuntimeException("Root folder object is not a folder!"); } return rootFolder; } public IItemEnumerable GetCheckedOutDocs() { return GetCheckedOutDocs(DefaultContext); } public IItemEnumerable GetCheckedOutDocs(IOperationContext context) { INavigationService service = Binding.GetNavigationService(); IOperationContext ctxt = new OperationContext(context); PageFetcher.FetchPage fetchPageDelegate = delegate(long maxNumItems, long skipCount) { // get all checked out documents IObjectList checkedOutDocs = service.GetCheckedOutDocs(RepositoryId, null, ctxt.FilterString, ctxt.OrderBy, ctxt.IncludeAllowableActions, ctxt.IncludeRelationships, ctxt.RenditionFilterString, maxNumItems, skipCount, null); // convert objects IList page = new List(); if (checkedOutDocs.Objects != null) { foreach (IObjectData objectData in checkedOutDocs.Objects) { IDocument doc = ObjectFactory.ConvertObject(objectData, ctxt) as IDocument; if (doc == null) { // should not happen... continue; } page.Add(doc); } } return new PageFetcher.Page(page, checkedOutDocs.NumItems, checkedOutDocs.HasMoreItems); }; return new CollectionEnumerable(new PageFetcher(DefaultContext.MaxItemsPerPage, fetchPageDelegate)); } public ICmisObject GetObject(IObjectId objectId) { return GetObject(objectId, DefaultContext); } public ICmisObject GetObject(IObjectId objectId, IOperationContext context) { if (objectId == null || objectId.Id == null) { throw new ArgumentException("Object Id must be set!"); } return GetObject(objectId.Id, context); } public ICmisObject GetObject(string objectId) { return GetObject(objectId, DefaultContext); } public ICmisObject GetObject(string objectId, IOperationContext context) { if (objectId == null) { throw new ArgumentException("Object Id must be set!"); } if (context == null) { throw new ArgumentException("Operation context must be set!"); } ICmisObject result = null; // ask the cache first if (context.CacheEnabled) { result = Cache.GetById(objectId, context.CacheKey); if (result != null) { return result; } } // get the object IObjectData objectData = Binding.GetObjectService().GetObject(RepositoryId, objectId, context.FilterString, context.IncludeAllowableActions, context.IncludeRelationships, context.RenditionFilterString, context.IncludePolicies, context.IncludeAcls, null); result = ObjectFactory.ConvertObject(objectData, context); // put into cache if (context.CacheEnabled) { Cache.Put(result, context.CacheKey); } return result; } public ICmisObject GetObjectByPath(string path) { return GetObjectByPath(path, DefaultContext); } public ICmisObject GetObjectByPath(string path, IOperationContext context) { if (path == null) { throw new ArgumentException("Path must be set!"); } if (context == null) { throw new ArgumentException("Operation context must be set!"); } ICmisObject result = null; // ask the cache first if (context.CacheEnabled && !cachePathOmit) { result = Cache.GetByPath(path, context.CacheKey); if (result != null) { return result; } } // get the object IObjectData objectData = Binding.GetObjectService().GetObjectByPath(RepositoryId, path, context.FilterString, context.IncludeAllowableActions, context.IncludeRelationships, context.RenditionFilterString, context.IncludePolicies, context.IncludeAcls, null); result = ObjectFactory.ConvertObject(objectData, context); // put into cache if (context.CacheEnabled) { Cache.PutPath(path, result, context.CacheKey); } return result; } public void RemoveObjectFromCache(IObjectId objectId) { if (objectId == null || objectId.Id == null) { return; } RemoveObjectFromCache(objectId.Id); } public void RemoveObjectFromCache(string objectId) { Cache.Remove(objectId); } // discovery public IItemEnumerable Query(string statement, bool searchAllVersions) { return Query(statement, searchAllVersions, DefaultContext); } public IItemEnumerable Query(string statement, bool searchAllVersions, IOperationContext context) { IDiscoveryService service = Binding.GetDiscoveryService(); IOperationContext ctxt = new OperationContext(context); PageFetcher.FetchPage fetchPageDelegate = delegate(long maxNumItems, long skipCount) { // fetch the data IObjectList resultList = service.Query(RepositoryId, statement, searchAllVersions, ctxt.IncludeAllowableActions, ctxt.IncludeRelationships, ctxt.RenditionFilterString, maxNumItems, skipCount, null); // convert query results IList page = new List(); if (resultList.Objects != null) { foreach (IObjectData objectData in resultList.Objects) { if (objectData == null) { continue; } page.Add(ObjectFactory.ConvertQueryResult(objectData)); } } return new PageFetcher.Page(page, resultList.NumItems, resultList.HasMoreItems); }; return new CollectionEnumerable(new PageFetcher(DefaultContext.MaxItemsPerPage, fetchPageDelegate)); } public IChangeEvents GetContentChanges(string changeLogToken, bool includeProperties, long maxNumItems) { return GetContentChanges(changeLogToken, includeProperties, maxNumItems, DefaultContext); } public IChangeEvents GetContentChanges(string changeLogToken, bool includeProperties, long maxNumItems, IOperationContext context) { Lock(); try { IObjectList objectList = Binding.GetDiscoveryService().GetContentChanges(RepositoryId, ref changeLogToken, includeProperties, context.FilterString, context.IncludePolicies, context.IncludeAcls, maxNumItems, null); return ObjectFactory.ConvertChangeEvents(changeLogToken, objectList); } finally { Unlock(); } } // create public IObjectId CreateDocument(IDictionary properties, IObjectId folderId, IContentStream contentStream, VersioningState? versioningState, IList policies, IList addAces, IList removeAces) { if (folderId != null && folderId.Id == null) { throw new ArgumentException("Folder Id must be set!"); } if (properties == null || properties.Count == 0) { throw new ArgumentException("Properties must not be empty!"); } string newId = Binding.GetObjectService().CreateDocument(RepositoryId, ObjectFactory.ConvertProperties(properties, null, CreateUpdatability), (folderId == null ? null : folderId.Id), contentStream, versioningState, ObjectFactory.ConvertPolicies(policies), ObjectFactory.ConvertAces(addAces), ObjectFactory.ConvertAces(removeAces), null); return newId == null ? null : CreateObjectId(newId); } public IObjectId CreateDocument(IDictionary properties, IObjectId folderId, IContentStream contentStream, VersioningState? versioningState) { return CreateDocument(properties, folderId, contentStream, versioningState, null, null, null); } public IObjectId CreateDocumentFromSource(IObjectId source, IDictionary properties, IObjectId folderId, VersioningState? versioningState, IList policies, IList addAces, IList removeAces) { if (source == null || source.Id == null) { throw new ArgumentException("Source must be set!"); } // get the type of the source document IObjectType type = null; if (source is ICmisObject) { type = ((ICmisObject)source).ObjectType; } else { ICmisObject sourceObj = GetObject(source); type = sourceObj.ObjectType; } if (type.BaseTypeId != BaseTypeId.CmisDocument) { throw new ArgumentException("Source object must be a document!"); } string newId = Binding.GetObjectService().CreateDocumentFromSource(RepositoryId, source.Id, ObjectFactory.ConvertProperties(properties, type, CreateUpdatability), (folderId == null ? null : folderId.Id), versioningState, ObjectFactory.ConvertPolicies(policies), ObjectFactory.ConvertAces(addAces), ObjectFactory.ConvertAces(removeAces), null); return newId == null ? null : CreateObjectId(newId); } public IObjectId CreateDocumentFromSource(IObjectId source, IDictionary properties, IObjectId folderId, VersioningState? versioningState) { return CreateDocumentFromSource(source, properties, folderId, versioningState, null, null, null); } public IObjectId CreateFolder(IDictionary properties, IObjectId folderId, IList policies, IList addAces, IList removeAces) { if (folderId == null || folderId.Id == null) { throw new ArgumentException("Folder Id must be set!"); } if (properties == null || properties.Count == 0) { throw new ArgumentException("Properties must not be empty!"); } string newId = Binding.GetObjectService().CreateFolder(RepositoryId, ObjectFactory.ConvertProperties(properties, null, CreateUpdatability), (folderId == null ? null : folderId.Id), ObjectFactory.ConvertPolicies(policies), ObjectFactory.ConvertAces(addAces), ObjectFactory.ConvertAces(removeAces), null); return newId == null ? null : CreateObjectId(newId); } public IObjectId CreateFolder(IDictionary properties, IObjectId folderId) { return CreateFolder(properties, folderId, null, null, null); } public IObjectId CreatePolicy(IDictionary properties, IObjectId folderId, IList policies, IList addAces, IList removeAces) { if (folderId == null || folderId.Id == null) { throw new ArgumentException("Folder Id must be set!"); } if (properties == null || properties.Count == 0) { throw new ArgumentException("Properties must not be empty!"); } string newId = Binding.GetObjectService().CreatePolicy(RepositoryId, ObjectFactory.ConvertProperties(properties, null, CreateUpdatability), (folderId == null ? null : folderId.Id), ObjectFactory.ConvertPolicies(policies), ObjectFactory.ConvertAces(addAces), ObjectFactory.ConvertAces(removeAces), null); return newId == null ? null : CreateObjectId(newId); } public IObjectId CreatePolicy(IDictionary properties, IObjectId folderId) { return CreatePolicy(properties, folderId, null, null, null); } public IObjectId CreateRelationship(IDictionary properties, IList policies, IList addAces, IList removeAces) { if (properties == null || properties.Count == 0) { throw new ArgumentException("Properties must not be empty!"); } string newId = Binding.GetObjectService().CreateRelationship(RepositoryId, ObjectFactory.ConvertProperties(properties, null, CreateUpdatability), ObjectFactory.ConvertPolicies(policies), ObjectFactory.ConvertAces(addAces), ObjectFactory.ConvertAces(removeAces), null); return newId == null ? null : CreateObjectId(newId); } public IObjectId CreateRelationship(IDictionary properties) { return CreateRelationship(properties, null, null, null); } public IItemEnumerable GetRelationships(IObjectId objectId, bool includeSubRelationshipTypes, RelationshipDirection? relationshipDirection, IObjectType type, IOperationContext context) { if (objectId == null || objectId.Id == null) { throw new ArgumentException("Invalid object id!"); } string id = objectId.Id; string typeId = (type == null ? null : type.Id); IRelationshipService service = Binding.GetRelationshipService(); IOperationContext ctxt = new OperationContext(context); PageFetcher.FetchPage fetchPageDelegate = delegate(long maxNumItems, long skipCount) { // fetch the relationships IObjectList relList = service.GetObjectRelationships(RepositoryId, id, includeSubRelationshipTypes, relationshipDirection, typeId, ctxt.FilterString, ctxt.IncludeAllowableActions, maxNumItems, skipCount, null); // convert relationship objects IList page = new List(); if (relList.Objects != null) { foreach (IObjectData rod in relList.Objects) { IRelationship relationship = GetObject(CreateObjectId(rod.Id), ctxt) as IRelationship; if (relationship == null) { throw new CmisRuntimeException("Repository returned an object that is not a relationship!"); } page.Add(relationship); } } return new PageFetcher.Page(page, relList.NumItems, relList.HasMoreItems); }; return new CollectionEnumerable(new PageFetcher(DefaultContext.MaxItemsPerPage, fetchPageDelegate)); } // permissions public IAcl GetAcl(IObjectId objectId, bool onlyBasicPermissions) { if (objectId == null || objectId.Id == null) { throw new ArgumentException("Invalid object id!"); } return Binding.GetAclService().GetAcl(RepositoryId, objectId.Id, onlyBasicPermissions, null); } public IAcl ApplyAcl(IObjectId objectId, IList addAces, IList removeAces, AclPropagation? aclPropagation) { if (objectId == null || objectId.Id == null) { throw new ArgumentException("Invalid object id!"); } return Binding.GetAclService().ApplyAcl(RepositoryId, objectId.Id, ObjectFactory.ConvertAces(addAces), ObjectFactory.ConvertAces(removeAces), aclPropagation, null); } public void ApplyPolicy(IObjectId objectId, params IObjectId[] policyIds) { if (objectId == null || objectId.Id == null) { throw new ArgumentException("Invalid object id!"); } if (policyIds == null || (policyIds.Length == 0)) { throw new ArgumentException("No Policies provided!"); } string[] ids = new string[policyIds.Length]; for (int i = 0; i < policyIds.Length; i++) { if (policyIds[i] == null || policyIds[i].Id == null) { throw new ArgumentException("A Policy Id is not set!"); } ids[i] = policyIds[i].Id; } foreach (string id in ids) { Binding.GetPolicyService().ApplyPolicy(RepositoryId, id, objectId.Id, null); } } public void RemovePolicy(IObjectId objectId, params IObjectId[] policyIds) { if (objectId == null || objectId.Id == null) { throw new ArgumentException("Invalid object id!"); } if (policyIds == null || (policyIds.Length == 0)) { throw new ArgumentException("No Policies provided!"); } string[] ids = new string[policyIds.Length]; for (int i = 0; i < policyIds.Length; i++) { if (policyIds[i] == null || policyIds[i].Id == null) { throw new ArgumentException("A Policy Id is not set!"); } ids[i] = policyIds[i].Id; } foreach (string id in ids) { Binding.GetPolicyService().RemovePolicy(RepositoryId, id, objectId.Id, null); } } protected void Lock() { Monitor.Enter(sessionLock); } protected void Unlock() { Monitor.Exit(sessionLock); } } }