From 27e2d25a8b1bf0a54b6fe83fe56527d5d75b55ed Mon Sep 17 00:00:00 2001 From: Mark Rogers Date: Tue, 22 Jul 2014 12:52:57 +0000 Subject: [PATCH] Merged HEAD-BUG-FIX (5.0/Cloud) to HEAD (5.0/Cloud) 75520: Merged V4.2-BUG-FIX (4.2.3) to HEAD-BUG-FIX (5.0/Cloud) 75206: Merged DEV to V4.2-BUG-FIX (4.2.3) 74383 : MNT-11726 : CMIS change log isn't returning valid cmis:objectId values for deleted objects - Use context safe objectId generation in CMISChangeLogDataExtractor. Test for the fix 74484 : MNT-11726 : CMIS change log isn't returning valid cmis:objectId values for deleted objects - Test implemented in CMISTest - Changed CMISConnector.isFolder(NodeRef) logic. Can cause NPE if NodeRef is not FOLDER or FILE 75113 : MNT-11726 : CMIS change log isn't returning valid cmis:objectId values for deleted objects - alfresco-audit-cmis.xml moved to Repository project git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@77482 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/audit/alfresco-audit-cmis.xml | 67 +++++++++++++ config/alfresco/opencmis-context.xml | 2 + .../opencmis/CMISChangeLogDataExtractor.java | 16 ++- .../org/alfresco/opencmis/CMISConnector.java | 43 ++++++-- .../org/alfresco/opencmis/CMISTest.java | 97 +++++++++++++++++++ 5 files changed, 207 insertions(+), 18 deletions(-) create mode 100644 config/alfresco/audit/alfresco-audit-cmis.xml diff --git a/config/alfresco/audit/alfresco-audit-cmis.xml b/config/alfresco/audit/alfresco-audit-cmis.xml new file mode 100644 index 0000000000..030aa697fb --- /dev/null +++ b/config/alfresco/audit/alfresco-audit-cmis.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/config/alfresco/opencmis-context.xml b/config/alfresco/opencmis-context.xml index b5fc0ab44a..8561513ca9 100644 --- a/config/alfresco/opencmis-context.xml +++ b/config/alfresco/opencmis-context.xml @@ -208,11 +208,13 @@ + + \ No newline at end of file diff --git a/source/java/org/alfresco/opencmis/CMISChangeLogDataExtractor.java b/source/java/org/alfresco/opencmis/CMISChangeLogDataExtractor.java index e97919a38d..7b20fb7bc4 100644 --- a/source/java/org/alfresco/opencmis/CMISChangeLogDataExtractor.java +++ b/source/java/org/alfresco/opencmis/CMISChangeLogDataExtractor.java @@ -22,7 +22,6 @@ import java.io.Serializable; import java.util.HashMap; import org.alfresco.opencmis.dictionary.CMISDictionaryService; -import org.alfresco.opencmis.dictionary.CMISPropertyAccessor; import org.alfresco.opencmis.dictionary.TypeDefinitionWrapper; import org.alfresco.repo.audit.extractor.AbstractDataExtractor; import org.alfresco.service.cmr.model.FileInfo; @@ -30,7 +29,6 @@ import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.namespace.QName; -import org.apache.chemistry.opencmis.commons.PropertyIds; import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; /** @@ -46,6 +44,7 @@ public class CMISChangeLogDataExtractor extends AbstractDataExtractor private NodeService nodeService; private CMISDictionaryService cmisDictionaryService; + private CMISConnector cmisConnector; /** * Extracts relevant node refs and Ids from auditing data @@ -57,15 +56,10 @@ public class CMISChangeLogDataExtractor extends AbstractDataExtractor { NodeRef nodeRef = getNodeRef(value); - QName typeQName = nodeService.getType(nodeRef); - TypeDefinitionWrapper type = cmisDictionaryService.findNodeType(typeQName); - HashMap result = new HashMap(5); result.put(KEY_NODE_REF, nodeRef); - // Support version nodes by recording the object ID - CMISPropertyAccessor accessor = type.getPropertyById(PropertyIds.OBJECT_ID).getPropertyAccessor(); - result.put(KEY_OBJECT_ID, accessor.getValue(accessor.createNodeInfo(nodeRef))); - + result.put(KEY_OBJECT_ID, cmisConnector.createObjectId(nodeRef, true)); + return result; } @@ -125,4 +119,8 @@ public class CMISChangeLogDataExtractor extends AbstractDataExtractor { this.cmisDictionaryService = cmisDictionaryService; } + + public void setCmisConnector(CMISConnector cmisConnector) { + this.cmisConnector = cmisConnector; + } } diff --git a/source/java/org/alfresco/opencmis/CMISConnector.java b/source/java/org/alfresco/opencmis/CMISConnector.java index b844d68d1a..637e1b5223 100644 --- a/source/java/org/alfresco/opencmis/CMISConnector.java +++ b/source/java/org/alfresco/opencmis/CMISConnector.java @@ -1170,9 +1170,14 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen * be the assocRef guid. */ public String constructObjectId(AssociationRef assocRef, String versionLabel) + { + return constructObjectId(assocRef, versionLabel, isPublicApi()); + } + + public String constructObjectId(AssociationRef assocRef, String versionLabel, boolean dropStoreRef) { StringBuilder sb = new StringBuilder(CMISConnector.ASSOC_ID_PREFIX); - if(isPublicApi()) + if(dropStoreRef) { // always return the guid sb.append(assocRef.getId()); @@ -1204,9 +1209,14 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen * be the node guid. */ public String constructObjectId(String incomingNodeId, String versionLabel) + { + return constructObjectId(incomingNodeId, versionLabel, isPublicApi()); + } + + public String constructObjectId(String incomingNodeId, String versionLabel, boolean dropStoreRef) { StringBuilder sb = new StringBuilder(); - if(isPublicApi()) + if(dropStoreRef) { // always return the guid sb.append(getGuid(incomingNodeId)); @@ -1264,9 +1274,14 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen * be the incomingNodeRef guid. */ public String constructObjectId(NodeRef incomingNodeRef, String versionLabel) + { + return constructObjectId(incomingNodeRef, versionLabel, isPublicApi()); + } + + public String constructObjectId(NodeRef incomingNodeRef, String versionLabel, boolean dropStoreRef) { StringBuilder sb = new StringBuilder(); - sb.append(isPublicApi() ? incomingNodeRef.getId() : incomingNodeRef.toString()); + sb.append(dropStoreRef ? incomingNodeRef.getId() : incomingNodeRef.toString()); if(versionLabel != null) { sb.append(CMISConnector.ID_SEPERATOR); @@ -1278,29 +1293,39 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen /** * Compiles a CMIS object if for a live node. */ - public String createObjectId(NodeRef currentVersionNodeRef) + public String createObjectId(NodeRef nodeRef) { - QName typeQName = nodeService.getType(currentVersionNodeRef); + return createObjectId(nodeRef, isPublicApi()); + } + + public String createObjectId(NodeRef nodeRef, boolean dropStoreRef) + { + QName typeQName = nodeService.getType(nodeRef); TypeDefinitionWrapper type = getOpenCMISDictionaryService().findNodeType(typeQName); if(type instanceof ItemTypeDefinitionWrapper) { - return constructObjectId(currentVersionNodeRef, null); + return constructObjectId(nodeRef, null); } if(type instanceof FolderTypeDefintionWrapper) { - return constructObjectId(currentVersionNodeRef, null); + return constructObjectId(nodeRef, null, dropStoreRef); } Serializable versionLabel = getNodeService() - .getProperty(currentVersionNodeRef, ContentModel.PROP_VERSION_LABEL); + .getProperty(nodeRef, ContentModel.PROP_VERSION_LABEL); if (versionLabel == null) { versionLabel = CMISConnector.UNVERSIONED_VERSION_LABEL; } - return constructObjectId(currentVersionNodeRef, (String)versionLabel); + return constructObjectId(nodeRef, (String)versionLabel, dropStoreRef); + } + + private boolean isFolder(NodeRef nodeRef) + { + return getType(nodeRef) instanceof FolderTypeDefintionWrapper; } /** diff --git a/source/test-java/org/alfresco/opencmis/CMISTest.java b/source/test-java/org/alfresco/opencmis/CMISTest.java index 8a0e697566..5c980f3cef 100644 --- a/source/test-java/org/alfresco/opencmis/CMISTest.java +++ b/source/test-java/org/alfresco/opencmis/CMISTest.java @@ -38,12 +38,19 @@ import java.util.Map; import java.util.Set; import org.alfresco.cmis.CMISAccessControlService; +import org.alfresco.cmis.CMISChangeEvent; +import org.alfresco.cmis.CMISChangeLog; +import org.alfresco.cmis.CMISChangeLogService; import org.alfresco.cmis.CMISDictionaryModel; import org.alfresco.model.ContentModel; 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.AuditServiceImpl; +import org.alfresco.repo.audit.UserAuditFilter; +import org.alfresco.repo.audit.model.AuditModelRegistryImpl; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.domain.node.NodeDAO; import org.alfresco.repo.model.Repository; @@ -134,6 +141,8 @@ public class CMISTest private TaggingService taggingService; private NamespaceService namespaceService; private AuthorityService authorityService; + private AuditModelRegistryImpl auditSubsystem; + private CMISChangeLogService changeLogService; private PermissionService permissionService; private AlfrescoCmisServiceFactory factory; @@ -302,6 +311,8 @@ public class CMISTest this.cmisConnector = (CMISConnector) ctx.getBean("CMISConnector"); this.nodeDAO = (NodeDAO) ctx.getBean("nodeDAO"); this.authorityService = (AuthorityService)ctx.getBean("AuthorityService"); + this.changeLogService = (CMISChangeLogService) ctx.getBean("CMISChangeLogService"); + this.auditSubsystem = (AuditModelRegistryImpl) ctx.getBean("Audit"); this.permissionService = (PermissionService) ctx.getBean("permissionService"); } @@ -2149,4 +2160,90 @@ public class CMISTest 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 + { + transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + NodeRef companyHomeNodeRef = repositoryHelper.getCompanyHome(); + + String changeToken = changeLogService.getLastChangeLogToken(); + + // 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()); + + CMISChangeLog changeLogEvents = changeLogService.getChangeLogEvents(changeToken, 10); + // get all recent events + for (CMISChangeEvent event : changeLogEvents.getChangeEvents()) + { + String objectId = event.getObjectId(); + NodeRef nodeRef = event.getChangedNode(); + + assertFalse("CMISChangeEvent " + event.getChangeType() + " should store short form of objectId " + objectId, + objectId.contains(nodeRef.getStoreRef().toString())); + } + + return null; + } + }); + + } + 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 initAudit = new RetryingTransactionCallback() + { + 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); + } }