blob: a80adceb711658cf3e4da5e207e69a9224a614c5 [file] [log] [blame]
/*
* 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.IO;
using System.Net;
using DotCMIS.Binding.Impl;
using DotCMIS.Binding.Services;
using DotCMIS.CMISWebServicesReference;
using DotCMIS.Data;
using DotCMIS.Data.Extensions;
using DotCMIS.Data.Impl;
using DotCMIS.Enums;
using DotCMIS.Exceptions;
namespace DotCMIS.Binding.AtomPub
{
/// <summary>
/// AtomPub binding SPI.
/// </summary>
internal class CmisAtomPubSpi : ICmisSpi
{
private RepositoryService repositoryService;
private NavigationService navigationService;
private ObjectService objectService;
private VersioningService versioningService;
private DiscoveryService discoveryService;
private MultiFilingService multiFilingService;
private RelationshipService relationshipService;
private PolicyService policyService;
private AclService aclService;
public void initialize(BindingSession session)
{
repositoryService = new RepositoryService(session);
navigationService = new NavigationService(session);
objectService = new ObjectService(session);
versioningService = new VersioningService(session);
discoveryService = new DiscoveryService(session);
multiFilingService = new MultiFilingService(session);
relationshipService = new RelationshipService(session);
policyService = new PolicyService(session);
aclService = new AclService(session);
}
public IRepositoryService GetRepositoryService()
{
return repositoryService;
}
public INavigationService GetNavigationService()
{
return navigationService;
}
public IObjectService GetObjectService()
{
return objectService;
}
public IVersioningService GetVersioningService()
{
return versioningService;
}
public IRelationshipService GetRelationshipService()
{
return relationshipService;
}
public IDiscoveryService GetDiscoveryService()
{
return discoveryService;
}
public IMultiFilingService GetMultiFilingService()
{
return multiFilingService;
}
public IAclService GetAclService()
{
return aclService;
}
public IPolicyService GetPolicyService()
{
return policyService;
}
public void ClearAllCaches()
{
// nothing to do
}
public void ClearRepositoryCache(string repositoryId)
{
// nothing to do
}
public void Dispose()
{
// nothing to do
}
}
internal abstract class AbstractAtomPubService
{
protected const string NameCollection = "collection";
protected const string NameURITemplate = "uritemplate";
protected const string NamePathSegment = "pathSegment";
protected const string NameRelativePathSegment = "relativePathSegment";
protected const string NameNumItems = "numItems";
private const string SessionLinkCache = "org.apache.chemistry.dotcmis.binding.atompub.linkcache";
protected enum IdentifierType
{
Id, Path
};
protected BindingSession Session { get; set; }
// ---- link cache ----
protected LinkCache GetLinkCache()
{
LinkCache linkCache = (LinkCache)Session.GetValue(SessionLinkCache);
if (linkCache == null)
{
linkCache = new LinkCache(Session);
Session.PutValue(SessionLinkCache, linkCache);
}
return linkCache;
}
protected string GetLink(string repositoryId, string id, string rel, string type)
{
if (repositoryId == null)
{
throw new CmisInvalidArgumentException("Repository id must be set!");
}
if (id == null)
{
throw new CmisInvalidArgumentException("Object id must be set!");
}
return GetLinkCache().GetLink(repositoryId, id, rel, type);
}
protected string GetLink(string repositoryId, string id, string rel)
{
return GetLink(repositoryId, id, rel, null);
}
protected string LoadLink(string repositoryId, string id, string rel, string type)
{
string link = GetLink(repositoryId, id, rel, type);
if (link == null)
{
GetObjectInternal(repositoryId, IdentifierType.Id, id, ReturnVersion.This, null, null, null, null, null, null, null);
link = GetLink(repositoryId, id, rel, type);
}
return link;
}
protected void AddLink(string repositoryId, string id, string rel, string type, string link)
{
GetLinkCache().AddLink(repositoryId, id, rel, type, link);
}
protected void AddLink(string repositoryId, string id, AtomLink link)
{
GetLinkCache().AddLink(repositoryId, id, link.Rel, link.Type, link.Href);
}
protected void RemoveLinks(string repositoryId, string id)
{
GetLinkCache().RemoveLinks(repositoryId, id);
}
protected void LockLinks()
{
GetLinkCache().LockLinks();
}
protected void UnlockLinks()
{
GetLinkCache().UnlockLinks();
}
protected string GetTypeLink(string repositoryId, string typeId, string rel, string type)
{
if (repositoryId == null)
{
throw new CmisInvalidArgumentException("Repository id must be set!");
}
if (typeId == null)
{
throw new CmisInvalidArgumentException("Type id must be set!");
}
return GetLinkCache().GetTypeLink(repositoryId, typeId, rel, type);
}
protected string GetTypeLink(string repositoryId, string typeId, string rel)
{
return GetTypeLink(repositoryId, typeId, rel, null);
}
protected string LoadTypeLink(string repositoryId, string typeId, string rel, string type)
{
string link = GetTypeLink(repositoryId, typeId, rel, type);
if (link == null)
{
GetTypeDefinitionInternal(repositoryId, typeId);
link = GetTypeLink(repositoryId, typeId, rel, type);
}
return link;
}
protected void AddTypeLink(string repositoryId, string typeId, string rel, string type, string link)
{
GetLinkCache().AddTypeLink(repositoryId, typeId, rel, type, link);
}
protected void AddTypeLink(string repositoryId, string typeId, AtomLink link)
{
GetLinkCache().AddTypeLink(repositoryId, typeId, link.Rel, link.Type, link.Href);
}
protected void RemoveTypeLinks(string repositoryId, string id)
{
GetLinkCache().RemoveTypeLinks(repositoryId, id);
}
protected void LockTypeLinks()
{
GetLinkCache().LockTypeLinks();
}
protected void UnlockTypeLinks()
{
GetLinkCache().UnlockTypeLinks();
}
protected string GetCollection(string repositoryId, string collection)
{
return GetLinkCache().GetCollection(repositoryId, collection);
}
protected string LoadCollection(string repositoryId, string collection)
{
string link = GetCollection(repositoryId, collection);
if (link == null)
{
GetRepositoriesInternal(repositoryId);
link = GetCollection(repositoryId, collection);
}
return link;
}
protected void AddCollection(string repositoryId, string collection, string link)
{
GetLinkCache().AddCollection(repositoryId, collection, link);
}
protected void AddCollection(string repositoryId, IDictionary<string, string> colDict)
{
string collection = null;
colDict.TryGetValue("collectionType", out collection);
string link = null;
colDict.TryGetValue("href", out link);
AddCollection(repositoryId, collection, link);
}
protected string GetRepositoryLink(string repositoryId, string rel)
{
return GetLinkCache().GetRepositoryLink(repositoryId, rel);
}
protected string LoadRepositoryLink(string repositoryId, string rel)
{
string link = GetRepositoryLink(repositoryId, rel);
if (link == null)
{
GetRepositoriesInternal(repositoryId);
link = GetRepositoryLink(repositoryId, rel);
}
return link;
}
protected void AddRepositoryLink(string repositoryId, string rel, string link)
{
GetLinkCache().AddRepositoryLink(repositoryId, rel, link);
}
protected void AddRepositoryLink(string repositoryId, AtomLink link)
{
AddRepositoryLink(repositoryId, link.Rel, link.Href);
}
protected string GetTemplateLink(string repositoryId, string type, IDictionary<string, object> parameters)
{
return GetLinkCache().GetTemplateLink(repositoryId, type, parameters);
}
protected string LoadTemplateLink(string repositoryId, string type, IDictionary<string, object> parameters)
{
string link = GetTemplateLink(repositoryId, type, parameters);
if (link == null)
{
GetRepositoriesInternal(repositoryId);
link = GetTemplateLink(repositoryId, type, parameters);
}
return link;
}
protected void AddTemplate(string repositoryId, string type, string link)
{
GetLinkCache().AddTemplate(repositoryId, type, link);
}
protected void AddTemplate(string repositoryId, IDictionary<string, string> tempDict)
{
string type = null;
tempDict.TryGetValue("type", out type);
string template = null;
tempDict.TryGetValue("template", out template);
AddTemplate(repositoryId, type, template);
}
// ---- exceptions ----
protected CmisBaseException ConvertStatusCode(HttpStatusCode code, string message, string errorContent, Exception e)
{
switch (code)
{
case HttpStatusCode.BadRequest:
return new CmisInvalidArgumentException(message, errorContent, e);
case HttpStatusCode.NotFound:
return new CmisObjectNotFoundException(message, errorContent, e);
case HttpStatusCode.Forbidden:
return new CmisPermissionDeniedException(message, errorContent, e);
case HttpStatusCode.MethodNotAllowed:
return new CmisNotSupportedException(message, errorContent, e);
case HttpStatusCode.Conflict:
return new CmisConstraintException(message, errorContent, e);
default:
return new CmisRuntimeException(message, errorContent, e);
}
}
protected void ThrowLinkException(String repositoryId, String id, String rel, String type)
{
int index = GetLinkCache().CheckLink(repositoryId, id, rel, type);
switch (index)
{
case 0:
throw new CmisObjectNotFoundException("Unknown repository!");
case 1:
throw new CmisObjectNotFoundException("Unknown object!");
case 2:
throw new CmisNotSupportedException("Operation not supported by the repository for this object!");
case 3:
throw new CmisNotSupportedException("No link with matching media type!");
case 4:
throw new CmisRuntimeException("Nothing wrong! Either this is a bug or threading issue.");
default:
throw new CmisRuntimeException("Unknown error!");
}
}
// ---- helpers ----
protected T Parse<T>(Stream stream) where T : AtomBase
{
AtomPubParser parser = new AtomPubParser(stream);
try
{
parser.Parse();
}
catch (Exception e)
{
throw new CmisConnectionException("Parsing exception!", e);
}
AtomBase parseResult = parser.GetParseResults();
if (!typeof(T).IsInstanceOfType(parseResult))
{
throw new CmisConnectionException("Unexpected document! Received "
+ (parseResult == null ? "something unknown" : parseResult.GetAtomType()) + "!");
}
return (T)parseResult;
}
protected HttpUtils.Response Read(UrlBuilder url)
{
HttpUtils.Response resp = HttpUtils.InvokeGET(url, Session);
if (resp.StatusCode != HttpStatusCode.OK)
{
throw ConvertStatusCode(resp.StatusCode, resp.Message, resp.ErrorContent, null);
}
return resp;
}
protected HttpUtils.Response Post(UrlBuilder url, string contentType, HttpUtils.Output writer)
{
HttpUtils.Response resp = HttpUtils.InvokePOST(url, contentType, writer, Session);
if (resp.StatusCode != HttpStatusCode.Created)
{
throw ConvertStatusCode(resp.StatusCode, resp.Message, resp.ErrorContent, null);
}
return resp;
}
protected HttpUtils.Response Put(UrlBuilder url, string contentType, HttpUtils.Output writer)
{
HttpUtils.Response resp = HttpUtils.InvokePUT(url, contentType, null, writer, Session);
if ((int)resp.StatusCode < 200 || (int)resp.StatusCode > 299)
{
throw ConvertStatusCode(resp.StatusCode, resp.Message, resp.ErrorContent, null);
}
return resp;
}
protected void Delete(UrlBuilder url)
{
HttpUtils.Response resp = HttpUtils.InvokeDELETE(url, Session);
if (resp.StatusCode != HttpStatusCode.NoContent)
{
throw ConvertStatusCode(resp.StatusCode, resp.Message, resp.ErrorContent, null);
}
}
protected string GetServiceDocURL()
{
return Session.GetValue(SessionParameter.AtomPubUrl) as string;
}
protected bool IsNextLink(AtomElement element)
{
return AtomPubConstants.RelNext == ((AtomLink)element.Object).Rel;
}
protected bool IsStr(string name, AtomElement element)
{
return name == element.LocalName && element.Object is string;
}
protected bool IsInt(string name, AtomElement element)
{
return name == element.LocalName && element.Object is Int64;
}
// ---- common methods ----
protected cmisObjectType CreateIdObject(string objectId)
{
cmisObjectType cmisObject = new cmisObjectType();
cmisPropertiesType properties = new cmisPropertiesType();
cmisObject.properties = properties;
cmisPropertyId idProperty = new cmisPropertyId();
properties.Items = new cmisProperty[] { idProperty };
idProperty.propertyDefinitionId = PropertyIds.ObjectId;
idProperty.value = new string[] { objectId };
return cmisObject;
}
protected bool IsAclMergeRequired(IAcl addAces, IAcl removeAces)
{
return (addAces != null && addAces.Aces != null && addAces.Aces.Count > 0)
|| (removeAces != null && removeAces.Aces != null && removeAces.Aces.Count > 0);
}
protected IAcl MergeAcls(IAcl originalAces, IAcl addAces, IAcl removeAces)
{
IDictionary<string, HashSet<string>> originals = ConvertAclToDict(originalAces);
IDictionary<string, HashSet<string>> adds = ConvertAclToDict(addAces);
IDictionary<string, HashSet<string>> removes = ConvertAclToDict(removeAces);
IList<IAce> newACEs = new List<IAce>();
// iterate through the original ACEs
foreach (KeyValuePair<string, HashSet<string>> ace in originals)
{
HashSet<string> permSet;
if (ace.Value == null)
{
permSet = new HashSet<string>();
}
else
{
permSet = new HashSet<string>(ace.Value);
}
// add permissions
HashSet<string> addPermissions;
if (adds.TryGetValue(ace.Key, out addPermissions))
{
foreach (string perm in addPermissions)
{
permSet.Add(perm);
}
}
// remove permissions
HashSet<string> removePermissions;
if (removes.TryGetValue(ace.Key, out removePermissions))
{
foreach (string perm in removePermissions)
{
permSet.Remove(perm);
}
}
// create new ACE
Ace resultAce = new Ace();
resultAce.IsDirect = true;
Principal resultPrincipal = new Principal();
resultPrincipal.Id = ace.Key;
resultAce.Principal = resultPrincipal;
resultAce.Permissions = new List<string>(permSet);
newACEs.Add(resultAce);
}
// find all ACEs that should be added but are not in the original ACE list
foreach (KeyValuePair<string, HashSet<string>> ace in adds)
{
if (!originals.ContainsKey(ace.Key) && ace.Value != null && ace.Value.Count > 0)
{
Ace resultAce = new Ace();
resultAce.IsDirect = true;
Principal resultPrincipal = new Principal();
resultPrincipal.Id = ace.Key;
resultAce.Principal = resultPrincipal;
resultAce.Permissions = new List<string>(ace.Value);
newACEs.Add(resultAce);
}
}
Acl result = new Acl();
result.Aces = newACEs;
return result;
}
private IDictionary<string, HashSet<string>> ConvertAclToDict(IAcl acl)
{
IDictionary<string, HashSet<string>> result = new Dictionary<string, HashSet<string>>();
if (acl == null || acl.Aces == null)
{
return result;
}
foreach (Ace ace in acl.Aces)
{
// don't consider indirect ACEs - we can't change them
if (!ace.IsDirect)
{
// ignore
continue;
}
// although a principal must not be null, check it
if ((ace.Principal == null) || (ace.Principal.Id == null))
{
// ignore
continue;
}
if (ace.Permissions == null)
{
continue;
}
HashSet<string> permissions;
if (!result.TryGetValue(ace.Principal.Id, out permissions))
{
permissions = new HashSet<string>();
result[ace.Principal.Id] = permissions;
}
foreach (string perm in ace.Permissions)
{
permissions.Add(perm);
}
}
return result;
}
protected AtomAcl UpdateAcl(string repositoryId, string objectId, IAcl acl, AclPropagation? aclPropagation)
{
// find the link
String link = LoadLink(repositoryId, objectId, AtomPubConstants.RelACL, AtomPubConstants.MediatypeACL);
if (link == null)
{
ThrowLinkException(repositoryId, objectId, AtomPubConstants.RelACL, AtomPubConstants.MediatypeACL);
}
UrlBuilder aclUrl = new UrlBuilder(link);
aclUrl.AddParameter(AtomPubConstants.ParamACLPropagation, aclPropagation);
// set up object and writer
cmisAccessControlListType cmisAcl = Converter.Convert(acl);
HttpUtils.Output output = delegate(Stream stream)
{
AtomWriter.AclSerializer.Serialize(stream, cmisAcl);
};
// update
HttpUtils.Response resp = Put(aclUrl, AtomPubConstants.MediatypeACL, output);
// parse new acl
return Parse<AtomAcl>(resp.Stream);
}
protected IList<IRepositoryInfo> GetRepositoriesInternal(string repositoryId)
{
IList<IRepositoryInfo> repInfos = new List<IRepositoryInfo>();
// retrieve service doc
UrlBuilder url = new UrlBuilder(GetServiceDocURL());
url.AddParameter(AtomPubConstants.ParamRepositoryId, repositoryId);
// read and parse
HttpUtils.Response resp = Read(url);
ServiceDoc serviceDoc = Parse<ServiceDoc>(resp.Stream);
// walk through the workspaces
foreach (RepositoryWorkspace ws in serviceDoc.GetWorkspaces())
{
if (ws.Id == null)
{
// found a non-CMIS workspace
continue;
}
foreach (AtomElement element in ws.GetElements())
{
if (element.LocalName == NameCollection)
{
AddCollection(ws.Id, (IDictionary<string, string>)element.Object);
}
else if (element.Object is AtomLink)
{
AddRepositoryLink(ws.Id, (AtomLink)element.Object);
}
else if (element.LocalName == NameURITemplate)
{
AddTemplate(ws.Id, (IDictionary<string, string>)element.Object);
}
else if (element.Object is cmisRepositoryInfoType)
{
repInfos.Add(Converter.Convert((cmisRepositoryInfoType)element.Object));
}
}
}
return repInfos;
}
protected IObjectData GetObjectInternal(string repositoryId, IdentifierType idOrPath, string objectIdOrPath,
ReturnVersion? returnVersion, string filter, bool? includeAllowableActions, IncludeRelationshipsFlag? includeRelationships,
string renditionFilter, bool? includePolicyIds, bool? includeAcl, IExtensionsData extension)
{
IObjectData result = null;
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters[AtomPubConstants.ParamId] = objectIdOrPath;
parameters[AtomPubConstants.ParamPath] = objectIdOrPath;
parameters[AtomPubConstants.ParamReturnVersion] = returnVersion;
parameters[AtomPubConstants.ParamFilter] = filter;
parameters[AtomPubConstants.ParamAllowableActions] = includeAllowableActions;
parameters[AtomPubConstants.ParamACL] = includeAcl;
parameters[AtomPubConstants.ParamPolicyIds] = includePolicyIds;
parameters[AtomPubConstants.ParamRelationships] = includeRelationships;
parameters[AtomPubConstants.ParamRenditionFilter] = renditionFilter;
string link = LoadTemplateLink(repositoryId, (idOrPath == IdentifierType.Id ? AtomPubConstants.TemplateObjectById
: AtomPubConstants.TemplateObjectByPath), parameters);
if (link == null)
{
throw new CmisObjectNotFoundException("Unknown repository!");
}
UrlBuilder url = new UrlBuilder(link);
// workaround for missing template parameter in the CMIS spec
if ((returnVersion != null) && (returnVersion != ReturnVersion.This))
{
url.AddParameter(AtomPubConstants.ParamReturnVersion, returnVersion);
}
HttpUtils.Response resp = Read(url);
AtomEntry entry = Parse<AtomEntry>(resp.Stream);
if (entry.Id == null)
{
throw new CmisConnectionException("Received Atom entry is not a CMIS entry!");
}
LockLinks();
try
{
RemoveLinks(repositoryId, entry.Id);
foreach (AtomElement element in entry.GetElements())
{
if (element.Object is AtomLink)
{
AddLink(repositoryId, entry.Id, (AtomLink)element.Object);
}
else if (element.Object is cmisObjectType)
{
result = Converter.Convert((cmisObjectType)element.Object);
}
}
}
finally
{
UnlockLinks();
}
return result;
}
protected ITypeDefinition GetTypeDefinitionInternal(string repositoryId, string typeId)
{
ITypeDefinition result = null;
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters[AtomPubConstants.ParamId] = typeId;
string link = LoadTemplateLink(repositoryId, AtomPubConstants.TemplateTypeById, parameters);
if (link == null)
{
throw new CmisObjectNotFoundException("Unknown repository!");
}
// read and parse
HttpUtils.Response resp = Read(new UrlBuilder(link));
AtomEntry entry = Parse<AtomEntry>(resp.Stream);
// we expect a CMIS entry
if (entry.Id == null)
{
throw new CmisConnectionException("Received Atom entry is not a CMIS entry!");
}
LockTypeLinks();
try
{
// clean up cache
RemoveTypeLinks(repositoryId, entry.Id);
// walk through the entry
foreach (AtomElement element in entry.GetElements())
{
if (element.Object is AtomLink)
{
AddTypeLink(repositoryId, entry.Id, (AtomLink)element.Object);
}
else if (element.Object is cmisTypeDefinitionType)
{
result = Converter.Convert((cmisTypeDefinitionType)element.Object);
}
}
}
finally
{
UnlockTypeLinks();
}
return result;
}
}
internal class RepositoryService : AbstractAtomPubService, IRepositoryService
{
public RepositoryService(BindingSession session)
{
Session = session;
}
public IList<IRepositoryInfo> GetRepositoryInfos(IExtensionsData extension)
{
return GetRepositoriesInternal(null);
}
public IRepositoryInfo GetRepositoryInfo(string repositoryId, IExtensionsData extension)
{
IList<IRepositoryInfo> repositoryInfos = GetRepositoriesInternal(repositoryId);
// find the repository
foreach (IRepositoryInfo info in repositoryInfos)
{
if (info.Id == null) { continue; }
if (info.Id == repositoryId) { return info; }
}
throw new CmisObjectNotFoundException("Repository not found!");
}
public ITypeDefinitionList GetTypeChildren(string repositoryId, string typeId, bool? includePropertyDefinitions,
long? maxItems, long? skipCount, IExtensionsData extension)
{
TypeDefinitionList result = new TypeDefinitionList();
// find the link
string link = null;
if (typeId == null)
{
link = LoadCollection(repositoryId, AtomPubConstants.CollectionTypes);
}
else
{
link = LoadTypeLink(repositoryId, typeId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeChildren);
}
if (link == null)
{
throw new CmisObjectNotFoundException("Unknown repository or type!");
}
UrlBuilder url = new UrlBuilder(link);
url.AddParameter(AtomPubConstants.ParamPropertyDefinitions, includePropertyDefinitions);
url.AddParameter(AtomPubConstants.ParamMaxItems, maxItems);
url.AddParameter(AtomPubConstants.ParamSkipCount, skipCount);
// read and parse
HttpUtils.Response resp = Read(url);
AtomFeed feed = Parse<AtomFeed>(resp.Stream);
// handle top level
foreach (AtomElement element in feed.GetElements())
{
if (element.Object is AtomLink)
{
if (IsNextLink(element)) { result.HasMoreItems = true; }
}
else if (IsInt(NameNumItems, element))
{
result.NumItems = (long)element.Object;
}
}
result.List = new List<ITypeDefinition>(feed.GetEntries().Count);
// get the children
foreach (AtomEntry entry in feed.GetEntries())
{
ITypeDefinition child = null;
LockTypeLinks();
try
{
foreach (AtomElement element in entry.GetElements())
{
if (element.Object is AtomLink)
{
AddTypeLink(repositoryId, entry.Id, (AtomLink)element.Object);
}
else if (element.Object is cmisTypeDefinitionType)
{
child = Converter.Convert((cmisTypeDefinitionType)element.Object);
}
}
}
finally
{
UnlockTypeLinks();
}
if (child != null)
{
result.List.Add(child);
}
}
return result;
}
public IList<ITypeDefinitionContainer> GetTypeDescendants(string repositoryId, string typeId, long? depth,
bool? includePropertyDefinitions, IExtensionsData extension)
{
List<ITypeDefinitionContainer> result = new List<ITypeDefinitionContainer>();
// find the link
string link = null;
if (typeId == null)
{
link = LoadRepositoryLink(repositoryId, AtomPubConstants.RepRelTypeDesc);
}
else
{
link = LoadTypeLink(repositoryId, typeId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeDescendants);
}
if (link == null)
{
throw new CmisObjectNotFoundException("Unknown repository or type!");
}
UrlBuilder url = new UrlBuilder(link);
url.AddParameter(AtomPubConstants.ParamDepth, depth);
url.AddParameter(AtomPubConstants.ParamPropertyDefinitions, includePropertyDefinitions);
// read and parse
HttpUtils.Response resp = Read(url);
AtomFeed feed = Parse<AtomFeed>(resp.Stream);
// process tree
AddTypeDescendantsLevel(repositoryId, feed, result);
return result;
}
private void AddTypeDescendantsLevel(string repositoryId, AtomFeed feed, List<ITypeDefinitionContainer> containerList)
{
if (feed == null || feed.GetEntries().Count == 0)
{
return;
}
foreach (AtomEntry entry in feed.GetEntries())
{
TypeDefinitionContainer childContainer = null;
List<ITypeDefinitionContainer> childContainerList = new List<ITypeDefinitionContainer>();
// walk through the entry
LockTypeLinks();
try
{
foreach (AtomElement element in entry.GetElements())
{
if (element.Object is AtomLink)
{
AddTypeLink(repositoryId, entry.Id, (AtomLink)element.Object);
}
else if (element.Object is cmisTypeDefinitionType)
{
childContainer = new TypeDefinitionContainer();
childContainer.TypeDefinition = Converter.Convert((cmisTypeDefinitionType)element.Object);
}
else if (element.Object is AtomFeed)
{
AddTypeDescendantsLevel(repositoryId, (AtomFeed)element.Object, childContainerList);
}
}
}
finally
{
UnlockTypeLinks();
}
if (childContainer != null)
{
childContainer.Children = childContainerList;
containerList.Add(childContainer);
}
}
}
public ITypeDefinition GetTypeDefinition(string repositoryId, string typeId, IExtensionsData extension)
{
return GetTypeDefinitionInternal(repositoryId, typeId);
}
}
internal class NavigationService : AbstractAtomPubService, INavigationService
{
public NavigationService(BindingSession session)
{
Session = session;
}
public IObjectInFolderList GetChildren(string repositoryId, string folderId, string filter, string orderBy,
bool? includeAllowableActions, IncludeRelationshipsFlag? includeRelationships, string renditionFilter,
bool? includePathSegment, long? maxItems, long? skipCount, IExtensionsData extension)
{
ObjectInFolderList result = new ObjectInFolderList();
// find the link
String link = LoadLink(repositoryId, folderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeChildren);
if (link == null)
{
ThrowLinkException(repositoryId, folderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeChildren);
}
UrlBuilder url = new UrlBuilder(link);
url.AddParameter(AtomPubConstants.ParamFilter, filter);
url.AddParameter(AtomPubConstants.ParamOrderBy, orderBy);
url.AddParameter(AtomPubConstants.ParamAllowableActions, includeAllowableActions);
url.AddParameter(AtomPubConstants.ParamRelationships, includeRelationships);
url.AddParameter(AtomPubConstants.ParamRenditionFilter, renditionFilter);
url.AddParameter(AtomPubConstants.ParamPathSegment, includePathSegment);
url.AddParameter(AtomPubConstants.ParamMaxItems, maxItems);
url.AddParameter(AtomPubConstants.ParamSkipCount, skipCount);
// read and parse
HttpUtils.Response resp = Read(url);
AtomFeed feed = Parse<AtomFeed>(resp.Stream);
// handle top level
foreach (AtomElement element in feed.GetElements())
{
if (element.Object is AtomLink)
{
if (IsNextLink(element)) { result.HasMoreItems = true; }
}
else if (IsInt(NameNumItems, element))
{
result.NumItems = (long)element.Object;
}
}
// get the children
if (feed.GetEntries().Count > 0)
{
result.Objects = new List<IObjectInFolderData>(feed.GetEntries().Count);
foreach (AtomEntry entry in feed.GetEntries())
{
ObjectInFolderData child = null;
String pathSegment = null;
LockLinks();
try
{
// clean up cache
RemoveLinks(repositoryId, entry.Id);
// walk through the entry
foreach (AtomElement element in entry.GetElements())
{
if (element.Object is AtomLink)
{
AddLink(repositoryId, entry.Id, (AtomLink)element.Object);
}
else if (IsStr(NamePathSegment, element))
{
pathSegment = (string)element.Object;
}
else if (element.Object is cmisObjectType)
{
child = new ObjectInFolderData();
child.Object = Converter.Convert((cmisObjectType)element.Object);
}
}
}
finally
{
UnlockLinks();
}
if (child != null)
{
child.PathSegment = pathSegment;
result.Objects.Add(child);
}
}
}
return result;
}
public IList<IObjectInFolderContainer> GetDescendants(string repositoryId, string folderId, long? depth, string filter,
bool? includeAllowableActions, IncludeRelationshipsFlag? includeRelationships, string renditionFilter,
bool? includePathSegment, IExtensionsData extension)
{
IList<IObjectInFolderContainer> result = new List<IObjectInFolderContainer>();
// find the link
String link = LoadLink(repositoryId, folderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeDescendants);
if (link == null)
{
ThrowLinkException(repositoryId, folderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeDescendants);
}
UrlBuilder url = new UrlBuilder(link);
url.AddParameter(AtomPubConstants.ParamDepth, depth);
url.AddParameter(AtomPubConstants.ParamFilter, filter);
url.AddParameter(AtomPubConstants.ParamAllowableActions, includeAllowableActions);
url.AddParameter(AtomPubConstants.ParamRelationships, includeRelationships);
url.AddParameter(AtomPubConstants.ParamRenditionFilter, renditionFilter);
url.AddParameter(AtomPubConstants.ParamPathSegment, includePathSegment);
// read and parse
HttpUtils.Response resp = Read(url);
AtomFeed feed = Parse<AtomFeed>(resp.Stream);
// process tree
AddDescendantsLevel(repositoryId, feed, result);
return result;
}
public IList<IObjectInFolderContainer> GetFolderTree(string repositoryId, string folderId, long? depth, string filter,
bool? includeAllowableActions, IncludeRelationshipsFlag? includeRelationships, string renditionFilter,
bool? includePathSegment, IExtensionsData extension)
{
IList<IObjectInFolderContainer> result = new List<IObjectInFolderContainer>();
// find the link
string link = LoadLink(repositoryId, folderId, AtomPubConstants.RelFolderTree, AtomPubConstants.MediatypeDescendants);
if (link == null)
{
ThrowLinkException(repositoryId, folderId, AtomPubConstants.RelFolderTree, AtomPubConstants.MediatypeDescendants);
}
UrlBuilder url = new UrlBuilder(link);
url.AddParameter(AtomPubConstants.ParamDepth, depth);
url.AddParameter(AtomPubConstants.ParamFilter, filter);
url.AddParameter(AtomPubConstants.ParamAllowableActions, includeAllowableActions);
url.AddParameter(AtomPubConstants.ParamRelationships, includeRelationships);
url.AddParameter(AtomPubConstants.ParamRenditionFilter, renditionFilter);
url.AddParameter(AtomPubConstants.ParamPathSegment, includePathSegment);
// read and parse
HttpUtils.Response resp = Read(url);
AtomFeed feed = Parse<AtomFeed>(resp.Stream);
// process tree
AddDescendantsLevel(repositoryId, feed, result);
return result;
}
public IList<IObjectParentData> GetObjectParents(string repositoryId, string objectId, string filter,
bool? includeAllowableActions, IncludeRelationshipsFlag? includeRelationships, string renditionFilter,
bool? includeRelativePathSegment, IExtensionsData extension)
{
IList<IObjectParentData> result = new List<IObjectParentData>();
// find the link
String link = LoadLink(repositoryId, objectId, AtomPubConstants.RelUp, AtomPubConstants.MediatypeFeed);
if (link == null)
{
// root and unfiled objects have no UP link
return result;
}
UrlBuilder url = new UrlBuilder(link);
url.AddParameter(AtomPubConstants.ParamFilter, filter);
url.AddParameter(AtomPubConstants.ParamAllowableActions, includeAllowableActions);
url.AddParameter(AtomPubConstants.ParamRelationships, includeRelationships);
url.AddParameter(AtomPubConstants.ParamRenditionFilter, renditionFilter);
url.AddParameter(AtomPubConstants.ParamRelativePathSegment, includeRelativePathSegment);
// read and parse
HttpUtils.Response resp = Read(url);
AtomBase atomBase = Parse<AtomBase>(resp.Stream);
if (atomBase is AtomFeed)
{
// it's a feed
AtomFeed feed = (AtomFeed)atomBase;
// walk through the feed
foreach (AtomEntry entry in feed.GetEntries())
{
IObjectParentData objectParent = ProcessParentEntry(entry, repositoryId);
if (objectParent != null)
{
result.Add(objectParent);
}
}
}
else if (atomBase is AtomEntry)
{
// it's an entry
AtomEntry entry = (AtomEntry)atomBase;
IObjectParentData objectParent = ProcessParentEntry(entry, repositoryId);
if (objectParent != null)
{
result.Add(objectParent);
}
}
return result;
}
private IObjectParentData ProcessParentEntry(AtomEntry entry, string repositoryId)
{
ObjectParentData result = null;
String relativePathSegment = null;
LockLinks();
try
{
// clean up cache
RemoveLinks(repositoryId, entry.Id);
// walk through the entry
foreach (AtomElement element in entry.GetElements())
{
if (element.Object is AtomLink)
{
AddLink(repositoryId, entry.Id, (AtomLink)element.Object);
}
else if (element.Object is cmisObjectType)
{
result = new ObjectParentData();
result.Object = Converter.Convert((cmisObjectType)element.Object);
}
else if (IsStr(NameRelativePathSegment, element))
{
relativePathSegment = (string)element.Object;
}
}
}
finally
{
UnlockLinks();
}
if (result != null)
{
result.RelativePathSegment = relativePathSegment;
}
return result;
}
public IObjectData GetFolderParent(string repositoryId, string folderId, string filter, ExtensionsData extension)
{
IObjectData result = null;
// find the link
String link = LoadLink(repositoryId, folderId, AtomPubConstants.RelUp, AtomPubConstants.MediatypeEntry);
if (link == null)
{
ThrowLinkException(repositoryId, folderId, AtomPubConstants.RelUp, AtomPubConstants.MediatypeEntry);
}
UrlBuilder url = new UrlBuilder(link);
url.AddParameter(AtomPubConstants.ParamFilter, filter);
// read
HttpUtils.Response resp = Read(url);
AtomBase atomBase = Parse<AtomBase>(resp.Stream);
// get the entry
AtomEntry entry = null;
if (atomBase is AtomFeed)
{
AtomFeed feed = (AtomFeed)atomBase;
if (feed.GetEntries().Count == 0)
{
throw new CmisRuntimeException("Parent feed is empty!");
}
entry = feed.GetEntries()[0];
}
else if (atomBase is AtomEntry)
{
entry = (AtomEntry)atomBase;
}
else
{
throw new CmisRuntimeException("Unexpected document!");
}
LockLinks();
try
{
// clean up cache
RemoveLinks(repositoryId, entry.Id);
// walk through the entry
foreach (AtomElement element in entry.GetElements())
{
if (element.Object is AtomLink)
{
AddLink(repositoryId, entry.Id, (AtomLink)element.Object);
}
else if (element.Object is cmisObjectType)
{
result = Converter.Convert((cmisObjectType)element.Object);
}
}
}
finally
{
UnlockLinks();
}
return result;
}
public IObjectList GetCheckedOutDocs(string repositoryId, string folderId, string filter, string orderBy,
bool? includeAllowableActions, IncludeRelationshipsFlag? includeRelationships, string renditionFilter,
long? maxItems, long? skipCount, IExtensionsData extension)
{
ObjectList result = new ObjectList();
// find the link
String link = LoadCollection(repositoryId, AtomPubConstants.CollectionCheckedout);
if (link == null)
{
throw new CmisObjectNotFoundException("Unknown repository or checkedout collection not supported!");
}
UrlBuilder url = new UrlBuilder(link);
url.AddParameter(AtomPubConstants.ParamFolderId, folderId);
url.AddParameter(AtomPubConstants.ParamFilter, filter);
url.AddParameter(AtomPubConstants.ParamOrderBy, orderBy);
url.AddParameter(AtomPubConstants.ParamAllowableActions, includeAllowableActions);
url.AddParameter(AtomPubConstants.ParamRelationships, includeRelationships);
url.AddParameter(AtomPubConstants.ParamRenditionFilter, renditionFilter);
url.AddParameter(AtomPubConstants.ParamMaxItems, maxItems);
url.AddParameter(AtomPubConstants.ParamSkipCount, skipCount);
// read and parse
HttpUtils.Response resp = Read(url);
AtomFeed feed = Parse<AtomFeed>(resp.Stream);
// handle top level
foreach (AtomElement element in feed.GetElements())
{
if (element.Object is AtomLink)
{
if (IsNextLink(element))
{
result.HasMoreItems = true;
}
}
else if (IsInt(NameNumItems, element))
{
result.NumItems = (long)element.Object;
}
}
// get the documents
if (feed.GetEntries().Count > 0)
{
result.Objects = new List<IObjectData>(feed.GetEntries().Count);
foreach (AtomEntry entry in feed.GetEntries())
{
IObjectData child = null;
LockLinks();
try
{
// clean up cache
RemoveLinks(repositoryId, entry.Id);
// walk through the entry
foreach (AtomElement element in entry.GetElements())
{
if (element.Object is AtomLink)
{
AddLink(repositoryId, entry.Id, (AtomLink)element.Object);
}
else if (element.Object is cmisObjectType)
{
child = Converter.Convert((cmisObjectType)element.Object);
}
}
}
finally
{
UnlockLinks();
}
if (child != null)
{
result.Objects.Add(child);
}
}
}
return result;
}
private void AddDescendantsLevel(String repositoryId, AtomFeed feed, IList<IObjectInFolderContainer> containerList)
{
if ((feed == null) || (feed.GetEntries().Count == 0))
{
return;
}
// walk through the feed
foreach (AtomEntry entry in feed.GetEntries())
{
ObjectInFolderData objectInFolder = null;
string pathSegment = null;
IList<IObjectInFolderContainer> childContainerList = new List<IObjectInFolderContainer>();
LockLinks();
try
{
// clean up cache
RemoveLinks(repositoryId, entry.Id);
// walk through the entry
foreach (AtomElement element in entry.GetElements())
{
if (element.Object is AtomLink)
{
AddLink(repositoryId, entry.Id, (AtomLink)element.Object);
}
else if (element.Object is cmisObjectType)
{
objectInFolder = new ObjectInFolderData();
objectInFolder.Object = Converter.Convert((cmisObjectType)element.Object);
}
else if (IsStr(NamePathSegment, element))
{
pathSegment = (string)element.Object;
}
else if (element.Object is AtomFeed)
{
AddDescendantsLevel(repositoryId, (AtomFeed)element.Object, childContainerList);
}
}
}
finally
{
UnlockLinks();
}
if (objectInFolder != null)
{
objectInFolder.PathSegment = pathSegment;
ObjectInFolderContainer childContainer = new ObjectInFolderContainer();
childContainer.Object = objectInFolder;
childContainer.Children = childContainerList;
containerList.Add(childContainer);
}
}
}
}
internal class ObjectService : AbstractAtomPubService, IObjectService
{
public ObjectService(BindingSession session)
{
Session = session;
}
public string CreateDocument(string repositoryId, IProperties properties, string folderId, IContentStream contentStream,
VersioningState? versioningState, IList<string> policies, IAcl addAces, IAcl removeAces, IExtensionsData extension)
{
CheckCreateProperties(properties);
// find the link
string link = LoadLink(repositoryId, folderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeChildren);
if (link == null)
{
ThrowLinkException(repositoryId, folderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeChildren);
}
UrlBuilder url = new UrlBuilder(link);
url.AddParameter(AtomPubConstants.ParamVersioningState, versioningState);
// set up object and writer
cmisObjectType cmisObject = new cmisObjectType();
cmisObject.properties = Converter.Convert(properties);
cmisObject.policyIds = Converter.ConvertPolicies(policies);
String mediaType = null;
Stream stream = null;
if (contentStream != null)
{
mediaType = contentStream.MimeType;
stream = contentStream.Stream;
}
AtomEntryWriter entryWriter = new AtomEntryWriter(cmisObject, mediaType, stream);
// post the new folder object
HttpUtils.Response resp = Post(url, AtomPubConstants.MediatypeEntry, new HttpUtils.Output(entryWriter.Write));
// parse the response
AtomEntry entry = Parse<AtomEntry>(resp.Stream);
// handle ACL modifications
HandleAclModifications(repositoryId, entry, addAces, removeAces);
return entry.Id;
}
public string CreateDocumentFromSource(string repositoryId, string sourceId, IProperties properties, string folderId,
VersioningState? versioningState, IList<string> policies, IAcl addAces, IAcl removeAces, IExtensionsData extension)
{
throw new CmisNotSupportedException("createDocumentFromSource is not supported by the AtomPub binding!");
}
public string CreateFolder(string repositoryId, IProperties properties, string folderId, IList<string> policies,
IAcl addAces, IAcl removeAces, IExtensionsData extension)
{
CheckCreateProperties(properties);
// find the link
string link = LoadLink(repositoryId, folderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeChildren);
if (link == null)
{
ThrowLinkException(repositoryId, folderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeChildren);
}
UrlBuilder url = new UrlBuilder(link);
// set up object and writer
cmisObjectType cmisObject = new cmisObjectType();
cmisObject.properties = Converter.Convert(properties);
cmisObject.policyIds = Converter.ConvertPolicies(policies);
AtomEntryWriter entryWriter = new AtomEntryWriter(cmisObject);
// post the new folder object
HttpUtils.Response resp = Post(url, AtomPubConstants.MediatypeEntry, new HttpUtils.Output(entryWriter.Write));
// parse the response
AtomEntry entry = Parse<AtomEntry>(resp.Stream);
// handle ACL modifications
HandleAclModifications(repositoryId, entry, addAces, removeAces);
return entry.Id;
}
public string CreateRelationship(string repositoryId, IProperties properties, IList<string> policies, IAcl addAces,
IAcl removeAces, IExtensionsData extension)
{
CheckCreateProperties(properties);
// find source id
IPropertyData sourceIdProperty = properties[PropertyIds.SourceId];
if (sourceIdProperty == null || sourceIdProperty.PropertyType != PropertyType.Id)
{
throw new CmisInvalidArgumentException("Source Id is not set!");
}
string sourceId = sourceIdProperty.FirstValue as string;
if (sourceId == null)
{
throw new CmisInvalidArgumentException("Source Id is not set!");
}
// find the link
string link = LoadLink(repositoryId, sourceId, AtomPubConstants.RelRelationships, AtomPubConstants.MediatypeFeed);
if (link == null)
{
ThrowLinkException(repositoryId, sourceId, AtomPubConstants.RelRelationships, AtomPubConstants.MediatypeFeed);
}
UrlBuilder url = new UrlBuilder(link);
// set up object and writer
cmisObjectType cmisObject = new cmisObjectType();
cmisObject.properties = Converter.Convert(properties);
cmisObject.policyIds = Converter.ConvertPolicies(policies);
AtomEntryWriter entryWriter = new AtomEntryWriter(cmisObject);
// post the new folder object
HttpUtils.Response resp = Post(url, AtomPubConstants.MediatypeEntry, new HttpUtils.Output(entryWriter.Write));
// parse the response
AtomEntry entry = Parse<AtomEntry>(resp.Stream);
// handle ACL modifications
HandleAclModifications(repositoryId, entry, addAces, removeAces);
return entry.Id;
}
public string CreatePolicy(string repositoryId, IProperties properties, string folderId, IList<string> policies,
IAcl addAces, IAcl removeAces, IExtensionsData extension)
{
CheckCreateProperties(properties);
// find the link
string link = LoadLink(repositoryId, folderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeChildren);
if (link == null)
{
ThrowLinkException(repositoryId, folderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeChildren);
}
UrlBuilder url = new UrlBuilder(link);
// set up object and writer
cmisObjectType cmisObject = new cmisObjectType();
cmisObject.properties = Converter.Convert(properties);
cmisObject.policyIds = Converter.ConvertPolicies(policies);
AtomEntryWriter entryWriter = new AtomEntryWriter(cmisObject);
// post the new folder object
HttpUtils.Response resp = Post(url, AtomPubConstants.MediatypeEntry, new HttpUtils.Output(entryWriter.Write));
// parse the response
AtomEntry entry = Parse<AtomEntry>(resp.Stream);
// handle ACL modifications
HandleAclModifications(repositoryId, entry, addAces, removeAces);
return entry.Id;
}
public IAllowableActions GetAllowableActions(string repositoryId, string objectId, IExtensionsData extension)
{
// find the link
string link = LoadLink(repositoryId, objectId, AtomPubConstants.RelAllowableActions, AtomPubConstants.MediatypeAllowableAction);
if (link == null)
{
ThrowLinkException(repositoryId, objectId, AtomPubConstants.RelAllowableActions, AtomPubConstants.MediatypeAllowableAction);
}
UrlBuilder url = new UrlBuilder(link);
// read and parse
HttpUtils.Response resp = Read(url);
AtomAllowableActions allowableActions = Parse<AtomAllowableActions>(resp.Stream);
return Converter.Convert(allowableActions.AllowableActions);
}
public IProperties GetProperties(string repositoryId, string objectId, string filter, IExtensionsData extension)
{
IObjectData obj = GetObjectInternal(repositoryId, IdentifierType.Id, objectId, ReturnVersion.This, filter,
false, IncludeRelationshipsFlag.None, "cmis:none", false, false, extension);
return obj.Properties;
}
public IList<IRenditionData> GetRenditions(string repositoryId, string objectId, string renditionFilter,
long? maxItems, long? skipCount, IExtensionsData extension)
{
IObjectData obj = GetObjectInternal(repositoryId, IdentifierType.Id, objectId, ReturnVersion.This,
PropertyIds.ObjectId, false, IncludeRelationshipsFlag.None, renditionFilter, false, false, extension);
IList<IRenditionData> result = obj.Renditions;
if (result == null)
{
result = new List<IRenditionData>();
}
return result;
}
public IObjectData GetObject(string repositoryId, string objectId, string filter, bool? includeAllowableActions,
IncludeRelationshipsFlag? includeRelationships, string renditionFilter, bool? includePolicyIds,
bool? includeAcl, IExtensionsData extension)
{
return GetObjectInternal(repositoryId, IdentifierType.Id, objectId, ReturnVersion.This, filter, includeAllowableActions,
includeRelationships, renditionFilter, includePolicyIds, includeAcl, extension);
}
public IObjectData GetObjectByPath(string repositoryId, string path, string filter, bool? includeAllowableActions,
IncludeRelationshipsFlag? includeRelationships, string renditionFilter, bool? includePolicyIds, bool? includeAcl,
IExtensionsData extension)
{
return GetObjectInternal(repositoryId, IdentifierType.Path, path, ReturnVersion.This, filter, includeAllowableActions,
includeRelationships, renditionFilter, includePolicyIds, includeAcl, extension);
}
public IContentStream GetContentStream(string repositoryId, string objectId, string streamId, long? offset, long? length,
IExtensionsData extension)
{
ContentStream result = new ContentStream();
// find the link
string link = LoadLink(repositoryId, objectId, AtomPubConstants.LinkRelContent, null);
if (link == null)
{
throw new CmisConstraintException("No content stream");
}
UrlBuilder url = new UrlBuilder(link);
url.AddParameter(AtomPubConstants.ParamStreamId, streamId);
// get the content
if (offset != null && offset > Int32.MaxValue)
{
throw new CmisInvalidArgumentException("Offset >" + Int32.MaxValue);
}
if (length != null && length > Int32.MaxValue)
{
throw new CmisInvalidArgumentException("Length >" + Int32.MaxValue);
}
HttpUtils.Response resp = HttpUtils.InvokeGET(url, Session, (int?)offset, (int?)length);
// check response code
if (resp.StatusCode != HttpStatusCode.OK && resp.StatusCode != HttpStatusCode.PartialContent)
{
throw ConvertStatusCode(resp.StatusCode, resp.Message, resp.ErrorContent, null);
}
result.FileName = null;
result.Length = resp.ContentLength;
result.MimeType = resp.ContentType;
result.Stream = resp.Stream;
return result;
}
public void UpdateProperties(string repositoryId, ref string objectId, ref string changeToken, IProperties properties,
IExtensionsData extension)
{
// we need an object id
if (objectId == null || objectId.Length == 0)
{
throw new CmisInvalidArgumentException("Object id must be set!");
}
// find the link
string link = LoadLink(repositoryId, objectId, AtomPubConstants.RelSelf, AtomPubConstants.MediatypeEntry);
if (link == null)
{
ThrowLinkException(repositoryId, objectId, AtomPubConstants.RelSelf, AtomPubConstants.MediatypeEntry);
}
UrlBuilder url = new UrlBuilder(link);
url.AddParameter(AtomPubConstants.ParamChangeToken, changeToken);
// set up object and writer
cmisObjectType cmisObject = new cmisObjectType();
cmisObject.properties = Converter.Convert(properties);
AtomEntryWriter entryWriter = new AtomEntryWriter(cmisObject);
// update
HttpUtils.Response resp = Put(url, AtomPubConstants.MediatypeEntry, new HttpUtils.Output(entryWriter.Write));
// parse new entry
AtomEntry entry = Parse<AtomEntry>(resp.Stream);
// we expect a CMIS entry
if (entry.Id == null)
{
throw new CmisConnectionException("Received Atom entry is not a CMIS entry!");
}
// set object id
objectId = entry.Id;
changeToken = null;
LockLinks();
try
{
// clean up cache
RemoveLinks(repositoryId, entry.Id);
// walk through the entry
foreach (AtomElement element in entry.GetElements())
{
if (element.Object is AtomLink)
{
AddLink(repositoryId, entry.Id, (AtomLink)element.Object);
}
else if (element.Object is cmisObjectType)
{
// extract new change token
cmisObject = (cmisObjectType)element.Object;
if (cmisObject.properties != null)
{
foreach (cmisProperty property in cmisObject.properties.Items)
{
if (PropertyIds.ChangeToken == property.propertyDefinitionId && property is cmisPropertyString)
{
cmisPropertyString changeTokenProperty = (cmisPropertyString)property;
if (changeTokenProperty.value != null && changeTokenProperty.value.Length > 0)
{
changeToken = changeTokenProperty.value[0];
}
break;
}
}
}
}
}
}
finally
{
UnlockLinks();
}
}
public void MoveObject(string repositoryId, ref string objectId, string targetFolderId, string sourceFolderId,
IExtensionsData extension)
{
if (objectId == null || objectId.Length == 0)
{
throw new CmisInvalidArgumentException("Object id must be set!");
}
if (targetFolderId == null || targetFolderId.Length == 0 || sourceFolderId == null || sourceFolderId.Length == 0)
{
throw new CmisInvalidArgumentException("Source and target folder must be set!");
}
// find the link
String link = LoadLink(repositoryId, targetFolderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeChildren);
if (link == null)
{
ThrowLinkException(repositoryId, targetFolderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeChildren);
}
UrlBuilder url = new UrlBuilder(link);
url.AddParameter(AtomPubConstants.ParamSourceFolderId, sourceFolderId);
// set up object and writer
AtomEntryWriter entryWriter = new AtomEntryWriter(CreateIdObject(objectId));
// post move request
HttpUtils.Response resp = Post(url, AtomPubConstants.MediatypeEntry, new HttpUtils.Output(entryWriter.Write));
// parse the response
AtomEntry entry = Parse<AtomEntry>(resp.Stream);
objectId = entry.Id;
}
public void DeleteObject(string repositoryId, string objectId, bool? allVersions, IExtensionsData extension)
{
// find the link
string link = LoadLink(repositoryId, objectId, AtomPubConstants.RelSelf, AtomPubConstants.MediatypeEntry);
if (link == null)
{
ThrowLinkException(repositoryId, objectId, AtomPubConstants.RelSelf, AtomPubConstants.MediatypeEntry);
}
UrlBuilder url = new UrlBuilder(link);
url.AddParameter(AtomPubConstants.ParamAllVersions, allVersions);
Delete(url);
}
public IFailedToDeleteData DeleteTree(string repositoryId, string folderId, bool? allVersions, UnfileObject? unfileObjects,
bool? continueOnFailure, ExtensionsData extension)
{
// find the link
String link = LoadLink(repositoryId, folderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeDescendants);
if (link == null)
{
link = LoadLink(repositoryId, folderId, AtomPubConstants.RelFolderTree, AtomPubConstants.MediatypeDescendants);
}
if (link == null)
{
ThrowLinkException(repositoryId, folderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeDescendants);
}
UrlBuilder url = new UrlBuilder(link);
url.AddParameter(AtomPubConstants.ParamAllVersions, allVersions);
url.AddParameter(AtomPubConstants.ParamUnfildeObjects, unfileObjects);
url.AddParameter(AtomPubConstants.ParamContinueOnFailure, continueOnFailure);
// make the call
HttpUtils.Response resp = HttpUtils.InvokeDELETE(url, Session);
// check response code
if (resp.StatusCode == HttpStatusCode.OK || resp.StatusCode == HttpStatusCode.Accepted || resp.StatusCode == HttpStatusCode.NoContent)
{
return new FailedToDeleteData();
}
// If the server returned an internal server error, get the remaining
// children of the folder. We only retrieve the first level, since
// getDescendants() is not supported by all repositories.
if (resp.StatusCode == HttpStatusCode.InternalServerError)
{
link = LoadLink(repositoryId, folderId, AtomPubConstants.RelDown, AtomPubConstants.MediatypeChildren);
if (link != null)
{
url = new UrlBuilder(link);
// we only want the object ids
url.AddParameter(AtomPubConstants.ParamFilter, "cmis:objectId");
url.AddParameter(AtomPubConstants.ParamAllowableActions, false);
url.AddParameter(AtomPubConstants.ParamRelationships, IncludeRelationshipsFlag.None);
url.AddParameter(AtomPubConstants.ParamRenditionFilter, "cmis:none");
url.AddParameter(AtomPubConstants.ParamPathSegment, false);
// 1000 children should be enough to indicate a problem
url.AddParameter(AtomPubConstants.ParamMaxItems, 1000);
url.AddParameter(AtomPubConstants.ParamSkipCount, 0);
// read and parse
resp = Read(url);
AtomFeed feed = Parse<AtomFeed>(resp.Stream);
// prepare result
FailedToDeleteData result = new FailedToDeleteData();
List<string> ids = new List<string>();
result.Ids = ids;
// get the children ids
foreach (AtomEntry entry in feed.GetEntries())
{
ids.Add(entry.Id);
}
return result;
}
}
throw ConvertStatusCode(resp.StatusCode, resp.Message, resp.ErrorContent, null);
}
public void SetContentStream(string repositoryId, ref string objectId, bool? overwriteFlag, ref string changeToken,
IContentStream contentStream, IExtensionsData extension)
{
if (objectId == null)
{
throw new CmisInvalidArgumentException("Object ID must be set!");
}
// we need content
if (contentStream == null || contentStream.Stream == null || contentStream.MimeType == null)
{
throw new CmisInvalidArgumentException("Content must be set!");
}
// find the link
String link = LoadLink(repositoryId, objectId, AtomPubConstants.RelEditMedia, null);
if (link == null)
{
ThrowLinkException(repositoryId, objectId, AtomPubConstants.RelEditMedia, null);
}
UrlBuilder url = new UrlBuilder(link);
url.AddParameter(AtomPubConstants.ParamChangeToken, changeToken);
url.AddParameter(AtomPubConstants.ParamOverwriteFlag, overwriteFlag);
HttpUtils.Output output = delegate(Stream stream)
{
int b;
byte[] buffer = new byte[4096];
while ((b = contentStream.Stream.Read(buffer, 0, buffer.Length)) > 0)
{
stream.Write(buffer, 0, b);
}
contentStream.Stream.Close();
};
IDictionary<string, string> headers = null;
if (contentStream.FileName != null)
{
headers = new Dictionary<string, string>();
headers.Add(MimeHelper.ContentDisposition,
MimeHelper.EncodeContentDisposition(MimeHelper.DispositionAttachment, contentStream.FileName));
}
// send content
HttpUtils.Response resp = HttpUtils.InvokePUT(url, contentStream.MimeType, headers, output, Session);
// check response code
if (resp.StatusCode != HttpStatusCode.OK && resp.StatusCode != HttpStatusCode.Created && resp.StatusCode != HttpStatusCode.NoContent)
{
throw ConvertStatusCode(resp.StatusCode, resp.Message, resp.ErrorContent, null);
}
objectId = null;
changeToken = null;
}
public void DeleteContentStream(string repositoryId, ref string objectId, ref string changeToken, IExtensionsData extension)
{
if (objectId == null)
{
throw new CmisInvalidArgumentException("Object ID must be set!");
}
// find the link
String link = LoadLink(repositoryId, objectId, AtomPubConstants.RelEditMedia, null);
if (link == null)
{
ThrowLinkException(repositoryId, objectId, AtomPubConstants.RelEditMedia, null);
}
UrlBuilder url = new UrlBuilder(link);
if (changeToken != null)
{
url.AddParameter(AtomPubConstants.ParamChangeToken, changeToken);
}
Delete(url);
objectId = null;
changeToken = null;
}
// ---- internal ---
private void CheckCreateProperties(IProperties properties)
{
if (properties == null || properties.PropertyList == null)
{
throw new CmisInvalidArgumentException("Properties must be set!");
}
if (properties[PropertyIds.ObjectTypeId] == null)
{
throw new CmisInvalidArgumentException("Property " + PropertyIds.ObjectTypeId + " must be set!");
}
if (properties[PropertyIds.ObjectId] != null)
{
throw new CmisInvalidArgumentException("Property " + PropertyIds.ObjectId + " must not be set!");
}
}
private void HandleAclModifications(String repositoryId, AtomEntry entry, IAcl addAces, IAcl removeAces)
{
if (!IsAclMergeRequired(addAces, removeAces))
{
return;
}
IAcl originalAces = null;
// walk through the entry and find the current ACL
foreach (AtomElement element in entry.GetElements())
{
if (element.Object is cmisObjectType)
{
// extract current ACL
cmisObjectType cmisObject = (cmisObjectType)element.Object;
originalAces = Converter.Convert(cmisObject.acl, cmisObject.exactACLSpecified ? (bool?)cmisObject.exactACL : null);
break;
}
}
if (originalAces != null)
{
// merge and update ACL
IAcl newACL = MergeAcls(originalAces, addAces, removeAces);
if (newACL != null)
{
UpdateAcl(repositoryId, entry.Id, newACL, null);
}
}
}
}
internal class VersioningService : AbstractAtomPubService, IVersioningService
{
public VersioningService(BindingSession session)
{
Session = session;
}
public void CheckOut(string repositoryId, ref string objectId, IExtensionsData extension, out bool? contentCopied)
{
if (objectId == null || objectId.Length == 0)
{
throw new CmisInvalidArgumentException("Object id must be set!");
}
// find the link
String link = LoadCollection(repositoryId, AtomPubConstants.CollectionCheckedout);
if (link == null)
{
throw new CmisObjectNotFoundException("Unknown repository or checkedout collection not supported!");
}
UrlBuilder url = new UrlBuilder(link);
// set up object and writer
AtomEntryWriter entryWriter = new AtomEntryWriter(CreateIdObject(objectId));
// post move request
HttpUtils.Response resp = Post(url, AtomPubConstants.MediatypeEntry, new HttpUtils.Output(entryWriter.Write));
// parse the response
AtomEntry entry = Parse<AtomEntry>(resp.Stream);
objectId = entry.Id;
LockLinks();
try
{
// clean up cache
RemoveLinks(repositoryId, entry.Id);
// walk through the entry
foreach (AtomElement element in entry.GetElements())
{
if (element.Object is AtomLink)
{
AddLink(repositoryId, entry.Id, (AtomLink)element.Object);
}
}
}
finally
{
UnlockLinks();
}
contentCopied = null;
}
public void CancelCheckOut(string repositoryId, string objectId, IExtensionsData extension)
{
// find the link
String link = LoadLink(repositoryId, objectId, AtomPubConstants.RelSelf, AtomPubConstants.MediatypeEntry);
if (link == null)
{
ThrowLinkException(repositoryId, objectId, AtomPubConstants.RelSelf, AtomPubConstants.MediatypeEntry);
}
// prefer working copy link if available
// (workaround for non-compliant repositories)
string wcLink = GetLink(repositoryId, objectId, AtomPubConstants.RelWorkingCopy, AtomPubConstants.MediatypeEntry);
if (wcLink != null)
{
link = wcLink;
}
Delete(new UrlBuilder(link));
}
public void CheckIn(string repositoryId, ref string objectId, bool? major, IProperties properties,
IContentStream contentStream, string checkinComment, IList<string> policies, IAcl addAces, IAcl removeAces,
IExtensionsData extension)
{
// we need an object id
if (objectId == null || objectId.Length == 0)
{
throw new CmisInvalidArgumentException("Object id must be set!");
}
// find the link
string link = LoadLink(repositoryId, objectId, AtomPubConstants.RelSelf, AtomPubConstants.MediatypeEntry);
if (link == null)
{
ThrowLinkException(repositoryId, objectId, AtomPubConstants.RelSelf, AtomPubConstants.MediatypeEntry);
}
// prefer working copy link if available
// (workaround for non-compliant repositories)
string wcLink = GetLink(repositoryId, objectId, AtomPubConstants.RelWorkingCopy, AtomPubConstants.MediatypeEntry);
if (wcLink != null)
{
link = wcLink;
}
UrlBuilder url = new UrlBuilder(link);
url.AddParameter(AtomPubConstants.ParamCheckinComment, checkinComment);
url.AddParameter(AtomPubConstants.ParamMajor, major);
url.AddParameter(AtomPubConstants.ParamCheckIn, "true");
// set up object and writer
cmisObjectType cmisObject = new cmisObjectType();
cmisObject.properties = Converter.Convert(properties);
cmisObject.policyIds = Converter.ConvertPolicies(policies);
if (cmisObject.properties == null)
{
cmisObject.properties = new cmisPropertiesType();
}
string mediaType = null;
Stream stream = null;
if (contentStream != null)
{
mediaType = contentStream.MimeType;
stream = contentStream.Stream;
}
AtomEntryWriter entryWriter = new AtomEntryWriter(cmisObject, mediaType, stream);
// update
HttpUtils.Response resp = Put(url, AtomPubConstants.MediatypeEntry, new HttpUtils.Output(entryWriter.Write));
// parse new entry
AtomEntry entry = Parse<AtomEntry>(resp.Stream);
// we expect a CMIS entry
if (entry.Id == null)
{
throw new CmisConnectionException("Received Atom entry is not a CMIS entry!");
}
// set object id
objectId = entry.Id;
IAcl originalAces = null;
LockLinks();
try
{
// clean up cache
RemoveLinks(repositoryId, entry.Id);
// walk through the entry
foreach (AtomElement element in entry.GetElements())
{
if (element.Object is AtomLink)
{
AddLink(repositoryId, entry.Id, (AtomLink)element.Object);
}
else if (element.Object is cmisObjectType)
{
// extract current ACL
cmisObject = (cmisObjectType)element.Object;
originalAces = Converter.Convert(cmisObject.acl, cmisObject.exactACLSpecified ? (bool?)cmisObject.exactACL : null);
}
}
}
finally
{
UnlockLinks();
}
// handle ACL modifications
if ((originalAces != null) && (IsAclMergeRequired(addAces, removeAces)))
{
// merge and update ACL
IAcl newACL = MergeAcls(originalAces, addAces, removeAces);
if (newACL != null)
{
UpdateAcl(repositoryId, entry.Id, newACL, null);
}
}
}
public IObjectData GetObjectOfLatestVersion(string repositoryId, string objectId, string versionSeriesId, bool major,
string filter, bool? includeAllowableActions, IncludeRelationshipsFlag? includeRelationships,
string renditionFilter, bool? includePolicyIds, bool? includeAcl, IExtensionsData extension)
{
ReturnVersion returnVersion = ReturnVersion.Latest;
if (major)
{
returnVersion = ReturnVersion.LatestMajor;
}
return GetObjectInternal(repositoryId, IdentifierType.Id, objectId, returnVersion, filter,
includeAllowableActions, includeRelationships, renditionFilter, includePolicyIds, includeAcl, extension);
}
public IProperties GetPropertiesOfLatestVersion(string repositoryId, string objectId, string versionSeriesId, bool major,
string filter, IExtensionsData extension)
{
ReturnVersion returnVersion = ReturnVersion.Latest;
if (major)
{
returnVersion = ReturnVersion.LatestMajor;
}
IObjectData objectData = GetObjectInternal(repositoryId, IdentifierType.Id, objectId, returnVersion, filter,
false, IncludeRelationshipsFlag.None, "cmis:none", false, false, extension);
return objectData.Properties;
}
public IList<IObjectData> GetAllVersions(string repositoryId, string objectId, string versionSeriesId, string filter,
bool? includeAllowableActions, IExtensionsData extension)
{
IList<IObjectData> result = new List<IObjectData>();
// find the link
string link = LoadLink(repositoryId, objectId, AtomPubConstants.RelVersionHistory, AtomPubConstants.MediatypeFeed);
if (link == null)
{
ThrowLinkException(repositoryId, objectId, AtomPubConstants.RelVersionHistory, AtomPubConstants.MediatypeFeed);
}
UrlBuilder url = new UrlBuilder(link);
url.AddParameter(AtomPubConstants.ParamFilter, filter);
url.AddParameter(AtomPubConstants.ParamAllowableActions, includeAllowableActions);
// read and parse
HttpUtils.Response resp = Read(url);
AtomFeed feed = Parse<AtomFeed>(resp.Stream);
// get the versions
if (feed.GetEntries().Count > 0)
{
foreach (AtomEntry entry in feed.GetEntries())
{
IObjectData version = null;
LockLinks();
try
{
// clean up cache
RemoveLinks(repositoryId, entry.Id);
// walk through the entry
foreach (AtomElement element in entry.GetElements())
{
if (element.Object is AtomLink)
{
AddLink(repositoryId, entry.Id, (AtomLink)element.Object);
}
else if (element.Object is cmisObjectType)
{
version = Converter.Convert((cmisObjectType)element.Object);
}
}
}
finally
{
UnlockLinks();
}
if (version != null)
{
result.Add(version);
}
}
}
return result;
}