/*
* 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.PropertyDefintionWrapper;
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.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.Authorization;
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.model.PagingFileInfoResults;
import org.alfresco.service.cmr.model.PagingSortRequest;
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.repository.PagingSortProp;
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;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* OpenCMIS service object.
*
* @author florian.mueller
*/
public class AlfrescoCmisService extends AbstractCmisService
{
private static Log logger = LogFactory.getLog(AlfrescoCmisService.class);
private CMISConnector connector;
private CallContext context;
private UserTransaction txn;
public AlfrescoCmisService(CMISConnector connector)
{
this.connector = connector;
}
public void beginCall(CallContext context)
{
this.context = context;
AuthenticationUtil.pushAuthentication();
try
{
String currentUser = connector.getAuthenticationService().getCurrentUserName();
String user = context.getUsername();
String password = context.getPassword();
if (currentUser == null)
{
Authorization auth = new Authorization(user, password);
if (auth.isTicket())
{
connector.getAuthenticationService().validate(auth.getTicket());
} else
{
connector.getAuthenticationService().authenticate(auth.getUserName(), auth.getPasswordCharArray());
}
} else if (currentUser.equals(connector.getProxyUser()))
{
if (user != null && user.length() > 0)
{
AuthenticationUtil.setFullyAuthenticatedUser(user);
}
}
} catch (AuthenticationException ae)
{
throw new CmisPermissionDeniedException(ae.getMessage(), ae);
}
// start read-only transaction
try
{
beginReadOnlyTransaction();
} catch (Exception e)
{
AuthenticationUtil.popAuthentication();
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
{
AuthenticationUtil.popAuthentication();
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)
{
long start = System.currentTimeMillis();
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);
// TEMP - impl subject to change
// convert orderBy to sortProps
List sortProps = null;
if (orderBy != null)
{
sortProps = new ArrayList(1);
String[] parts = orderBy.split(",");
if (parts.length > 0)
{
for (int i = 0; i < parts.length; i++)
{
String[] sort = parts[i].split(" "); // TODO support multiple spaces
if (sort.length > 0)
{
PropertyDefintionWrapper propDef = connector.getOpenCMISDictionaryService().findPropertyByQueryName(sort[0]);
if (propDef != null)
{
QName sortProp = propDef.getPropertyAccessor().getMappedProperty();
if (sortProp != null)
{
boolean sortAsc = ((sort.length == 1) || (sortAsc = (sort[1].equalsIgnoreCase("asc"))));
sortProps.add(new PagingSortProp(sortProp, sortAsc));
}
}
}
}
}
}
PagingSortRequest pageRequest = new PagingSortRequest(skipCount.intValue(), maxItems.intValue(), true, sortProps);
PagingFileInfoResults pageOfNodeInfos = connector.getFileFolderService().list(folderNodeRef, true, true, null, pageRequest);
List childrenList = pageOfNodeInfos.getResultsForPage();
if (max > 0)
{
int lastIndex = (max + skip > childrenList.size() ? childrenList.size() : max + skip) - 1;
for (int i = skip; i <= lastIndex; i++)
{
FileInfo child = childrenList.get(i);
try
{
// create a child CMIS object
ObjectData object = connector.createCMISObject(child, 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(child.getName());
}
// 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()));
if (logger.isDebugEnabled())
{
logger.debug("getChildren: "+childrenList.size()+" in "+(System.currentTimeMillis()-start)+" msecs");
}
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);
TypeDefinitionWrapper type = connector.getType(nodeRef);
if (type instanceof FolderTypeDefintionWrapper)
{
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);
}
}
} else
{
List parents = connector.getNodeService().getParentAssocs(nodeRef,
ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
if (parents != null)
{
for (ChildAssociationRef parent : parents)
{
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