Derek Hulley f0f74b86f1 Merged DEV/SWIFT to HEAD
27600: ALF-8522: MyBatis: Performance: ensure <association> elements are properly keyed
   27608: RINF 09 / RINF 10: DB-based paged query for get children (DocLib & CMIS) 
          - update FileFolder paging API
          - update GetChildren canned query sorting (to return sort props prior to batched pre-load)
   27614: RINF 09 / RINF 10: DB-based paged query for get children (DocLib & CMIS) 
          - add missing file (and fix my SmartSVN ignore unversioned list ;-)
          - follow-on to r27608
   27621: RINF 11: Extended generalized CannedQueryDAO to support common selects
          - MyBatis resultset termination included
          - RINF 11: Integrate existing CannedQueryDAO (ALF-8417)
          - RINF 11: Canned queries (ALF-7167)
   27623: RINF 09 / RINF 10: DB-based paged query for get children (DocLib & CMIS) 
          - if total count not requested then can cut-off permission checks (based on max items) and instead set "hasMore" flag (if applicable)
          - now used by Share DocLib folder tree listing (on LHS) - see ALF-6570
   27636: RINF 09 / RINF 10: DB-based getChildren
          - improvements to treenode (push down sort by name, get subfolders with max 1, remove redundant max check) - re: ALF-6570
          - add default child assoc sort (if no sort requested)
          - minor tweak to getChildren CQ trace logging
   27641: RINF 11: Extended generalized CannedQueryDAO
          - add temporary MyBatis workarounds for ResultHandler-based queries
            --> support unlimited queries for nested results maps (MyBatis #129)
            --> clear cache to return results on subsequent calls (MyBatis #58 ...)
   27642: RINF 16: Ordered associations: Added NodeService.setAssociations
          - All reordering, deleting and adding node by NodeService impl.
          - ALF-7405 (ALF-7404 RINF 16: Peer association enhancements)
   27643: ALF-8183 SVC 01: I18N List of values constraint.
          Added ListOfValuesConstraint.getDisplayLabel(constraintAllowableValue) method which uses the usual I18NUtil methods
             to retrieve a localised display-label for the LoV constraint values.
          These can be added to resource bundles in the usual way using a key as follows:
             listconstraint.test_list1.ABC=ABC display where "listconstraint." is a fixed prefix.
             "test_list1" is the shortform QName for the constraint with colon replaced by underscore.
             "ABC" is one of the allowed values for the named constraint.
          The test case changes & the associated model & property changes should make things clear.
   27644: Test cases for ChannelService.createChannel and ChannelService.getChannels
   27645: ALF-8183. I18N LIST constraint.
          This check-in ensures that requests for a LIST.value display label for:
             a non-existent value returns null
             an unlocalised value returns the allowableValue String from the model (backwards compatibility).
   27651: Fixing compile errors caused by moving NodeLocator classes.
   27652: RINF 09 / RINF 10: DB-based getChildren CQ
          - replace direct use of "sqlSessionTemplate" with "cannedQueryDAO"(RINF 11)
   27657: RINF 09: GetChildren CQ
          - temp fix for cm:modified (when qname does not exist) - TODO review
   27666: Support for ALF-8594: RSOLR 039: SOLR sorting
          - sorting for all single valued types
   27671: Ignore virtual tomcat build
   27673: Projects core, datamodel and solr now explicitly list their library dependencies and no longer include the 3rd party project
   27681: Added pub:channelInfo aspect tows:webasset and ws:section.
          Also ws:website now extends pub:DeliveryChannel.
		  Added new behaviours to section and website to ensure the channel info is populated.
   27682: Added example SOLR config dir and instructions on how to set up - simple dev set up only subject to lots of change
   27685: Cleanup: Removed non-javadoc; Removed unused methods; Added @override
   27689: Update SOLR test instance dependencies
   27692: ALF-8183 SVC 01: I18N Lost of values constraint.
          Added code to the FormService's PropertyFieldProcessor so that the allowedValues for a ListOfValuesConstraint
		  have both the allowed value and its localised display label, if there is one.
          Added English property values for the bpm:allowedStatus constraint and also for the LIST constraint in the FDK.
   27693: WCM-QS ML work - Have the webscript return more details on the node and its parents,
          and begin to use this in the UI to allow you to create as well as manage translations
   27695: Workaround/fix for ALF-5747. Incompatibilities with OpenOffice 3.3
          It looks like OOo 3.3.0 shipped with a missing component. The ConfigurationRegistry, which is used to remotely query the
		  OOo version, was not implemented in 3.3.0 and therefore gives an ugly (and unavoidable) exception on OOo startup.
          There's not much we can do here, beyond getting users to upgrade their OOo install.
          I've added a catch clause that detects this problem and emits a slightly friendlier warning to the log.
   27720: ALF-8532: WPUB: F148: Foundation API: Request that a node be published to the live environment
   27727: Removed duplicates
   27734: WCM-QS ML Updates to handle marking a node as an initial translation, and a start on creating translations
   27735: Fixed generics warning
   27736: Added PermissionService.getReaders(List<Long> aclIds)
          - Support for ALF-8335: RSOLR 013: DAOs and remote APIs for ACL changesets (ALF-7072)
          - Also cleaned up empty javadoc
   27739: Implemented NodeFilters and NodeFinders for AbstractChannelType and ChannelServiceImpl.
   27741: RINF 09: GetChildren CQ - fix sorting - with unit tests (for default Share DocLib fields)
          - partial ALF-8638
          - ALF-8584 (follow-on for r27657)
   27746: Start on linking the WCM-QS ML ui and the forms service for creating the translation
   27747: Added --INCLUDE: directive to SQL scripts to allow importing of DB-specific snippets
          - ALF-8498: SVC 06: Property holding comment count for a node
   27748: Web scripts for ACLs and ACL readers
          - Unit tests at all levels (Webscripts, Service and DAO)
          - Tweaks to the DB queries to support index-based sorting
          - Support for ALF-8335: RSOLR 013: DAOs and remote APIs for ACL changesets (ALF-7072)
   27753: Fixed enum naming
   27754: Undo changes to ChannelServiceImplIntegratedTest made in 27739 that broke the build
   27762: Part I: ALF-7992: RSOLR 024: Locale support for query: SOLR
          - restructure and split localisation from generic query parsing
          - Lucene impl migrated and tested
          - No SOLR impl yet
          - Fix CMIS QueyTest 
          - Fix references for LuceneQueryParser mostly to AbstractLuceneQueryParser
   27763: Initial implementation of ALF-8498. SVC 06: Property holding comment count for a node.
          This check-in adds a rollup property to hold the commentCount (fm:commentCount under fm:discussable).
          It adds registered behaviours for fm:post deletion/creation that will decrement/increment the commentCount for the discussable ancestor node.
          I've also added a new test class to test this rollup property. There was no existing test class at this level and indeed there is no
		  CommentService or DiscussionService. Instead the REST API of comment.put.desc.xml uses the ScriptNodeService to create the correct node structure.
		  Therefore the test code has had to do the same thing.
   27766: Slight reimpl of ALF-8498. Moved the rollup property onto its own aspect.
          So now we have fm:discussable, as before, for generic discussions and posts (including Explorer comments).
          And we have fm:commentsRollup which is only for rolling up Share comments.
   27777: WCM QS ML Forms config and related tweaks to support having newly created nodes made multilingual where required
   27781: Add repository project dependency for test context and debug
          - fixes model issue with SOLRAPIClientTest
   27782: SOLR - move back to dynamic catch all field which seems to be working
          -> simpler build and model management
   27810: ALF-8405 SiteAspect needs to handle child types of Site as well as Site itself
   27821: ALF-7070: Fixed SOLRSerializer to handle residual properties (not in dictionary)
   27828: RINF 11: Canned Queries (CQ)
          - update API post review .
          - add underlying pageRequest/pageResult for single page (or max items if skip is 0)
          - add hasMoreItems (ALF-8470) and update unit tests
          - return total count range - accurate count (if lower=upper), approximation (lower<upper) or more than (lower known, upper unknown)
          - update option to request total count with max (if cutoff/trimmed then returns unknown upper count)
   27829: RINF 09: GetChildren CQ - paging support for FileFolderService list
          - update impl wrt CQ API changes (follow-on to r27828)
          - add factory method to return CQ with common params & update FileFolderService
          - update CMIS getChildren (AlrescoCmisService)
          - update scripting layer (ScriptNode, ScriptPagingNodes)
          - update Share DocLib (including doclist & treenode)
          - ALF-8641 - add proto (UI subject to review) to demo Share DocLib requesting max total count (eg. "Showing items 1 - 50 of 1000++")
   27837: Moving the pdfbox, fontbox, jempbox libs from 1.3.1 to 1.5.0, which adds various bugfixes.
   27838: Web scripts for ACLs and ACL readers
           - Simplification of paging API for ACLs
           - Support for ALF-8335: RSOLR 013: DAOs and remote APIs for ACL changesets (ALF-7072)
   27839: SOLR ACL tracking client code and tests
           - Support for ALF-8335: RSOLR 013: ACL changesets client APIs (ALF-8463)
   27844: Build fix. Making SiteServiceImplTest more tolerant of preexisting sites.
   27845: Build fix. Making SiteServiceTest more tolerant of preexisting sites in databse.
   27846: Build fix relating to ALF-8183. An extra test constraint requires an increment to a assertion expectedValue in this test.
   27849: ALF-8532: WPUB: F148: Foundation API: Request that a node be published to the live environment
           - Added more necessary interfaces and framework code
   27850: RINF 09: GetChildren CQ - update sorting unit test + fixes
          - nulls sort 'low'
          - fix multi sort props
   27858: ALF-8532: WPUB: F148: Foundation API: Request that a node be published to the live environment
          - Initial cut of persisting publishing events (not tested yet) 
   27861: WCM QS ML webscript work - fix some issues, and start on webscript unit test
          Also updates a failing test with details of why it's failing (switch from JSON to XML some time ago)
   27864: Fix context minimal tests - web publishing context is already included from the high level context, and shouldn't be in the core one
   27867: ALF-8184: SVC 02: Encode and decode of forms itemId should be done in the REST layer and not in each individual form processor.
   27868: Sample Adobe Illustrator files from Linton, for use in testing future metadata/transformer support
   27869: Fixed ChannelServiceImplTest failures.
   27870: Created JBPM process definition to publish scheduled Publishing Events.
   27872: Sample Adobe Illustrator 3 file (PS not PDF based) from Linton, for use in testing future metadata/transformer support
   27874: OpenCMIS update
   27875: Change the Tika auto transformer to register aliases of mime types, as well as the canonical ones, for when Alfresco uses the alias
   27876: RINF 11: Permission checking in canned queries (ALF-8419)
   27877: RINF 39: Optimise GetChildren CQ for unsorted maxItems (ALF-8576)
   27878: RINF 09: Update FileFolderService - all list methods should use GetChildren CQ (ALF-8733)
   27882: ALF-8532: WPUB: F148: Foundation API: Request that a node be published to the live environment
          - Unit test for the publishing package serializer/deserializer 
   27885: Change order of publishing context so it comes after transfer service context. 
   27886: Fix for ALF-7992: RSOLR 024: Locale support for query: SOLR
          - d:mltext, d:text. d:content
          - phrase, term, prefix, wild, ranges, fuzzy etc
          - localised tokenisation
          - consistent tokenisation for cross language support ( phrase, term, prefix, wild, ranges, fuzzy etc)
          - CMIS related stuff to be tested later
   27897: Minor tweak to prevent repeated gets of the default locale when writing properties
   27900: WCM QS ML webscript tests
   27905: Fixed PostgreSQL HeartbeatTest: WorkflowDeployer was checking for read-only server too late
   27906: Remove unused imports
   27907: Used common SQL snippets for DB2 node inserts.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@28319 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
2011-06-09 21:14:18 +00:00

2729 lines
110 KiB
Java

/*
* Copyright (C) 2005-2011 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 <http://www.gnu.org/licenses/>.
*/
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.PagingFileInfoRequest;
import org.alfresco.service.cmr.model.PagingFileInfoResults;
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.Pair;
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<RepositoryInfo> 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<TypeDefinition> list = new ArrayList<TypeDefinition>();
result.setList(list);
// get the types from the dictionary
List<TypeDefinitionWrapper> 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<TypeDefinitionContainer> getTypeDescendants(String repositoryId, String typeId, BigInteger depth,
Boolean includePropertyDefinitions, ExtensionsData extension)
{
checkRepositoryId(repositoryId);
List<TypeDefinitionContainer> result = new ArrayList<TypeDefinitionContainer>();
// 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<TypeDefinitionContainer>());
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<ObjectInFolderData> list = new ArrayList<ObjectInFolderData>();
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<NodeRef> 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<ObjectInFolderData> list = new ArrayList<ObjectInFolderData>();
result.setObjects(list);
// get the children references
NodeRef folderNodeRef = connector.getFolderNodeRef("Folder", folderId);
// convert orderBy to sortProps
List<Pair<QName, Boolean>> sortProps = null;
if (orderBy != null)
{
sortProps = new ArrayList<Pair<QName, Boolean>>(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 Pair<QName, Boolean>(sortProp, sortAsc));
}
}
}
}
}
}
PagingFileInfoRequest pageRequest = new PagingFileInfoRequest(skipCount.intValue(), maxItems.intValue(), sortProps, null);
pageRequest.setRequestTotalCountMax(skipCount.intValue() + 1000); // TODO make this optional/configurable - affects whether numItems may be returned
PagingFileInfoResults pageOfNodeInfos = connector.getFileFolderService().list(folderNodeRef, true, true, null, pageRequest);
List<FileInfo> childrenList = pageOfNodeInfos.getPage();
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
}
}
}
if (pageOfNodeInfos.hasMoreItems() != null)
{
result.setHasMoreItems(pageOfNodeInfos.hasMoreItems());
}
Pair<Integer, Integer> totalCounts = pageOfNodeInfos.getTotalResultCount();
if (totalCounts != null)
{
Integer totalCountLower = totalCounts.getFirst();
Integer totalCountUpper = totalCounts.getSecond();
if ((totalCountLower != null) && (totalCountLower.equals(totalCountUpper)))
{
result.setNumItems(BigInteger.valueOf(totalCountLower));
}
}
if (logger.isDebugEnabled())
{
logger.debug("getChildren: "+childrenList.size()+" in "+(System.currentTimeMillis()-start)+" msecs");
}
return result;
}
@Override
public List<ObjectInFolderContainer> getDescendants(String repositoryId, String folderId, BigInteger depth,
String filter, Boolean includeAllowableActions, IncludeRelationships includeRelationships,
String renditionFilter, Boolean includePathSegment, ExtensionsData extension)
{
checkRepositoryId(repositoryId);
List<ObjectInFolderContainer> result = new ArrayList<ObjectInFolderContainer>();
getDescendantsTree(repositoryId, connector.getFolderNodeRef("Folder", folderId), depth.intValue(), filter,
includeAllowableActions, includeRelationships, renditionFilter, includePathSegment, false, result);
return result;
}
@Override
public List<ObjectInFolderContainer> getFolderTree(String repositoryId, String folderId, BigInteger depth,
String filter, Boolean includeAllowableActions, IncludeRelationships includeRelationships,
String renditionFilter, Boolean includePathSegment, ExtensionsData extension)
{
checkRepositoryId(repositoryId);
List<ObjectInFolderContainer> result = new ArrayList<ObjectInFolderContainer>();
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<ObjectInFolderContainer> list)
{
// get the children references
List<ChildAssociationRef> 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<ObjectInFolderContainer>());
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<ObjectParentData> getObjectParents(String repositoryId, String objectId, String filter,
Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter,
Boolean includeRelativePathSegment, ExtensionsData extension)
{
checkRepositoryId(repositoryId);
List<ObjectParentData> result = new ArrayList<ObjectParentData>();
// 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<ChildAssociationRef> 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<String> 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<String> 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<NodeRef>()
{
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<String> 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<NodeRef>()
{
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<String> 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<NodeRef>()
{
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<String> 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<String> 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<AssociationRef>()
{
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<String> objectId, Boolean overwriteFlag,
Holder<String> 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<Object>()
{
public Object execute() throws Exception
{
try
{
ContentWriter writer = connector.getFileFolderService().getWriter(nodeRef);
writer.setMimetype(contentStream.getMimeType());
writer.setEncoding(encoding.name());
writer.putContent(tempFile);
return null;
} 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);
objectId.setValue(connector.createObjectId(nodeRef));
}
@Override
public void deleteContentStream(String repositoryId, Holder<String> objectId, Holder<String> changeToken,
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.REQUIRED)
{
throw new CmisInvalidArgumentException("Document type requires content!");
}
endReadOnlyTransaction();
connector.getTransactionService().getRetryingTransactionHelper()
.doInTransaction(new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
{
try
{
connector.getNodeService().setProperty(nodeRef, ContentModel.PROP_CONTENT, null);
return null;
} catch (IntegrityException ie)
{
throw new CmisConstraintException("Constraint violation: " + ie.getMessage(), ie);
} catch (AccessDeniedException ade)
{
throw new CmisPermissionDeniedException("Permission denied!", ade);
}
};
}, false, true);
beginReadOnlyTransaction();
objectId.setValue(connector.createObjectId(nodeRef));
}
@Override
public void moveObject(String repositoryId, Holder<String> objectId, String targetFolderId, String sourceFolderId,
ExtensionsData extension)
{
checkRepositoryId(repositoryId);
// get object and source and target parent
ObjectVariantEnum variant = connector.getObjectVariant(objectId.getValue());
connector.throwCommonExceptions(variant, "Object", objectId.getValue());
final NodeRef nodeRef = connector.getNodeRefIfCurrent("Object", objectId.getValue());
final NodeRef sourceNodeRef = connector.getFolderNodeRef("Source folder", sourceFolderId);
final NodeRef targetNodeRef = connector.getFolderNodeRef("Target folder", targetFolderId);
TypeDefinitionWrapper type = connector.getAndCheckType(nodeRef);
connector.checkChildObjectType(targetNodeRef, type.getTypeId());
endReadOnlyTransaction();
connector.getTransactionService().getRetryingTransactionHelper()
.doInTransaction(new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
{
try
{
ChildAssociationRef primaryParentRef = connector.getNodeService().getPrimaryParent(nodeRef);
// if this is a primary child node, move it
if (primaryParentRef.getParentRef().equals(sourceNodeRef))
{
connector.getNodeService().moveNode(nodeRef, targetNodeRef,
primaryParentRef.getTypeQName(), primaryParentRef.getQName());
} else
{
// otherwise, reparent it
for (ChildAssociationRef parent : connector.getNodeService().getParentAssocs(nodeRef,
ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL))
{
if (parent.getParentRef().equals(sourceNodeRef))
{
connector.getNodeService().removeChildAssociation(parent);
connector.getNodeService().addChild(targetNodeRef, nodeRef,
ContentModel.ASSOC_CONTAINS, parent.getQName());
return null;
}
}
throw new CMISInvalidArgumentException(
"Document is not a child of the source folder that was specified!");
}
return null;
} catch (IntegrityException ie)
{
throw new CmisConstraintException("Constraint violation: " + ie.getMessage(), ie);
} catch (AccessDeniedException ade)
{
throw new CmisPermissionDeniedException("Permission denied!", ade);
}
};
}, false, true);
beginReadOnlyTransaction();
}
@Override
public void updateProperties(String repositoryId, Holder<String> objectId, Holder<String> changeToken,
final Properties properties, ExtensionsData extension)
{
checkRepositoryId(repositoryId);
final ObjectVariantEnum variant = connector.getObjectVariant(objectId.getValue());
connector.throwCommonExceptions(variant, "Object", objectId.getValue());
if (variant == ObjectVariantEnum.ASSOC)
{
throw new CmisInvalidArgumentException("Relationship properties cannot be updated!");
} else
{
final NodeRef nodeRef = connector.getNodeRefIfCurrent("Object", objectId.getValue());
final TypeDefinitionWrapper type = connector.getAndCheckType(nodeRef);
// run transaction
endReadOnlyTransaction();
connector.getTransactionService().getRetryingTransactionHelper()
.doInTransaction(new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
{
try
{
connector.setProperties(nodeRef, type, properties, new String[0]);
return null;
} catch (IntegrityException ie)
{
throw new CmisConstraintException("Constraint violation: " + ie.getMessage(), ie);
} catch (AccessDeniedException ade)
{
throw new CmisPermissionDeniedException("Permission denied!", ade);
}
};
}, false, true);
beginReadOnlyTransaction();
objectId.setValue(connector.createObjectId(nodeRef));
}
}
@Override
public void deleteObject(String repositoryId, String objectId, Boolean allVersions, ExtensionsData extension)
{
deleteObjectOrCancelCheckOut(repositoryId, objectId, allVersions, extension);
}
@Override
public void deleteObjectOrCancelCheckOut(String repositoryId, final String objectId, final Boolean allVersions,
ExtensionsData extension)
{
checkRepositoryId(repositoryId);
// what kind of object is it?
final ObjectVariantEnum variant = connector.getObjectVariant(objectId);
connector.throwCommonExceptions(variant, "Object", objectId);
// run transaction
endReadOnlyTransaction();
connector.getTransactionService().getRetryingTransactionHelper()
.doInTransaction(new RetryingTransactionCallback<Boolean>()
{
public Boolean execute() throws Exception
{
try
{
// handle relationships
if (variant == ObjectVariantEnum.ASSOC)
{
AssociationRef assocRef = connector.getAssociationRef(objectId);
connector.getNodeService().removeAssociation(assocRef.getSourceRef(),
assocRef.getTargetRef(), assocRef.getTypeQName());
return true;
}
NodeRef nodeRef = connector.getNodeRef(objectId);
// handle PWC
if (variant == ObjectVariantEnum.PWC)
{
connector.getCheckOutCheckInService().cancelCheckout(nodeRef);
return true;
}
TypeDefinitionWrapper type = connector.getType(nodeRef);
// handle folders
if (type instanceof FolderTypeDefintionWrapper)
{
if (connector.getNodeService().getChildAssocs(nodeRef).size() > 0)
{
throw new CmisConstraintException(
"Could not delete folder with at least one child!");
}
connector.getNodeService().deleteNode(nodeRef);
return true;
}
// handle versions
if (allVersions)
{
NodeRef workingCopy = connector.getCheckOutCheckInService().getWorkingCopy(nodeRef);
if (workingCopy != null)
{
connector.getCheckOutCheckInService().cancelCheckout(workingCopy);
}
} else if (variant == ObjectVariantEnum.VERSION)
{
Version version = connector.getVersion(objectId);
connector.getVersionService().deleteVersion(nodeRef, version);
return true;
}
if (variant == ObjectVariantEnum.VERSION)
{
nodeRef = connector.getNodeRef(connector.getCurrentVersionId(objectId));
}
// remove not primary parent associations
List<ChildAssociationRef> childAssociations = connector.getNodeService().getParentAssocs(
nodeRef);
if (childAssociations != null)
{
for (ChildAssociationRef childAssoc : childAssociations)
{
if (!childAssoc.isPrimary())
{
connector.getNodeService().removeChildAssociation(childAssoc);
}
}
}
// attempt to delete the node
connector.getNodeService().deleteNode(nodeRef);
return true;
} catch (AccessDeniedException ade)
{
throw new CmisPermissionDeniedException("Permission denied!", ade);
}
};
}, false, true);
endReadOnlyTransaction();
}
@Override
public FailedToDeleteData deleteTree(String repositoryId, String folderId, Boolean allVersions,
UnfileObject unfileObjects, final Boolean continueOnFailure, ExtensionsData extension)
{
checkRepositoryId(repositoryId);
if (!allVersions)
{
throw new CmisInvalidArgumentException("Only allVersions=true supported!");
}
if (unfileObjects == UnfileObject.UNFILE)
{
throw new CmisInvalidArgumentException("Unfiling not supported!");
}
final NodeRef folderNodeRef = connector.getFolderNodeRef("Folder", folderId);
final FailedToDeleteDataImpl result = new FailedToDeleteDataImpl();
// run transaction
endReadOnlyTransaction();
result.setIds(connector.getTransactionService().getRetryingTransactionHelper()
.doInTransaction(new RetryingTransactionCallback<List<String>>()
{
public List<String> execute() throws Exception
{
return deleteBranch(folderNodeRef, continueOnFailure);
};
}, false, true));
beginReadOnlyTransaction();
return result;
}
private List<String> deleteBranch(NodeRef nodeRef, boolean continueOnFailure)
{
List<String> result = new ArrayList<String>();
try
{
// remove children
List<ChildAssociationRef> childrenList = connector.getNodeService().getChildAssocs(nodeRef);
if (childrenList != null)
{
for (ChildAssociationRef child : childrenList)
{
List<String> ftod = deleteBranch(child.getChildRef(), continueOnFailure);
if (!ftod.isEmpty())
{
result.addAll(ftod);
if (!continueOnFailure)
{
return result;
}
}
}
}
// remove not primary parent associations
List<ChildAssociationRef> childAssociations = connector.getNodeService().getParentAssocs(nodeRef);
if (childAssociations != null)
{
for (ChildAssociationRef childAssoc : childAssociations)
{
if (!childAssoc.isPrimary())
{
connector.getNodeService().removeChildAssociation(childAssoc);
}
}
}
// attempt to delete the node
connector.getNodeService().deleteNode(nodeRef);
} catch (Exception e)
{
result.add(nodeRef.toString());
}
return result;
}
@Override
public ObjectData getObject(String repositoryId, String objectId, String filter, Boolean includeAllowableActions,
IncludeRelationships includeRelationships, String renditionFilter, Boolean includePolicyIds,
Boolean includeAcl, ExtensionsData extension)
{
checkRepositoryId(repositoryId);
// what kind of object is it?
ObjectVariantEnum variant = connector.getObjectVariant(objectId);
connector.throwCommonExceptions(variant, "Object", objectId);
// create a CMIS object
if (variant == ObjectVariantEnum.ASSOC)
{
AssociationRef assocRef = connector.getAssociationRef(objectId);
return connector.createCMISObject(assocRef, filter, includeAllowableActions, includeRelationships,
renditionFilter, includePolicyIds, includeAcl);
} else
{
NodeRef nodeRef = connector.getNodeRef(objectId);
return connector.createCMISObject(nodeRef, filter, includeAllowableActions, includeRelationships,
renditionFilter, includePolicyIds, includeAcl);
}
}
@Override
public ObjectData getObjectByPath(String repositoryId, String path, String filter, Boolean includeAllowableActions,
IncludeRelationships includeRelationships, String renditionFilter, Boolean includePolicyIds,
Boolean includeAcl, ExtensionsData extension)
{
checkRepositoryId(repositoryId);
// start at the root node
NodeRef rootNodeRef = connector.getRootNodeRef();
NodeRef nodeRef = null;
if (path.equals("/"))
{
nodeRef = rootNodeRef;
} else
{
try
{
// resolve path and get the node ref
FileInfo info = connector.getFileFolderService().resolveNamePath(rootNodeRef,
Arrays.asList(path.substring(1).split("/")));
nodeRef = info.getNodeRef();
} catch (FileNotFoundException e)
{
throw new CmisObjectNotFoundException("Object not found: " + path);
}
}
// create the CMIS object
return connector.createCMISObject(nodeRef, filter, includeAllowableActions, includeRelationships,
renditionFilter, includePolicyIds, includeAcl);
}
@Override
public Properties getProperties(String repositoryId, String objectId, String filter, ExtensionsData extension)
{
checkRepositoryId(repositoryId);
// what kind of object is it?
ObjectVariantEnum variant = connector.getObjectVariant(objectId);
connector.throwCommonExceptions(variant, "Object", objectId);
if (variant == ObjectVariantEnum.ASSOC)
{
AssociationRef assocRef = connector.getAssociationRef(objectId);
TypeDefinitionWrapper type = connector.getType(assocRef);
if (type == null)
{
throw new CmisObjectNotFoundException("No corresponding type found! Not a CMIS object?");
}
return connector.getAssocProperties(assocRef, filter, type);
} else
{
NodeRef nodeRef = connector.getNodeRef(objectId);
TypeDefinitionWrapper type = connector.getAndCheckType(nodeRef);
return connector.getNodeProperties(nodeRef, filter, type);
}
}
@Override
public AllowableActions getAllowableActions(String repositoryId, String objectId, ExtensionsData extension)
{
checkRepositoryId(repositoryId);
// what kind of object is it?
ObjectVariantEnum variant = connector.getObjectVariant(objectId);
connector.throwCommonExceptions(variant, "Object", objectId);
TypeDefinitionWrapper type = null;
EntityRef ref = null;
if (variant == ObjectVariantEnum.ASSOC)
{
AssociationRef assocRef = connector.getAssociationRef(objectId);
type = connector.getType(assocRef);
ref = assocRef;
} else
{
NodeRef nodeRef = connector.getNodeRef(objectId);
type = connector.getType(nodeRef);
ref = nodeRef;
}
if (type == null)
{
throw new CmisObjectNotFoundException("No corresponding type found! Not a CMIS object?");
}
return connector.getAllowableActions(type, ref);
}
@Override
public ContentStream getContentStream(String repositoryId, String objectId, String streamId, BigInteger offset,
BigInteger length, ExtensionsData extension)
{
checkRepositoryId(repositoryId);
// what kind of object is it?
ObjectVariantEnum variant = connector.getObjectVariant(objectId);
connector.throwCommonExceptions(variant, "Object", objectId);
// relationships cannot have content
if (variant == ObjectVariantEnum.ASSOC)
{
throw new CmisInvalidArgumentException("Object is a relationship and cannot have content!");
}
// now get it
NodeRef nodeRef = connector.getNodeRef(objectId);
return connector.getContentStream(nodeRef, streamId, offset, length);
}
@Override
public List<RenditionData> getRenditions(String repositoryId, String objectId, String renditionFilter,
BigInteger maxItems, BigInteger skipCount, ExtensionsData extension)
{
checkRepositoryId(repositoryId);
// what kind of object is it?
ObjectVariantEnum variant = connector.getObjectVariant(objectId);
connector.throwCommonExceptions(variant, "Object", objectId);
if (variant == ObjectVariantEnum.ASSOC)
{
return Collections.emptyList();
} else
{
NodeRef nodeRef = connector.getNodeRef(objectId);
return connector.getRendtions(nodeRef, renditionFilter, maxItems, skipCount);
}
}
// --- versioning service ---
@Override
public void checkOut(String repositoryId, final Holder<String> objectId, ExtensionsData extension,
final Holder<Boolean> contentCopied)
{
checkRepositoryId(repositoryId);
ObjectVariantEnum variant = connector.getObjectVariant(objectId.getValue());
connector.throwCommonExceptions(variant, "Object", objectId.getValue());
// relationships cannot be checked out
if (variant == ObjectVariantEnum.ASSOC)
{
throw new CmisInvalidArgumentException("Unable to check-out a relationship!");
}
// get object
final NodeRef nodeRef = connector.getNodeRefIfCurrent("Document", objectId.getValue());
TypeDefinitionWrapper type = connector.getAndCheckType(nodeRef);
if (!(type instanceof DocumentTypeDefinitionWrapper))
{
throw new CmisInvalidArgumentException("Object is not a document!");
}
if (!((DocumentTypeDefinition) type.getTypeDefinition(false)).isVersionable())
{
throw new CmisConstraintException("Document is not versionable!");
}
// check out
endReadOnlyTransaction();
connector.getTransactionService().getRetryingTransactionHelper()
.doInTransaction(new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
{
try
{
NodeRef pwcNodeRef = connector.getCheckOutCheckInService().checkout(nodeRef);
objectId.setValue(pwcNodeRef.toString());
if (contentCopied != null)
{
contentCopied.setValue(connector.getFileFolderService().getReader(pwcNodeRef) != null);
}
return null;
} catch (CheckOutCheckInServiceException e)
{
throw new CmisVersioningException("Check out failed: " + e.getMessage(), e);
} catch (AccessDeniedException ade)
{
throw new CmisPermissionDeniedException("Permission denied!", ade);
}
};
}, false, true);
beginReadOnlyTransaction();
}
@Override
public void cancelCheckOut(String repositoryId, String objectId, ExtensionsData extension)
{
checkRepositoryId(repositoryId);
ObjectVariantEnum variant = connector.getObjectVariant(objectId);
connector.throwCommonExceptions(variant, "Object", objectId);
// only accept a PWC
if (variant != ObjectVariantEnum.PWC)
{
throw new CmisVersioningException("Object is not a PWC!");
}
// get object
final NodeRef nodeRef = connector.getNodeRef(objectId);
// cancel check out
endReadOnlyTransaction();
connector.getTransactionService().getRetryingTransactionHelper()
.doInTransaction(new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
{
try
{
connector.getCheckOutCheckInService().cancelCheckout(nodeRef);
return null;
} catch (CheckOutCheckInServiceException e)
{
throw new CmisVersioningException("Check out failed: " + e.getMessage(), e);
} catch (AccessDeniedException ade)
{
throw new CmisPermissionDeniedException("Permission denied!", ade);
}
};
}, false, true);
beginReadOnlyTransaction();
}
@Override
public void checkIn(String repositoryId, final Holder<String> objectId, final Boolean major,
final Properties properties, final ContentStream contentStream, final String checkinComment,
final List<String> policies, final Acl addAces, final Acl removeAces, ExtensionsData extension)
{
checkRepositoryId(repositoryId);
ObjectVariantEnum variant = connector.getObjectVariant(objectId.getValue());
connector.throwCommonExceptions(variant, "Object", objectId.getValue());
// only accept a PWC
if (variant != ObjectVariantEnum.PWC)
{
throw new CmisVersioningException("Object is not a PWC!");
}
// get object
final NodeRef nodeRef = connector.getNodeRef(objectId.getValue());
final TypeDefinitionWrapper type = connector.getAndCheckType(nodeRef);
// copy stream to temp file
final File tempFile = copyToTempFile(contentStream);
final Charset encoding = (tempFile == null ? null : getEncoding(tempFile, contentStream.getMimeType()));
// check in
endReadOnlyTransaction();
connector.getTransactionService().getRetryingTransactionHelper()
.doInTransaction(new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
{
try
{
// update PWC
connector.setProperties(nodeRef, type, properties,
new String[] { 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);
}
// check aspect
if (connector.getNodeService().hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == false)
{
Map<QName, Serializable> props = new HashMap<QName, Serializable>();
props.put(ContentModel.PROP_INITIAL_VERSION, false);
props.put(ContentModel.PROP_AUTO_VERSION, false);
connector.getNodeService().addAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE, props);
}
// create version properties
Map<String, Serializable> versionProperties = new HashMap<String, Serializable>(5);
versionProperties.put(VersionModel.PROP_VERSION_TYPE, major ? VersionType.MAJOR
: VersionType.MINOR);
if (checkinComment != null)
{
versionProperties.put(VersionModel.PROP_DESCRIPTION, checkinComment);
}
// check in
NodeRef newNodeRef = connector.getCheckOutCheckInService().checkin(nodeRef,
versionProperties);
objectId.setValue(connector.createObjectId(newNodeRef));
return null;
} 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 (CheckOutCheckInServiceException e)
{
removeTempFile(tempFile);
throw new CmisVersioningException("Check out failed: " + e.getMessage(), e);
} catch (AccessDeniedException ade)
{
removeTempFile(tempFile);
throw new CmisPermissionDeniedException("Permission denied!", ade);
}
};
}, false, true);
beginReadOnlyTransaction();
removeTempFile(tempFile);
}
@Override
public List<ObjectData> getAllVersions(String repositoryId, String objectId, String versionSeriesId, String filter,
Boolean includeAllowableActions, ExtensionsData extension)
{
checkRepositoryId(repositoryId);
if (objectId != null)
{
// it's an AtomPub call
versionSeriesId = connector.getCurrentVersionId(objectId);
}
if (versionSeriesId == null)
{
throw new CmisInvalidArgumentException("Object Id or Object Series Id must be set!");
}
List<ObjectData> result = new ArrayList<ObjectData>();
// what kind of object is it?
ObjectVariantEnum variant = connector.getObjectVariant(versionSeriesId);
connector.throwCommonExceptions(variant, "Version Series", versionSeriesId);
if (variant == ObjectVariantEnum.ASSOC)
{
// the relationship history is always empty
return result;
}
if (variant != ObjectVariantEnum.NODE)
{
// the version series id is the id of current version, which is a
// node
throw new CmisInvalidArgumentException("Version Series does not exist!");
}
// get current version and it's history
NodeRef nodeRef = connector.getNodeRef(versionSeriesId);
VersionHistory versionHistory = connector.getVersionService().getVersionHistory(nodeRef);
if (versionHistory == null)
{
// add current version
result.add(connector.createCMISObject(nodeRef, filter, includeAllowableActions, IncludeRelationships.NONE,
CMISConnector.RENDITION_NONE, false, false));
} else
{
NodeRef pwcNodeRef = connector.getCheckOutCheckInService().getWorkingCopy(nodeRef);
if (pwcNodeRef != null)
{
result.add(connector.createCMISObject(pwcNodeRef, filter, includeAllowableActions,
IncludeRelationships.NONE, CMISConnector.RENDITION_NONE, false, false));
}
// convert the version history
for (Version version : versionHistory.getAllVersions())
{
result.add(connector.createCMISObject(version.getFrozenStateNodeRef(), filter, includeAllowableActions,
IncludeRelationships.NONE, CMISConnector.RENDITION_NONE, false, false));
}
}
return result;
}
@Override
public ObjectData getObjectOfLatestVersion(String repositoryId, String objectId, String versionSeriesId,
Boolean major, String filter, Boolean includeAllowableActions, IncludeRelationships includeRelationships,
String renditionFilter, Boolean includePolicyIds, Boolean includeAcl, ExtensionsData extension)
{
checkRepositoryId(repositoryId);
if (objectId != null)
{
// it's an AtomPub call
versionSeriesId = connector.getCurrentVersionId(objectId);
}
// what kind of object is it?
ObjectVariantEnum variant = connector.getObjectVariant(versionSeriesId);
connector.throwCommonExceptions(variant, "Version series", versionSeriesId);
// create a CMIS object
if (variant == ObjectVariantEnum.ASSOC)
{
AssociationRef assocRef = connector.getAssociationRef(versionSeriesId);
return connector.createCMISObject(assocRef, filter, includeAllowableActions, includeRelationships,
renditionFilter, includePolicyIds, includeAcl);
} else
{
NodeRef nodeRef = connector.getLatestVersionNodeRef(versionSeriesId, major);
return connector.createCMISObject(nodeRef, filter, includeAllowableActions, includeRelationships,
renditionFilter, includePolicyIds, includeAcl);
}
}
@Override
public Properties getPropertiesOfLatestVersion(String repositoryId, String objectId, String versionSeriesId,
Boolean major, String filter, ExtensionsData extension)
{
checkRepositoryId(repositoryId);
if (objectId != null)
{
// it's an AtomPub call
versionSeriesId = connector.getCurrentVersionId(objectId);
}
// what kind of object is it?
ObjectVariantEnum variant = connector.getObjectVariant(versionSeriesId);
connector.throwCommonExceptions(variant, "Version series", versionSeriesId);
if (variant == ObjectVariantEnum.ASSOC)
{
AssociationRef assocRef = connector.getAssociationRef(versionSeriesId);
TypeDefinitionWrapper type = connector.getType(assocRef);
if (type == null)
{
throw new CmisObjectNotFoundException("No corresponding type found! Not a CMIS object?");
}
return connector.getAssocProperties(assocRef, filter, type);
} else
{
NodeRef nodeRef = connector.getLatestVersionNodeRef(versionSeriesId, major);
TypeDefinitionWrapper type = connector.getAndCheckType(nodeRef);
return connector.getNodeProperties(nodeRef, filter, type);
}
}
// --- multifiling service ---
@Override
public void addObjectToFolder(String repositoryId, String objectId, String folderId, Boolean allVersions,
ExtensionsData extension)
{
checkRepositoryId(repositoryId);
if (!allVersions)
{
throw new CmisInvalidArgumentException("Only allVersions=true supported!");
}
// get node ref
ObjectVariantEnum variant = connector.getObjectVariant(objectId);
connector.throwCommonExceptions(variant, "Object", objectId);
if (variant != ObjectVariantEnum.NODE)
{
throw new CmisInvalidArgumentException("Object is not a current version of a document!");
}
final NodeRef nodeRef = connector.getNodeRefIfCurrent("Object", objectId);
TypeDefinitionWrapper type = connector.getAndCheckType(nodeRef);
if (!(type instanceof DocumentTypeDefinitionWrapper))
{
throw new CmisInvalidArgumentException("Object is not a document!");
}
// get the folder node ref
final NodeRef folderNodeRef = connector.getFolderNodeRef("Folder", folderId);
connector.checkChildObjectType(folderNodeRef, type.getTypeId());
final QName name = QName.createQName(
NamespaceService.CONTENT_MODEL_1_0_URI,
QName.createValidLocalName((String) connector.getNodeService().getProperty(nodeRef,
ContentModel.PROP_NAME)));
endReadOnlyTransaction();
connector.getTransactionService().getRetryingTransactionHelper()
.doInTransaction(new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
{
try
{
connector.getNodeService().addChild(folderNodeRef, nodeRef, ContentModel.ASSOC_CONTAINS,
name);
return null;
} catch (IntegrityException ie)
{
throw new CmisConstraintException("Constraint violation: " + ie.getMessage(), ie);
} catch (AccessDeniedException ade)
{
throw new CmisPermissionDeniedException("Permission denied!", ade);
}
};
}, false, true);
beginReadOnlyTransaction();
}
@Override
public void removeObjectFromFolder(String repositoryId, String objectId, String folderId, ExtensionsData extension)
{
checkRepositoryId(repositoryId);
// get node ref
ObjectVariantEnum variant = connector.getObjectVariant(objectId);
connector.throwCommonExceptions(variant, "Object", objectId);
if (variant != ObjectVariantEnum.NODE)
{
throw new CmisInvalidArgumentException("Object is not a current version of a document!");
}
final NodeRef nodeRef = connector.getNodeRefIfCurrent("Object", objectId);
TypeDefinitionWrapper type = connector.getAndCheckType(nodeRef);
if (!(type instanceof DocumentTypeDefinitionWrapper))
{
throw new CmisInvalidArgumentException("Object is not a document!");
}
// get the folder node ref
final NodeRef folderNodeRef = connector.getFolderNodeRef("Folder", folderId);
// check primary parent
if (connector.getNodeService().getPrimaryParent(nodeRef).getParentRef().equals(folderNodeRef))
{
throw new CmisConstraintException(
"Unfiling from primary parent folder is not supported! Use deleteObject() instead.");
}
endReadOnlyTransaction();
connector.getTransactionService().getRetryingTransactionHelper()
.doInTransaction(new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
{
try
{
connector.getNodeService().removeChild(folderNodeRef, nodeRef);
return null;
} catch (IntegrityException ie)
{
throw new CmisConstraintException("Constraint violation: " + ie.getMessage(), ie);
} catch (AccessDeniedException ade)
{
throw new CmisPermissionDeniedException("Permission denied!", ade);
}
};
}, false, true);
beginReadOnlyTransaction();
}
// --- discovery service ---
@Override
public ObjectList getContentChanges(String repositoryId, Holder<String> changeLogToken, Boolean includeProperties,
String filter, Boolean includePolicyIds, Boolean includeAcl, BigInteger maxItems, ExtensionsData extension)
{
checkRepositoryId(repositoryId);
return connector.getContentChanges(changeLogToken, maxItems);
}
@Override
public ObjectList query(String repositoryId, String statement, Boolean searchAllVersions,
Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter,
BigInteger maxItems, BigInteger skipCount, ExtensionsData extension)
{
checkRepositoryId(repositoryId);
if (searchAllVersions.booleanValue())
{
throw new CmisInvalidArgumentException("Search all version is not supported!");
}
return connector.query(statement, includeAllowableActions, includeRelationships, renditionFilter, maxItems,
skipCount);
}
// --- relationship service ---
@Override
public ObjectList getObjectRelationships(String repositoryId, String objectId, Boolean includeSubRelationshipTypes,
RelationshipDirection relationshipDirection, String typeId, String filter, Boolean includeAllowableActions,
BigInteger maxItems, BigInteger skipCount, ExtensionsData extension)
{
checkRepositoryId(repositoryId);
// what kind of object is it?
ObjectVariantEnum variant = connector.getObjectVariant(objectId);
connector.throwCommonExceptions(variant, "Object", objectId);
// create a CMIS object
if (variant == ObjectVariantEnum.ASSOC)
{
throw new CmisInvalidArgumentException("Object is a relationship!");
}
NodeRef nodeRef = connector.getNodeRefIfCurrent("Object", objectId);
return connector.getObjectRelationships(nodeRef, relationshipDirection, typeId, filter,
includeAllowableActions, maxItems, skipCount);
}
// --- policy service ---
@Override
public void applyPolicy(String repositoryId, String policyId, String objectId, ExtensionsData extension)
{
checkRepositoryId(repositoryId);
// what kind of object is it?
ObjectVariantEnum variant = connector.getObjectVariant(objectId);
connector.throwCommonExceptions(variant, "Object", objectId);
NodeRef nodeRef = connector.getNodeRefIfCurrent("Object", objectId);
TypeDefinitionWrapper type = connector.getAndCheckType(nodeRef);
connector.applyPolicies(nodeRef, type, Collections.singletonList(policyId));
}
@Override
public void removePolicy(String repositoryId, String policyId, String objectId, ExtensionsData extension)
{
checkRepositoryId(repositoryId);
// what kind of object is it?
ObjectVariantEnum variant = connector.getObjectVariant(objectId);
connector.throwCommonExceptions(variant, "Object", objectId);
NodeRef nodeRef = connector.getNodeRefIfCurrent("Object", objectId);
connector.getAndCheckType(nodeRef);
throw new CmisConstraintException("Object is not policy controllable!");
}
@Override
public List<ObjectData> getAppliedPolicies(String repositoryId, String objectId, String filter,
ExtensionsData extension)
{
checkRepositoryId(repositoryId);
// what kind of object is it?
ObjectVariantEnum variant = connector.getObjectVariant(objectId);
connector.throwCommonExceptions(variant, "Object", objectId);
NodeRef nodeRef = connector.getNodeRef(objectId);
connector.getAndCheckType(nodeRef);
// policies are not supported -> return empty list
return Collections.emptyList();
}
// --- ACL service ---
@Override
public Acl applyAcl(String repositoryId, String objectId, final Acl addAces, final Acl removeAces,
AclPropagation aclPropagation, ExtensionsData extension)
{
checkRepositoryId(repositoryId);
if (aclPropagation == AclPropagation.OBJECTONLY)
{
throw new CmisInvalidArgumentException("ACL propagation 'objectonly' is not supported!");
}
ObjectVariantEnum variant = connector.getObjectVariant(objectId);
connector.throwCommonExceptions(variant, "Object", objectId);
// relationships don't have ACLs
if (variant == ObjectVariantEnum.ASSOC)
{
throw new CmisConstraintException("Relationships are not ACL controllable!");
}
final NodeRef nodeRef = connector.getNodeRefIfCurrent("Object id", objectId);
final TypeDefinitionWrapper type = connector.getAndCheckType(nodeRef);
endReadOnlyTransaction();
connector.getTransactionService().getRetryingTransactionHelper()
.doInTransaction(new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
{
try
{
connector.applyACL(nodeRef, type, addAces, removeAces);
return null;
} 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.getACL(nodeRef, false);
}
@Override
public Acl applyAcl(String repositoryId, String objectId, final Acl aces, AclPropagation aclPropagation)
{
checkRepositoryId(repositoryId);
if (aclPropagation == AclPropagation.OBJECTONLY)
{
throw new CmisInvalidArgumentException("ACL propagation 'objectonly' is not supported!");
}
ObjectVariantEnum variant = connector.getObjectVariant(objectId);
connector.throwCommonExceptions(variant, "Object", objectId);
// relationships don't have ACLs
if (variant == ObjectVariantEnum.ASSOC)
{
throw new CmisConstraintException("Relationships are not ACL controllable!");
}
final NodeRef nodeRef = connector.getNodeRefIfCurrent("Object", objectId);
final TypeDefinitionWrapper type = connector.getAndCheckType(nodeRef);
endReadOnlyTransaction();
connector.getTransactionService().getRetryingTransactionHelper()
.doInTransaction(new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
{
try
{
connector.applyACL(nodeRef, type, aces);
return null;
} 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.getACL(nodeRef, false);
}
@SuppressWarnings("unchecked")
@Override
public Acl getAcl(String repositoryId, String objectId, Boolean onlyBasicPermissions, ExtensionsData extension)
{
checkRepositoryId(repositoryId);
ObjectVariantEnum variant = connector.getObjectVariant(objectId);
connector.throwCommonExceptions(variant, "Object", objectId);
// relationships don't have ACLs
if (variant == ObjectVariantEnum.ASSOC)
{
return new AccessControlListImpl(Collections.EMPTY_LIST);
}
// get the ACL
String currentVersionId = connector.getCurrentVersionId(objectId);
NodeRef nodeRef = connector.getNodeRef(currentVersionId);
return connector.getACL(nodeRef, onlyBasicPermissions);
}
// --------------------------------------------------------
/**
* Collects the {@link ObjectInfo} about an object.
*
* (Provided by OpenCMIS, but optimized for Alfresco.)
*/
protected ObjectInfo getObjectInfoIntern(String repositoryId, ObjectData object)
{
// if the object has no properties, stop here
if (object.getProperties() == null || object.getProperties().getProperties() == null)
{
throw new CmisRuntimeException("No properties!");
}
ObjectInfoImpl info = new ObjectInfoImpl();
// general properties
info.setObject(object);
info.setId(object.getId());
info.setName(getStringProperty(object, PropertyIds.NAME));
info.setCreatedBy(getStringProperty(object, PropertyIds.CREATED_BY));
info.setCreationDate(getDateTimeProperty(object, PropertyIds.CREATED_BY));
info.setLastModificationDate(getDateTimeProperty(object, PropertyIds.LAST_MODIFICATION_DATE));
info.setTypeId(getIdProperty(object, PropertyIds.OBJECT_TYPE_ID));
info.setBaseType(object.getBaseTypeId());
if (object.getBaseTypeId() == BaseTypeId.CMIS_RELATIONSHIP)
{
// versioning
info.setIsCurrentVersion(false);
info.setWorkingCopyId(null);
info.setWorkingCopyOriginalId(null);
info.setVersionSeriesId(getIdProperty(object, PropertyIds.VERSION_SERIES_ID));
info.setIsCurrentVersion(true);
info.setWorkingCopyId(null);
info.setWorkingCopyOriginalId(null);
// content
info.setHasContent(false);
info.setContentType(null);
info.setFileName(null);
// parent
info.setHasParent(false);
// policies and relationships
info.setSupportsRelationships(false);
info.setSupportsPolicies(false);
// renditions
info.setRenditionInfos(null);
// relationships
info.setRelationshipSourceIds(null);
info.setRelationshipTargetIds(null);
// global settings
info.setHasAcl(false);
info.setSupportsDescendants(false);
info.setSupportsFolderTree(false);
} else
{
// versioning
info.setIsCurrentVersion(object.getBaseTypeId() == BaseTypeId.CMIS_DOCUMENT);
info.setWorkingCopyId(null);
info.setWorkingCopyOriginalId(null);
info.setVersionSeriesId(null);
if (object.getBaseTypeId() == BaseTypeId.CMIS_DOCUMENT)
{
info.setVersionSeriesId(getIdProperty(object, PropertyIds.VERSION_SERIES_ID));
if (info.getVersionSeriesId() != null)
{
String versionLabel = getStringProperty(object, PropertyIds.VERSION_LABEL);
if (CMISConnector.PWC_VERSION_LABEL.equals(versionLabel))
{
info.setIsCurrentVersion(false);
info.setWorkingCopyId(null);
// get latest version
List<ObjectData> versions = getAllVersions(repositoryId, null, info.getVersionSeriesId(), null,
Boolean.FALSE, null);
if (versions != null && versions.size() > 0)
{
info.setWorkingCopyOriginalId(versions.get(0).getId());
}
} else
{
Boolean isLatest = getBooleanProperty(object, PropertyIds.IS_LATEST_VERSION);
info.setIsCurrentVersion(isLatest == null ? true : isLatest.booleanValue());
Boolean isCheckedOut = getBooleanProperty(object, PropertyIds.IS_VERSION_SERIES_CHECKED_OUT);
if (isCheckedOut != null && isCheckedOut.booleanValue())
{
info.setWorkingCopyId(getIdProperty(object, PropertyIds.VERSION_SERIES_CHECKED_OUT_ID));
// get latest version
List<ObjectData> versions = getAllVersions(repositoryId, object.getId(),
info.getVersionSeriesId(), null, Boolean.FALSE, null);
if (versions != null && versions.size() > 0)
{
info.setWorkingCopyOriginalId(versions.get(0).getId());
}
}
}
}
}
// content
info.setHasContent(false);
info.setContentType(null);
info.setFileName(null);
if (object.getBaseTypeId() == BaseTypeId.CMIS_DOCUMENT)
{
String fileName = getStringProperty(object, PropertyIds.CONTENT_STREAM_FILE_NAME);
String mimeType = getStringProperty(object, PropertyIds.CONTENT_STREAM_MIME_TYPE);
String streamId = getIdProperty(object, PropertyIds.CONTENT_STREAM_ID);
BigInteger length = getIntegerProperty(object, PropertyIds.CONTENT_STREAM_LENGTH);
boolean hasContent = fileName != null || mimeType != null || streamId != null || length != null;
if (hasContent)
{
info.setHasContent(hasContent);
info.setContentType(mimeType);
info.setFileName(fileName);
}
}
// parent
info.setHasParent(true);
if (object.getBaseTypeId() == BaseTypeId.CMIS_FOLDER)
{
List<ObjectParentData> parents = getObjectParents(repositoryId, object.getId(), null, Boolean.FALSE,
IncludeRelationships.NONE, "cmis:none", Boolean.FALSE, null);
info.setHasParent(parents.size() > 0);
}
// policies and relationships
info.setSupportsRelationships(true);
info.setSupportsPolicies(true);
// renditions
info.setRenditionInfos(null);
if (object.getBaseTypeId() == BaseTypeId.CMIS_DOCUMENT)
{
List<RenditionData> renditions = object.getRenditions();
if (renditions != null && renditions.size() > 0)
{
List<RenditionInfo> renditionInfos = new ArrayList<RenditionInfo>();
for (RenditionData rendition : renditions)
{
RenditionInfoImpl renditionInfo = new RenditionInfoImpl();
renditionInfo.setId(rendition.getStreamId());
renditionInfo.setKind(rendition.getKind());
renditionInfo.setContentType(rendition.getMimeType());
renditionInfo.setTitle(rendition.getTitle());
renditionInfo.setLength(rendition.getBigLength());
renditionInfos.add(renditionInfo);
}
info.setRenditionInfos(renditionInfos);
}
}
// relationships
info.setRelationshipSourceIds(null);
info.setRelationshipTargetIds(null);
List<ObjectData> relationships = object.getRelationships();
if (relationships != null && relationships.size() > 0)
{
List<String> sourceIds = new ArrayList<String>();
List<String> targetIds = new ArrayList<String>();
for (ObjectData relationship : relationships)
{
String sourceId = getIdProperty(relationship, PropertyIds.SOURCE_ID);
String targetId = getIdProperty(relationship, PropertyIds.TARGET_ID);
if (object.getId().equals(sourceId))
{
sourceIds.add(relationship.getId());
}
if (object.getId().equals(targetId))
{
targetIds.add(relationship.getId());
}
}
if (sourceIds.size() > 0)
{
info.setRelationshipSourceIds(sourceIds);
}
if (targetIds.size() > 0)
{
info.setRelationshipTargetIds(targetIds);
}
}
// global settings
info.setHasAcl(true);
info.setSupportsDescendants(true);
info.setSupportsFolderTree(true);
}
return info;
}
// --------------------------------------------------------
private void checkRepositoryId(String repositoryId)
{
if (!connector.getRepositoryInfo().getId().equals(repositoryId))
{
throw new CmisObjectNotFoundException("Unknown repository '" + repositoryId + "'!");
}
}
private Charset getEncoding(File tempFile, String mimeType)
{
Charset encoding = null;
try
{
InputStream tfis = new BufferedInputStream(new FileInputStream(tempFile));
ContentCharsetFinder charsetFinder = connector.getMimetypeService().getContentCharsetFinder();
encoding = charsetFinder.getCharset(tfis, mimeType);
tfis.close();
} catch (Exception e)
{
throw new CmisStorageException("Unable to read content: " + e.getMessage(), e);
}
return encoding;
}
private File copyToTempFile(ContentStream contentStream)
{
File tempFile = null;
if (contentStream == null)
{
return tempFile;
}
int bufferSize = 40 * 1014;
try
{
tempFile = TempFileProvider.createTempFile("cmis", "content");
if (contentStream.getStream() != null)
{
OutputStream out = new BufferedOutputStream(new FileOutputStream(tempFile), bufferSize);
InputStream in = new BufferedInputStream(contentStream.getStream(), bufferSize);
byte[] buffer = new byte[bufferSize];
int i;
while ((i = in.read(buffer)) > -1)
{
out.write(buffer, 0, i);
}
in.close();
out.close();
}
} catch (Exception e)
{
throw new CmisStorageException("Unable to store content: " + e.getMessage(), e);
}
return tempFile;
}
private void removeTempFile(File tempFile)
{
if (tempFile == null)
{
return;
}
try
{
tempFile.delete();
} catch (Exception e)
{
// ignore - file will be removed by TempFileProvider
}
}
}