org.alfresco.opencmis.CMISTest.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.opencmis.CMISTest.java

Source

/*
 * #%L
 * Alfresco Repository
 * %%
 * Copyright (C) 2005 - 2016 Alfresco Software Limited
 * %%
 * This file is part of the Alfresco software. 
 * If the software was purchased under a paid Alfresco license, the terms of 
 * the paid license agreement will prevail.  Otherwise, the software is 
 * provided under the following open source license terms:
 * 
 * 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/>.
 * #L%
 */

package org.alfresco.opencmis;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.File;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.alfresco.model.ContentModel;
import org.alfresco.opencmis.dictionary.CMISDictionaryService;
import org.alfresco.opencmis.dictionary.PropertyDefinitionWrapper;
import org.alfresco.opencmis.dictionary.TypeDefinitionWrapper;
import org.alfresco.opencmis.search.CMISQueryOptions;
import org.alfresco.opencmis.search.CMISQueryOptions.CMISQueryMode;
import org.alfresco.repo.action.evaluator.ComparePropertyValueEvaluator;
import org.alfresco.repo.action.executer.AddFeaturesActionExecuter;
import org.alfresco.repo.audit.AuditComponent;
import org.alfresco.repo.audit.AuditComponentImpl;
import org.alfresco.repo.audit.AuditServiceImpl;
import org.alfresco.repo.audit.UserAuditFilter;
import org.alfresco.repo.audit.model.AuditModelRegistryImpl;
import org.alfresco.repo.batch.BatchProcessWorkProvider;
import org.alfresco.repo.batch.BatchProcessor;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.dictionary.DictionaryDAO;
import org.alfresco.repo.dictionary.M2Model;
import org.alfresco.repo.domain.audit.AuditDAO;
import org.alfresco.repo.domain.node.NodeDAO;
import org.alfresco.repo.model.Repository;
import org.alfresco.repo.node.archive.NodeArchiveService;
import org.alfresco.repo.security.authentication.AuthenticationContext;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.tenant.TenantAdminService;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.repo.tenant.TenantUtil;
import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.repo.version.VersionableAspectTest;
import org.alfresco.repo.workflow.WorkflowDeployer;
import org.alfresco.service.cmr.action.ActionCondition;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.dictionary.AspectDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.lock.LockService;
import org.alfresco.service.cmr.lock.LockType;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.rule.Rule;
import org.alfresco.service.cmr.rule.RuleService;
import org.alfresco.service.cmr.rule.RuleType;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.AccessPermission;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.tagging.TaggingService;
import org.alfresco.service.cmr.version.Version;
import org.alfresco.service.cmr.version.VersionService;
import org.alfresco.service.cmr.version.VersionType;
import org.alfresco.service.cmr.workflow.WorkflowAdminService;
import org.alfresco.service.cmr.workflow.WorkflowService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.Pair;
import org.apache.chemistry.opencmis.commons.PropertyIds;
import org.apache.chemistry.opencmis.commons.data.Ace;
import org.apache.chemistry.opencmis.commons.data.AllowableActions;
import org.apache.chemistry.opencmis.commons.data.CmisExtensionElement;
import org.apache.chemistry.opencmis.commons.data.FailedToDeleteData;
import org.apache.chemistry.opencmis.commons.data.ObjectData;
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.PropertyData;
import org.apache.chemistry.opencmis.commons.data.RepositoryInfo;
import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionContainer;
import org.apache.chemistry.opencmis.commons.enums.AclPropagation;
import org.apache.chemistry.opencmis.commons.enums.Action;
import org.apache.chemistry.opencmis.commons.enums.ChangeType;
import org.apache.chemistry.opencmis.commons.enums.CmisVersion;
import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships;
import org.apache.chemistry.opencmis.commons.enums.UnfileObject;
import org.apache.chemistry.opencmis.commons.enums.VersioningState;
import org.apache.chemistry.opencmis.commons.exceptions.CmisConstraintException;
import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
import org.apache.chemistry.opencmis.commons.exceptions.CmisUpdateConflictException;
import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlListImpl;
import org.apache.chemistry.opencmis.commons.impl.dataobjects.CmisExtensionElementImpl;
import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl;
import org.apache.chemistry.opencmis.commons.impl.dataobjects.ExtensionDataImpl;
import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertiesImpl;
import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyDecimalDefinitionImpl;
import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIdImpl;
import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIntegerDefinitionImpl;
import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIntegerImpl;
import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyStringImpl;
import org.apache.chemistry.opencmis.commons.impl.server.AbstractServiceFactory;
import org.apache.chemistry.opencmis.commons.server.CallContext;
import org.apache.chemistry.opencmis.commons.server.CmisService;
import org.apache.chemistry.opencmis.commons.spi.Holder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.extensions.webscripts.GUID;

/**
 * OpenCMIS tests.
 * 
 * @author steveglover
 *
 */
public class CMISTest {
    private static Log logger = LogFactory.getLog(CMISTest.class);

    private static final QName TEST_START_TASK = QName
            .createQName("http://www.alfresco.org/model/workflow/test/1.0", "startTaskVarScriptAssign");
    private static final QName TEST_WORKFLOW_TASK = QName
            .createQName("http://www.alfresco.org/model/workflow/test/1.0", "assignVarTask");

    private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(new String[] {
            ApplicationContextHelper.CONFIG_LOCATIONS[0], "classpath:test-cmisinteger_modell-context.xml" });

    private FileFolderService fileFolderService;
    private TransactionService transactionService;
    private NodeService nodeService;
    private ContentService contentService;
    private Repository repositoryHelper;
    private VersionService versionService;
    private LockService lockService;
    private TaggingService taggingService;
    private NamespaceService namespaceService;
    private AuthorityService authorityService;
    private AuditModelRegistryImpl auditSubsystem;
    private PermissionService permissionService;
    private DictionaryDAO dictionaryDAO;
    private CMISDictionaryService cmisDictionaryService;
    private AuditDAO auditDAO;
    private ActionService actionService;
    private RuleService ruleService;
    private NodeArchiveService nodeArchiveService;
    private DictionaryService dictionaryService;
    private WorkflowService workflowService;
    private WorkflowAdminService workflowAdminService;
    private AuthenticationContext authenticationContext;
    private TenantAdminService tenantAdminService;
    private TenantService tenantService;
    private SearchService searchService;
    private java.util.Properties globalProperties;
    private AuditComponentImpl auditComponent;

    private AlfrescoCmisServiceFactory factory;

    private CMISConnector cmisConnector;

    private NodeDAO nodeDAO;

    /**
     * Test class to provide the service factory
     * 
     * @author Derek Hulley
     * @since 4.0
     */
    public static class TestCmisServiceFactory extends AbstractServiceFactory {
        private static AlfrescoCmisServiceFactory serviceFactory = (AlfrescoCmisServiceFactory) ctx
                .getBean("CMISServiceFactory");

        @Override
        public void init(Map<String, String> parameters) {
            serviceFactory.init(parameters);
        }

        @Override
        public void destroy() {
        }

        @Override
        public CmisService getService(CallContext context) {
            return serviceFactory.getService(context);
        }
    }

    /**
     * Test class to provide the service factory
     * 
     * @author Derek Hulley
     * @since 4.0
     */
    public static class TestCmisServiceFactory11 extends AbstractServiceFactory {
        private static AlfrescoCmisServiceFactory serviceFactory = (AlfrescoCmisServiceFactory) ctx
                .getBean("CMISServiceFactory1.1");

        @Override
        public void init(Map<String, String> parameters) {
            serviceFactory.init(parameters);
        }

        @Override
        public void destroy() {
        }

        @Override
        public CmisService getService(CallContext context) {
            return serviceFactory.getService(context);
        }
    }

    public static class SimpleCallContext implements CallContext {
        private final Map<String, Object> contextMap = new HashMap<String, Object>();
        private CmisVersion cmisVersion;

        public SimpleCallContext(String user, String password, CmisVersion cmisVersion) {
            contextMap.put(USERNAME, user);
            contextMap.put(PASSWORD, password);
            this.cmisVersion = cmisVersion;
        }

        public String getBinding() {
            return BINDING_LOCAL;
        }

        public Object get(String key) {
            return contextMap.get(key);
        }

        public String getRepositoryId() {
            return (String) get(REPOSITORY_ID);
        }

        public String getUsername() {
            return (String) get(USERNAME);
        }

        public String getPassword() {
            return (String) get(PASSWORD);
        }

        public String getLocale() {
            return null;
        }

        public BigInteger getOffset() {
            return (BigInteger) get(OFFSET);
        }

        public BigInteger getLength() {
            return (BigInteger) get(LENGTH);
        }

        public boolean isObjectInfoRequired() {
            return false;
        }

        public File getTempDirectory() {
            return null;
        }

        public int getMemoryThreshold() {
            return 0;
        }

        public long getMaxContentSize() {
            return Long.MAX_VALUE;
        }

        @Override
        public boolean encryptTempFiles() {
            return false;
        }

        @Override
        public CmisVersion getCmisVersion() {
            return cmisVersion;
        }
    }

    @Before
    public void before() {
        this.actionService = (ActionService) ctx.getBean("actionService");
        this.ruleService = (RuleService) ctx.getBean("ruleService");
        this.fileFolderService = (FileFolderService) ctx.getBean("FileFolderService");
        this.transactionService = (TransactionService) ctx.getBean("transactionService");
        this.nodeService = (NodeService) ctx.getBean("NodeService");
        this.contentService = (ContentService) ctx.getBean("ContentService");
        this.versionService = (VersionService) ctx.getBean("versionService");
        this.lockService = (LockService) ctx.getBean("lockService");
        this.taggingService = (TaggingService) ctx.getBean("TaggingService");
        this.namespaceService = (NamespaceService) ctx.getBean("namespaceService");
        this.repositoryHelper = (Repository) ctx.getBean("repositoryHelper");
        this.factory = (AlfrescoCmisServiceFactory) ctx.getBean("CMISServiceFactory");
        this.versionService = (VersionService) ctx.getBean("versionService");
        this.cmisConnector = (CMISConnector) ctx.getBean("CMISConnector");
        this.nodeDAO = (NodeDAO) ctx.getBean("nodeDAO");
        this.authorityService = (AuthorityService) ctx.getBean("AuthorityService");
        this.auditSubsystem = (AuditModelRegistryImpl) ctx.getBean("Audit");
        this.permissionService = (PermissionService) ctx.getBean("permissionService");
        this.dictionaryDAO = (DictionaryDAO) ctx.getBean("dictionaryDAO");
        this.cmisDictionaryService = (CMISDictionaryService) ctx.getBean("OpenCMISDictionaryService1.1");
        this.auditDAO = (AuditDAO) ctx.getBean("auditDAO");
        this.nodeArchiveService = (NodeArchiveService) ctx.getBean("nodeArchiveService");
        this.dictionaryService = (DictionaryService) ctx.getBean("dictionaryService");
        this.workflowService = (WorkflowService) ctx.getBean("WorkflowService");
        this.workflowAdminService = (WorkflowAdminService) ctx.getBean("workflowAdminService");
        this.authenticationContext = (AuthenticationContext) ctx.getBean("authenticationContext");
        this.tenantAdminService = (TenantAdminService) ctx.getBean("tenantAdminService");
        this.tenantService = (TenantService) ctx.getBean("tenantService");
        this.searchService = (SearchService) ctx.getBean("SearchService");
        this.auditComponent = (AuditComponentImpl) ctx.getBean("auditComponent");

        this.globalProperties = (java.util.Properties) ctx.getBean("global-properties");
        this.globalProperties.setProperty(VersionableAspectTest.AUTO_VERSION_PROPS_KEY, "true");
    }

    @After
    public void after() {
        this.globalProperties.setProperty(VersionableAspectTest.AUTO_VERSION_PROPS_KEY, "false");
    }

    /**
     * MNT-10868 CMIS: Incorrect value of Latest Major version on Versions and Properties tabs.
     */
    @Test
    public void testIsLatestMajorVersionMNT10868() {
        CallContext context = new SimpleCallContext("admin", "admin", CmisVersion.CMIS_1_0);

        String repositoryId = null;

        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());

        CmisService cmisService = factory.getService(context);
        try {
            // get repository id
            List<RepositoryInfo> repositories = cmisService.getRepositoryInfos(null);
            assertTrue(repositories.size() > 0);
            RepositoryInfo repo = repositories.get(0);
            repositoryId = repo.getId();
            final String folderName = "testfolder" + GUID.generate();
            final String docName = "testdoc.txt" + GUID.generate();
            final FileInfo fileInfo = transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<FileInfo>() {
                        @Override
                        public FileInfo execute() throws Throwable {
                            NodeRef companyHomeNodeRef = repositoryHelper.getCompanyHome();

                            FileInfo folderInfo = fileFolderService.create(companyHomeNodeRef, folderName,
                                    ContentModel.TYPE_FOLDER);
                            nodeService.setProperty(folderInfo.getNodeRef(), ContentModel.PROP_NAME, folderName);

                            FileInfo fileInfo = fileFolderService.create(folderInfo.getNodeRef(), docName,
                                    ContentModel.TYPE_CONTENT);
                            nodeService.setProperty(fileInfo.getNodeRef(), ContentModel.PROP_NAME, docName);
                            nodeService.addAspect(fileInfo.getNodeRef(), ContentModel.ASPECT_VERSIONABLE, null);

                            return fileInfo;
                        }
                    });

            ObjectData objectData = cmisService.getObjectByPath(repositoryId, "/" + folderName + "/" + docName,
                    null, true, IncludeRelationships.NONE, null, false, true, null);

            PropertyData<?> pd = getPropIsLatestMajorVersion(objectData);

            if (pd != null) {
                assertTrue(
                        "The CMISDictionaryModel.PROP_IS_LATEST_MAJOR_VERSION should be true as major version was created",
                        (Boolean) pd.getValues().get(0));
            }

            nodeService.setProperty(fileInfo.getNodeRef(), ContentModel.PROP_TITLE, docName);

            // Create minor version   
            transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<Object>() {
                        public Object execute() throws Throwable {
                            // get an updating writer
                            ContentWriter writer = contentService.getWriter(fileInfo.getNodeRef(),
                                    ContentModel.PROP_CONTENT, true);

                            writer.setMimetype("text/plain");

                            writer.putContent("New Version");
                            return null;
                        }
                    });

            objectData = cmisService.getObjectByPath(repositoryId, "/" + folderName + "/" + docName, null, true,
                    IncludeRelationships.NONE, null, false, true, null);

            pd = getPropIsLatestMajorVersion(objectData);

            if (pd != null) {
                assertFalse(
                        "The CMISDictionaryModel.PROP_IS_LATEST_MAJOR_VERSION should be false as minor version was created",
                        (Boolean) pd.getValues().get(0));
            }
        } finally {
            cmisService.close();
        }
    }

    private PropertyData<?> getPropIsLatestMajorVersion(ObjectData objectData) {
        List<PropertyData<?>> properties = objectData.getProperties().getPropertyList();
        boolean found = false;
        PropertyData<?> propIsLatestMajorVersion = null;
        for (PropertyData<?> property : properties) {
            if (property.getId().equals(PropertyIds.IS_LATEST_MAJOR_VERSION)) {
                found = true;
                propIsLatestMajorVersion = property;
                break;
            }
        }
        //properties..contains(PropertyIds.IS_LATEST_MAJOR_VERSION);
        assertTrue("The PropertyIds.IS_LATEST_MAJOR_VERSION property was not found", found);
        if (found) {
            return propIsLatestMajorVersion;
        }

        return null;
    }

    /**
     * Test for MNT-9203.
     */
    @Test
    public void testCheckIn() {
        String repositoryId = null;
        ObjectData objectData = null;
        Holder<String> objectId = null;
        CallContext context = new SimpleCallContext("admin", "admin", CmisVersion.CMIS_1_0);

        final String folderName = "testfolder." + GUID.generate();
        final String docName = "testdoc.txt." + GUID.generate();
        final String customModel = "cmistest.model";

        final QName testCustomTypeQName = QName.createQName(customModel, "sop");
        final QName authorisedByQname = QName.createQName(customModel, "authorisedBy");
        AuthenticationUtil.pushAuthentication();
        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
        try {
            final FileInfo fileInfo = transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<FileInfo>() {
                        @Override
                        public FileInfo execute() throws Throwable {
                            NodeRef companyHomeNodeRef = repositoryHelper.getCompanyHome();

                            FileInfo folderInfo = fileFolderService.create(companyHomeNodeRef, folderName,
                                    ContentModel.TYPE_FOLDER);
                            nodeService.setProperty(folderInfo.getNodeRef(), ContentModel.PROP_NAME, folderName);

                            FileInfo fileInfo = fileFolderService.create(folderInfo.getNodeRef(), docName,
                                    testCustomTypeQName);
                            Map<QName, Serializable> customProperties = new HashMap<QName, Serializable>();

                            customProperties.put(authorisedByQname, "customPropertyString");
                            customProperties.put(ContentModel.PROP_NAME, docName);
                            nodeService.setProperties(fileInfo.getNodeRef(), customProperties);

                            return fileInfo;
                        }
                    });

            CmisService service = factory.getService(context);
            try {
                List<RepositoryInfo> repositories = service.getRepositoryInfos(null);
                assertTrue(repositories.size() > 0);
                RepositoryInfo repo = repositories.get(0);
                repositoryId = repo.getId();
                objectData = service.getObjectByPath(repositoryId, "/" + folderName + "/" + docName, null, true,
                        IncludeRelationships.NONE, null, false, true, null);

                // checkout
                objectId = new Holder<String>(objectData.getId());
                service.checkOut(repositoryId, objectId, null, new Holder<Boolean>(true));
            } finally {
                service.close();
            }

            try {
                service = factory.getService(context);

                PropertyStringImpl prop = new PropertyStringImpl();
                prop.setId("abc:" + authorisedByQname.toPrefixString());
                prop.setValue(null);

                Collection<PropertyData<?>> propsList = new ArrayList<PropertyData<?>>();
                propsList.add(prop);

                Properties properties = new PropertiesImpl(propsList);

                // checkIn on pwc
                service.checkIn(repositoryId, objectId, false, properties, null, null, null, null, null, null);
            } finally {
                service.close();
            }
            // check that value is null
            assertTrue(nodeService.getProperty(fileInfo.getNodeRef(), authorisedByQname) == null);
        } finally {
            AuthenticationUtil.popAuthentication();
        }
    }

    /**
     * Test for MNT-10537.
     */
    @Test
    public void testModelAvailability() throws Exception {
        final WorkflowDeployer testWorkflowDeployer = new WorkflowDeployer();

        // setup dependencies
        testWorkflowDeployer.setTransactionService(transactionService);
        testWorkflowDeployer.setWorkflowService(workflowService);
        testWorkflowDeployer.setWorkflowAdminService(workflowAdminService);
        testWorkflowDeployer.setAuthenticationContext(authenticationContext);
        testWorkflowDeployer.setDictionaryDAO(dictionaryDAO);
        testWorkflowDeployer.setTenantAdminService(tenantAdminService);
        testWorkflowDeployer.setTenantService(tenantService);
        testWorkflowDeployer.setNodeService(nodeService);
        testWorkflowDeployer.setNamespaceService(namespaceService);
        testWorkflowDeployer.setSearchService(searchService);

        // populate workflow parameters
        java.util.Properties props = new java.util.Properties();
        props.setProperty(WorkflowDeployer.ENGINE_ID, "jbpm");
        props.setProperty(WorkflowDeployer.LOCATION, "jbpmresources/test_taskVarScriptAssign.xml");
        props.setProperty(WorkflowDeployer.MIMETYPE, "text/xml");
        props.setProperty(WorkflowDeployer.REDEPLOY, Boolean.FALSE.toString());

        List<java.util.Properties> definitions = new ArrayList<java.util.Properties>(1);
        definitions.add(props);

        testWorkflowDeployer.setWorkflowDefinitions(definitions);

        List<String> models = new ArrayList<String>(1);
        models.add("jbpmresources/testWorkflowModel.xml");

        testWorkflowDeployer.setModels(models);

        // deploy test workflow
        RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper();
        txnHelper.setForceWritable(true);
        txnHelper.doInTransaction(new RetryingTransactionCallback<Object>() {
            @Override
            public Object execute() throws Throwable {
                return AuthenticationUtil.runAs(new RunAsWork<Object>() {
                    public Object doWork() {
                        testWorkflowDeployer.init();
                        return null;
                    }
                }, AuthenticationUtil.getSystemUserName());
            }

        }, false, true);

        org.alfresco.service.cmr.dictionary.TypeDefinition startTaskTypeDefinition = this.dictionaryService
                .getType(TEST_START_TASK);
        org.alfresco.service.cmr.dictionary.TypeDefinition workflowTaskTypeDefinition = this.dictionaryService
                .getType(TEST_WORKFLOW_TASK);

        // check that workflow types were correctly bootstrapped
        assertNotNull(startTaskTypeDefinition);
        assertNotNull(workflowTaskTypeDefinition);

        // check that loaded model is available via CMIS API
        CallContext context = new SimpleCallContext("admin", "admin", CmisVersion.CMIS_1_1);
        CmisService service = factory.getService(context);
        try {
            List<RepositoryInfo> repositories = service.getRepositoryInfos(null);
            assertTrue(repositories.size() > 0);
            List<TypeDefinitionContainer> container = service.getTypeDescendants(repositories.get(0).getId(), null,
                    new BigInteger("-1"), true, null);
            assertTrue("Workflow model haven't been loaded",
                    container.toString().contains("testwf:startTaskVarScriptAssign"));
        } finally {
            service.close();
        }
    }

    private FileInfo createContent(final String folderName, final String docName, final boolean isRule) {
        return createContent(null, folderName, docName, isRule);
    }

    private FileInfo createContent(final FileInfo parentFolder, final String folderName, final String docName,
            final boolean isRule) {
        final FileInfo folderInfo = transactionService.getRetryingTransactionHelper()
                .doInTransaction(new RetryingTransactionCallback<FileInfo>() {
                    @Override
                    public FileInfo execute() throws Throwable {
                        NodeRef nodeRef;

                        if (parentFolder != null) {
                            nodeRef = parentFolder.getNodeRef();
                        } else {
                            nodeRef = repositoryHelper.getCompanyHome();
                        }

                        FileInfo folderInfo = fileFolderService.create(nodeRef, folderName,
                                ContentModel.TYPE_FOLDER);
                        nodeService.setProperty(folderInfo.getNodeRef(), ContentModel.PROP_NAME, folderName);
                        assertNotNull(folderInfo);

                        FileInfo fileInfo;
                        if (docName != null) {
                            fileInfo = fileFolderService.create(folderInfo.getNodeRef(), docName,
                                    ContentModel.TYPE_CONTENT);
                            nodeService.setProperty(fileInfo.getNodeRef(), ContentModel.PROP_NAME, docName);
                            assertNotNull(fileInfo);
                        }

                        if (isRule) {
                            Rule rule = addRule(true, folderName);

                            assertNotNull(rule);

                            // Attach the rule to the node
                            ruleService.saveRule(folderInfo.getNodeRef(), rule);

                            assertTrue(ruleService.getRules(folderInfo.getNodeRef()).size() > 0);
                        }

                        return folderInfo;
                    }
                });

        return folderInfo;
    }

    private Rule addRule(boolean isAppliedToChildren, String title) {

        // Rule properties
        Map<String, Serializable> conditionProps = new HashMap<String, Serializable>();
        conditionProps.put(ComparePropertyValueEvaluator.PARAM_VALUE, ".txt");

        Map<String, Serializable> actionProps = new HashMap<String, Serializable>();
        actionProps.put(AddFeaturesActionExecuter.PARAM_ASPECT_NAME, ContentModel.ASPECT_VERSIONABLE);

        List<String> ruleTypes = new ArrayList<String>(1);
        ruleTypes.add(RuleType.INBOUND);

        // Create the action
        org.alfresco.service.cmr.action.Action action = actionService.createAction(title);
        action.setParameterValues(conditionProps);

        ActionCondition actionCondition = actionService.createActionCondition(ComparePropertyValueEvaluator.NAME);
        actionCondition.setParameterValues(conditionProps);
        action.addActionCondition(actionCondition);

        // Create the rule
        Rule rule = new Rule();
        rule.setRuleTypes(ruleTypes);
        rule.setTitle(title);
        rule.setDescription("description");
        rule.applyToChildren(isAppliedToChildren);
        rule.setAction(action);

        return rule;
    }

    private <T extends Object> T withCmisService(CmisServiceCallback<T> callback) {
        return withCmisService(callback, CmisVersion.CMIS_1_0);
    }

    private <T extends Object> T withCmisService(CmisServiceCallback<T> callback, CmisVersion cmisVersion) {
        CmisService cmisService = null;

        try {
            CallContext context = new SimpleCallContext("admin", "admin", cmisVersion);
            cmisService = factory.getService(context);
            T ret = callback.execute(cmisService);
            return ret;
        } finally {
            if (cmisService != null) {
                cmisService.close();
            }
        }
    }

    private static interface CmisServiceCallback<T> {
        T execute(CmisService cmisService);
    }

    /**
     * ALF-18006 Test content mimetype auto-detection into CmisStreamInterceptor when "Content-Type" is not defined.
     */
    @Test
    public void testContentMimeTypeDetection() {
        // get repository id
        List<RepositoryInfo> repositories = withCmisService(new CmisServiceCallback<List<RepositoryInfo>>() {
            @Override
            public List<RepositoryInfo> execute(CmisService cmisService) {
                List<RepositoryInfo> repositories = cmisService.getRepositoryInfos(null);
                return repositories;
            }
        });

        assertTrue(repositories.size() > 0);
        RepositoryInfo repo = repositories.get(0);
        final String repositoryId = repo.getId();

        // create simple text plain content
        final PropertiesImpl properties = new PropertiesImpl();
        String objectTypeId = "cmis:document";
        properties.addProperty(new PropertyIdImpl(PropertyIds.OBJECT_TYPE_ID, objectTypeId));
        String fileName = "textFile" + GUID.generate();
        properties.addProperty(new PropertyStringImpl(PropertyIds.NAME, fileName));
        final ContentStreamImpl contentStream = new ContentStreamImpl(fileName, MimetypeMap.MIMETYPE_TEXT_PLAIN,
                "Simple text plain document");

        String objectId = withCmisService(new CmisServiceCallback<String>() {
            @Override
            public String execute(CmisService cmisService) {
                String objectId = cmisService.create(repositoryId, properties,
                        repositoryHelper.getCompanyHome().getId(), contentStream, VersioningState.MAJOR, null,
                        null);
                return objectId;
            }
        });

        final Holder<String> objectIdHolder = new Holder<String>(objectId);
        final String path = "/" + fileName;

        // create content stream with undefined mimetype and file name
        {
            final ContentStreamImpl contentStreamHTML = new ContentStreamImpl(null, null,
                    "<html><head><title> Hello </title></head><body><p> Test html</p></body></html></body></html>");
            withCmisService(new CmisServiceCallback<Void>() {
                @Override
                public Void execute(CmisService cmisService) {
                    cmisService.setContentStream(repositoryId, objectIdHolder, true, null, contentStreamHTML, null);
                    return null;
                }
            });

            // check mimetype
            ObjectData objectData = withCmisService(new CmisServiceCallback<ObjectData>() {
                @Override
                public ObjectData execute(CmisService cmisService) {
                    return cmisService.getObjectByPath(repositoryId, path, null, false, IncludeRelationships.NONE,
                            null, false, false, null);
                }
            });

            final String objectId1 = objectData.getId();
            String contentType = withCmisService(new CmisServiceCallback<String>() {
                @Override
                public String execute(CmisService cmisService) {
                    String contentType = cmisService.getObjectInfo(repositoryId, objectId1).getContentType();
                    return contentType;
                }
            });
            assertEquals("Mimetype is not defined correctly.", MimetypeMap.MIMETYPE_HTML, contentType);
        }

        // create content stream with mimetype and encoding
        {
            String mimeType = MimetypeMap.MIMETYPE_TEXT_PLAIN + "; charset=UTF-8";
            final ContentStreamImpl contentStreamHTML = new ContentStreamImpl(null, mimeType,
                    "<html><head><title> Hello </title></head><body><p> Test html</p></body></html></body></html>");
            withCmisService(new CmisServiceCallback<Void>() {
                @Override
                public Void execute(CmisService cmisService) {
                    Holder<String> latestObjectIdHolder = getHolderOfObjectOfLatestVersion(cmisService,
                            repositoryId, objectIdHolder);
                    cmisService.setContentStream(repositoryId, latestObjectIdHolder, true, null, contentStreamHTML,
                            null);
                    return null;
                }
            });

            // check mimetype
            final ObjectData objectData = withCmisService(new CmisServiceCallback<ObjectData>() {
                @Override
                public ObjectData execute(CmisService cmisService) {
                    ObjectData objectData = cmisService.getObjectByPath(repositoryId, path, null, false,
                            IncludeRelationships.NONE, null, false, false, null);
                    return objectData;
                }
            });
            String contentType = withCmisService(new CmisServiceCallback<String>() {
                @Override
                public String execute(CmisService cmisService) {
                    String contentType = cmisService.getObjectInfo(repositoryId, objectData.getId())
                            .getContentType();
                    return contentType;
                }
            });
            assertEquals("Mimetype is not defined correctly.", MimetypeMap.MIMETYPE_TEXT_PLAIN, contentType);
        }

        // checkout/checkin object with mimetype and encoding
        {
            ObjectData objectDa = withCmisService(new CmisServiceCallback<ObjectData>() {
                @Override
                public ObjectData execute(CmisService cmisService) {
                    return cmisService.getObjectByPath(repositoryId, path, null, false, IncludeRelationships.NONE,
                            null, false, false, null);
                }
            });

            objectIdHolder.setValue(objectDa.getId());
            withCmisService(new CmisServiceCallback<Void>() {
                @Override
                public Void execute(CmisService cmisService) {
                    cmisService.checkOut(repositoryId, objectIdHolder, null, new Holder<Boolean>());
                    return null;
                }
            });
            String mimeType = MimetypeMap.MIMETYPE_HTML + "; charset=UTF-8";
            final ContentStreamImpl contentStreamHTML = new ContentStreamImpl(null, mimeType,
                    "<html><head><title> Hello </title></head><body><p> Test html</p></body></html></body></html>");
            withCmisService(new CmisServiceCallback<Void>() {
                @Override
                public Void execute(CmisService cmisService) {
                    cmisService.checkIn(repositoryId, objectIdHolder, false, null, contentStreamHTML, "checkin",
                            null, null, null, null);
                    return null;
                }
            });

            // check mimetype
            final ObjectData objectData = withCmisService(new CmisServiceCallback<ObjectData>() {
                @Override
                public ObjectData execute(CmisService cmisService) {
                    ObjectData objectData = cmisService.getObjectByPath(repositoryId, path, null, false,
                            IncludeRelationships.NONE, null, false, false, null);
                    return objectData;
                }
            });
            String contentType = withCmisService(new CmisServiceCallback<String>() {
                @Override
                public String execute(CmisService cmisService) {
                    String contentType = cmisService.getObjectInfo(repositoryId, objectData.getId())
                            .getContentType();
                    return contentType;
                }
            });
            assertEquals("Mimetype is not defined correctly.", MimetypeMap.MIMETYPE_HTML, contentType);
        }
    }

    private Holder<String> getHolderOfObjectOfLatestVersion(CmisService cmisService, String repositoryId,
            Holder<String> currentHolder) {
        ObjectData oData = cmisService.getObjectOfLatestVersion(repositoryId, currentHolder.getValue(), null,
                Boolean.FALSE, null, null, null, null, null, null, null);
        return new Holder<String>(oData.getId());
    }

    /**
     * ALF-20389 Test Alfresco cmis stream interceptor that checks content stream for mimetype. Only ContentStreamImpl extensions should take palace.
     */
    @Test
    public void testGetRepositoryInfos() {
        boolean cmisEx = false;
        List<RepositoryInfo> infoDataList = null;
        try {
            infoDataList = withCmisService(new CmisServiceCallback<List<RepositoryInfo>>() {
                @Override
                public List<RepositoryInfo> execute(CmisService cmisService) {
                    ExtensionDataImpl result = new ExtensionDataImpl();
                    List<CmisExtensionElement> extensions = new ArrayList<CmisExtensionElement>();
                    result.setExtensions(extensions);

                    return cmisService.getRepositoryInfos(result);
                }
            });
        } catch (CmisRuntimeException e) {
            cmisEx = true;
        }

        assertNotNull(cmisEx ? "CmisRuntimeException was thrown. Please, take a look on ALF-20389"
                : "No CMIS repository information was retrieved", infoDataList);
    }

    private static class TestContext {
        private String repositoryId = null;
        @SuppressWarnings("unused")
        private ObjectData objectData = null;
        private Holder<String> objectId = null;

        public TestContext() {
            super();
        }

        public String getRepositoryId() {
            return repositoryId;
        }

        public void setRepositoryId(String repositoryId) {
            this.repositoryId = repositoryId;
        }

        public void setObjectData(ObjectData objectData) {
            this.objectData = objectData;
        }

        public Holder<String> getObjectId() {
            return objectId;
        }

        public void setObjectId(Holder<String> objectId) {
            this.objectId = objectId;
        }
    }

    /**
     * Test for ALF-16310.
     * 
     * Check that, for AtomPub binding, cancel checkout on the originating checked out document i.e. not the working
     * copy throws an exception and does not delete the document.
     */
    @SuppressWarnings("unchecked")
    @Test
    public void testCancelCheckout() {
        final TestContext testContext = new TestContext();

        final String folderName = "testfolder." + GUID.generate();
        final String docName = "testdoc.txt." + GUID.generate();

        AuthenticationUtil.pushAuthentication();
        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
        try {
            final FileInfo folderInfo = transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<FileInfo>() {
                        @Override
                        public FileInfo execute() throws Throwable {
                            NodeRef companyHomeNodeRef = repositoryHelper.getCompanyHome();

                            FileInfo folderInfo = fileFolderService.create(companyHomeNodeRef, folderName,
                                    ContentModel.TYPE_FOLDER);
                            nodeService.setProperty(folderInfo.getNodeRef(), ContentModel.PROP_NAME, folderName);

                            FileInfo fileInfo = fileFolderService.create(folderInfo.getNodeRef(), docName,
                                    ContentModel.TYPE_CONTENT);
                            nodeService.setProperty(fileInfo.getNodeRef(), ContentModel.PROP_NAME, docName);

                            return folderInfo;
                        }
                    });

            final ObjectData objectData = withCmisService(new CmisServiceCallback<ObjectData>() {
                @Override
                public ObjectData execute(CmisService cmisService) {
                    List<RepositoryInfo> repositories = cmisService.getRepositoryInfos(null);
                    assertTrue(repositories.size() > 0);
                    RepositoryInfo repo = repositories.get(0);
                    String repositoryId = repo.getId();
                    testContext.setRepositoryId(repositoryId);
                    ObjectData objectData = cmisService.getObjectByPath(repositoryId,
                            "/" + folderName + "/" + docName, null, true, IncludeRelationships.NONE, null, false,
                            true, null);
                    testContext.setObjectData(objectData);

                    // checkout
                    Holder<String> objectId = new Holder<String>(objectData.getId());
                    testContext.setObjectId(objectId);
                    cmisService.checkOut(repositoryId, objectId, null, new Holder<Boolean>(true));

                    return objectData;
                }
            });

            // AtomPub cancel checkout
            withCmisService(new CmisServiceCallback<Void>() {
                @Override
                public Void execute(CmisService cmisService) {
                    try {
                        // check allowable actions
                        ObjectData originalDoc = cmisService.getObject(testContext.getRepositoryId(),
                                objectData.getId(), null, true, IncludeRelationships.NONE, null, false, true, null);
                        AllowableActions allowableActions = originalDoc.getAllowableActions();
                        assertNotNull(allowableActions);
                        assertFalse(allowableActions.getAllowableActions().contains(Action.CAN_DELETE_OBJECT));

                        // try to cancel the checkout
                        cmisService.deleteObjectOrCancelCheckOut(testContext.getRepositoryId(), objectData.getId(),
                                Boolean.TRUE, null);
                        fail();
                    } catch (CmisConstraintException e) {
                        // expected
                    }

                    return null;
                }
            });

            withCmisService(new CmisServiceCallback<Void>() {
                @Override
                public Void execute(CmisService cmisService) {
                    // cancel checkout on pwc
                    cmisService.deleteObjectOrCancelCheckOut(testContext.getRepositoryId(),
                            testContext.getObjectId().getValue(), Boolean.TRUE, null);
                    return null;
                }
            });

            withCmisService(new CmisServiceCallback<Void>() {
                @Override
                public Void execute(CmisService cmisService) {
                    // get original document
                    ObjectData originalDoc = cmisService.getObject(testContext.getRepositoryId(),
                            objectData.getId(), null, true, IncludeRelationships.NONE, null, false, true, null);
                    Map<String, PropertyData<?>> properties = originalDoc.getProperties().getProperties();
                    PropertyData<Boolean> isVersionSeriesCheckedOutProp = (PropertyData<Boolean>) properties
                            .get(PropertyIds.IS_VERSION_SERIES_CHECKED_OUT);
                    assertNotNull(isVersionSeriesCheckedOutProp);
                    Boolean isVersionSeriesCheckedOut = isVersionSeriesCheckedOutProp.getFirstValue();
                    assertNotNull(isVersionSeriesCheckedOut);
                    assertEquals(Boolean.FALSE, isVersionSeriesCheckedOut);

                    return null;
                }
            });

            withCmisService(new CmisServiceCallback<Void>() {
                @Override
                public Void execute(CmisService cmisService) {
                    // delete original document
                    cmisService.deleteObject(testContext.getRepositoryId(), objectData.getId(), true, null);
                    return null;
                }
            });

            List<FileInfo> children = transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<List<FileInfo>>() {
                        @Override
                        public List<FileInfo> execute() throws Throwable {
                            List<FileInfo> children = fileFolderService.list(folderInfo.getNodeRef());
                            return children;
                        }
                    });
            assertEquals(0, children.size());
        } finally {
            AuthenticationUtil.popAuthentication();
        }
    }

    /**
     * Test for MNT-13366.
     */
    @Test
    public void testDeleteTree() {
        AuthenticationUtil.pushAuthentication();
        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());

        FileInfo parentFolder = null;
        FileInfo childFolder1 = null;

        try {
            // Create a parent folder: parentFolder:
            String parentFolderName = "parentFolder" + GUID.generate();
            parentFolder = createContent(parentFolderName, null, false);
            final NodeRef parentFolderNodeRef = parentFolder.getNodeRef();
            final String parentFolderID = parentFolderNodeRef.getId();

            // Create a child folder: parentFolder -> childFolder1:
            String childFolder1Name = "childFolder1" + GUID.generate();
            childFolder1 = createContent(parentFolder, childFolder1Name, null, false);
            final NodeRef childFolder1NodeRef = childFolder1.getNodeRef();

            // Create a child folder for previous child folder, which will contain a file:
            // parentFolder -> childFolder1 -> childFolder2 -> testdoc.txt
            String childFolder2Name = "childFolder2" + GUID.generate();
            String docName = "testdoc.txt" + GUID.generate();
            final NodeRef childFolder2NodeRef = createContent(childFolder1, childFolder2Name, docName, false)
                    .getNodeRef();

            // Store a reference to the file "testdoc.txt" contained by childFolder2:
            List<FileInfo> childFolder2FileList = fileFolderService.list(childFolder2NodeRef);
            final NodeRef childFolder2FileNodeRef = childFolder2FileList.get(0).getNodeRef();

            List<RepositoryInfo> repositories = withCmisService(new CmisServiceCallback<List<RepositoryInfo>>() {
                @Override
                public List<RepositoryInfo> execute(CmisService cmisService) {
                    List<RepositoryInfo> repositories = cmisService.getRepositoryInfos(null);
                    return repositories;
                }
            });

            assertTrue(repositories.size() > 0);
            RepositoryInfo repo = repositories.get(0);
            final String repositoryId = repo.getId();

            withCmisService(new CmisServiceCallback<Void>() {
                @Override
                public Void execute(CmisService cmisService) {

                    // CMIS delete tree:
                    FailedToDeleteData failedItems = cmisService.deleteTree(repositoryId, parentFolderID,
                            Boolean.TRUE, UnfileObject.DELETE, Boolean.TRUE, null);

                    assertEquals(failedItems.getIds().size(), 0);

                    // Reference to the archive root node (the trash-can):
                    NodeRef archiveRootNode = nodeArchiveService
                            .getStoreArchiveNode(repositoryHelper.getCompanyHome().getStoreRef());

                    // Get the archived ("canned") version of folders and file and check that hirarchy is correct:
                    // ArchiveRoot -> archivedParentFolder -> archivedChildFolder1 -> archivedChildFolder2 -> archivedChildFolder2File.

                    // Check parentFolder:
                    NodeRef archivedParentFolderNodeRef = nodeArchiveService.getArchivedNode(parentFolderNodeRef);
                    assertTrue(nodeService.getPrimaryParent(archivedParentFolderNodeRef).getParentRef()
                            .equals(archiveRootNode));

                    // Check childFolder1:               
                    NodeRef archivedChildFolder1NodeRef = nodeArchiveService.getArchivedNode(childFolder1NodeRef);
                    assertTrue(nodeService.getPrimaryParent(archivedChildFolder1NodeRef).getParentRef()
                            .equals(archivedParentFolderNodeRef));
                    assertFalse(nodeService.getPrimaryParent(archivedChildFolder1NodeRef).getParentRef()
                            .equals(archiveRootNode));

                    // Check childFolder2:                    
                    NodeRef archivedChildFolder2NodeRef = nodeArchiveService.getArchivedNode(childFolder2NodeRef);
                    assertTrue(nodeService.getPrimaryParent(archivedChildFolder2NodeRef).getParentRef()
                            .equals(archivedChildFolder1NodeRef));
                    assertFalse(nodeService.getPrimaryParent(archivedChildFolder2NodeRef).getParentRef()
                            .equals(archiveRootNode));

                    // Check childFolder2's file ("testdoc.txt"):                     
                    NodeRef archivedChildFolder2FileNodeRef = nodeArchiveService
                            .getArchivedNode(childFolder2FileNodeRef);
                    assertTrue(nodeService.getPrimaryParent(archivedChildFolder2FileNodeRef).getParentRef()
                            .equals(archivedChildFolder2NodeRef));
                    assertFalse(nodeService.getPrimaryParent(archivedChildFolder2FileNodeRef).getParentRef()
                            .equals(archiveRootNode));

                    return null;
                };
            });
        } finally {
            if (parentFolder != null && fileFolderService.exists(parentFolder.getNodeRef())) {
                fileFolderService.delete(parentFolder.getNodeRef());
            }

            AuthenticationUtil.popAuthentication();
        }
    }

    /**
     * Test for ALF-18151.
     */
    @Test
    public void testDeleteFolder() {
        AuthenticationUtil.pushAuthentication();
        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());

        final Map<FileInfo, Boolean> testFolderMap = new HashMap<FileInfo, Boolean>(4);

        try {
            // create folder with file
            String folderName = "testfolder" + GUID.generate();
            String docName = "testdoc.txt" + GUID.generate();
            FileInfo folder = createContent(folderName, docName, false);
            testFolderMap.put(folder, Boolean.FALSE);

            // create empty folder
            String folderNameEmpty = "testfolder_empty1" + GUID.generate();
            FileInfo folderEmpty = createContent(folderNameEmpty, null, false);
            testFolderMap.put(folderEmpty, Boolean.TRUE);

            // create folder with file
            String folderNameRule = "testfolde_rule" + GUID.generate();
            String docNameRule = "testdoc_rule.txt" + GUID.generate();
            FileInfo folderWithRule = createContent(folderNameRule, docNameRule, true);
            testFolderMap.put(folderWithRule, Boolean.FALSE);

            // create empty folder
            String folderNameEmptyRule = "testfolde_empty_rule1" + GUID.generate();
            FileInfo folderEmptyWithRule = createContent(folderNameEmptyRule, null, true);
            testFolderMap.put(folderEmptyWithRule, Boolean.TRUE);

            withCmisService(new CmisServiceCallback<Void>() {
                @Override
                public Void execute(CmisService cmisService) {
                    List<RepositoryInfo> repositories = cmisService.getRepositoryInfos(null);
                    RepositoryInfo repo = repositories.get(0);
                    String repositoryId = repo.getId();

                    for (Map.Entry<FileInfo, Boolean> entry : testFolderMap.entrySet()) {
                        ObjectData objectData = cmisService.getObjectByPath(repositoryId,
                                "/" + entry.getKey().getName(), null, true, IncludeRelationships.NONE, null, false,
                                true, null);

                        Holder<String> objectId = new Holder<String>(objectData.getId());

                        try {
                            // delete folder
                            cmisService.deleteObjectOrCancelCheckOut(repositoryId, objectId.getValue(),
                                    Boolean.TRUE, null);
                        } catch (CmisConstraintException ex) {
                            assertTrue(!entry.getValue());
                            continue;
                        }

                        assertTrue(entry.getValue());
                    }

                    return null;
                }
            });
        } finally {
            for (Map.Entry<FileInfo, Boolean> entry : testFolderMap.entrySet()) {
                if (fileFolderService.exists(entry.getKey().getNodeRef())) {
                    fileFolderService.delete(entry.getKey().getNodeRef());
                }
            }

            AuthenticationUtil.popAuthentication();
        }
    }

    /**
     * Test
     * <ul>
     *   <li>MNT-8825: READ_ONLYLOCK prevent getAllVersions via new CMIS enpoint.</li>
     *   <li>ACE-762: BM-0012: NodeLockedException not handled by CMIS</li>
     * </ul>
     */
    @Test
    public void testOperationsOnReadOnlyLockedNode() {
        final String folderName = "testfolder." + GUID.generate();
        final String docName = "testdoc.txt." + GUID.generate();

        AuthenticationUtil.pushAuthentication();
        AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser();

        try {
            final FileInfo fileInfo = transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<FileInfo>() {
                        @Override
                        public FileInfo execute() throws Throwable {
                            NodeRef companyHomeNodeRef = repositoryHelper.getCompanyHome();

                            FileInfo folderInfo = fileFolderService.create(companyHomeNodeRef, folderName,
                                    ContentModel.TYPE_FOLDER);
                            nodeService.setProperty(folderInfo.getNodeRef(), ContentModel.PROP_NAME, folderName);

                            FileInfo fileInfo = fileFolderService.create(folderInfo.getNodeRef(), docName,
                                    ContentModel.TYPE_CONTENT);
                            nodeService.setProperty(fileInfo.getNodeRef(), ContentModel.PROP_NAME, docName);

                            versionService.createVersion(fileInfo.getNodeRef(),
                                    new HashMap<String, Serializable>());
                            lockService.lock(fileInfo.getNodeRef(), LockType.READ_ONLY_LOCK, 0, true);

                            return fileInfo;
                        }
                    });

            withCmisService(new CmisServiceCallback<Void>() {
                @Override
                public Void execute(CmisService cmisService) {
                    List<RepositoryInfo> repositories = cmisService.getRepositoryInfos(null);
                    assertTrue(repositories.size() > 0);
                    RepositoryInfo repo = repositories.get(0);
                    String repositoryId = repo.getId();
                    ObjectData objectData = cmisService.getObjectByPath(repositoryId,
                            "/" + folderName + "/" + docName, null, true, IncludeRelationships.NONE, null, false,
                            true, null);

                    // Expect no failure
                    cmisService.getAllVersions(repositoryId, objectData.getId(), fileInfo.getNodeRef().getId(),
                            null, true, null);

                    return null;
                }
            });

            withCmisService(new CmisServiceCallback<Void>() {
                @Override
                public Void execute(CmisService cmisService) {
                    List<RepositoryInfo> repositories = cmisService.getRepositoryInfos(null);
                    assertTrue(repositories.size() > 0);
                    RepositoryInfo repo = repositories.get(0);
                    String repositoryId = repo.getId();
                    ObjectData objectData = cmisService.getObjectByPath(repositoryId,
                            "/" + folderName + "/" + docName, null, true, IncludeRelationships.NONE, null, false,
                            true, null);
                    String objectId = objectData.getId();

                    // Expect failure as the node is locked
                    try {
                        cmisService.deleteObject(repositoryId, objectId, true, null);
                        fail("Locked node should not be deletable.");
                    } catch (CmisUpdateConflictException e) {
                        // Expected
                    }

                    return null;
                }
            });

        } finally {
            AuthenticationUtil.popAuthentication();
        }
    }

    /**
     * ALF-18455
     */
    @Test
    public void testOrderByCreationAndModificationDate() {
        final List<FileInfo> nodes = new ArrayList<FileInfo>(10);
        final List<FileInfo> expectedChildrenByCreationDate = new ArrayList<FileInfo>(10);
        final List<FileInfo> expectedChildrenByModificationDate = new ArrayList<FileInfo>(10);

        AuthenticationUtil.pushAuthentication();
        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
        try {
            transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<Void>() {
                        @Override
                        public Void execute() throws Throwable {
                            NodeRef companyHomeNodeRef = repositoryHelper.getCompanyHome();

                            String folderName = GUID.generate();
                            FileInfo folderInfo = fileFolderService.create(companyHomeNodeRef, folderName,
                                    ContentModel.TYPE_FOLDER);
                            nodeService.setProperty(folderInfo.getNodeRef(), ContentModel.PROP_NAME, folderName);
                            assertNotNull(folderInfo);
                            nodes.add(folderInfo);

                            for (int i = 0; i < 5; i++) {
                                String docName = GUID.generate();
                                FileInfo fileInfo = fileFolderService.create(folderInfo.getNodeRef(), docName,
                                        ContentModel.TYPE_CONTENT);
                                assertNotNull(fileInfo);
                                nodeService.setProperty(fileInfo.getNodeRef(), ContentModel.PROP_NAME, docName);

                                expectedChildrenByCreationDate.add(0, fileInfo);
                                nodes.add(fileInfo);

                                // make sure there is some difference in creation times
                                Thread.sleep(400);
                            }

                            // make modifications
                            for (int i = 5; i > 0; i--) {
                                FileInfo fileInfo = nodes.get(i);
                                assertNotNull(fileInfo);
                                nodeService.setProperty(fileInfo.getNodeRef(), ContentModel.PROP_DESCRIPTION,
                                        GUID.generate());

                                // "refresh" fileInfo
                                fileInfo = fileFolderService.getFileInfo(fileInfo.getNodeRef());
                                assertNotNull(fileInfo);
                                expectedChildrenByModificationDate.add(0, fileInfo);

                                // make sure there is some difference in modification times
                                Thread.sleep(400);
                            }

                            return null;
                        }
                    });
        } finally {
            AuthenticationUtil.popAuthentication();
        }

        withCmisService(new CmisServiceCallback<Void>() {
            @Override
            public Void execute(CmisService cmisService) {
                // get repository id
                List<RepositoryInfo> repositories = cmisService.getRepositoryInfos(null);
                assertTrue(repositories.size() > 0);
                RepositoryInfo repo = repositories.get(0);
                String repositoryId = repo.getId();

                String folderId = nodes.get(0).getNodeRef().getId();
                String orderBy = PropertyIds.CREATION_DATE + " DESC";
                ObjectInFolderList children = cmisService.getChildren(repositoryId, folderId, null, orderBy, false,
                        IncludeRelationships.NONE, null, false, BigInteger.valueOf(Integer.MAX_VALUE),
                        BigInteger.valueOf(0), null);
                int i = 0;
                for (ObjectInFolderData child : children.getObjects()) {
                    Map<String, PropertyData<?>> properties = child.getObject().getProperties().getProperties();

                    PropertyData<?> pObjectId = properties.get(PropertyIds.VERSION_SERIES_ID);
                    String actualObjectId = (String) pObjectId.getFirstValue();
                    PropertyData<?> pCreationDate = properties.get(PropertyIds.CREATION_DATE);
                    GregorianCalendar actualCreationDate = (GregorianCalendar) pCreationDate.getFirstValue();

                    FileInfo expectedChild = expectedChildrenByCreationDate.get(i++);
                    assertEquals(expectedChild.getNodeRef().toString(), actualObjectId);
                    assertEquals(expectedChild.getCreatedDate().getTime(), actualCreationDate.getTimeInMillis());
                }

                orderBy = PropertyIds.LAST_MODIFICATION_DATE + " DESC";
                children = cmisService.getChildren(repositoryId, folderId, null, orderBy, false,
                        IncludeRelationships.NONE, null, false, BigInteger.valueOf(Integer.MAX_VALUE),
                        BigInteger.valueOf(0), null);
                i = 0;
                for (ObjectInFolderData child : children.getObjects()) {
                    Map<String, PropertyData<?>> properties = child.getObject().getProperties().getProperties();

                    PropertyData<?> pObjectId = properties.get(PropertyIds.VERSION_SERIES_ID);
                    String actualObjectId = (String) pObjectId.getFirstValue();
                    PropertyData<?> pModificationDate = properties.get(PropertyIds.LAST_MODIFICATION_DATE);
                    GregorianCalendar actualModificationDate = (GregorianCalendar) pModificationDate
                            .getFirstValue();

                    FileInfo expectedChild = expectedChildrenByModificationDate.get(i++);
                    assertEquals(expectedChild.getNodeRef().toString(), actualObjectId);
                    assertEquals(expectedChild.getModifiedDate().getTime(),
                            actualModificationDate.getTimeInMillis());
                }

                return null;
            }
        });
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    @Test
    public void testSecondaryTypes() {
        final String aspectName = "P:cm:indexControl";

        // get repository id
        final String repositoryId = withCmisService(new CmisServiceCallback<String>() {
            @Override
            public String execute(CmisService cmisService) {
                List<RepositoryInfo> repositories = cmisService.getRepositoryInfos(null);
                assertTrue(repositories.size() > 0);
                RepositoryInfo repo = repositories.get(0);
                final String repositoryId = repo.getId();
                return repositoryId;
            }
        }, CmisVersion.CMIS_1_1);

        final String objectId = withCmisService(new CmisServiceCallback<String>() {
            @Override
            public String execute(CmisService cmisService) {
                final PropertiesImpl properties = new PropertiesImpl();
                String objectTypeId = "cmis:document";
                properties.addProperty(new PropertyIdImpl(PropertyIds.OBJECT_TYPE_ID, objectTypeId));
                String fileName = "textFile" + GUID.generate();
                properties.addProperty(new PropertyStringImpl(PropertyIds.NAME, fileName));
                final ContentStreamImpl contentStream = new ContentStreamImpl(fileName,
                        MimetypeMap.MIMETYPE_TEXT_PLAIN, "Simple text plain document");

                String objectId = cmisService.create(repositoryId, properties,
                        repositoryHelper.getCompanyHome().getId(), contentStream, VersioningState.MAJOR, null,
                        null);
                return objectId;
            }
        }, CmisVersion.CMIS_1_1);

        final Holder<String> objectIdHolder = new Holder<String>(objectId);

        withCmisService(new CmisServiceCallback<Void>() {
            @Override
            public Void execute(CmisService cmisService) {
                final PropertiesImpl properties = new PropertiesImpl();
                properties.addProperty(
                        new PropertyStringImpl(PropertyIds.SECONDARY_OBJECT_TYPE_IDS, Arrays.asList(aspectName)));

                cmisService.updateProperties(repositoryId, objectIdHolder, null, properties, null);
                return null;
            }
        }, CmisVersion.CMIS_1_1);

        final Properties currentProperties = withCmisService(new CmisServiceCallback<Properties>() {
            @Override
            public Properties execute(CmisService cmisService) {
                Properties properties = cmisService.getProperties(repositoryId, objectIdHolder.getValue(), null,
                        null);
                return properties;
            }
        }, CmisVersion.CMIS_1_1);

        List secondaryTypeIds = currentProperties.getProperties().get(PropertyIds.SECONDARY_OBJECT_TYPE_IDS)
                .getValues();

        secondaryTypeIds.remove(aspectName);
        final PropertiesImpl newProperties = new PropertiesImpl();
        newProperties.addProperty(new PropertyStringImpl(PropertyIds.SECONDARY_OBJECT_TYPE_IDS, secondaryTypeIds));

        withCmisService(new CmisServiceCallback<Void>() {
            @Override
            public Void execute(CmisService cmisService) {
                Holder<String> latestObjectIdHolder = getHolderOfObjectOfLatestVersion(cmisService, repositoryId,
                        objectIdHolder);
                cmisService.updateProperties(repositoryId, latestObjectIdHolder, null, newProperties, null);
                return null;
            }
        }, CmisVersion.CMIS_1_1);

        Properties currentProperties1 = withCmisService(new CmisServiceCallback<Properties>() {
            @Override
            public Properties execute(CmisService cmisService) {
                Properties properties = cmisService.getProperties(repositoryId, objectIdHolder.getValue(), null,
                        null);
                return properties;
            }
        }, CmisVersion.CMIS_1_1);
        secondaryTypeIds = currentProperties1.getProperties().get(PropertyIds.SECONDARY_OBJECT_TYPE_IDS)
                .getValues();

    }

    /**
     * Test for MNT-9089
     */
    @Test
    public void testIntegerBoudaries() throws Exception {
        AuthenticationUtil.pushAuthentication();
        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());

        try {
            final FileInfo fileInfo = transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<FileInfo>() {
                        @Override
                        public FileInfo execute() throws Throwable {
                            NodeRef companyHomeNodeRef = repositoryHelper.getCompanyHome();

                            QName testIntTypeQName = QName.createQName("http://testCMISIntegersModel/1.0/",
                                    "testintegerstype");

                            String folderName = GUID.generate();
                            FileInfo folderInfo = fileFolderService.create(companyHomeNodeRef, folderName,
                                    ContentModel.TYPE_FOLDER);
                            nodeService.setProperty(folderInfo.getNodeRef(), ContentModel.PROP_NAME, folderName);
                            assertNotNull(folderInfo);

                            String docName = GUID.generate();
                            FileInfo fileInfo = fileFolderService.create(folderInfo.getNodeRef(), docName,
                                    testIntTypeQName);
                            assertNotNull(fileInfo);
                            nodeService.setProperty(fileInfo.getNodeRef(), ContentModel.PROP_NAME, docName);

                            return fileInfo;
                        }
                    });

            // get repository id
            withCmisService(new CmisServiceCallback<Void>() {
                @Override
                public Void execute(CmisService cmisService) {
                    List<RepositoryInfo> repositories = cmisService.getRepositoryInfos(null);
                    assertTrue(repositories.size() > 0);
                    RepositoryInfo repo = repositories.get(0);
                    String repositoryId = repo.getId();

                    String objectIdStr = fileInfo.getNodeRef().toString();

                    TypeDefinition typeDef = cmisService.getTypeDefinition(repositoryId, "D:tcim:testintegerstype",
                            null);

                    PropertyIntegerDefinitionImpl intNoBoundsTypeDef = (PropertyIntegerDefinitionImpl) typeDef
                            .getPropertyDefinitions().get("tcim:int");
                    PropertyIntegerDefinitionImpl longNoBoundsTypeDef = (PropertyIntegerDefinitionImpl) typeDef
                            .getPropertyDefinitions().get("tcim:long");

                    PropertyIntegerDefinitionImpl intWithBoundsTypeDef = (PropertyIntegerDefinitionImpl) typeDef
                            .getPropertyDefinitions().get("tcim:intwithbounds");
                    PropertyIntegerDefinitionImpl longWithBoundsTypeDef = (PropertyIntegerDefinitionImpl) typeDef
                            .getPropertyDefinitions().get("tcim:longwithbounds");

                    BigInteger minInteger = BigInteger.valueOf(Integer.MIN_VALUE);
                    BigInteger maxInteger = BigInteger.valueOf(Integer.MAX_VALUE);

                    BigInteger minLong = BigInteger.valueOf(Long.MIN_VALUE);
                    BigInteger maxLong = BigInteger.valueOf(Long.MAX_VALUE);

                    // test for default boundaries
                    assertTrue(intNoBoundsTypeDef.getMinValue().equals(minInteger));
                    assertTrue(intNoBoundsTypeDef.getMaxValue().equals(maxInteger));

                    assertTrue(longNoBoundsTypeDef.getMinValue().equals(minLong));
                    assertTrue(longNoBoundsTypeDef.getMaxValue().equals(maxLong));

                    // test for pre-defined boundaries
                    assertTrue(intWithBoundsTypeDef.getMinValue().equals(BigInteger.valueOf(-10L)));
                    assertTrue(intWithBoundsTypeDef.getMaxValue().equals(BigInteger.valueOf(10L)));

                    assertTrue(longWithBoundsTypeDef.getMinValue().equals(BigInteger.valueOf(-10L)));
                    assertTrue(longWithBoundsTypeDef.getMaxValue().equals(BigInteger.valueOf(10L)));

                    try // try to overfloat long without boundaries
                    {
                        BigInteger aValue = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.valueOf(1L));
                        setProperiesToObject(cmisService, repositoryId, objectIdStr, "tcim:long", aValue);
                        fail();
                    } catch (Exception e) {
                        assertTrue(e instanceof CmisConstraintException);
                    }

                    try // try to overfloat int without boundaries
                    {
                        BigInteger aValue = BigInteger.valueOf(Integer.MAX_VALUE).add(BigInteger.valueOf(1L));
                        setProperiesToObject(cmisService, repositoryId, objectIdStr, "tcim:int", aValue);
                        fail();
                    } catch (Exception e) {
                        assertTrue(e instanceof CmisConstraintException);
                    }

                    try // try to overfloat int with boundaries
                    {
                        BigInteger aValue = BigInteger.valueOf(11l);
                        setProperiesToObject(cmisService, repositoryId, objectIdStr, "tcim:intwithbounds", aValue);
                        fail();
                    } catch (Exception e) {
                        assertTrue(e instanceof CmisConstraintException);
                    }

                    try // try to overfloat long with boundaries
                    {
                        BigInteger aValue = BigInteger.valueOf(11l);
                        setProperiesToObject(cmisService, repositoryId, objectIdStr, "tcim:longwithbounds", aValue);
                        fail();
                    } catch (Exception e) {
                        assertTrue(e instanceof CmisConstraintException);
                    }

                    return null;
                }
            }, CmisVersion.CMIS_1_0);
        } catch (Exception e) {
            fail(e.getMessage());
        } finally {
            AuthenticationUtil.popAuthentication();
        }
    }

    private void setProperiesToObject(CmisService cmisService, String repositoryId, String objectIdStr,
            String propertyStr, BigInteger bigIntValue) throws CmisConstraintException {
        Properties properties = cmisService.getProperties(repositoryId, objectIdStr, null, null);
        PropertyIntegerImpl pd = (PropertyIntegerImpl) properties.getProperties().get(propertyStr);
        pd.setValue(bigIntValue);

        Collection<PropertyData<?>> propsList = new ArrayList<PropertyData<?>>();
        propsList.add(pd);

        Properties newProps = new PropertiesImpl(propsList);

        cmisService.updateProperties(repositoryId, new Holder<String>(objectIdStr), null, newProps, null);
    }

    @Test
    public void testMNT9090() throws Exception {
        AuthenticationUtil.pushAuthentication();
        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());

        try {
            final FileInfo fileInfo = transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<FileInfo>() {
                        @Override
                        public FileInfo execute() throws Throwable {
                            NodeRef companyHomeNodeRef = repositoryHelper.getCompanyHome();

                            String folderName = GUID.generate();
                            FileInfo folderInfo = fileFolderService.create(companyHomeNodeRef, folderName,
                                    ContentModel.TYPE_FOLDER);
                            nodeService.setProperty(folderInfo.getNodeRef(), ContentModel.PROP_NAME, folderName);
                            assertNotNull(folderInfo);

                            String docName = GUID.generate();
                            FileInfo fileInfo = fileFolderService.create(folderInfo.getNodeRef(), docName,
                                    ContentModel.TYPE_CONTENT);
                            assertNotNull(fileInfo);
                            nodeService.setProperty(fileInfo.getNodeRef(), ContentModel.PROP_NAME, docName);

                            QName ASPECT_AUDIO = QName.createQName(NamespaceService.AUDIO_MODEL_1_0_URI, "audio");
                            Map<QName, Serializable> aspectProperties = new HashMap<QName, Serializable>();
                            nodeService.addAspect(fileInfo.getNodeRef(), ASPECT_AUDIO, aspectProperties);

                            return fileInfo;
                        }
                    });

            withCmisService(new CmisServiceCallback<Void>() {
                @Override
                public Void execute(CmisService cmisService) {
                    // get repository id
                    List<RepositoryInfo> repositories = cmisService.getRepositoryInfos(null);
                    assertTrue(repositories.size() > 0);
                    RepositoryInfo repo = repositories.get(0);
                    String repositoryId = repo.getId();

                    String objectIdStr = fileInfo.getNodeRef().toString();
                    Holder<String> objectId = new Holder<String>(objectIdStr);

                    // try to overflow the value
                    Object value = BigInteger.valueOf(Integer.MAX_VALUE + 1l);

                    Properties properties = new PropertiesImpl();
                    List<CmisExtensionElement> extensions = new ArrayList<CmisExtensionElement>();

                    CmisExtensionElement valueElem = new CmisExtensionElementImpl(
                            CMISConnector.ALFRESCO_EXTENSION_NAMESPACE, "value", null, value.toString());
                    List<CmisExtensionElement> valueElems = new ArrayList<CmisExtensionElement>();
                    valueElems.add(valueElem);

                    List<CmisExtensionElement> children = new ArrayList<CmisExtensionElement>();
                    Map<String, String> attributes = new HashMap<String, String>();
                    attributes.put("propertyDefinitionId", "audio:trackNumber");
                    children.add(new CmisExtensionElementImpl(CMISConnector.ALFRESCO_EXTENSION_NAMESPACE,
                            "propertyInteger", attributes, valueElems));

                    List<CmisExtensionElement> propertyValuesExtension = new ArrayList<CmisExtensionElement>();
                    propertyValuesExtension.add(new CmisExtensionElementImpl(
                            CMISConnector.ALFRESCO_EXTENSION_NAMESPACE, CMISConnector.PROPERTIES, null, children));

                    CmisExtensionElement setAspectsExtension = new CmisExtensionElementImpl(
                            CMISConnector.ALFRESCO_EXTENSION_NAMESPACE, CMISConnector.SET_ASPECTS, null,
                            propertyValuesExtension);
                    extensions.add(setAspectsExtension);
                    properties.setExtensions(extensions);

                    // should throw a CMISConstraintException
                    cmisService.updateProperties(repositoryId, objectId, null, properties, null);
                    fail();

                    return null;
                }
            }, CmisVersion.CMIS_1_0);
        } catch (CmisConstraintException e) {
            assertTrue(e.getMessage().startsWith("Value is out of range for property"));
            // ok
        } finally {
            AuthenticationUtil.popAuthentication();
        }
    }

    public void testGetContentChanges() {
        // create folder with file
        String folderName = "testfolder" + GUID.generate();
        String docName = "testdoc.txt" + GUID.generate();
        createContent(folderName, docName, false);
        folderName = "testfolder" + GUID.generate();
        docName = "testdoc.txt" + GUID.generate();
        createContent(folderName, docName, false);
        Holder<String> changeLogToken = new Holder<String>();
        ObjectList ol = this.cmisConnector.getContentChanges(changeLogToken, new BigInteger("2"));
        assertEquals(2, ol.getNumItems());
        assertEquals("3", changeLogToken.getValue());
    }

    /**
     * MNT-10223
     * Check the IsLatestMajorVersion for a doc with minor version.
     */
    @SuppressWarnings("unused")
    @Test
    public void testIsLatestMajorVersion() {
        final TestContext testContext = new TestContext();

        // create simple text plain content
        final PropertiesImpl properties = new PropertiesImpl();
        String objectTypeId = "cmis:document";
        properties.addProperty(new PropertyIdImpl(PropertyIds.OBJECT_TYPE_ID, objectTypeId));
        String fileName = "textFile" + GUID.generate();
        properties.addProperty(new PropertyStringImpl(PropertyIds.NAME, fileName));
        final ContentStreamImpl contentStream = new ContentStreamImpl(fileName, MimetypeMap.MIMETYPE_TEXT_PLAIN,
                "Simple text plain document");

        withCmisService(new CmisServiceCallback<String>() {
            @Override
            public String execute(CmisService cmisService) {
                List<RepositoryInfo> repositories = cmisService.getRepositoryInfos(null);
                assertTrue(repositories.size() > 0);
                RepositoryInfo repo = repositories.get(0);
                String repositoryId = repo.getId();

                String objectId = cmisService.create(repositoryId, properties,
                        repositoryHelper.getCompanyHome().getId(), contentStream, VersioningState.MINOR, null,
                        null);

                ObjectData cmidDoc = cmisService.getObject(repositoryId, objectId, null, true,
                        IncludeRelationships.NONE, null, false, false, null);
                List<PropertyData<?>> properties = cmidDoc.getProperties().getPropertyList();
                boolean found = false;
                PropertyData<?> propIsLatestMajorVersion = null;
                for (PropertyData<?> property : properties) {
                    if (property.getId().equals(PropertyIds.IS_LATEST_MAJOR_VERSION)) {
                        found = true;
                        propIsLatestMajorVersion = property;
                        break;
                    }
                }
                //properties..contains(CMISDictionaryModel.PROP_IS_LATEST_MAJOR_VERSION);
                assertTrue("The CMISDictionaryModel.PROP_IS_LATEST_MAJOR_VERSION property was not found", found);
                if (found) {
                    assertFalse(
                            "The CMISDictionaryModel.PROP_IS_LATEST_MAJOR_VERSION should be false as minor version was created",
                            (Boolean) propIsLatestMajorVersion.getValues().get(0));
                }
                return objectId;
            }
        });
    }

    /**
     * ACE-33
     * 
     * Cmis Item support
     */
    @Test
    public void testItems() {

        withCmisService(new CmisServiceCallback<String>() {
            @Override
            public String execute(CmisService cmisService) {
                List<RepositoryInfo> repositories = cmisService.getRepositoryInfos(null);
                assertTrue(repositories.size() > 0);
                RepositoryInfo repo = repositories.get(0);
                String repositoryId = repo.getId();

                TypeDefinition def = cmisService.getTypeDefinition(repositoryId, "cmis:item", null);
                assertNotNull("the cmis:item type is not defined", def);

                @SuppressWarnings("unused")
                TypeDefinition p = cmisService.getTypeDefinition(repositoryId, "I:cm:person", null);
                assertNotNull("the I:cm:person type is not defined", def);

                ObjectList result = cmisService.query(repositoryId, "select * from cm:person", Boolean.FALSE,
                        Boolean.TRUE, IncludeRelationships.NONE, "", BigInteger.TEN, BigInteger.ZERO, null);
                assertTrue("", result.getNumItems().intValue() > 0);
                return "";

            };
        }, CmisVersion.CMIS_1_1);

    }

    /**
     * MNT-11339 related test :
     * Unable to create relationship between cmis:document and cmis:item
     */
    @Test
    public void testItemRelations() {
        AuthenticationUtil.pushAuthentication();
        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());

        final String TEST_NAME = "testItemRelations-";
        final String FOLDER_NAME = TEST_NAME + "FOLDER" + GUID.generate();
        final String DOCUMENT_NAME = TEST_NAME + "DOCUMENT" + GUID.generate();
        final String CLIENT_NAME = "Some Test Client " + GUID.generate();

        try {
            transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<Void>() {
                        @Override
                        public Void execute() throws Throwable {
                            NodeRef companyHomeNodeRef = repositoryHelper.getCompanyHome();

                            /* Create folder within companyHome */
                            FileInfo folderInfo = fileFolderService.create(companyHomeNodeRef, FOLDER_NAME,
                                    ContentModel.TYPE_FOLDER);
                            nodeService.setProperty(folderInfo.getNodeRef(), ContentModel.PROP_NAME, FOLDER_NAME);
                            assertNotNull(folderInfo);

                            // and document
                            FileInfo document = fileFolderService.create(folderInfo.getNodeRef(), DOCUMENT_NAME,
                                    ContentModel.TYPE_CONTENT);
                            assertNotNull(document);
                            nodeService.setProperty(document.getNodeRef(), ContentModel.PROP_NAME, DOCUMENT_NAME);

                            return null;
                        }
                    });

            withCmisService(new CmisServiceCallback<String>() {
                @SuppressWarnings("unchecked")
                @Override
                public String execute(CmisService cmisService) {
                    List<RepositoryInfo> repositories = cmisService.getRepositoryInfos(null);
                    assertTrue(repositories.size() > 0);
                    RepositoryInfo repo = repositories.get(0);
                    String repositoryId = repo.getId();

                    // ensure there are custom type, aspect and association defined
                    TypeDefinition tpdfn = cmisService.getTypeDefinition(repositoryId, "I:sctst:client", null);
                    assertNotNull("the I:sctst:client type is not defined", tpdfn);
                    TypeDefinition aspectDfn = cmisService.getTypeDefinition(repositoryId, "P:sctst:clientRelated",
                            null);
                    assertNotNull("the P:sctst:clientRelated aspect is not defined", aspectDfn);
                    TypeDefinition relDfn = cmisService.getTypeDefinition(repositoryId, "R:sctst:relatedClients",
                            null);
                    assertNotNull("the R:sctst:relatedClients association is not defined", relDfn);

                    // create cmis:item within test folder
                    PropertiesImpl properties = new PropertiesImpl();
                    properties.addProperty(new PropertyIdImpl(PropertyIds.OBJECT_TYPE_ID, tpdfn.getId()));
                    properties.addProperty(new PropertyStringImpl(PropertyIds.NAME, CLIENT_NAME));
                    properties.addProperty(new PropertyStringImpl("sctst:clientId", "id" + GUID.generate()));
                    properties.addProperty(new PropertyStringImpl("sctst:clientName", CLIENT_NAME));

                    ObjectData folderData = cmisService.getObjectByPath(repositoryId, "/" + FOLDER_NAME, null, null,
                            null, null, null, null, null);

                    cmisService.createItem(repositoryId, properties, folderData.getId(), null, null, null, null);

                    ObjectData contentData = cmisService.getObjectByPath(repositoryId,
                            "/" + FOLDER_NAME + "/" + DOCUMENT_NAME, null, null, null, null, null, null, null);

                    // add test aspect sctst:clientRelated to document
                    Properties props = cmisService.getProperties(repositoryId, contentData.getId(), null, null);

                    PropertyData<?> propAspects = props.getProperties().get(PropertyIds.SECONDARY_OBJECT_TYPE_IDS);

                    @SuppressWarnings("rawtypes")
                    List aspects = propAspects.getValues();
                    aspects.add("P:sctst:clientRelated");

                    properties = new PropertiesImpl();
                    properties.addProperty(new PropertyStringImpl(PropertyIds.SECONDARY_OBJECT_TYPE_IDS, aspects));
                    cmisService.updateProperties(repositoryId, new Holder<String>(contentData.getId()), null,
                            properties, null);
                    // ensure document has sctst:clientRelated aspect applied
                    aspects = cmisService.getProperties(repositoryId, contentData.getId(), null, null)
                            .getProperties().get(PropertyIds.SECONDARY_OBJECT_TYPE_IDS).getValues();
                    assertTrue("P:sctst:clientRelated excpected", aspects.contains("P:sctst:clientRelated"));

                    ObjectData itemData = cmisService.getObjectByPath(repositoryId,
                            "/" + FOLDER_NAME + "/" + CLIENT_NAME, null, null, null, null, null, null, null);
                    // create relationship between cmis:document and cmis:item 
                    properties = new PropertiesImpl();
                    properties
                            .addProperty(new PropertyIdImpl(PropertyIds.OBJECT_TYPE_ID, "R:sctst:relatedClients"));
                    properties.addProperty(new PropertyIdImpl(PropertyIds.SOURCE_ID, contentData.getId()));
                    properties.addProperty(new PropertyIdImpl(PropertyIds.TARGET_ID, itemData.getId()));
                    cmisService.createRelationship(repositoryId, properties, null, null, null, null);

                    return "";

                };
            }, CmisVersion.CMIS_1_1);
        } finally {
            AuthenticationUtil.popAuthentication();
        }
    }

    @Test
    public void testMNT10529() throws Exception {
        AuthenticationUtil.pushAuthentication();
        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());

        try {
            final Pair<FileInfo, FileInfo> folderAndDocument = transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<Pair<FileInfo, FileInfo>>() {
                        @Override
                        public Pair<FileInfo, FileInfo> execute() throws Throwable {
                            NodeRef companyHomeNodeRef = repositoryHelper.getCompanyHome();

                            String folderName = GUID.generate();
                            FileInfo folderInfo = fileFolderService.create(companyHomeNodeRef, folderName,
                                    ContentModel.TYPE_FOLDER);
                            nodeService.setProperty(folderInfo.getNodeRef(), ContentModel.PROP_NAME, folderName);
                            assertNotNull(folderInfo);

                            String docName = GUID.generate();
                            FileInfo document = fileFolderService.create(folderInfo.getNodeRef(), docName,
                                    ContentModel.TYPE_CONTENT);
                            assertNotNull(document);
                            nodeService.setProperty(document.getNodeRef(), ContentModel.PROP_NAME, docName);

                            return new Pair<FileInfo, FileInfo>(folderInfo, document);
                        }
                    });

            withCmisService(new CmisServiceCallback<Void>() {
                @Override
                public Void execute(CmisService cmisService) {
                    List<RepositoryInfo> repositories = cmisService.getRepositoryInfos(null);
                    assertNotNull(repositories);
                    assertTrue(repositories.size() > 0);
                    RepositoryInfo repo = repositories.iterator().next();
                    String repositoryId = repo.getId();

                    String objectIdStr = folderAndDocument.getFirst().getNodeRef().toString();

                    ObjectInFolderList children = cmisService.getChildren(repositoryId, objectIdStr, null,
                            "cmis:name ASC", false, IncludeRelationships.NONE, null, false, null, null, null);
                    assertChildren(folderAndDocument, children);

                    children = cmisService.getChildren(repositoryId, objectIdStr, null,
                            "cmis:name ASC, cmis:creationDate ASC", false, IncludeRelationships.NONE, null, false,
                            null, null, null);
                    assertChildren(folderAndDocument, children);

                    children = cmisService.getChildren(repositoryId, objectIdStr, null, "    cmis:name ASC", false,
                            IncludeRelationships.NONE, null, false, null, null, null);
                    assertChildren(folderAndDocument, children);

                    children = cmisService.getChildren(repositoryId, objectIdStr, null,
                            "    cmis:name ASC, cmis:creationDate ASC   ", false, IncludeRelationships.NONE, null,
                            false, null, null, null);
                    assertChildren(folderAndDocument, children);

                    return null;
                }

                private void assertChildren(final Pair<FileInfo, FileInfo> folderAndDocument,
                        ObjectInFolderList children) {
                    assertNotNull(children);
                    assertTrue(1 == children.getNumItems().longValue());

                    PropertyData<?> nameData = children.getObjects().iterator().next().getObject().getProperties()
                            .getProperties().get("cmis:name");
                    assertNotNull(nameData);
                    Object name = nameData.getValues().iterator().next();
                    assertEquals(folderAndDocument.getSecond().getName(), name);
                }
            }, CmisVersion.CMIS_1_0);
        } catch (CmisConstraintException e) {
            fail(e.toString());
        } finally {
            AuthenticationUtil.popAuthentication();
        }
    }

    @Test
    public void mnt10548test() throws Exception {
        AuthenticationUtil.pushAuthentication();
        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());

        final Pair<FileInfo, FileInfo> folderAndDocument = transactionService.getRetryingTransactionHelper()
                .doInTransaction(new RetryingTransactionCallback<Pair<FileInfo, FileInfo>>() {
                    private final static String TEST_NAME = "mnt10548test-";

                    @Override
                    public Pair<FileInfo, FileInfo> execute() throws Throwable {
                        NodeRef companyHomeNodeRef = repositoryHelper.getCompanyHome();

                        /* Create folder within companyHome */
                        String folderName = TEST_NAME + GUID.generate();
                        FileInfo folderInfo = fileFolderService.create(companyHomeNodeRef, folderName,
                                ContentModel.TYPE_FOLDER);
                        nodeService.setProperty(folderInfo.getNodeRef(), ContentModel.PROP_NAME, folderName);
                        assertNotNull(folderInfo);

                        /* Create content */
                        String docName = TEST_NAME + GUID.generate();
                        FileInfo document = fileFolderService.create(folderInfo.getNodeRef(), docName,
                                ContentModel.TYPE_CONTENT);
                        assertNotNull(document);
                        nodeService.setProperty(document.getNodeRef(), ContentModel.PROP_NAME, docName);

                        /* Add some tags */
                        NodeRef nodeRef = document.getNodeRef();
                        taggingService.addTag(nodeRef, "tag1");
                        taggingService.addTag(nodeRef, "tag2");
                        taggingService.addTag(nodeRef, "tag3");

                        return new Pair<FileInfo, FileInfo>(folderInfo, document);
                    }
                });

        ObjectData objData = withCmisService(new CmisServiceCallback<ObjectData>() {
            private static final String FILE_FOLDER_SEPARATOR = "/";

            @Override
            public ObjectData execute(CmisService cmisService) {
                List<RepositoryInfo> repositories = cmisService.getRepositoryInfos(null);
                assertTrue(repositories.size() > 0);
                RepositoryInfo repo = repositories.get(0);
                String repositoryId = repo.getId();

                String path = FILE_FOLDER_SEPARATOR + folderAndDocument.getFirst().getName() + FILE_FOLDER_SEPARATOR
                        + folderAndDocument.getSecond().getName();
                /* get CMIS object of document */
                ObjectData objData = cmisService.getObjectByPath(repositoryId, path, null, false, null, null, false,
                        false, null);
                return objData;
            }
        }, CmisVersion.CMIS_1_1);

        Map<String, PropertyData<?>> cmisProps = objData.getProperties().getProperties();

        String taggable = ContentModel.ASPECT_TAGGABLE.getPrefixedQName(namespaceService).toPrefixString();
        PropertyData<?> propData = cmisProps.get(taggable);
        assertNotNull(propData);

        List<?> props = propData.getValues();
        assertTrue(props.size() == 3);

        /* MNT-10548 fix : CMIS should return List of String, not List of NodeRef */
        for (Object o : props) {
            assertTrue(o.getClass() + " found but String expected", o instanceof String);
        }
    }

    /**
     * MNT-8804 related test :
     * Check CMISConnector.query for search nodes in environment with corrupted indexes
     */
    @Test
    public void testQueryNodesWithCorruptedIndexes() {
        AuthenticationUtil.pushAuthentication();
        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());

        final String TEST_NAME = "mnt8804test-";
        final String docName = TEST_NAME + GUID.generate();

        /* Create node */
        final FileInfo document = transactionService.getRetryingTransactionHelper()
                .doInTransaction(new RetryingTransactionCallback<FileInfo>() {
                    @Override
                    public FileInfo execute() throws Throwable {
                        NodeRef companyHomeNodeRef = repositoryHelper.getCompanyHome();

                        /* Create folder within companyHome */
                        String folderName = TEST_NAME + GUID.generate();
                        FileInfo folderInfo = fileFolderService.create(companyHomeNodeRef, folderName,
                                ContentModel.TYPE_FOLDER);
                        nodeService.setProperty(folderInfo.getNodeRef(), ContentModel.PROP_NAME, folderName);
                        assertNotNull(folderInfo);

                        /* Create document to query */
                        FileInfo document = fileFolderService.create(folderInfo.getNodeRef(), docName,
                                ContentModel.TYPE_CONTENT);
                        assertNotNull(document);
                        nodeService.setProperty(document.getNodeRef(), ContentModel.PROP_NAME, docName);

                        return document;
                    }
                });

        final Pair<Long, NodeRef> nodePair = nodeDAO.getNodePair(document.getNodeRef());

        /* delete node's metadata directly */
        transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Void>() {
            @Override
            public Void execute() throws Throwable {
                Pair<Long, ChildAssociationRef> childAssocPair = nodeDAO.getPrimaryParentAssoc(nodePair.getFirst());
                nodeDAO.deleteChildAssoc(childAssocPair.getFirst());
                nodeDAO.deleteNode(nodePair.getFirst());
                return null;
            }
        });

        /* ensure the node does not exist */
        assertTrue(!nodeService.exists(nodePair.getSecond()));

        String queryString = "SELECT * FROM cmis:document WHERE cmis:name='" + docName + "'";

        ObjectList resultList = cmisConnector.query(queryString, Boolean.FALSE, IncludeRelationships.NONE,
                "cmis:none", BigInteger.ONE, BigInteger.ZERO);
        assertEquals(resultList.getNumItems(), BigInteger.ZERO);

        // prepare cmis query
        CMISQueryOptions options = new CMISQueryOptions(queryString, cmisConnector.getRootStoreRef());
        CmisVersion cmisVersion = cmisConnector.getRequestCmisVersion();
        options.setCmisVersion(cmisVersion);
        options.setQueryMode(CMISQueryMode.CMS_WITH_ALFRESCO_EXTENSIONS);
        options.setSkipCount(0);
        options.setMaxItems(100);

        /* make query bypassing CMISConnector */
        org.alfresco.opencmis.search.CMISResultSet rs = cmisConnector.getOpenCMISQueryService().query(options);
        assertEquals(rs.getNumberFound(), 0);
    }

    /**
     * CMIS 1.0 aspect properties should provide the following CMIS attributes:
     * propertyDefinitionId, displayName, localName, queryName
     */
    @Test
    public void testMNT10021() throws Exception {
        final String folderName = "testfolder." + GUID.generate();
        final String docName = "testdoc.txt." + GUID.generate();

        AuthenticationUtil.pushAuthentication();
        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());

        try {
            transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<Void>() {
                        @Override
                        public Void execute() throws Throwable {
                            NodeRef companyHomeNodeRef = repositoryHelper.getCompanyHome();

                            FileInfo folderInfo = fileFolderService.create(companyHomeNodeRef, folderName,
                                    ContentModel.TYPE_FOLDER);
                            nodeService.setProperty(folderInfo.getNodeRef(), ContentModel.PROP_NAME, folderName);
                            assertNotNull(folderInfo);

                            FileInfo document = fileFolderService.create(folderInfo.getNodeRef(), docName,
                                    ContentModel.TYPE_CONTENT);
                            assertNotNull(document);
                            nodeService.setProperty(document.getNodeRef(), ContentModel.PROP_NAME, docName);

                            // lock adds aspects to the document with properties: lockIsDeep, lockOwner, lockType, expiryDate, lockLifetime
                            lockService.lock(document.getNodeRef(), LockType.READ_ONLY_LOCK, 0, true);

                            return null;
                        }
                    });

            final ObjectData objectData = withCmisService(new CmisServiceCallback<ObjectData>() {
                @Override
                public ObjectData execute(CmisService cmisService) {
                    List<RepositoryInfo> repositories = cmisService.getRepositoryInfos(null);
                    assertTrue(repositories.size() > 0);
                    RepositoryInfo repo = repositories.get(0);
                    String repositoryId = repo.getId();
                    ObjectData objectData = cmisService.getObjectByPath(repositoryId,
                            "/" + folderName + "/" + docName, null, true, IncludeRelationships.NONE, null, false,
                            true, null);
                    return objectData;
                }
            }, CmisVersion.CMIS_1_0);

            List<CmisExtensionElement> propertyExtensionList = objectData.getProperties().getExtensions();
            assertEquals("propertyExtensionList should be singletonList", propertyExtensionList.size(), 1);
            List<CmisExtensionElement> extensions = propertyExtensionList.iterator().next().getChildren();
            for (CmisExtensionElement extension : extensions) {
                if ("properties".equals(extension.getName())) {
                    // check properties extension
                    List<CmisExtensionElement> propExtensions = extension.getChildren();
                    assertTrue("cmisObject should contain aspect properties", propExtensions.size() > 0);
                    for (CmisExtensionElement prop : propExtensions) {
                        Map<String, String> cmisAspectProperty = prop.getAttributes();
                        Set<String> cmisAspectPropertyNames = cmisAspectProperty.keySet();
                        assertTrue("propertyDefinitionId attribute should be present",
                                cmisAspectPropertyNames.contains("propertyDefinitionId"));
                        assertTrue("queryName attribute should be present",
                                cmisAspectPropertyNames.contains("queryName"));
                        // optional values that are present for test document
                        assertTrue("displayName attribute should be present for property of test node",
                                cmisAspectPropertyNames.contains("displayName"));
                        assertTrue("localName attribute should be present for property of test node",
                                cmisAspectPropertyNames.contains("localName"));
                        assertEquals(cmisAspectPropertyNames.size(), 4);
                        // check values
                        for (String aspectPropertyName : cmisAspectPropertyNames) {
                            String value = cmisAspectProperty.get(aspectPropertyName);
                            assertTrue("value for " + aspectPropertyName + " should be present",
                                    value != null && value.length() > 0);
                        }
                    }
                }
            }
        } catch (CmisConstraintException e) {
            fail(e.toString());
        } finally {
            AuthenticationUtil.popAuthentication();
        }
    }

    @Test
    public void dictionaryTest() {
        TenantUtil.runAsUserTenant(new TenantRunAsWork<Void>() {
            @Override
            public Void doWork() throws Exception {
                M2Model customModel = M2Model.createModel(Thread.currentThread().getContextClassLoader()
                        .getResourceAsStream("dictionary/dictionarydaotest_model1.xml"));
                dictionaryDAO.putModel(customModel);
                assertNotNull(cmisDictionaryService.findType("P:cm:dublincore"));
                TypeDefinitionWrapper td = cmisDictionaryService.findType("D:daotest1:type1");
                assertNotNull(td);
                return null;
            }
        }, "user1", "tenant1");

        TenantUtil.runAsUserTenant(new TenantRunAsWork<Void>() {
            @Override
            public Void doWork() throws Exception {
                assertNotNull(cmisDictionaryService.findType("P:cm:dublincore"));
                TypeDefinitionWrapper td = cmisDictionaryService.findType("D:daotest1:type1");
                assertNull(td);
                return null;
            }
        }, "user2", "tenant2");
    }

    /**
     * MNT-13529: Just-installed Alfresco does not return a CMIS latestChangeLogToken
     * 
     * @throws Exception
     */
    @Test
    public void testMNT13529() throws Exception {
        setupAudit();

        AuthenticationUtil.pushAuthentication();
        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
        try {
            // Delete the entries, it simulates just installed Alfresco for reproduce the issue
            final Long appId = auditSubsystem.getAuditApplicationByName("CMISChangeLog").getApplicationId();
            RetryingTransactionCallback<Void> deletedCallback = new RetryingTransactionCallback<Void>() {
                public Void execute() throws Throwable {
                    auditDAO.deleteAuditEntries(appId, null, null);
                    return null;
                }
            };
            transactionService.getRetryingTransactionHelper().doInTransaction(deletedCallback);

            // Retrieve initial latestChangeLogToken
            final String initialChangeLogToken = withCmisService(new CmisServiceCallback<String>() {
                @Override
                public String execute(CmisService cmisService) {
                    List<RepositoryInfo> repositories = cmisService.getRepositoryInfos(null);
                    assertNotNull(repositories);
                    assertTrue(repositories.size() > 0);
                    RepositoryInfo repo = repositories.iterator().next();

                    return repo.getLatestChangeLogToken();
                }
            }, CmisVersion.CMIS_1_1);

            assertNotNull(initialChangeLogToken);
            assertEquals("0", initialChangeLogToken);
        } finally {
            auditSubsystem.destroy();
            AuthenticationUtil.popAuthentication();
        }
    }

    /**
     * MNT-11726: Test that {@link CMISChangeEvent} contains objectId of node in short form (without StoreRef).
     */
    @Test
    public void testCMISChangeLogObjectIds() throws Exception {
        // setUp audit subsystem
        setupAudit();

        AuthenticationUtil.pushAuthentication();
        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());

        try {
            final String changeToken = withCmisService(new CmisServiceCallback<String>() {
                @Override
                public String execute(CmisService cmisService) {
                    List<RepositoryInfo> repositories = cmisService.getRepositoryInfos(null);
                    assertNotNull(repositories);
                    assertTrue(repositories.size() > 0);
                    RepositoryInfo repo = repositories.iterator().next();

                    return repo.getLatestChangeLogToken();
                }
            }, CmisVersion.CMIS_1_1);

            transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<Void>() {
                        @Override
                        public Void execute() throws Throwable {
                            NodeRef companyHomeNodeRef = repositoryHelper.getCompanyHome();

                            // perform CREATED, UPDATED, SECURITY, DELETED CMIS change type actions
                            String folder = GUID.generate();
                            FileInfo folderInfo = fileFolderService.create(companyHomeNodeRef, folder,
                                    ContentModel.TYPE_FOLDER);
                            nodeService.setProperty(folderInfo.getNodeRef(), ContentModel.PROP_NAME, folder);
                            assertNotNull(folderInfo);

                            String content = GUID.generate();
                            FileInfo document = fileFolderService.create(folderInfo.getNodeRef(), content,
                                    ContentModel.TYPE_CONTENT);
                            assertNotNull(document);
                            nodeService.setProperty(document.getNodeRef(), ContentModel.PROP_NAME, content);

                            permissionService.setPermission(document.getNodeRef(), "SomeAuthority",
                                    PermissionService.EXECUTE_CONTENT, true);

                            fileFolderService.delete(document.getNodeRef());
                            fileFolderService.delete(folderInfo.getNodeRef());

                            return null;
                        }
                    });

            withCmisService(new CmisServiceCallback<Void>() {
                @Override
                public Void execute(CmisService cmisService) {
                    List<RepositoryInfo> repositories = cmisService.getRepositoryInfos(null);
                    assertNotNull(repositories);
                    assertTrue(repositories.size() > 0);
                    String repositoryId = repositories.iterator().next().getId();

                    ObjectList changes = cmisService.getContentChanges(repositoryId,
                            new Holder<String>(changeToken), Boolean.TRUE, null, Boolean.FALSE, Boolean.FALSE,
                            BigInteger.valueOf(1000), null);

                    for (ObjectData od : changes.getObjects()) {
                        ChangeType changeType = od.getChangeEventInfo().getChangeType();
                        Object objectId = od.getProperties().getProperties().get("cmis:objectId").getValues()
                                .get(0);

                        assertFalse(
                                "CMISChangeEvent " + changeType + " should store short form of objectId "
                                        + objectId,
                                objectId.toString().contains(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE.toString()));
                    }

                    return null;
                }
            }, CmisVersion.CMIS_1_1);
        } finally {
            auditSubsystem.destroy();
            AuthenticationUtil.popAuthentication();
        }
    }

    private void setupAudit() {
        UserAuditFilter userAuditFilter = new UserAuditFilter();
        userAuditFilter.setUserFilterPattern("System;.*");
        userAuditFilter.afterPropertiesSet();
        AuditComponent auditComponent = (AuditComponent) ctx.getBean("auditComponent");
        auditComponent.setUserAuditFilter(userAuditFilter);
        AuditServiceImpl auditServiceImpl = (AuditServiceImpl) ctx.getBean("auditService");
        auditServiceImpl.setAuditComponent(auditComponent);

        RetryingTransactionCallback<Void> initAudit = new RetryingTransactionCallback<Void>() {
            public Void execute() throws Exception {
                auditSubsystem.stop();
                auditSubsystem.setProperty("audit.enabled", "true");
                auditSubsystem.setProperty("audit.cmischangelog.enabled", "true");
                auditSubsystem.start();
                return null;
            }
        };
        transactionService.getRetryingTransactionHelper().doInTransaction(initAudit, false, true);
    }

    /**
     * MNT-11727: move and rename operations should be shown as an UPDATE in the CMIS change log
     */
    @Test
    public void testMoveRenameWithCMISshouldBeAuditedAsUPDATE() throws Exception {
        // setUp audit subsystem
        setupAudit();

        AuthenticationUtil.pushAuthentication();
        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());

        try {
            assertTrue("Audit is not enabled", auditSubsystem.isAuditEnabled());
            assertNotNull("CMIS audit is not enabled", auditSubsystem.getAuditApplicationByName("CMISChangeLog"));

            NodeRef companyHomeNodeRef = repositoryHelper.getCompanyHome();

            String folder = GUID.generate();
            FileInfo folderInfo = fileFolderService.create(companyHomeNodeRef, folder, ContentModel.TYPE_FOLDER);

            final String actualToken = transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<String>() {
                        public String execute() throws Exception {
                            return cmisConnector.getRepositoryInfo(CmisVersion.CMIS_1_1).getLatestChangeLogToken();
                        }
                    }, true, false);

            String content = GUID.generate();
            FileInfo document = fileFolderService.create(folderInfo.getNodeRef(), content,
                    ContentModel.TYPE_CONTENT);
            assertNotNull(document);
            nodeService.setProperty(document.getNodeRef(), ContentModel.PROP_NAME, content);

            Holder<String> changeLogToken = new Holder<String>();
            changeLogToken.setValue(actualToken);
            ObjectList changeLog = CMISTest.this.cmisConnector.getContentChanges(changeLogToken,
                    new BigInteger("10"));
            List<ObjectData> events = changeLog.getObjects();
            int count = events.size();
            // it should be 3 entries: 1 for previous folder create, 1 new CREATE (for document create)
            // and 1 NEW UPDATE
            assertEquals(3, count);

            assertEquals(events.get(0).getProperties().getPropertyList().get(0).getValues().get(0),
                    folderInfo.getNodeRef().getId());
            assertEquals(events.get(0).getChangeEventInfo().getChangeType(), ChangeType.CREATED);

            assertTrue(((String) events.get(1).getProperties().getPropertyList().get(0).getValues().get(0))
                    .contains(document.getNodeRef().getId()));
            assertEquals(events.get(1).getChangeEventInfo().getChangeType(), ChangeType.CREATED);

            assertTrue(((String) events.get(2).getProperties().getPropertyList().get(0).getValues().get(0))
                    .contains(document.getNodeRef().getId()));
            assertEquals(events.get(2).getChangeEventInfo().getChangeType(), ChangeType.UPDATED);

            // test rename
            final String actualToken2 = transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<String>() {
                        public String execute() throws Exception {
                            return cmisConnector.getRepositoryInfo(CmisVersion.CMIS_1_1).getLatestChangeLogToken();
                        }
                    }, true, false);
            nodeService.setProperty(document.getNodeRef(), ContentModel.PROP_NAME, content + "-updated");

            changeLogToken = new Holder<String>();
            changeLogToken.setValue(actualToken2);
            changeLog = CMISTest.this.cmisConnector.getContentChanges(changeLogToken, new BigInteger("10"));
            events = changeLog.getObjects();
            count = events.size();
            assertEquals(2, count);
            assertEquals("Rename operation should be shown as an UPDATE in the CMIS change log",
                    events.get(1).getChangeEventInfo().getChangeType(), ChangeType.UPDATED);

            // test move
            String targetFolder = GUID.generate();
            FileInfo targetFolderInfo = fileFolderService.create(companyHomeNodeRef, targetFolder,
                    ContentModel.TYPE_FOLDER);

            final String actualToken3 = transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<String>() {
                        public String execute() throws Exception {
                            return cmisConnector.getRepositoryInfo(CmisVersion.CMIS_1_1).getLatestChangeLogToken();
                        }
                    }, true, false);
            nodeService.moveNode(document.getNodeRef(), targetFolderInfo.getNodeRef(), ContentModel.ASSOC_CONTAINS,
                    ContentModel.ASSOC_CONTAINS);

            changeLogToken = new Holder<String>();
            changeLogToken.setValue(actualToken3);
            changeLog = CMISTest.this.cmisConnector.getContentChanges(changeLogToken, new BigInteger("10"));
            events = changeLog.getObjects();
            count = events.size();
            assertEquals(2, count);
            assertEquals("Move operation should be shown as an UPDATE in the CMIS change log",
                    events.get(1).getChangeEventInfo().getChangeType(), ChangeType.UPDATED);
        } finally {
            auditSubsystem.destroy();
            AuthenticationUtil.popAuthentication();
        }
    }

    /**
     * MNT-11304: Test that Alfresco has no default boundaries for decimals
     * @throws Exception
     */
    @Test
    public void testDecimalDefaultBoundaries() throws Exception {
        AuthenticationUtil.pushAuthentication();
        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());

        try {
            withCmisService(new CmisServiceCallback<Void>() {
                @Override
                public Void execute(CmisService cmisService) {
                    List<RepositoryInfo> repositories = cmisService.getRepositoryInfos(null);
                    assertTrue(repositories.size() > 0);
                    RepositoryInfo repo = repositories.get(0);
                    String repositoryId = repo.getId();

                    TypeDefinition decimalTypeDef = cmisService.getTypeDefinition(repositoryId,
                            "D:tcdm:testdecimalstype", null);

                    PropertyDecimalDefinitionImpl floatNoBoundsTypeDef = (PropertyDecimalDefinitionImpl) decimalTypeDef
                            .getPropertyDefinitions().get("tcdm:float");
                    PropertyDecimalDefinitionImpl doubleNoBoundsTypeDef = (PropertyDecimalDefinitionImpl) decimalTypeDef
                            .getPropertyDefinitions().get("tcdm:double");

                    PropertyDecimalDefinitionImpl floatWithBoundsTypeDef = (PropertyDecimalDefinitionImpl) decimalTypeDef
                            .getPropertyDefinitions().get("tcdm:floatwithbounds");
                    PropertyDecimalDefinitionImpl doubleWithBoundsTypeDef = (PropertyDecimalDefinitionImpl) decimalTypeDef
                            .getPropertyDefinitions().get("tcdm:doublewithbounds");

                    // test that there is not default boundaries for decimals
                    assertNull(floatNoBoundsTypeDef.getMinValue());
                    assertNull(floatNoBoundsTypeDef.getMaxValue());

                    assertNull(doubleNoBoundsTypeDef.getMinValue());
                    assertNull(doubleNoBoundsTypeDef.getMaxValue());

                    // test for pre-defined boundaries
                    assertTrue(floatWithBoundsTypeDef.getMinValue().equals(BigDecimal.valueOf(-10f)));
                    assertTrue(floatWithBoundsTypeDef.getMaxValue().equals(BigDecimal.valueOf(10f)));

                    assertTrue(doubleWithBoundsTypeDef.getMinValue().equals(BigDecimal.valueOf(-10d)));
                    assertTrue(doubleWithBoundsTypeDef.getMaxValue().equals(BigDecimal.valueOf(10d)));

                    return null;
                }
            }, CmisVersion.CMIS_1_1);

        } finally {
            AuthenticationUtil.popAuthentication();
        }
    }

    /**
     * MNT-11876 : Test that Alfresco CMIS 1.1 Implementation is returning aspect and aspect properties as extension data
     * @throws Exception
     */
    @Test
    public void testExtensionDataIsReturnedViaCmis1_1() throws Exception {
        AuthenticationUtil.pushAuthentication();
        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());

        final String FOLDER = "testExtensionDataIsReturnedViaCmis1_1-" + GUID.generate();
        final String CONTENT = FOLDER + "-file";

        try {
            transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<Void>() {
                        @Override
                        public Void execute() throws Throwable {
                            // create folder
                            FileInfo folderInfo = fileFolderService.create(repositoryHelper.getCompanyHome(),
                                    FOLDER, ContentModel.TYPE_FOLDER);
                            nodeService.setProperty(folderInfo.getNodeRef(), ContentModel.PROP_NAME, FOLDER);
                            assertNotNull(folderInfo);

                            // create document
                            FileInfo document = fileFolderService.create(folderInfo.getNodeRef(), CONTENT,
                                    ContentModel.TYPE_CONTENT);
                            assertNotNull(document);
                            nodeService.setProperty(document.getNodeRef(), ContentModel.PROP_NAME, CONTENT);

                            // apply aspect with properties
                            Map<QName, Serializable> props = new HashMap<QName, Serializable>();
                            props.put(ContentModel.PROP_LATITUDE, Double.valueOf(1.0d));
                            props.put(ContentModel.PROP_LONGITUDE, Double.valueOf(1.0d));
                            nodeService.addAspect(document.getNodeRef(), ContentModel.ASPECT_GEOGRAPHIC, props);

                            return null;
                        }
                    });

            final ObjectData objectData = withCmisService(new CmisServiceCallback<ObjectData>() {
                @Override
                public ObjectData execute(CmisService cmisService) {
                    List<RepositoryInfo> repositories = cmisService.getRepositoryInfos(null);
                    assertTrue(repositories.size() > 0);
                    RepositoryInfo repo = repositories.get(0);
                    String repositoryId = repo.getId();
                    // get object data 
                    ObjectData objectData = cmisService.getObjectByPath(repositoryId, "/" + FOLDER + "/" + CONTENT,
                            null, true, IncludeRelationships.NONE, null, false, true, null);
                    return objectData;
                }
            }, CmisVersion.CMIS_1_1);

            // get extension data from object properties
            List<CmisExtensionElement> extensions = objectData.getProperties().getExtensions().iterator().next()
                    .getChildren();

            Set<String> appliedAspects = new HashSet<String>();
            Set<String> aspectProperties = new HashSet<String>();

            for (CmisExtensionElement extension : extensions) {
                if (CMISConnector.PROPERTIES.equals(extension.getName())) {
                    // check properties extension
                    List<CmisExtensionElement> propExtensions = extension.getChildren();
                    assertTrue("cmisObject should contain aspect properties", propExtensions.size() > 0);
                    for (CmisExtensionElement prop : propExtensions) {
                        Map<String, String> cmisAspectProperty = prop.getAttributes();
                        Set<String> cmisAspectPropertyNames = cmisAspectProperty.keySet();
                        assertTrue("propertyDefinitionId attribute should be present",
                                cmisAspectPropertyNames.contains("propertyDefinitionId"));
                        aspectProperties.add(cmisAspectProperty.get("propertyDefinitionId"));
                    }
                } else if (CMISConnector.APPLIED_ASPECTS.equals(extension.getName())) {
                    appliedAspects.add(extension.getValue());
                }
            }

            // extension data should contain applied aspects and aspect properties
            assertTrue("Extensions should contain " + ContentModel.ASPECT_GEOGRAPHIC,
                    appliedAspects.contains("P:cm:geographic"));
            assertTrue("Extensions should contain " + ContentModel.PROP_LATITUDE,
                    aspectProperties.contains("cm:latitude"));
            assertTrue("Extensions should contain " + ContentModel.PROP_LONGITUDE,
                    aspectProperties.contains("cm:longitude"));
        } finally {
            AuthenticationUtil.popAuthentication();
        }
    }

    /**
     * MNT-10165: Check that all concomitant basic CMIS permissions are deleted
     * when permission is deleted vai CMIS 1.1 API. For Atom binding it applies
     * new set of permissions instead of deleting the old ones.
     */
    @Test
    public void testRemoveACL() throws Exception {
        AuthenticationUtil.pushAuthentication();
        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
        final String groupName = "group" + GUID.generate();
        final String testGroup = PermissionService.GROUP_PREFIX + groupName;
        try {
            // preconditions: create test document
            if (!authorityService.authorityExists(testGroup)) {
                authorityService.createAuthority(AuthorityType.GROUP, groupName);
            }

            final FileInfo document = transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<FileInfo>() {
                        @Override
                        public FileInfo execute() throws Throwable {
                            NodeRef companyHomeNodeRef = repositoryHelper.getCompanyHome();

                            String folderName = GUID.generate();
                            FileInfo folderInfo = fileFolderService.create(companyHomeNodeRef, folderName,
                                    ContentModel.TYPE_FOLDER);
                            nodeService.setProperty(folderInfo.getNodeRef(), ContentModel.PROP_NAME, folderName);
                            assertNotNull(folderInfo);

                            String docName = GUID.generate();
                            FileInfo document = fileFolderService.create(folderInfo.getNodeRef(), docName,
                                    ContentModel.TYPE_CONTENT);
                            assertNotNull(document);
                            nodeService.setProperty(document.getNodeRef(), ContentModel.PROP_NAME, docName);

                            return document;
                        }
                    });

            Set<AccessPermission> permissions = permissionService.getAllSetPermissions(document.getNodeRef());
            assertEquals(permissions.size(), 1);
            AccessPermission current = permissions.iterator().next();
            assertEquals(current.getAuthority(), "GROUP_EVERYONE");
            assertEquals(current.getPermission(), "Consumer");

            // add group1 with Coordinator permissions
            permissionService.setPermission(document.getNodeRef(), testGroup, PermissionService.COORDINATOR, true);
            permissions = permissionService.getAllSetPermissions(document.getNodeRef());

            Map<String, String> docPermissions = new HashMap<String, String>();
            for (AccessPermission permission : permissions) {
                docPermissions.put(permission.getAuthority(), permission.getPermission());
            }
            assertTrue(docPermissions.keySet().contains(testGroup));
            assertEquals(docPermissions.get(testGroup), PermissionService.COORDINATOR);

            // update permissions for group1 via CMIS 1.1 API 
            withCmisService(new CmisServiceCallback<Void>() {
                @Override
                public Void execute(CmisService cmisService) {
                    List<RepositoryInfo> repositories = cmisService.getRepositoryInfos(null);
                    assertNotNull(repositories);
                    assertTrue(repositories.size() > 0);
                    RepositoryInfo repo = repositories.iterator().next();
                    String repositoryId = repo.getId();
                    String docIdStr = document.getNodeRef().toString();

                    // when removing Coordinator ACE there are only inherited permissions
                    // so empty list of direct permissions is sent to be set
                    AccessControlListImpl acesToPut = new AccessControlListImpl();
                    List<Ace> acesList = Collections.emptyList();
                    acesToPut.setAces(acesList);
                    cmisService.applyAcl(repositoryId, docIdStr, acesToPut, AclPropagation.REPOSITORYDETERMINED);

                    return null;
                }
            }, CmisVersion.CMIS_1_1);

            // check that permissions are the same as they were before Coordinator was added
            permissions = permissionService.getAllSetPermissions(document.getNodeRef());
            docPermissions = new HashMap<String, String>();
            for (AccessPermission permission : permissions) {
                docPermissions.put(permission.getAuthority(), permission.getPermission());
            }
            assertFalse(docPermissions.keySet().contains(testGroup));
            assertEquals(permissions.size(), 1);
            current = permissions.iterator().next();
            assertEquals(current.getAuthority(), "GROUP_EVERYONE");
            assertEquals(current.getPermission(), "Consumer");
        } catch (CmisConstraintException e) {
            fail(e.toString());
        } finally {
            if (authorityService.authorityExists(testGroup)) {
                authorityService.deleteAuthority(testGroup);
            }
            AuthenticationUtil.popAuthentication();
        }
    }

    /**
     * ACE-2904
     */
    @Test
    public void testACE2904() { // Basic CMIS Types                                                               // Additional types from Content Model
        final String[] types = { "cmis:document", "cmis:relationship", "cmis:folder", "cmis:policy", "cmis:item",
                "R:cm:replaces", "P:cm:author", "I:cm:cmobject" };
        final String[] displayNames = { "Document", "Relationship", "Folder", "Policy", "Item Type", "Replaces",
                "Author", "Object" };
        final String[] descriptions = { "Document Type", "Relationship Type", "Folder Type", "Policy Type",
                "CMIS Item", "Replaces", "Author", "I:cm:cmobject" };

        CmisServiceCallback<String> callback = new CmisServiceCallback<String>() {
            @Override
            public String execute(CmisService cmisService) {
                List<RepositoryInfo> repositories = cmisService.getRepositoryInfos(null);
                assertTrue(repositories.size() > 0);
                RepositoryInfo repo = repositories.get(0);
                String repositoryId = repo.getId();

                for (int i = 0; i < types.length; i++) {
                    TypeDefinition def = cmisService.getTypeDefinition(repositoryId, types[i], null);
                    assertNotNull("The " + types[i] + " type is not defined", def);
                    assertNotNull("The display name is incorrect. Please, refer to ACE-2904.",
                            def.getDisplayName());
                    assertEquals("The display name is incorrect. Please, refer to ACE-2904.", def.getDisplayName(),
                            displayNames[i]);
                    assertEquals("The description is incorrect. Please, refer to ACE-2904.", def.getDescription(),
                            descriptions[i]);

                    for (PropertyDefinition<?> property : def.getPropertyDefinitions().values()) {
                        assertNotNull("Property definition dispaly name is incorrect. Please, refer to ACE-2904.",
                                property.getDisplayName());
                        assertNotNull("Property definition description is incorrect. Please, refer to ACE-2904.",
                                property.getDescription());
                    }
                }

                return "";
            };
        };

        // Lets test types for cmis 1.1 and cmis 1.0
        withCmisService(callback, CmisVersion.CMIS_1_1);
        withCmisService(callback, CmisVersion.CMIS_1_0);
    }

    /**
     * ACE-3322
     */
    @Test
    public void testACE3322() {
        final String[] types = { "cmis:document", "cmis:relationship", "cmis:folder", "cmis:item" };

        CmisServiceCallback<String> callback = new CmisServiceCallback<String>() {
            @Override
            public String execute(CmisService cmisService) {
                for (int i = 0; i < types.length; i++) {

                    List<TypeDefinitionWrapper> baseTypes = cmisDictionaryService.getBaseTypes();
                    assertNotNull(baseTypes);
                    checkDefs(baseTypes);

                    List<TypeDefinitionWrapper> children = cmisDictionaryService.getChildren(types[i]);
                    assertNotNull(children);

                    // Check that children were updated
                    checkDefs(children);
                }
                return "";
            };

            private void checkDefs(List<TypeDefinitionWrapper> defs) {
                for (TypeDefinitionWrapper def : defs) {
                    assertNotNull("Type definition was not updated. Please refer to ACE-3322",
                            def.getTypeDefinition(false).getDisplayName());
                    assertNotNull("Type definition was not updated. Please refer to ACE-3322",
                            def.getTypeDefinition(false).getDescription());

                    // Check that property's display name and description were updated
                    for (PropertyDefinitionWrapper property : def.getProperties()) {
                        assertNotNull("Display name is null", property.getPropertyDefinition().getDisplayName());
                        assertNotNull("Description is null", property.getPropertyDefinition().getDescription());
                    }
                }
            }
        };

        withCmisService(callback, CmisVersion.CMIS_1_1);
    }

    /**
     * Test to ensure auto version behavior for update properties, set and delete content using both Alfresco and CMIS perspectives.
     * Testing different combinations of <b>cm:initialVersion</b>, <b>cm:autoVersion</b> and <b>cm:autoVersionOnUpdateProps</b> properties 
     * <br>
     * OnUpdateProperties MINOR version should be created if <b>cm:initialVersion</b> and <b>cm:autoVersionOnUpdateProps</b> are both TRUE
     * <br>
     * OnContentUpdate MINOR version should be created if <b>cm:initialVersion</b> and <b>cm:autoVersion</b> are both TRUE
     * 
     * @throws Exception
     */
    @Test
    public void testUpdatePropertiesSetDeleteContentVersioning() throws Exception {
        AuthenticationUtil.pushAuthentication();
        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());

        final String FOLDER = "testUpdatePropertiesSetDeleteContentVersioning-" + GUID.generate();
        final String DOC1 = "documentProperties1-" + GUID.generate();
        final String DOC2 = "documentProperties2-" + GUID.generate();
        final String DOC3 = "documentProperties3-" + GUID.generate();
        final String DOC4 = "documentProperties4-" + GUID.generate();

        try {
            final List<FileInfo> docs = transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<List<FileInfo>>() {
                        @Override
                        public List<FileInfo> execute() throws Throwable {
                            // create folder
                            FileInfo folderInfo = fileFolderService.create(repositoryHelper.getCompanyHome(),
                                    FOLDER, ContentModel.TYPE_FOLDER);
                            nodeService.setProperty(folderInfo.getNodeRef(), ContentModel.PROP_NAME, FOLDER);
                            assertNotNull(folderInfo);

                            FileInfo document;
                            List<FileInfo> docs = new ArrayList<FileInfo>();
                            // create documents
                            document = fileFolderService.create(folderInfo.getNodeRef(), DOC1,
                                    ContentModel.TYPE_CONTENT);
                            nodeService.setProperty(document.getNodeRef(), ContentModel.PROP_NAME, DOC1);
                            docs.add(document);
                            document = fileFolderService.create(folderInfo.getNodeRef(), DOC2,
                                    ContentModel.TYPE_CONTENT);
                            nodeService.setProperty(document.getNodeRef(), ContentModel.PROP_NAME, DOC2);
                            docs.add(document);
                            document = fileFolderService.create(folderInfo.getNodeRef(), DOC3,
                                    ContentModel.TYPE_CONTENT);
                            nodeService.setProperty(document.getNodeRef(), ContentModel.PROP_NAME, DOC3);
                            docs.add(document);
                            document = fileFolderService.create(folderInfo.getNodeRef(), DOC4,
                                    ContentModel.TYPE_CONTENT);
                            nodeService.setProperty(document.getNodeRef(), ContentModel.PROP_NAME, DOC4);
                            docs.add(document);

                            Map<QName, Serializable> props = new HashMap<QName, Serializable>();
                            props.put(ContentModel.PROP_TITLE, "Initial Title");
                            props.put(ContentModel.PROP_DESCRIPTION, "Initial Description");

                            for (FileInfo fileInfo : docs) {
                                nodeService.addAspect(fileInfo.getNodeRef(), ContentModel.ASPECT_TITLED, props);
                            }

                            // apply versionable aspect with properties
                            props = new HashMap<QName, Serializable>();
                            // ContentModel.PROP_INITIAL_VERSION always true
                            props.put(ContentModel.PROP_INITIAL_VERSION, true);

                            props.put(ContentModel.PROP_AUTO_VERSION, false);
                            props.put(ContentModel.PROP_AUTO_VERSION_PROPS, false);
                            versionService.ensureVersioningEnabled(docs.get(0).getNodeRef(), props);

                            props.put(ContentModel.PROP_AUTO_VERSION, false);
                            props.put(ContentModel.PROP_AUTO_VERSION_PROPS, true);
                            versionService.ensureVersioningEnabled(docs.get(1).getNodeRef(), props);

                            props.put(ContentModel.PROP_AUTO_VERSION, true);
                            props.put(ContentModel.PROP_AUTO_VERSION_PROPS, false);
                            versionService.ensureVersioningEnabled(docs.get(2).getNodeRef(), props);

                            props.put(ContentModel.PROP_AUTO_VERSION, true);
                            props.put(ContentModel.PROP_AUTO_VERSION_PROPS, true);
                            versionService.ensureVersioningEnabled(docs.get(3).getNodeRef(), props);

                            return docs;
                        }
                    });

            assertVersions(docs.get(0).getNodeRef(), "1.0", VersionType.MAJOR);
            assertVersions(docs.get(1).getNodeRef(), "1.0", VersionType.MAJOR);
            assertVersions(docs.get(2).getNodeRef(), "1.0", VersionType.MAJOR);
            assertVersions(docs.get(3).getNodeRef(), "1.0", VersionType.MAJOR);

            // update node properties using Alfresco
            transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<List<Void>>() {
                        @Override
                        public List<Void> execute() throws Throwable {
                            for (FileInfo fileInfo : docs) {
                                Map<QName, Serializable> props = nodeService.getProperties(fileInfo.getNodeRef());

                                props.put(ContentModel.PROP_DESCRIPTION, "description-" + GUID.generate());
                                props.put(ContentModel.PROP_TITLE, "title-" + GUID.generate());

                                nodeService.setProperties(fileInfo.getNodeRef(), props);
                            }
                            return null;
                        }
                    });

            assertVersions(docs.get(0).getNodeRef(), "1.0", VersionType.MAJOR);
            assertVersions(docs.get(1).getNodeRef(), "1.1", VersionType.MINOR);
            assertVersions(docs.get(2).getNodeRef(), "1.0", VersionType.MAJOR);
            assertVersions(docs.get(3).getNodeRef(), "1.1", VersionType.MINOR);

            // update properties using CMIS perspective 
            final String repositoryId = withCmisService(new CmisServiceCallback<String>() {
                @Override
                public String execute(CmisService cmisService) {
                    String repositoryId = cmisService.getRepositoryInfos(null).get(0).getId();

                    for (FileInfo fileInfo : docs) {
                        PropertiesImpl properties = new PropertiesImpl();
                        properties.addProperty(
                                new PropertyStringImpl(PropertyIds.DESCRIPTION, "description-" + GUID.generate()));

                        cmisService.updateProperties(repositoryId,
                                new Holder<String>(fileInfo.getNodeRef().toString()), null, properties, null);
                    }

                    return repositoryId;
                }
            }, CmisVersion.CMIS_1_1);

            assertVersions(docs.get(0).getNodeRef(), "1.0", VersionType.MAJOR);
            assertVersions(docs.get(1).getNodeRef(), "1.2", VersionType.MINOR);
            assertVersions(docs.get(2).getNodeRef(), "1.0", VersionType.MAJOR);
            assertVersions(docs.get(3).getNodeRef(), "1.2", VersionType.MINOR);

            // CMIS setContentStream
            withCmisService(new CmisServiceCallback<Void>() {
                @Override
                public Void execute(CmisService cmisService) {
                    for (FileInfo fileInfo : docs) {
                        ContentStreamImpl contentStream = new ContentStreamImpl(null,
                                MimetypeMap.MIMETYPE_TEXT_PLAIN, "Content " + GUID.generate());

                        cmisService.setContentStream(repositoryId,
                                new Holder<String>(fileInfo.getNodeRef().toString()), true, null, contentStream,
                                null);
                    }
                    return null;
                }
            }, CmisVersion.CMIS_1_1);

            assertVersions(docs.get(0).getNodeRef(), "1.0", VersionType.MAJOR);
            assertVersions(docs.get(1).getNodeRef(), "1.2", VersionType.MINOR);
            assertVersions(docs.get(2).getNodeRef(), "1.1", VersionType.MINOR);
            assertVersions(docs.get(3).getNodeRef(), "1.3", VersionType.MINOR);

            // update content using Alfresco
            transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<List<Void>>() {
                        @Override
                        public List<Void> execute() throws Throwable {
                            for (FileInfo fileInfo : docs) {
                                ContentWriter writer = contentService.getWriter(fileInfo.getNodeRef(),
                                        ContentModel.PROP_CONTENT, true);
                                writer.putContent("Content " + GUID.generate());
                            }
                            return null;
                        }
                    });

            assertVersions(docs.get(0).getNodeRef(), "1.0", VersionType.MAJOR);
            assertVersions(docs.get(1).getNodeRef(), "1.2", VersionType.MINOR);
            assertVersions(docs.get(2).getNodeRef(), "1.2", VersionType.MINOR);
            assertVersions(docs.get(3).getNodeRef(), "1.4", VersionType.MINOR);

            // CMIS deleteContentStream
            withCmisService(new CmisServiceCallback<Void>() {
                @Override
                public Void execute(CmisService cmisService) {
                    for (FileInfo fileInfo : docs) {
                        cmisService.deleteContentStream(repositoryId,
                                new Holder<String>(fileInfo.getNodeRef().toString()), null, null);
                    }
                    return null;
                }
            }, CmisVersion.CMIS_1_1);

            assertVersions(docs.get(0).getNodeRef(), "1.0", VersionType.MAJOR);
            assertVersions(docs.get(1).getNodeRef(), "1.2", VersionType.MINOR);
            assertVersions(docs.get(2).getNodeRef(), "1.3", VersionType.MINOR);
            assertVersions(docs.get(3).getNodeRef(), "1.5", VersionType.MINOR);
        } finally {
            AuthenticationUtil.popAuthentication();
        }
    }

    private void assertVersions(final NodeRef nodeRef, final String expectedVersionLabel,
            final VersionType expectedVersionType) {
        transactionService.getRetryingTransactionHelper()
                .doInTransaction(new RetryingTransactionCallback<List<Void>>() {
                    @Override
                    public List<Void> execute() throws Throwable {
                        assertTrue("Node should be versionable",
                                nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE));

                        Version version = versionService.getCurrentVersion(nodeRef);

                        assertNotNull(version);
                        assertEquals(expectedVersionLabel, version.getVersionLabel());
                        assertEquals(expectedVersionType, version.getVersionType());

                        return null;
                    }
                });

        withCmisService(new CmisServiceCallback<Void>() {
            @Override
            public Void execute(CmisService cmisService) {
                String repositoryId = cmisService.getRepositoryInfos(null).get(0).getId();

                ObjectData data = cmisService.getObjectOfLatestVersion(repositoryId, nodeRef.toString(), null,
                        Boolean.FALSE, null, null, null, null, null, null, null);

                assertNotNull(data);

                PropertyData<?> prop = data.getProperties().getProperties().get(PropertyIds.VERSION_LABEL);
                Object versionLabelCmisValue = prop.getValues().get(0);

                assertEquals(expectedVersionLabel, versionLabelCmisValue);

                return null;
            }
        }, CmisVersion.CMIS_1_1);
    }

    /**
     * Test to ensure that set of aspect returned by cmisService#getAllVersions for latest version is the same 
     * as for the object returned by cmisService#getObjectByPath.
     * 
     * See <a href="https://issues.alfresco.com/jira/browse/MNT-9557">MNT-9557</a>
     */
    @Test
    public void testLastVersionOfVersionSeries() {
        CallContext context = new SimpleCallContext("admin", "admin", CmisVersion.CMIS_1_0);

        AuthenticationUtil.pushAuthentication();
        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());

        final String FOLDER = "testUpdatePropertiesSetDeleteContentVersioning-" + GUID.generate(),
                DOC = "documentProperties-" + GUID.generate();

        try {
            final NodeRef nodeRef = transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<NodeRef>() {
                        @Override
                        public NodeRef execute() throws Throwable {
                            // create folder
                            FileInfo folderInfo = fileFolderService.create(repositoryHelper.getCompanyHome(),
                                    FOLDER, ContentModel.TYPE_FOLDER);
                            nodeService.setProperty(folderInfo.getNodeRef(), ContentModel.PROP_NAME, FOLDER);
                            assertNotNull(folderInfo);

                            // create documents
                            FileInfo document = fileFolderService.create(folderInfo.getNodeRef(), DOC,
                                    ContentModel.TYPE_CONTENT);
                            nodeService.setProperty(document.getNodeRef(), ContentModel.PROP_NAME, DOC);
                            nodeService.setProperty(document.getNodeRef(), ContentModel.PROP_DESCRIPTION,
                                    "Initial doc");

                            return document.getNodeRef();
                        }
                    });

            transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<Void>() {
                        @Override
                        public Void execute() throws Throwable {
                            // make sure that there is no version history yet
                            assertNull(versionService.getVersionHistory(nodeRef));

                            // create a version
                            // turn off auto-versioning
                            Map<QName, Serializable> props = new HashMap<QName, Serializable>();
                            props.put(ContentModel.PROP_INITIAL_VERSION, false);
                            props.put(ContentModel.PROP_AUTO_VERSION, false);
                            props.put(ContentModel.PROP_AUTO_VERSION_PROPS, false);

                            versionService.ensureVersioningEnabled(nodeRef, props);

                            return null;
                        }
                    });

            transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<Void>() {
                        @Override
                        public Void execute() throws Throwable {
                            assertNotNull(versionService.getVersionHistory(nodeRef));
                            // create another one version
                            versionService.createVersion(nodeRef, null);

                            return null;
                        }
                    });

            final String NEW_DOC_NAME = transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<String>() {
                        @Override
                        public String execute() throws Throwable {
                            // add aspect to the node
                            String newDocName = DOC + GUID.generate();
                            nodeService.addAspect(nodeRef, ContentModel.ASPECT_AUTHOR, null);
                            nodeService.setProperty(nodeRef, ContentModel.PROP_NAME, newDocName);

                            return newDocName;
                        }
                    });

            CmisService cmisService = factory.getService(context);
            String repositoryId = cmisService.getRepositoryInfos(null).get(0).getId();

            List<ObjectData> versions = cmisService.getAllVersions(repositoryId, nodeRef.toString(), null, null,
                    null, null);
            assertNotNull(versions);
            // get the latest version
            ObjectData latestVersion = versions.get(0);
            // get the object
            ObjectData object =
                    // cmisService.getObjectOfLatestVersion(repositoryId, nodeRef.toString(), null, false, null, null, null, null, false, false, null);
                    cmisService.getObjectByPath(repositoryId, "/" + FOLDER + "/" + NEW_DOC_NAME, null, null, null,
                            null, false, false, null);

            assertNotNull(latestVersion);
            assertNotNull(object);

            Object objectDescriptionString = object.getProperties().getProperties().get("cmis:name").getValues()
                    .get(0);
            Object latestVersionDescriptionString = latestVersion.getProperties().getProperties().get("cmis:name")
                    .getValues().get(0);
            // ensure that node and latest version both have same description
            assertEquals(objectDescriptionString, latestVersionDescriptionString);

            Set<String> documentAspects = new HashSet<String>();
            for (CmisExtensionElement cmisEE : object.getProperties().getExtensions().get(0).getChildren()) {
                documentAspects.add(cmisEE.getValue());
            }
            Set<String> latestVersionAspects = new HashSet<String>();
            for (CmisExtensionElement cmisEE : latestVersion.getProperties().getExtensions().get(0).getChildren()) {
                latestVersionAspects.add(cmisEE.getValue());
            }
            // ensure that node and latest version both have the same set of aspects 
            assertEquals(latestVersionAspects, documentAspects);
        } finally {
            AuthenticationUtil.popAuthentication();
        }
    }

    /**
     * Test to ensure that versioning properties have default values defined in Alfresco content model.
     * Testing  <b>cm:initialVersion</b>, <b>cm:autoVersion</b> and <b>cm:autoVersionOnUpdateProps</b> properties 
     * 
     * @throws Exception
     */
    @Test
    public void testVersioningPropertiesHaveDefaultValue() throws Exception {
        AuthenticationUtil.pushAuthentication();
        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());

        try {
            // Create document via CMIS
            final NodeRef documentNodeRef = withCmisService(new CmisServiceCallback<NodeRef>() {
                @Override
                public NodeRef execute(CmisService cmisService) {
                    String repositoryId = cmisService.getRepositoryInfos(null).get(0).getId();

                    String rootNodeId = cmisService.getObjectByPath(repositoryId, "/", null, true,
                            IncludeRelationships.NONE, null, false, true, null).getId();

                    Collection<PropertyData<?>> propsList = new ArrayList<PropertyData<?>>();
                    propsList.add(new PropertyStringImpl(PropertyIds.NAME, "Folder-" + GUID.generate()));
                    propsList.add(new PropertyIdImpl(PropertyIds.OBJECT_TYPE_ID, "cmis:folder"));

                    String folderId = cmisService.createFolder(repositoryId, new PropertiesImpl(propsList),
                            rootNodeId, null, null, null, null);

                    propsList = new ArrayList<PropertyData<?>>();
                    propsList.add(new PropertyStringImpl(PropertyIds.NAME, "File-" + GUID.generate()));
                    propsList.add(new PropertyIdImpl(PropertyIds.OBJECT_TYPE_ID, "cmis:document"));

                    String nodeId = cmisService.createDocument(repositoryId, new PropertiesImpl(propsList),
                            folderId, null, null, null, null, null, null);

                    return new NodeRef(nodeId.substring(0, nodeId.indexOf(';')));
                }
            }, CmisVersion.CMIS_1_1);

            // check versioning properties
            transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<List<Void>>() {
                        @Override
                        public List<Void> execute() throws Throwable {
                            assertTrue(nodeService.exists(documentNodeRef));
                            assertTrue(nodeService.hasAspect(documentNodeRef, ContentModel.ASPECT_VERSIONABLE));

                            AspectDefinition ad = dictionaryService.getAspect(ContentModel.ASPECT_VERSIONABLE);
                            Map<QName, org.alfresco.service.cmr.dictionary.PropertyDefinition> properties = ad
                                    .getProperties();

                            for (QName qName : new QName[] { ContentModel.PROP_INITIAL_VERSION,
                                    ContentModel.PROP_AUTO_VERSION, ContentModel.PROP_AUTO_VERSION_PROPS }) {
                                Serializable property = nodeService.getProperty(documentNodeRef, qName);

                                assertNotNull(property);

                                org.alfresco.service.cmr.dictionary.PropertyDefinition pd = properties.get(qName);
                                assertNotNull(pd.getDefaultValue());

                                assertEquals(property, Boolean.parseBoolean(pd.getDefaultValue()));
                            }

                            return null;
                        }
                    });
        } finally {
            AuthenticationUtil.popAuthentication();
        }
    }

    /**
     * MNT-14951: Test that the list of parents can be retrieved for a folder.
     */
    @Test
    public void testCMISGetObjectParents() throws Exception {
        // setUp audit subsystem
        setupAudit();

        AuthenticationUtil.pushAuthentication();
        AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());

        try {
            final NodeRef folderWithTwoParents = transactionService.getRetryingTransactionHelper()
                    .doInTransaction(new RetryingTransactionCallback<NodeRef>() {
                        @Override
                        public NodeRef execute() throws Throwable {
                            NodeRef companyHomeNodeRef = repositoryHelper.getCompanyHome();

                            String folder1 = GUID.generate();
                            FileInfo folderInfo1 = fileFolderService.create(companyHomeNodeRef, folder1,
                                    ContentModel.TYPE_FOLDER);
                            assertNotNull(folderInfo1);

                            String folder2 = GUID.generate();
                            FileInfo folderInfo2 = fileFolderService.create(companyHomeNodeRef, folder2,
                                    ContentModel.TYPE_FOLDER);
                            assertNotNull(folderInfo2);

                            // Create folder11 as a subfolder of folder1
                            String folder11 = GUID.generate();
                            FileInfo folderInfo11 = fileFolderService.create(folderInfo1.getNodeRef(), folder11,
                                    ContentModel.TYPE_FOLDER);
                            assertNotNull(folderInfo11);

                            // Add folder2 as second parent for folder11
                            nodeService.addChild(folderInfo2.getNodeRef(), folderInfo11.getNodeRef(),
                                    ContentModel.ASSOC_CONTAINS, ContentModel.ASSOC_CONTAINS);

                            return folderInfo11.getNodeRef();
                        }
                    });

            withCmisService(new CmisServiceCallback<Void>() {
                @Override
                public Void execute(CmisService cmisService) {
                    List<RepositoryInfo> repositories = cmisService.getRepositoryInfos(null);
                    assertNotNull(repositories);
                    assertTrue(repositories.size() > 0);
                    String repositoryId = repositories.iterator().next().getId();

                    List<ObjectParentData> parents = cmisService.getObjectParents(repositoryId,
                            folderWithTwoParents.getId(), null, Boolean.FALSE, IncludeRelationships.NONE,
                            "cmis:none", Boolean.FALSE, null);
                    // Check if the second parent was also returned.
                    assertEquals(2, parents.size());

                    return null;
                }
            }, CmisVersion.CMIS_1_1);
        } finally {
            auditSubsystem.destroy();
            AuthenticationUtil.popAuthentication();
        }
    }
}