diff --git a/config/alfresco/application-context-highlevel.xml b/config/alfresco/application-context-highlevel.xml
index 0d6e846270..d53053cb80 100644
--- a/config/alfresco/application-context-highlevel.xml
+++ b/config/alfresco/application-context-highlevel.xml
@@ -17,6 +17,7 @@
+
diff --git a/config/alfresco/model/cmisModel.xml b/config/alfresco/model/cmisModel.xml
index 7a728d01bd..7c1e74fbb1 100644
--- a/config/alfresco/model/cmisModel.xml
+++ b/config/alfresco/model/cmisModel.xml
@@ -10,6 +10,8 @@
+
+
@@ -47,7 +49,7 @@
-
+
Object Id
@@ -82,6 +84,17 @@
true
+
+ Name
+ Name
+ d:text
+ false
+ false
+ false
+
+ both
+
+
Created by
The authority who created this object
@@ -135,23 +148,14 @@
false
-
-
-
-
-
- cmis:object
-
-
- Name
- Name
- d:text
- false
+
+ Alfresco Node Ref
+ Alfresco Node Ref
+ cmis:id
+ true
false
false
-
- both
-
+
@@ -159,14 +163,14 @@
Document
Document Type
- cmis:filesystemobject
+ cmisext:object
Is Immutable
Is the document immutable?
d:boolean
true
- false
+ false
false
@@ -184,7 +188,7 @@
Is this a major version of the document?
d:boolean
true
- false
+ false
false
@@ -193,7 +197,7 @@
Is this the latest major version of the document?
d:boolean
true
- false
+ false
false
@@ -229,7 +233,7 @@
The authority who checked out this document version series
d:text
true
- false
+ false
false
@@ -238,7 +242,7 @@
The checked out version series id
cmis:id
true
- false
+ false
false
@@ -247,7 +251,7 @@
The checkin comment
d:text
true
- false
+ false
false
@@ -256,7 +260,7 @@
The length of the content stream
d:long
true
- false
+ false
false
false
@@ -267,7 +271,7 @@
The content stream MIME type
d:text
true
- false
+ false
false
false
@@ -278,7 +282,7 @@
The content stream filename
d:text
true
- false
+ false
false
true
@@ -289,7 +293,7 @@
Id of the stream
cmis:id
true
- false
+ false
false
@@ -299,7 +303,7 @@
Folder
Folder Type
- cmis:filesystemobject
+ cmisext:object
Parent Id
@@ -326,7 +330,7 @@
The allowed child object type ids
cmis:id
true
- false
+ false
true
@@ -336,7 +340,7 @@
Relationship
Relationship Type
- cmis:filesystemobject
+ cmisext:object
Source Id
@@ -362,7 +366,7 @@
Policy
Policy Type
- cmis:object
+ cmisext:object
Policy Text
@@ -376,6 +380,11 @@
+
+ Aspects
+ Aspects Type
+ cmis:policy
+
\ No newline at end of file
diff --git a/config/alfresco/opencmis-context.xml b/config/alfresco/opencmis-context.xml
new file mode 100644
index 0000000000..b5b0f56ea3
--- /dev/null
+++ b/config/alfresco/opencmis-context.xml
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ org.alfresco.repo.search.impl.querymodel.QueryEngine
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ org.alfresco.repo.search.impl.querymodel.QueryEngine.executeQuery=ACL_ALLOW,AFTER_ACL_NODE.sys:base.Read
+ org.alfresco.repo.search.impl.querymodel.QueryEngine.getQueryModelFactory=ACL_ALLOW
+
+
+
+
\ No newline at end of file
diff --git a/source/java/org/alfresco/cmis/mapping/CMISMapping.java b/source/java/org/alfresco/cmis/mapping/CMISMapping.java
index 8891a654e8..018cbde47f 100644
--- a/source/java/org/alfresco/cmis/mapping/CMISMapping.java
+++ b/source/java/org/alfresco/cmis/mapping/CMISMapping.java
@@ -70,6 +70,9 @@ public class CMISMapping implements InitializingBean
public static String CMIS_MODEL_NS = "cmis";
public static String CMIS_MODEL_URI = "http://www.alfresco.org/model/cmis/1.0/cs01";
+ public static String CMIS_EXT_NS = "cmisext";
+ public static String CMIS_EXT_URI = "http://www.alfresco.org/model/cmis/1.0/cs01ext";
+
/**
* The Alfresco CMIS Model name.
*/
@@ -87,7 +90,7 @@ public class CMISMapping implements InitializingBean
public static QName CMIS_DATATYPE_HTML = QName.createQName(CMIS_MODEL_URI, "html");
// CMIS Types
- public static QName OBJECT_QNAME = QName.createQName(CMIS_MODEL_URI, "object");
+ public static QName OBJECT_QNAME = QName.createQName(CMIS_EXT_URI, "object");
public static QName FILESYSTEM_OBJECT_QNAME = QName.createQName(CMIS_MODEL_URI, "filesystemobject");
public static QName DOCUMENT_QNAME = QName.createQName(CMIS_MODEL_URI, "document");
public static QName FOLDER_QNAME = QName.createQName(CMIS_MODEL_URI, "folder");
diff --git a/source/java/org/alfresco/opencmis/AlfrescoCmisService.java b/source/java/org/alfresco/opencmis/AlfrescoCmisService.java
new file mode 100644
index 0000000000..04a5013713
--- /dev/null
+++ b/source/java/org/alfresco/opencmis/AlfrescoCmisService.java
@@ -0,0 +1,2613 @@
+/*
+ * Copyright (C) 2005-2010 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+package org.alfresco.opencmis;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.math.BigInteger;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.transaction.Status;
+import javax.transaction.UserTransaction;
+
+import org.alfresco.cmis.CMISInvalidArgumentException;
+import org.alfresco.model.ContentModel;
+import org.alfresco.opencmis.dictionary.DocumentTypeDefinitionWrapper;
+import org.alfresco.opencmis.dictionary.FolderTypeDefintionWrapper;
+import org.alfresco.opencmis.dictionary.TypeDefinitionWrapper;
+import org.alfresco.repo.content.encoding.ContentCharsetFinder;
+import org.alfresco.repo.node.integrity.IntegrityException;
+import org.alfresco.repo.search.QueryParameterDefImpl;
+import org.alfresco.repo.security.authentication.AuthenticationException;
+import org.alfresco.repo.security.permissions.AccessDeniedException;
+import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
+import org.alfresco.repo.version.VersionModel;
+import org.alfresco.service.cmr.coci.CheckOutCheckInServiceException;
+import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
+import org.alfresco.service.cmr.model.FileExistsException;
+import org.alfresco.service.cmr.model.FileInfo;
+import org.alfresco.service.cmr.model.FileNotFoundException;
+import org.alfresco.service.cmr.repository.AssociationRef;
+import org.alfresco.service.cmr.repository.ChildAssociationRef;
+import org.alfresco.service.cmr.repository.ContentWriter;
+import org.alfresco.service.cmr.repository.EntityRef;
+import org.alfresco.service.cmr.repository.InvalidNodeRefException;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.search.QueryParameterDefinition;
+import org.alfresco.service.cmr.search.ResultSet;
+import org.alfresco.service.cmr.search.SearchParameters;
+import org.alfresco.service.cmr.search.SearchService;
+import org.alfresco.service.cmr.version.Version;
+import org.alfresco.service.cmr.version.VersionHistory;
+import org.alfresco.service.cmr.version.VersionType;
+import org.alfresco.service.namespace.NamespaceService;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.service.namespace.RegexQNamePattern;
+import org.alfresco.util.TempFileProvider;
+import org.apache.chemistry.opencmis.commons.PropertyIds;
+import org.apache.chemistry.opencmis.commons.data.Acl;
+import org.apache.chemistry.opencmis.commons.data.AllowableActions;
+import org.apache.chemistry.opencmis.commons.data.ContentStream;
+import org.apache.chemistry.opencmis.commons.data.ExtensionsData;
+import org.apache.chemistry.opencmis.commons.data.FailedToDeleteData;
+import org.apache.chemistry.opencmis.commons.data.ObjectData;
+import org.apache.chemistry.opencmis.commons.data.ObjectInFolderContainer;
+import org.apache.chemistry.opencmis.commons.data.ObjectInFolderData;
+import org.apache.chemistry.opencmis.commons.data.ObjectInFolderList;
+import org.apache.chemistry.opencmis.commons.data.ObjectList;
+import org.apache.chemistry.opencmis.commons.data.ObjectParentData;
+import org.apache.chemistry.opencmis.commons.data.Properties;
+import org.apache.chemistry.opencmis.commons.data.RenditionData;
+import org.apache.chemistry.opencmis.commons.data.RepositoryInfo;
+import org.apache.chemistry.opencmis.commons.definitions.DocumentTypeDefinition;
+import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
+import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionContainer;
+import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionList;
+import org.apache.chemistry.opencmis.commons.enums.AclPropagation;
+import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
+import org.apache.chemistry.opencmis.commons.enums.ContentStreamAllowed;
+import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships;
+import org.apache.chemistry.opencmis.commons.enums.RelationshipDirection;
+import org.apache.chemistry.opencmis.commons.enums.UnfileObject;
+import org.apache.chemistry.opencmis.commons.enums.VersioningState;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisBaseException;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisConstraintException;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisContentAlreadyExistsException;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisPermissionDeniedException;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisStorageException;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisStreamNotSupportedException;
+import org.apache.chemistry.opencmis.commons.exceptions.CmisVersioningException;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlListImpl;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.FailedToDeleteDataImpl;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectInFolderContainerImpl;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectInFolderDataImpl;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectInFolderListImpl;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectListImpl;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectParentDataImpl;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.TypeDefinitionContainerImpl;
+import org.apache.chemistry.opencmis.commons.impl.dataobjects.TypeDefinitionListImpl;
+import org.apache.chemistry.opencmis.commons.impl.server.AbstractCmisService;
+import org.apache.chemistry.opencmis.commons.impl.server.ObjectInfoImpl;
+import org.apache.chemistry.opencmis.commons.impl.server.RenditionInfoImpl;
+import org.apache.chemistry.opencmis.commons.server.CallContext;
+import org.apache.chemistry.opencmis.commons.server.ObjectInfo;
+import org.apache.chemistry.opencmis.commons.server.RenditionInfo;
+import org.apache.chemistry.opencmis.commons.spi.Holder;
+
+/**
+ * OpenCMIS service object.
+ *
+ * @author florian.mueller
+ */
+public class AlfrescoCmisService extends AbstractCmisService
+{
+ private CMISConnector connector;
+ private CallContext context;
+ private UserTransaction txn;
+
+ public AlfrescoCmisService(CMISConnector connector)
+ {
+ this.connector = connector;
+ }
+
+ public void beginCall(CallContext context)
+ {
+ this.context = context;
+
+ // authenticate user
+ String user = context.getUsername();
+ String password = context.getPassword();
+
+ if ((user == null) || (user.length() == 0))
+ {
+ throw new CmisPermissionDeniedException("No user provided!");
+ }
+
+ if (password == null)
+ {
+ password = "";
+ }
+
+ try
+ {
+ connector.getAuthenticationService().authenticate(user, password.toCharArray());
+ } catch (AuthenticationException ae)
+ {
+ throw new CmisPermissionDeniedException(ae.getMessage(), ae);
+ }
+
+ // start read-only transaction
+ try
+ {
+ beginReadOnlyTransaction();
+ } catch (Exception e)
+ {
+ connector.getAuthenticationService().clearCurrentSecurityContext();
+
+ if (e instanceof CmisBaseException)
+ {
+ throw (CmisBaseException) e;
+ } else
+ {
+ throw new CmisRuntimeException(e.getMessage(), e);
+ }
+ }
+ }
+
+ @Override
+ public void close()
+ {
+ try
+ {
+ endReadOnlyTransaction();
+ } catch (Exception e)
+ {
+ if (e instanceof CmisBaseException)
+ {
+ throw (CmisBaseException) e;
+ } else
+ {
+ throw new CmisRuntimeException(e.getMessage(), e);
+ }
+ } finally
+ {
+ // clean up
+ connector.getAuthenticationService().clearCurrentSecurityContext();
+ context = null;
+ }
+ }
+
+ /**
+ * Begins the embracing read-only transaction.
+ */
+ protected void beginReadOnlyTransaction()
+ {
+ txn = null;
+ try
+ {
+ txn = connector.getTransactionService().getNonPropagatingUserTransaction(true);
+ txn.begin();
+ } catch (Exception e)
+ {
+ throw new CmisRuntimeException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Ends embracing read-only transaction.
+ */
+ protected void endReadOnlyTransaction()
+ {
+ try
+ {
+ if (txn != null)
+ {
+ // there isn't anything to commit, really
+ // we just have to end the transaction
+ if (txn.getStatus() == Status.STATUS_MARKED_ROLLBACK)
+ {
+ txn.rollback();
+ } else
+ {
+ txn.commit();
+ }
+ txn = null;
+ }
+ } catch (Exception e)
+ {
+ throw new CmisRuntimeException(e.getMessage(), e);
+ }
+ }
+
+ // --- repository service ---
+
+ @Override
+ public List getRepositoryInfos(ExtensionsData extension)
+ {
+ return Collections.singletonList(connector.getRepositoryInfo());
+ }
+
+ @Override
+ public RepositoryInfo getRepositoryInfo(String repositoryId, ExtensionsData extension)
+ {
+ checkRepositoryId(repositoryId);
+
+ return connector.getRepositoryInfo();
+ }
+
+ @Override
+ public TypeDefinitionList getTypeChildren(String repositoryId, String typeId, Boolean includePropertyDefinitions,
+ BigInteger maxItems, BigInteger skipCount, ExtensionsData extension)
+ {
+ checkRepositoryId(repositoryId);
+
+ // convert BigIntegers to int
+ int max = (maxItems == null ? Integer.MAX_VALUE : maxItems.intValue());
+ int skip = (skipCount == null || skipCount.intValue() < 0 ? 0 : skipCount.intValue());
+
+ // set up the result
+ TypeDefinitionListImpl result = new TypeDefinitionListImpl();
+ List list = new ArrayList();
+ result.setList(list);
+
+ // get the types from the dictionary
+ List childrenList;
+ if (typeId == null)
+ {
+ childrenList = connector.getOpenCMISDictionaryService().getBaseTypes();
+ } else
+ {
+ TypeDefinitionWrapper tdw = connector.getOpenCMISDictionaryService().findType(typeId);
+ if (tdw == null)
+ {
+ throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
+ }
+
+ childrenList = tdw.getChildren();
+ }
+
+ // create result
+ if (max > 0)
+ {
+ int lastIndex = (max + skip > childrenList.size() ? childrenList.size() : max + skip) - 1;
+ for (int i = skip; i <= lastIndex; i++)
+ {
+ list.add(childrenList.get(i).getTypeDefinition(includePropertyDefinitions));
+ }
+ }
+
+ result.setHasMoreItems(childrenList.size() - skip > result.getList().size());
+ result.setNumItems(BigInteger.valueOf(childrenList.size()));
+
+ return result;
+ }
+
+ @Override
+ public TypeDefinition getTypeDefinition(String repositoryId, String typeId, ExtensionsData extension)
+ {
+ checkRepositoryId(repositoryId);
+
+ // find the type
+ TypeDefinitionWrapper tdw = connector.getOpenCMISDictionaryService().findType(typeId);
+ if (tdw == null)
+ {
+ throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
+ }
+
+ // return type definition
+ return tdw.getTypeDefinition(true);
+ }
+
+ @Override
+ public List getTypeDescendants(String repositoryId, String typeId, BigInteger depth,
+ Boolean includePropertyDefinitions, ExtensionsData extension)
+ {
+ checkRepositoryId(repositoryId);
+
+ List result = new ArrayList();
+
+ // check depth
+ int d = (depth == null ? -1 : depth.intValue());
+ if (d == 0)
+ {
+ throw new CmisInvalidArgumentException("Depth must not be 0!");
+ }
+
+ if (typeId == null)
+ {
+ for (TypeDefinitionWrapper tdw : connector.getOpenCMISDictionaryService().getBaseTypes())
+ {
+ result.add(getTypesDescendants(d, tdw, includePropertyDefinitions));
+ }
+ } else
+ {
+ TypeDefinitionWrapper tdw = connector.getOpenCMISDictionaryService().findType(typeId);
+ if (tdw == null)
+ {
+ throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!");
+ }
+
+ if (tdw.getChildren() != null)
+ {
+ for (TypeDefinitionWrapper child : tdw.getChildren())
+ {
+ result.add(getTypesDescendants(d, child, includePropertyDefinitions));
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Gathers the type descendants tree.
+ */
+ private TypeDefinitionContainer getTypesDescendants(int depth, TypeDefinitionWrapper tdw,
+ boolean includePropertyDefinitions)
+ {
+ TypeDefinitionContainerImpl result = new TypeDefinitionContainerImpl();
+
+ result.setTypeDefinition(tdw.getTypeDefinition(includePropertyDefinitions));
+
+ if (depth != 0)
+ {
+ if (tdw.getChildren() != null)
+ {
+ result.setChildren(new ArrayList());
+ for (TypeDefinitionWrapper tdc : tdw.getChildren())
+ {
+ result.getChildren().add(
+ getTypesDescendants(depth < 0 ? -1 : depth - 1, tdc, includePropertyDefinitions));
+ }
+ }
+ }
+
+ return result;
+ }
+
+ // --- navigation service ---
+
+ /*
+ * Lucene based getChildren - deactivated
+ */
+ public ObjectInFolderList XgetChildren(String repositoryId, String folderId, String filter, String orderBy,
+ Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter,
+ Boolean includePathSegment, BigInteger maxItems, BigInteger skipCount, ExtensionsData extension)
+ {
+ checkRepositoryId(repositoryId);
+
+ // convert BigIntegers to int
+ int max = (maxItems == null ? Integer.MAX_VALUE : maxItems.intValue());
+ int skip = (skipCount == null || skipCount.intValue() < 0 ? 0 : skipCount.intValue());
+
+ ObjectInFolderListImpl result = new ObjectInFolderListImpl();
+ List list = new ArrayList();
+ result.setObjects(list);
+
+ // get the children references
+ NodeRef folderNodeRef = connector.getFolderNodeRef("Folder", folderId);
+
+ // lucene part
+ QName PARAM_PARENT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "parent");
+ DataTypeDefinition nodeRefDataType = connector.getDictionaryService().getDataType(DataTypeDefinition.NODE_REF);
+
+ SearchParameters params = new SearchParameters();
+ params.setLanguage(SearchService.LANGUAGE_LUCENE);
+ params.addStore(folderNodeRef.getStoreRef());
+ QueryParameterDefinition parentDef = new QueryParameterDefImpl(PARAM_PARENT, nodeRefDataType, true,
+ folderNodeRef.toString());
+ params.addQueryParameterDefinition(parentDef);
+
+ // Build a query for the appropriate types
+ StringBuilder query = new StringBuilder(1024).append("+PARENT:\"${cm:parent}\" -ASPECT:\"")
+ .append(ContentModel.ASPECT_WORKING_COPY).append("\" +TYPE:(");
+
+ // Include doc type if necessary
+ query.append('"').append(ContentModel.TYPE_CONTENT).append('"');
+ query.append(" ");
+ query.append('"').append(ContentModel.TYPE_FOLDER).append('"');
+
+ // Always exclude system folders
+ query.append(") -TYPE:\"").append(ContentModel.TYPE_SYSTEM_FOLDER).append("\"");
+ params.setQuery(query.toString());
+ // parseOrderBy(orderBy, params);
+ ResultSet resultSet = null;
+
+ List childrenList;
+ try
+ {
+ resultSet = connector.getSearchService().query(params);
+ childrenList = resultSet.getNodeRefs();
+ } finally
+ {
+ if (resultSet != null)
+ resultSet.close();
+ }
+
+ if (max > 0)
+ {
+ int lastIndex = (max + skip > childrenList.size() ? childrenList.size() : max + skip) - 1;
+ for (int i = skip; i <= lastIndex; i++)
+ {
+ NodeRef child = childrenList.get(i);
+
+ // create a child CMIS object
+ ObjectData object = connector.createCMISObject(child, filter, includeAllowableActions,
+ includeRelationships, renditionFilter, false, false);
+
+ ObjectInFolderDataImpl childData = new ObjectInFolderDataImpl();
+ childData.setObject(object);
+
+ // include path segment
+ if (includePathSegment)
+ {
+ childData.setPathSegment(connector.getName(child));
+ }
+
+ // add it
+ list.add(childData);
+ }
+ }
+
+ result.setHasMoreItems(childrenList.size() - skip > result.getObjects().size());
+ result.setNumItems(BigInteger.valueOf(childrenList.size()));
+
+ return result;
+ }
+
+ public ObjectInFolderList getChildren(String repositoryId, String folderId, String filter, String orderBy,
+ Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter,
+ Boolean includePathSegment, BigInteger maxItems, BigInteger skipCount, ExtensionsData extension)
+ {
+ checkRepositoryId(repositoryId);
+
+ // convert BigIntegers to int
+ int max = (maxItems == null ? Integer.MAX_VALUE : maxItems.intValue());
+ int skip = (skipCount == null || skipCount.intValue() < 0 ? 0 : skipCount.intValue());
+
+ ObjectInFolderListImpl result = new ObjectInFolderListImpl();
+ List list = new ArrayList();
+ result.setObjects(list);
+
+ // get the children references
+ NodeRef folderNodeRef = connector.getFolderNodeRef("Folder", folderId);
+ List childrenList = connector.getNodeService().getChildAssocs(folderNodeRef);
+
+ if (max > 0)
+ {
+ int lastIndex = (max + skip > childrenList.size() ? childrenList.size() : max + skip) - 1;
+ for (int i = skip; i <= lastIndex; i++)
+ {
+ ChildAssociationRef child = childrenList.get(i);
+
+ try
+ {
+ // create a child CMIS object
+ ObjectData object = connector.createCMISObject(child.getChildRef(), filter,
+ includeAllowableActions, includeRelationships, renditionFilter, false, false);
+ if (context.isObjectInfoRequired())
+ {
+ getObjectInfo(repositoryId, object.getId());
+ }
+
+ ObjectInFolderDataImpl childData = new ObjectInFolderDataImpl();
+ childData.setObject(object);
+
+ // include path segment
+ if (includePathSegment)
+ {
+ childData.setPathSegment(connector.getName(child.getChildRef()));
+ }
+
+ // add it
+ list.add(childData);
+ } catch (InvalidNodeRefException e)
+ {
+ // ignore invalid children
+ }
+ }
+ }
+
+ result.setHasMoreItems(childrenList.size() - skip > result.getObjects().size());
+ result.setNumItems(BigInteger.valueOf(childrenList.size()));
+
+ return result;
+ }
+
+ @Override
+ public List getDescendants(String repositoryId, String folderId, BigInteger depth,
+ String filter, Boolean includeAllowableActions, IncludeRelationships includeRelationships,
+ String renditionFilter, Boolean includePathSegment, ExtensionsData extension)
+ {
+ checkRepositoryId(repositoryId);
+
+ List result = new ArrayList();
+
+ getDescendantsTree(repositoryId, connector.getFolderNodeRef("Folder", folderId), depth.intValue(), filter,
+ includeAllowableActions, includeRelationships, renditionFilter, includePathSegment, false, result);
+
+ return result;
+ }
+
+ @Override
+ public List getFolderTree(String repositoryId, String folderId, BigInteger depth,
+ String filter, Boolean includeAllowableActions, IncludeRelationships includeRelationships,
+ String renditionFilter, Boolean includePathSegment, ExtensionsData extension)
+ {
+ checkRepositoryId(repositoryId);
+
+ List result = new ArrayList();
+
+ getDescendantsTree(repositoryId, connector.getFolderNodeRef("Folder", folderId), depth.intValue(), filter,
+ includeAllowableActions, includeRelationships, renditionFilter, includePathSegment, true, result);
+
+ return result;
+ }
+
+ private void getDescendantsTree(String repositoryId, NodeRef folderNodeRef, int depth, String filter,
+ Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter,
+ Boolean includePathSegment, boolean foldersOnly, List list)
+ {
+ // get the children references
+ List childrenList = connector.getNodeService().getChildAssocs(folderNodeRef);
+ for (ChildAssociationRef child : childrenList)
+ {
+ try
+ {
+ TypeDefinitionWrapper type = connector.getType(child.getChildRef());
+ if (type == null)
+ {
+ continue;
+ }
+
+ boolean isFolder = (type instanceof FolderTypeDefintionWrapper);
+
+ if (foldersOnly && !isFolder)
+ {
+ continue;
+ }
+
+ // create a child CMIS object
+ ObjectInFolderDataImpl object = new ObjectInFolderDataImpl();
+ object.setObject(connector.createCMISObject(child.getChildRef(), filter, includeAllowableActions,
+ includeRelationships, renditionFilter, false, false));
+ if (context.isObjectInfoRequired())
+ {
+ getObjectInfo(repositoryId, object.getObject().getId());
+ }
+
+ if (includePathSegment)
+ {
+ object.setPathSegment(connector.getName(child.getChildRef()));
+ }
+
+ // create the container
+ ObjectInFolderContainerImpl container = new ObjectInFolderContainerImpl();
+ container.setObject(object);
+
+ if ((depth != 1) && isFolder)
+ {
+ container.setChildren(new ArrayList());
+ getDescendantsTree(repositoryId, child.getChildRef(), depth - 1, filter, includeAllowableActions,
+ includeRelationships, renditionFilter, includePathSegment, foldersOnly,
+ container.getChildren());
+ }
+
+ // add it
+ list.add(container);
+ } catch (InvalidNodeRefException e)
+ {
+ // ignore invalid children
+ }
+ }
+ }
+
+ @Override
+ public ObjectData getFolderParent(String repositoryId, String folderId, String filter, ExtensionsData extension)
+ {
+ checkRepositoryId(repositoryId);
+
+ // get the node ref
+ NodeRef nodeRef = connector.getFolderNodeRef("Folder", folderId);
+
+ // the root folder has no parent
+ if (nodeRef.equals(connector.getRootNodeRef()))
+ {
+ throw new CmisInvalidArgumentException("Root folder has no parent!");
+ }
+
+ // get the parent
+ ChildAssociationRef parent = connector.getNodeService().getPrimaryParent(nodeRef);
+ if (parent == null)
+ {
+ throw new CmisRuntimeException("Folder has no parent and is not the root folder?!");
+ }
+
+ // create parent object
+ ObjectData result = connector.createCMISObject(parent.getParentRef(), filter, false, IncludeRelationships.NONE,
+ CMISConnector.RENDITION_NONE, false, false);
+ if (context.isObjectInfoRequired())
+ {
+ getObjectInfo(repositoryId, result.getId());
+ }
+
+ return result;
+ }
+
+ @Override
+ public List getObjectParents(String repositoryId, String objectId, String filter,
+ Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter,
+ Boolean includeRelativePathSegment, ExtensionsData extension)
+ {
+ checkRepositoryId(repositoryId);
+
+ List result = new ArrayList();
+
+ // what kind of object is it?
+ ObjectVariantEnum variant = connector.getObjectVariant(objectId);
+ connector.throwCommonExceptions(variant, "Object", objectId);
+
+ if (variant != ObjectVariantEnum.ASSOC)
+ {
+ // versions are filed in the same folder -> cut off version suffix
+ String currentVersionId = connector.getCurrentVersionId(objectId);
+ NodeRef nodeRef = connector.getNodeRef(currentVersionId);
+ NodeRef rootNodeRef = connector.getRootNodeRef();
+
+ if (!nodeRef.equals(rootNodeRef))
+ {
+ ChildAssociationRef parent = connector.getNodeService().getPrimaryParent(nodeRef);
+ if (parent != null)
+ {
+ ObjectData object = connector.createCMISObject(parent.getParentRef(), filter,
+ includeAllowableActions, includeRelationships, renditionFilter, false, false);
+ if (context.isObjectInfoRequired())
+ {
+ getObjectInfo(repositoryId, object.getId());
+ }
+
+ ObjectParentDataImpl objectParent = new ObjectParentDataImpl();
+ objectParent.setObject(object);
+
+ // include relative path segment
+ if (includeRelativePathSegment)
+ {
+ objectParent.setRelativePathSegment(connector.getName(nodeRef));
+ }
+
+ result.add(objectParent);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ @Override
+ public ObjectList getCheckedOutDocs(String repositoryId, String folderId, String filter, String orderBy,
+ Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter,
+ BigInteger maxItems, BigInteger skipCount, ExtensionsData extension)
+ {
+ checkRepositoryId(repositoryId);
+
+ return new ObjectListImpl();
+ }
+
+ // --- object service ---
+
+ @Override
+ public String create(String repositoryId, Properties properties, String folderId, ContentStream contentStream,
+ VersioningState versioningState, List policies, ExtensionsData extension)
+ {
+ checkRepositoryId(repositoryId);
+
+ // check properties
+ if (properties == null || properties.getProperties() == null)
+ {
+ throw new CmisInvalidArgumentException("Properties must be set!");
+ }
+
+ // get the type
+ String objectTypeId = connector.getObjectTypeIdProperty(properties);
+
+ // find the type
+ TypeDefinitionWrapper type = connector.getOpenCMISDictionaryService().findType(objectTypeId);
+ if (type == null)
+ {
+ throw new CmisInvalidArgumentException("Type '" + objectTypeId + "' is unknown!");
+ }
+
+ // create object
+ String newId = null;
+ switch (type.getBaseTypeId())
+ {
+ case CMIS_DOCUMENT:
+ newId = createDocument(repositoryId, properties, folderId, contentStream, versioningState, policies, null,
+ null, extension);
+ break;
+ case CMIS_FOLDER:
+ newId = createFolder(repositoryId, properties, folderId, policies, null, null, extension);
+ break;
+ case CMIS_POLICY:
+ newId = createPolicy(repositoryId, properties, folderId, policies, null, null, extension);
+ break;
+ }
+
+ // check new object id
+ if (newId == null)
+ {
+ throw new CmisRuntimeException("Creation failed!");
+ }
+
+ if (context.isObjectInfoRequired())
+ {
+ try
+ {
+ getObjectInfo(repositoryId, newId);
+ } catch (InvalidNodeRefException e)
+ {
+ throw new CmisRuntimeException("Creation failed! New object not found!");
+ }
+ }
+
+ // return the new object id
+ return newId;
+ }
+
+ @Override
+ public String createFolder(String repositoryId, final Properties properties, String folderId,
+ final List policies, final Acl addAces, final Acl removeAces, ExtensionsData extension)
+ {
+ checkRepositoryId(repositoryId);
+
+ // get the parent folder node ref
+ final NodeRef parentNodeRef = connector.getFolderNodeRef("Parent folder", folderId);
+
+ // get name and type
+ final String name = connector.getNameProperty(properties);
+ final String objectTypeId = connector.getObjectTypeIdProperty(properties);
+ final TypeDefinitionWrapper type = connector.getTypeForCreate(objectTypeId, BaseTypeId.CMIS_FOLDER);
+
+ connector.checkChildObjectType(parentNodeRef, type.getTypeId());
+
+ // run transaction
+ endReadOnlyTransaction();
+ NodeRef newNodeRef = connector.getTransactionService().getRetryingTransactionHelper()
+ .doInTransaction(new RetryingTransactionCallback()
+ {
+ public NodeRef execute() throws Exception
+ {
+ try
+ {
+ NodeRef nodeRef = connector.getFileFolderService()
+ .create(parentNodeRef, name, type.getAlfrescoClass()).getNodeRef();
+
+ connector.setProperties(nodeRef, type, properties, new String[] { PropertyIds.NAME,
+ PropertyIds.OBJECT_TYPE_ID });
+ connector.applyPolicies(nodeRef, type, policies);
+ connector.applyACL(nodeRef, type, addAces, removeAces);
+
+ return nodeRef;
+ } catch (FileExistsException fee)
+ {
+ throw new CmisContentAlreadyExistsException("An object with this name already exists!", fee);
+ } catch (IntegrityException ie)
+ {
+ throw new CmisConstraintException("Constraint violation: " + ie.getMessage(), ie);
+ } catch (AccessDeniedException ade)
+ {
+ throw new CmisPermissionDeniedException("Permission denied!", ade);
+ }
+ };
+ }, false, true);
+ beginReadOnlyTransaction();
+
+ return newNodeRef.toString();
+ }
+
+ @Override
+ public String createDocument(String repositoryId, final Properties properties, String folderId,
+ final ContentStream contentStream, final VersioningState versioningState, final List policies,
+ final Acl addAces, final Acl removeAces, ExtensionsData extension)
+ {
+ checkRepositoryId(repositoryId);
+
+ // get the parent folder node ref
+ final NodeRef parentNodeRef = connector.getFolderNodeRef("Parent folder", folderId);
+
+ // get name and type
+ final String name = connector.getNameProperty(properties);
+ final String objectTypeId = connector.getObjectTypeIdProperty(properties);
+ final TypeDefinitionWrapper type = connector.getTypeForCreate(objectTypeId, BaseTypeId.CMIS_DOCUMENT);
+
+ connector.checkChildObjectType(parentNodeRef, type.getTypeId());
+
+ DocumentTypeDefinition docType = (DocumentTypeDefinition) type.getTypeDefinition(false);
+
+ if ((docType.getContentStreamAllowed() == ContentStreamAllowed.NOTALLOWED) && (contentStream != null))
+ {
+ throw new CmisConstraintException("This document type does not support content!");
+ }
+
+ if ((docType.getContentStreamAllowed() == ContentStreamAllowed.REQUIRED) && (contentStream == null))
+ {
+ throw new CmisConstraintException("This document type does requires content!");
+ }
+
+ if (docType.isVersionable() && (versioningState == VersioningState.NONE))
+ {
+ throw new CmisConstraintException("This document type is versionable!");
+ }
+
+ if (!docType.isVersionable() && (versioningState != VersioningState.NONE))
+ {
+ throw new CmisConstraintException("This document type is not versionable!");
+ }
+
+ // copy stream to temp file
+ final File tempFile = copyToTempFile(contentStream);
+ final Charset encoding = (tempFile == null ? null : getEncoding(tempFile, contentStream.getMimeType()));
+
+ // run transaction
+ endReadOnlyTransaction();
+ NodeRef newNodeRef = connector.getTransactionService().getRetryingTransactionHelper()
+ .doInTransaction(new RetryingTransactionCallback()
+ {
+ public NodeRef execute() throws Exception
+ {
+ try
+ {
+ NodeRef nodeRef = connector.getFileFolderService()
+ .create(parentNodeRef, name, type.getAlfrescoClass()).getNodeRef();
+
+ connector.setProperties(nodeRef, type, properties, new String[] { PropertyIds.NAME,
+ PropertyIds.OBJECT_TYPE_ID });
+ connector.applyPolicies(nodeRef, type, policies);
+ connector.applyACL(nodeRef, type, addAces, removeAces);
+
+ // handle content
+ if (contentStream != null)
+ {
+ // write content
+ ContentWriter writer = connector.getFileFolderService().getWriter(nodeRef);
+ writer.setMimetype(contentStream.getMimeType());
+ writer.setEncoding(encoding.name());
+ writer.putContent(tempFile);
+ }
+
+ connector.applyVersioningState(nodeRef, versioningState);
+
+ return nodeRef;
+ } catch (FileExistsException fee)
+ {
+ removeTempFile(tempFile);
+ throw new CmisContentAlreadyExistsException("An object with this name already exists!", fee);
+ } catch (IntegrityException ie)
+ {
+ removeTempFile(tempFile);
+ throw new CmisConstraintException("Constraint violation: " + ie.getMessage(), ie);
+ } catch (AccessDeniedException ade)
+ {
+ removeTempFile(tempFile);
+ throw new CmisPermissionDeniedException("Permission denied!", ade);
+ }
+ };
+ }, false, true);
+ beginReadOnlyTransaction();
+
+ removeTempFile(tempFile);
+
+ return connector.createObjectId(newNodeRef);
+ }
+
+ @Override
+ public String createDocumentFromSource(String repositoryId, String sourceId, final Properties properties,
+ String folderId, final VersioningState versioningState, final List policies, final Acl addAces,
+ final Acl removeAces, ExtensionsData extension)
+ {
+ checkRepositoryId(repositoryId);
+
+ // get the parent folder node ref
+ final NodeRef parentNodeRef = connector.getFolderNodeRef("Parent folder", folderId);
+
+ // get name and type
+ final String name = connector.getNameProperty(properties);
+
+ // get source
+ ObjectVariantEnum variant = connector.getObjectVariant(sourceId);
+ connector.throwCommonExceptions(variant, "Source", sourceId);
+
+ // check source
+ if (variant == ObjectVariantEnum.ASSOC)
+ {
+ throw new CmisConstraintException("Source object is not a document!");
+ }
+
+ final NodeRef sourceNodeRef = connector.getNodeRef(sourceId);
+ final TypeDefinitionWrapper type = connector.getAndCheckType(sourceNodeRef);
+
+ if (!(type instanceof DocumentTypeDefinitionWrapper))
+ {
+ throw new CmisConstraintException("Source object is not a document!");
+ }
+
+ connector.checkChildObjectType(parentNodeRef, type.getTypeId());
+
+ // run transaction
+ endReadOnlyTransaction();
+ NodeRef newNodeRef = connector.getTransactionService().getRetryingTransactionHelper()
+ .doInTransaction(new RetryingTransactionCallback()
+ {
+ public NodeRef execute() throws Exception
+ {
+ try
+ {
+ NodeRef newDocumentNodeRef = connector.getFileFolderService()
+ .copy(sourceNodeRef, parentNodeRef, name).getNodeRef();
+
+ connector.setProperties(newDocumentNodeRef, type, properties, new String[] {
+ PropertyIds.NAME, PropertyIds.OBJECT_TYPE_ID });
+ connector.applyPolicies(newDocumentNodeRef, type, policies);
+ connector.applyACL(newDocumentNodeRef, type, addAces, removeAces);
+ connector.applyVersioningState(newDocumentNodeRef, versioningState);
+
+ return newDocumentNodeRef;
+ } catch (FileExistsException fee)
+ {
+ throw new CmisContentAlreadyExistsException("An object with this name already exists!", fee);
+ } catch (IntegrityException ie)
+ {
+ throw new CmisConstraintException("Constraint violation: " + ie.getMessage(), ie);
+ } catch (AccessDeniedException ade)
+ {
+ throw new CmisPermissionDeniedException("Permission denied!", ade);
+ }
+ };
+ }, false, true);
+ beginReadOnlyTransaction();
+
+ return connector.createObjectId(newNodeRef);
+ }
+
+ @Override
+ public String createPolicy(String repositoryId, Properties properties, String folderId, List policies,
+ Acl addAces, Acl removeAces, ExtensionsData extension)
+ {
+ checkRepositoryId(repositoryId);
+
+ // get the parent folder node ref
+ connector.getFolderNodeRef("Parent folder", folderId);
+
+ String objectTypeId = connector.getObjectTypeIdProperty(properties);
+ connector.getTypeForCreate(objectTypeId, BaseTypeId.CMIS_POLICY);
+
+ // we should never get here - policies are not creatable!
+ throw new CmisRuntimeException("Polcies cannot be created!");
+ }
+
+ @Override
+ public String createRelationship(String repositoryId, Properties properties, List policies, Acl addAces,
+ Acl removeAces, ExtensionsData extension)
+ {
+ checkRepositoryId(repositoryId);
+
+ // get type
+ String objectTypeId = connector.getObjectTypeIdProperty(properties);
+ final TypeDefinitionWrapper type = connector.getTypeForCreate(objectTypeId, BaseTypeId.CMIS_RELATIONSHIP);
+
+ // get source object
+ String sourceId = connector.getSourceIdProperty(properties);
+ ObjectVariantEnum sourceVariant = connector.getObjectVariant(sourceId);
+ connector.throwCommonExceptions(sourceVariant, "Source", sourceId);
+
+ if (sourceVariant != ObjectVariantEnum.NODE)
+ {
+ throw new CmisInvalidArgumentException("Source is not a document or folder object!");
+ }
+
+ final NodeRef sourceNodeRef = connector.getNodeRefIfCurrent("Source", sourceId);
+
+ // get target object
+ String targetId = connector.getTargetIdProperty(properties);
+ ObjectVariantEnum targetVariant = connector.getObjectVariant(targetId);
+ connector.throwCommonExceptions(targetVariant, "Target", sourceId);
+
+ if (targetVariant != ObjectVariantEnum.NODE)
+ {
+ throw new CmisInvalidArgumentException("Target is not a document or folder object!");
+ }
+
+ final NodeRef targetNodeRef = connector.getNodeRefIfCurrent("Target", targetId);
+
+ // check policies and ACLs
+ if ((policies != null) && (!policies.isEmpty()))
+ {
+ throw new CmisConstraintException("Relationships are not policy controllable!");
+ }
+
+ if ((addAces != null) && (addAces.getAces() != null) && (!addAces.getAces().isEmpty()))
+ {
+ throw new CmisConstraintException("Relationships are not ACL controllable!");
+ }
+
+ if ((removeAces != null) && (removeAces.getAces() != null) && (!removeAces.getAces().isEmpty()))
+ {
+ throw new CmisConstraintException("Relationships are not ACL controllable!");
+ }
+
+ // create relationship
+ endReadOnlyTransaction();
+ AssociationRef assocRef = connector.getTransactionService().getRetryingTransactionHelper()
+ .doInTransaction(new RetryingTransactionCallback()
+ {
+ public AssociationRef execute() throws Exception
+ {
+ try
+ {
+ return connector.getNodeService().createAssociation(sourceNodeRef, targetNodeRef,
+ type.getAlfrescoClass());
+ } catch (IntegrityException ie)
+ {
+ throw new CmisConstraintException("Constraint violation: " + ie.getMessage(), ie);
+ } catch (AccessDeniedException ade)
+ {
+ throw new CmisPermissionDeniedException("Permission denied!", ade);
+ }
+ };
+ }, false, true);
+ beginReadOnlyTransaction();
+
+ return CMISConnector.ASSOC_ID_PREFIX + assocRef.getId();
+ }
+
+ @Override
+ public void setContentStream(String repositoryId, Holder objectId, Boolean overwriteFlag,
+ Holder changeToken, final ContentStream contentStream, ExtensionsData extension)
+ {
+ checkRepositoryId(repositoryId);
+
+ ObjectVariantEnum variant = connector.getObjectVariant(objectId.getValue());
+ connector.throwCommonExceptions(variant, "Object", objectId.getValue());
+
+ if (variant == ObjectVariantEnum.ASSOC)
+ {
+ throw new CmisStreamNotSupportedException("Relationships don't support content!");
+ }
+
+ final NodeRef nodeRef = connector.getNodeRefIfCurrent("Object", objectId.getValue());
+ TypeDefinitionWrapper type = connector.getAndCheckType(nodeRef);
+
+ if (!(type instanceof DocumentTypeDefinitionWrapper))
+ {
+ throw new CmisStreamNotSupportedException("Object type doesn't support content!");
+ }
+
+ if (((DocumentTypeDefinition) type.getTypeDefinition(false)).getContentStreamAllowed() == ContentStreamAllowed.NOTALLOWED)
+ {
+ throw new CmisStreamNotSupportedException("Document type doesn't allow content!");
+ }
+
+ boolean existed = connector.getContentService().getReader(nodeRef, ContentModel.PROP_CONTENT) != null;
+ if (existed && !overwriteFlag)
+ {
+ throw new CmisContentAlreadyExistsException("Content already exists!");
+ }
+
+ if ((contentStream == null) || (contentStream.getStream() == null))
+ {
+ throw new CmisInvalidArgumentException("No content!");
+ }
+
+ // copy stream to temp file
+ final File tempFile = copyToTempFile(contentStream);
+ final Charset encoding = getEncoding(tempFile, contentStream.getMimeType());
+
+ endReadOnlyTransaction();
+ connector.getTransactionService().getRetryingTransactionHelper()
+ .doInTransaction(new RetryingTransactionCallback