From c0c2d3ca39d7219e26d47c2e780c516145faeb11 Mon Sep 17 00:00:00 2001 From: Roy Wetherall Date: Mon, 25 Aug 2014 06:08:06 +0000 Subject: [PATCH] RM-1635: Recordable Version Store Service Prototype * integration tests to ensure recorded version retireves the state of the versioned content accurately * various improvements to satisfy tests git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/HEAD@81055 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../model/recordableVersionModel.xml | 4 + .../org_alfresco_module_rm/module-context.xml | 3 + .../rm-service-context.xml | 3 + .../rm-version-context.xml | 5 + .../security/rm-method-security.properties | 1 + .../hold/HoldServiceImpl.java | 8 +- .../record/RecordService.java | 23 ++ .../record/RecordServiceImpl.java | 35 ++- .../security/ExtendedSecurityServiceImpl.java | 12 +- .../FilePlanPermissionServiceImpl.java | 10 +- .../util/AuthenticationUtil.java | 81 +++++++ .../util/ServiceBaseImpl.java | 39 +--- .../version/RecordableVersionModel.java | 1 + .../RecordableVersionNodeServiceImpl.java | 172 +++++++++++++- .../version/RecordableVersionServiceImpl.java | 220 ++++++++++++++---- .../recordfolder/MoveRecordFolderTest.java | 6 +- .../version/AdHocRecordableVersions.java | 162 +++++++++---- .../version/AutoRecordableVersions.java | 70 ++++++ .../version/RecordableVersionsBaseTest.java | 212 +++++++++++++++++ .../integration/version/VersionTestSuite.java | 3 +- .../test/util/BaseRMTestCase.java | 35 ++- .../test/util/TestModel.java | 34 +++ rm-server/test/resources/test-model.xml | 7 +- ...FilePlanPermissionServiceImplUnitTest.java | 3 - .../test/util/BaseUnitTest.java | 81 ++++--- 25 files changed, 1043 insertions(+), 187 deletions(-) create mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/util/AuthenticationUtil.java create mode 100644 rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AutoRecordableVersions.java create mode 100644 rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/RecordableVersionsBaseTest.java create mode 100644 rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/TestModel.java diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/model/recordableVersionModel.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/model/recordableVersionModel.xml index f6fa3fb753..f0e810723e 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/model/recordableVersionModel.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/model/recordableVersionModel.xml @@ -70,6 +70,10 @@ Record Node Reference d:noderef + + Frozen Owner + d:text + diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml index 68681e812d..31cafb99c3 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml @@ -18,6 +18,9 @@ + + + diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml index 09c21d73e6..139752b62c 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml @@ -31,6 +31,7 @@ + @@ -1086,6 +1087,8 @@ org.alfresco.module.org_alfresco_module_rm.record.RecordService.disablePropertyEditableCheck=RM_ALLOW org.alfresco.module.org_alfresco_module_rm.record.RecordService.enablePropertyEditableCheck=RM_ALLOW org.alfresco.module.org_alfresco_module_rm.record.RecordService.getRecordMetaDataAspects=RM_ALLOW + org.alfresco.module.org_alfresco_module_rm.record.RecordService.isRecordMetadataAspect=RM_ALLOW + org.alfresco.module.org_alfresco_module_rm.record.RecordService.isRecordMetadataProperty=RM_ALLOW org.alfresco.module.org_alfresco_module_rm.record.RecordService.getRecordMetadataAspects=RM.Read.0 org.alfresco.module.org_alfresco_module_rm.record.RecordService.isRecord=ACL_ALLOW,RM_ALLOW org.alfresco.module.org_alfresco_module_rm.record.RecordService.isDeclared=RM.Read.0 diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-version-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-version-context.xml index b21756a61d..27f8a8f49a 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-version-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-version-context.xml @@ -16,6 +16,10 @@ + + + + @@ -24,6 +28,7 @@ + diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/security/rm-method-security.properties b/rm-server/config/alfresco/module/org_alfresco_module_rm/security/rm-method-security.properties index d282d207e2..916a69ca45 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/security/rm-method-security.properties +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/security/rm-method-security.properties @@ -50,6 +50,7 @@ rm.methodsecurity.org.alfresco.service.cmr.repository.NodeService.getChildAssocs rm.methodsecurity.org.alfresco.service.cmr.repository.NodeService.getNodeRef=RM.Read.0 rm.methodsecurity.org.alfresco.service.cmr.repository.NodeService.getChildAssocsByPropertyValue=RM.Read.0,AFTER_RM.FilterNode rm.methodsecurity.org.alfresco.service.cmr.repository.NodeService.countChildAssocs=RM.Read.0 +rm.methodsecurity.org.alfresco.service.cmr.repository.NodeService.getNodeAclId=RM.Read.0 rm.methodsecurity.org.alfresco.service.cmr.repository.NodeService.*=RM_DENY ## File Folder Service diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/hold/HoldServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/hold/HoldServiceImpl.java index 22ea92d531..6702abc7e0 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/hold/HoldServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/hold/HoldServiceImpl.java @@ -195,7 +195,7 @@ public class HoldServiceImpl extends ServiceBaseImpl }; // run as system user - runAsSystem(work); + authenticationUtil.runAsSystem(work); } } @@ -561,7 +561,7 @@ public class HoldServiceImpl extends ServiceBaseImpl if (!getHeld(hold).contains(nodeRef)) { // run as system to ensure we have all the appropriate permissions to perform the manipulations we require - runAsSystem(new RunAsWork() + authenticationUtil.runAsSystem(new RunAsWork() { @Override public Void doWork() @@ -684,7 +684,7 @@ public class HoldServiceImpl extends ServiceBaseImpl { // run as system so we don't run into further permission issues // we already know we have to have the correct capability to get here - runAsSystem(new RunAsWork() + authenticationUtil.runAsSystem(new RunAsWork() { @Override public Void doWork() @@ -703,7 +703,7 @@ public class HoldServiceImpl extends ServiceBaseImpl } // run as system as we can't be sure if have remove aspect rights on node - runAsSystem(new RunAsWork() + authenticationUtil.runAsSystem(new RunAsWork() { @Override public Void doWork() diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordService.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordService.java index 9c402deb4b..fd284c3e30 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordService.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordService.java @@ -79,6 +79,29 @@ public interface RecordService */ @Deprecated Set getRecordMetaDataAspects(); + + /** + * Indicates whether the provided aspect is a registered record meta-data + * aspect. + * + * @param aspect aspect {@link QName} + * @return boolean true if the aspect is a registered record meta-data aspect, false otherwise + * + * @since 2.3 + */ + boolean isRecordMetadataAspect(QName aspect); + + /** + * Indicates whther the provided property is declared on a registered record + * meta-data aspect. + * + * @param property property {@link QName} + * @return boolean true if the property is declared on a registered record meta-data aspect, + * false otherwise + * + * @since 2.3 + */ + boolean isRecordMetadataProperty(QName property); /** * Gets a list of all the record metadata aspects relevant to the file plan type of the diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java index e62419a5ae..4b7dc3702d 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java @@ -47,9 +47,11 @@ import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.module.org_alfresco_module_rm.model.security.ModelAccessDeniedException; import org.alfresco.module.org_alfresco_module_rm.notification.RecordsManagementNotificationHelper; import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderService; +import org.alfresco.module.org_alfresco_module_rm.report.ReportModel; import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService; import org.alfresco.module.org_alfresco_module_rm.role.Role; import org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService; +import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionModel; import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.policy.Behaviour.NotificationFrequency; import org.alfresco.repo.policy.ClassPolicyDelegate; @@ -136,10 +138,12 @@ public class RecordServiceImpl extends BaseBehaviourBean }; /** record model URI's */ - private static final String[] RECORD_MODEL_URIS = new String[] + public static final String[] RECORD_MODEL_URIS = new String[] { RM_URI, RM_CUSTOM_URI, + ReportModel.RMR_URI, + RecordableVersionModel.RMV_URI, DOD5015Model.DOD_URI }; @@ -682,6 +686,35 @@ public class RecordServiceImpl extends BaseBehaviourBean return getRecordMetadataAspects(TYPE_FILE_PLAN); } + /** + * @see org.alfresco.module.org_alfresco_module_rm.record.RecordService#isRecordMetadataAspect(org.alfresco.service.namespace.QName) + */ + @Override + public boolean isRecordMetadataAspect(QName aspect) + { + return getRecordMetadataAspectsMap().containsKey(aspect); + } + + /** + * @see org.alfresco.module.org_alfresco_module_rm.record.RecordService#isRecordMetadataProperty(org.alfresco.service.namespace.QName) + */ + @Override + public boolean isRecordMetadataProperty(QName property) + { + boolean result = false; + PropertyDefinition propertyDefinition = dictionaryService.getProperty(property); + if (propertyDefinition != null) + { + ClassDefinition classDefinition = propertyDefinition.getContainerClass(); + if (classDefinition != null && + getRecordMetadataAspectsMap().containsKey(classDefinition.getName())) + { + result = true; + } + } + return result; + } + /** * @see org.alfresco.module.org_alfresco_module_rm.record.RecordService#getRecordMetaDataAspects(org.alfresco.service.cmr.repository.NodeRef) */ diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/ExtendedSecurityServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/ExtendedSecurityServiceImpl.java index 61268ae7cb..099904fa9b 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/ExtendedSecurityServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/ExtendedSecurityServiceImpl.java @@ -20,6 +20,7 @@ package org.alfresco.module.org_alfresco_module_rm.security; import java.io.Serializable; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -31,6 +32,7 @@ import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService; import org.alfresco.module.org_alfresco_module_rm.util.ServiceBaseImpl; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; @@ -236,18 +238,24 @@ public class ExtendedSecurityServiceImpl extends ServiceBaseImpl // get the reference count Map referenceCountMap = (Map)nodeService.getProperty(filePlan, propertyName); + // set of assigned authorities + Set assignedAuthorities = new HashSet(authorities.size()); + for (String authority : authorities) { - if ((!authority.equals(PermissionService.ALL_AUTHORITIES) && !authority.equals(PermissionService.OWNER_AUTHORITY)) && + if ((!authority.equals(PermissionService.ALL_AUTHORITIES) && + !authority.equals(PermissionService.OWNER_AUTHORITY)) && + !AuthorityType.ROLE.equals(AuthorityType.getAuthorityType(authority)) && (referenceCountMap == null || !referenceCountMap.containsKey(authority))) { // add the authority to the role filePlanRoleService.assignRoleToAuthority(filePlan, roleName, authority); + assignedAuthorities.add(authority); } } // update the reference count - nodeService.setProperty(filePlan, propertyName, (Serializable)addToMap(referenceCountMap, authorities)); + nodeService.setProperty(filePlan, propertyName, (Serializable)addToMap(referenceCountMap, assignedAuthorities)); } } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/FilePlanPermissionServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/FilePlanPermissionServiceImpl.java index 61c36d5027..8e5d4734aa 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/FilePlanPermissionServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/security/FilePlanPermissionServiceImpl.java @@ -225,7 +225,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl if (nodeService.exists(parent)) { - runAsSystem(new AuthenticationUtil.RunAsWork() + authenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork() { public Object doWork() { @@ -300,7 +300,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl */ public void onAddRecord(final NodeRef record, final QName aspectTypeQName) { - runAsSystem(new AuthenticationUtil.RunAsWork() + authenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork() { public Object doWork() { @@ -375,7 +375,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl { if (nodeService.exists(nodeRef)) { - runAsSystem(new AuthenticationUtil.RunAsWork() + authenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork() { public Object doWork() { @@ -410,7 +410,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl ParameterCheck.mandatory("authority", authority); ParameterCheck.mandatory("permission", permission); - runAsSystem(new AuthenticationUtil.RunAsWork() + authenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork() { public Void doWork() { @@ -557,7 +557,7 @@ public class FilePlanPermissionServiceImpl extends ServiceBaseImpl */ public void deletePermission(final NodeRef nodeRef, final String authority, final String permission) { - runAsSystem(new AuthenticationUtil.RunAsWork() + authenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork() { public Void doWork() { diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/util/AuthenticationUtil.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/util/AuthenticationUtil.java new file mode 100644 index 0000000000..d40dd28420 --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/util/AuthenticationUtil.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.util; + +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; + +/** + * Helper bean to allow injection of AuthenticationUtil methods. + *

+ * Useful when testing using mocks. + * + * @author Roy Wetherall + * @since 2.3 + */ +public class AuthenticationUtil +{ + /** + * Helper method that executed work as system user. + *

+ * Useful when testing using mocks. + * + * @see org.alfresco.repo.security.authentication.AuthenticationUtil#runAsSystem(RunAsWork, String) + */ + public R runAsSystem(RunAsWork runAsWork) + { + return org.alfresco.repo.security.authentication.AuthenticationUtil.runAsSystem(runAsWork); + } + + /** + * Helper method that executed work as given user. + *

+ * Useful when testing using mocks. + * + * @see org.alfresco.repo.security.authentication.AuthenticationUtil#runAs(RunAsWork, String) + */ + public R runAs(RunAsWork runAsWork, String uid) + { + return org.alfresco.repo.security.authentication.AuthenticationUtil.runAs(runAsWork, uid); + } + + /** + * Helper method that gets the fully authenticated user. + *

+ * Useful when testing using mocks. + * + * @see org.alfresco.repo.security.authentication.AuthenticationUtil#getFullyAuthenticatedUser() + */ + public String getFullyAuthenticatedUser() + { + return org.alfresco.repo.security.authentication.AuthenticationUtil.getFullyAuthenticatedUser(); + } + + /** + * Helper method that gets the admin user name. + *

+ * Usefule when testing using mocks. + * + * @see org.alfresco.repo.security.authentication.AuthenticationUtil#getAdminUserName() + */ + public String getAdminUserName() + { + return org.alfresco.repo.security.authentication.AuthenticationUtil.getAdminUserName(); + } + +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/util/ServiceBaseImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/util/ServiceBaseImpl.java index 5492868b47..99d7bbea6d 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/util/ServiceBaseImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/util/ServiceBaseImpl.java @@ -26,8 +26,6 @@ import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanComponentKind import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService; import org.alfresco.module.org_alfresco_module_rm.hold.HoldService; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.transaction.TransactionalResourceHelper; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.ChildAssociationRef; @@ -58,6 +56,9 @@ public class ServiceBaseImpl implements RecordsManagementModel, ApplicationConte /** internal node service */ private NodeService internalNodeService; + + /** authentication helper */ + protected AuthenticationUtil authenticationUtil; /** * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) @@ -84,6 +85,14 @@ public class ServiceBaseImpl implements RecordsManagementModel, ApplicationConte this.dictionaryService = dictionaryService; } + /** + * @param authenticationUtil authentication util helper + */ + public void setAuthenticationUtil(AuthenticationUtil authenticationUtil) + { + this.authenticationUtil = authenticationUtil; + } + /** * Helper to get internal node service. *

@@ -470,30 +479,4 @@ public class ServiceBaseImpl implements RecordsManagementModel, ApplicationConte result.add(nodeService.getType(nodeRef)); return result; } - - /** - * Helper method that executed work as system user. - *

- * Useful when testing using mocks. - * - * @param runAsWork work to execute as system user - * @return - */ - public R runAsSystem(RunAsWork runAsWork) - { - return AuthenticationUtil.runAsSystem(runAsWork); - } - - /** - * Helper method that executed work as given user. - *

- * Useful when testing using mocks. - * - * @param runAsWork work to execute as given user - * @return - */ - public R runAs(RunAsWork runAsWork, String uid) - { - return AuthenticationUtil.runAs(runAsWork, uid); - } } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionModel.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionModel.java index 7f5f6389aa..85116b4805 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionModel.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionModel.java @@ -40,4 +40,5 @@ public interface RecordableVersionModel /** recorded version aspect */ public QName ASPECT_RECORDED_VERSION = QName.createQName(RMV_URI, "recordedVersion"); public QName PROP_RECORD_NODE_REF = QName.createQName(RMV_URI, "recordNodeRef"); + public QName PROP_FROZEN_OWNER = QName.createQName(RMV_URI, "frozenOwner"); } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionNodeServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionNodeServiceImpl.java index 9809bd12c3..9396a4b0d7 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionNodeServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionNodeServiceImpl.java @@ -19,17 +19,27 @@ package org.alfresco.module.org_alfresco_module_rm.version; import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.module.org_alfresco_module_rm.record.RecordService; +import org.alfresco.module.org_alfresco_module_rm.record.RecordServiceImpl; import org.alfresco.repo.version.Node2ServiceImpl; +import org.alfresco.repo.version.Version2Model; import org.alfresco.repo.version.common.VersionUtil; import org.alfresco.service.cmr.repository.InvalidNodeRefException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; +import org.apache.commons.lang.ArrayUtils; /** + * Extended version node service implementation that supports the retrieval of + * recorded version state. * * @author Roy Wetherall * @since 2.3 @@ -37,16 +47,31 @@ import org.alfresco.service.namespace.QName; public class RecordableVersionNodeServiceImpl extends Node2ServiceImpl implements RecordableVersionModel { - + /** record service */ + private RecordService recordService; + + /** + * @param recordService record service + */ + public void setRecordService(RecordService recordService) + { + this.recordService = recordService; + } + + /** + * @see org.alfresco.repo.version.Node2ServiceImpl#getProperties(org.alfresco.service.cmr.repository.NodeRef) + */ @Override public Map getProperties(NodeRef nodeRef) throws InvalidNodeRefException { + // TODO only supported for Version2 + NodeRef converted = VersionUtil.convertNodeRef(nodeRef); if (dbNodeService.hasAspect(converted, ASPECT_RECORDED_VERSION)) { NodeRef record = (NodeRef)dbNodeService.getProperty(converted, PROP_RECORD_NODE_REF); Map properties = dbNodeService.getProperties(record); - return processProperties(properties); + return processProperties(converted, properties); } else { @@ -54,18 +79,153 @@ public class RecordableVersionNodeServiceImpl extends Node2ServiceImpl } } - protected Map processProperties(Map properties) + /** + * Process properties map before returning as frozen state. + * + * @param properties properties map + * @return {@link Map}<{@link QName}, {@link Serializable}> processed property map + */ + protected Map processProperties(NodeRef version, Map properties) { + Map cloneProperties = new HashMap(properties); + // revert modified record name properties.put(ContentModel.PROP_NAME, properties.get(RecordsManagementModel.PROP_ORIGIONAL_NAME)); // remove all rma, rmc, rmr and rmv properties - - // remove any properties relating to custom record-meta data + for (QName property : cloneProperties.keySet()) + { + if (!PROP_RECORDABLE_VERSION_POLICY.equals(property) && + !PROP_FILE_PLAN.equals(property) && + (recordService.isRecordMetadataProperty(property) || + ArrayUtils.contains(RecordServiceImpl.RECORD_MODEL_URIS, property.getNamespaceURI()))) + { + properties.remove(property); + } + } // do standard property processing - VersionUtil.convertFrozenToOriginalProps(properties); + processVersionProperties(version, properties); return properties; } + + /** + * Process version properties. + * + * @param version version node reference + * @param properties properties map + */ + protected void processVersionProperties(NodeRef version, Map properties) throws InvalidNodeRefException + { + // get version properties + Map versionProperties = dbNodeService.getProperties(version); + + if (versionProperties != null) + { + String versionLabel = (String)versionProperties.get(Version2Model.PROP_QNAME_VERSION_LABEL); + properties.put(ContentModel.PROP_VERSION_LABEL, versionLabel); + + // Convert frozen sys:referenceable properties + NodeRef nodeRef = (NodeRef)versionProperties.get(Version2Model.PROP_QNAME_FROZEN_NODE_REF); + if (nodeRef != null) + { + properties.put(ContentModel.PROP_STORE_PROTOCOL, nodeRef.getStoreRef().getProtocol()); + properties.put(ContentModel.PROP_STORE_IDENTIFIER, nodeRef.getStoreRef().getIdentifier()); + properties.put(ContentModel.PROP_NODE_UUID, nodeRef.getId()); + } + + Long dbid = (Long)versionProperties.get(Version2Model.PROP_QNAME_FROZEN_NODE_DBID); + properties.put(ContentModel.PROP_NODE_DBID, dbid); + + // Convert frozen cm:auditable properties + String creator = (String)versionProperties.get(Version2Model.PROP_QNAME_FROZEN_CREATOR); + if (creator != null) + { + properties.put(ContentModel.PROP_CREATOR, creator); + } + + Date created = (Date)versionProperties.get(Version2Model.PROP_QNAME_FROZEN_CREATED); + if (created != null) + { + properties.put(ContentModel.PROP_CREATED, created); + } + + // TODO - check use-cases for get version, revert, restore .... + String modifier = (String)versionProperties.get(Version2Model.PROP_QNAME_FROZEN_MODIFIER); + if (modifier != null) + { + properties.put(ContentModel.PROP_MODIFIER, modifier); + } + + Date modified = (Date)versionProperties.get(Version2Model.PROP_QNAME_FROZEN_MODIFIED); + if (modified != null) + { + properties.put(ContentModel.PROP_MODIFIED, modified); + } + + Date accessed = (Date)versionProperties.get(Version2Model.PROP_QNAME_FROZEN_ACCESSED); + if (accessed != null) + { + properties.put(ContentModel.PROP_ACCESSED, accessed); + } + + String owner = (String)versionProperties.get(PROP_FROZEN_OWNER); + if (owner != null) + { + properties.put(ContentModel.PROP_OWNER, owner); + } + } + } + + /** + * @see org.alfresco.repo.version.Node2ServiceImpl#getAspects(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public Set getAspects(NodeRef nodeRef) throws InvalidNodeRefException + { + // TODO only supported for Version2 + + NodeRef converted = VersionUtil.convertNodeRef(nodeRef); + if (dbNodeService.hasAspect(converted, ASPECT_RECORDED_VERSION)) + { + NodeRef record = (NodeRef)dbNodeService.getProperty(converted, PROP_RECORD_NODE_REF); + Set aspects = dbNodeService.getAspects(record); + return processAspects(aspects); + } + else + { + return super.getAspects(nodeRef); + } + } + + /** + * Process frozen aspects. + * + * @param aspects aspect set + * @return {@link Set}<{@link QName}> processed aspect set + */ + protected Set processAspects(Set aspects) + { + Set result = new HashSet(aspects); + + // remove version aspects + result.remove(ASPECT_VERSION); + result.remove(ASPECT_RECORDED_VERSION); + + // remove rm aspects + for (QName aspect : aspects) + { + if (!ASPECT_VERSIONABLE.equals(aspect) && + (recordService.isRecordMetadataAspect(aspect) || + ArrayUtils.contains(RecordServiceImpl.RECORD_MODEL_URIS, aspect.getNamespaceURI()))) + { + result.remove(aspect); + } + } + + // remove custom record meta-data aspects + + return result; + } } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionServiceImpl.java index 42860a9d01..cce5e0ba99 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/version/RecordableVersionServiceImpl.java @@ -21,20 +21,29 @@ package org.alfresco.module.org_alfresco_module_rm.version; import java.io.Serializable; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService; +import org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService; +import org.alfresco.module.org_alfresco_module_rm.util.AuthenticationUtil; import org.alfresco.repo.policy.PolicyScope; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.security.permissions.impl.ExtendedPermissionService; import org.alfresco.repo.version.Version2Model; import org.alfresco.repo.version.Version2ServiceImpl; import org.alfresco.repo.version.VersionModel; import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.model.FileNotFoundException; +import org.alfresco.service.cmr.repository.AssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.security.OwnableService; import org.alfresco.service.cmr.version.ReservedVersionNameException; import org.alfresco.service.cmr.version.Version; import org.alfresco.service.cmr.version.VersionType; @@ -64,6 +73,18 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl /** file folder service */ protected FileFolderService fileFolderService; + /** extended permission service */ + protected ExtendedPermissionService extendedPermissionService; + + /** ownable service */ + protected OwnableService ownableService; + + /** extended security service */ + protected ExtendedSecurityService extendedSecurityService; + + /** authentication util helper */ + protected AuthenticationUtil authenticationUtil; + /** * @param filePlanService file plan service */ @@ -80,6 +101,38 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl this.fileFolderService = fileFolderService; } + /** + * @param extendedPermissionService extended permission service + */ + public void setExtendedPermissionService(ExtendedPermissionService extendedPermissionService) + { + this.extendedPermissionService = extendedPermissionService; + } + + /** + * @param ownableService ownable service + */ + public void setOwnableService(OwnableService ownableService) + { + this.ownableService = ownableService; + } + + /** + * @param extendedSecurityService extended security service + */ + public void setExtendedSecurityService(ExtendedSecurityService extendedSecurityService) + { + this.extendedSecurityService = extendedSecurityService; + } + + /** + * @param authenticationUtil authentication util helper + */ + public void setAuthenticationUtil(AuthenticationUtil authenticationUtil) + { + this.authenticationUtil = authenticationUtil; + } + /** * @see org.alfresco.repo.version.Version2ServiceImpl#createVersion(org.alfresco.service.cmr.repository.NodeRef, java.util.Map, int) */ @@ -128,9 +181,8 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl } /** - * - * @param nodeRef - * @return + * @param nodeRef node reference + * @return {@link NodeRef} associated file plan, default if none */ private NodeRef getFilePlan(NodeRef nodeRef) { @@ -143,8 +195,7 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl } /** - * - * @return + * @return {@link NodeRef} default file plan, exception if none */ private NodeRef getFilePlan() { @@ -212,6 +263,7 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl } /** + * Creates a new recorded version * * @param sourceTypeRef * @param versionHistoryRef @@ -240,61 +292,35 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl try { // get the destination file plan - NodeRef filePlan = (NodeRef)versionProperties.get(KEY_FILE_PLAN); + final NodeRef filePlan = (NodeRef)versionProperties.get(KEY_FILE_PLAN); if (filePlan == null) { throw new AlfrescoRuntimeException("Can't create a new recorded version, because no file plan has been specified in the version properties."); } - - // get the unfiled record folder - final NodeRef unfiledRecordFolder = filePlanService.getUnfiledContainer(filePlan); - + // create a copy of the source node and place in the file plan final NodeRef nodeRef = (NodeRef)standardVersionProperties.get(Version2Model.PROP_QNAME_FROZEN_NODE_REF); - - // copy version state and create record - NodeRef record = null; - try - { - FileInfo recordInfo = fileFolderService.copy(nodeRef, unfiledRecordFolder, null); - record = recordInfo.getNodeRef(); - } - catch (FileNotFoundException e) - { - throw new AlfrescoRuntimeException("Can't create recorded version, because copy fails.", e); - } - - // set up extended permissions - // TODO + + // create record + NodeRef record = createRecord(nodeRef, filePlan); // create version nodeRef - ChildAssociationRef childAssocRef = this.dbNodeService.createNode( + ChildAssociationRef childAssocRef = dbNodeService.createNode( versionHistoryRef, Version2Model.CHILD_QNAME_VERSIONS, QName.createQName(Version2Model.NAMESPACE_URI, Version2Model.CHILD_VERSIONS + "-" + versionNumber), // TODO - testing - note: all children (of a versioned node) will have the same version number, maybe replace with a version sequence of some sort 001-...00n sourceTypeRef, null); - versionNodeRef = childAssocRef.getChildRef(); - - // NOTE: special ML case - see also MultilingualContentServiceImpl.makeMLContainer -// if (sourceTypeRef.equals(ContentModel.TYPE_MULTILINGUAL_CONTAINER)) -// { -// // Set the permissions to allow anything by anyone -// permissionService.setPermission( -// versionNodeRef, -// PermissionService.ALL_AUTHORITIES, -// PermissionService.ALL_PERMISSIONS, true); -// permissionService.setPermission( -// versionNodeRef, -// AuthenticationUtil.getGuestUserName(), -// PermissionService.ALL_PERMISSIONS, true); -// } + versionNodeRef = childAssocRef.getChildRef(); // add aspect with the standard version properties to the 'version' node nodeService.addAspect(versionNodeRef, Version2Model.ASPECT_VERSION, standardVersionProperties); // add the recordedVersion aspect with link to record nodeService.addAspect(versionNodeRef, ASPECT_RECORDED_VERSION, Collections.singletonMap(PROP_RECORD_NODE_REF, (Serializable)record)); + + // freeze auditable aspect information + freezeAuditableAspect(nodeRef, versionNodeRef); } finally { @@ -318,6 +344,118 @@ public class RecordableVersionServiceImpl extends Version2ServiceImpl return versionNodeRef; } + /** + * Create record from current version + * + * @param nodeRef state to freeze + * @param filePlan destination file plan + * @return {@link NodeRef} versioned record + */ + private NodeRef createRecord(final NodeRef nodeRef, final NodeRef filePlan) + { + return authenticationUtil.runAs(new RunAsWork() + { + public NodeRef doWork() throws Exception + { + // get the unfiled record folder + final NodeRef unfiledRecordFolder = filePlanService.getUnfiledContainer(filePlan); + + // get the documents readers + Long aclId = dbNodeService.getNodeAclId(nodeRef); + Set readers = extendedPermissionService.getReaders(aclId); + Set writers = extendedPermissionService.getWriters(aclId); + + // add the current owner to the list of extended writers + Set modifiedWrtiers = new HashSet(writers); + if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_OWNABLE)) + { + String owner = ownableService.getOwner(nodeRef); + if (owner != null && !owner.isEmpty() && !owner.equals(OwnableService.NO_OWNER)) + { + modifiedWrtiers.add(owner); + } + } + + // add the current user as extended writer + modifiedWrtiers.add(authenticationUtil.getFullyAuthenticatedUser()); + + // copy version state and create record + NodeRef record = null; + try + { + List originalAssocs = null; + if (dbNodeService.hasAspect(nodeRef, ContentModel.ASPECT_COPIEDFROM)) + { + // take a note of any copyFrom information already on the node + originalAssocs = dbNodeService.getTargetAssocs(nodeRef, ContentModel.ASSOC_ORIGINAL); + } + + // create a copy of the original state and add it to the unfiled record container + FileInfo recordInfo = fileFolderService.copy(nodeRef, unfiledRecordFolder, null); + record = recordInfo.getNodeRef(); + + // remove added copy assocs + List recordAssocs = dbNodeService.getTargetAssocs(record, ContentModel.ASSOC_ORIGINAL); + for (AssociationRef recordAssoc : recordAssocs) + { + dbNodeService.removeAssociation( + recordAssoc.getSourceRef(), + recordAssoc.getTargetRef(), + ContentModel.ASSOC_ORIGINAL); + } + + // re-add origional assocs or remove aspect + if (originalAssocs == null) + { + dbNodeService.removeAspect(record, ContentModel.ASPECT_COPIEDFROM); + } + else + { + for (AssociationRef originalAssoc : originalAssocs) + { + dbNodeService.createAssociation(originalAssoc.getSourceRef(), originalAssoc.getTargetRef(), ContentModel.ASSOC_ORIGINAL); + } + } + } + catch (FileNotFoundException e) + { + throw new AlfrescoRuntimeException("Can't create recorded version, because copy fails.", e); + } + + // set extended security on record + extendedSecurityService.addExtendedSecurity(record, readers, writers); + + return record; + } + }, authenticationUtil.getAdminUserName()); + } + + /** + * Freezes audit aspect properties. + * + * @param nodeRef + * @param versionNodeRef + */ + private void freezeAuditableAspect(NodeRef nodeRef, NodeRef versionNodeRef) + { + if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_AUDITABLE)) + { + Map properties = dbNodeService.getProperties(nodeRef); + dbNodeService.setProperty(versionNodeRef, Version2Model.PROP_QNAME_FROZEN_CREATOR, properties.get(ContentModel.PROP_CREATOR)); + dbNodeService.setProperty(versionNodeRef, Version2Model.PROP_QNAME_FROZEN_CREATED, properties.get(ContentModel.PROP_CREATED)); + dbNodeService.setProperty(versionNodeRef, Version2Model.PROP_QNAME_FROZEN_MODIFIER, properties.get(ContentModel.PROP_MODIFIER)); + dbNodeService.setProperty(versionNodeRef, Version2Model.PROP_QNAME_FROZEN_MODIFIED, properties.get(ContentModel.PROP_MODIFIED)); + dbNodeService.setProperty(versionNodeRef, Version2Model.PROP_QNAME_FROZEN_ACCESSED, properties.get(ContentModel.PROP_ACCESSED)); + if (properties.get(ContentModel.PROP_OWNER) != null) + { + dbNodeService.setProperty(versionNodeRef, PROP_FROZEN_OWNER, properties.get(ContentModel.PROP_OWNER)); + } + } + } + + /** + * @see org.alfresco.repo.version.Version2ServiceImpl#getVersion(org.alfresco.service.cmr.repository.NodeRef) + */ @Override protected Version getVersion(NodeRef versionRef) { diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/recordfolder/MoveRecordFolderTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/recordfolder/MoveRecordFolderTest.java index e0f383a5bf..2cac3c292d 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/recordfolder/MoveRecordFolderTest.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/recordfolder/MoveRecordFolderTest.java @@ -59,7 +59,7 @@ public class MoveRecordFolderTest extends BaseRMTestCase */ public void testMoveRecordFolderBeforeCutOffFolderLevelDisposition() throws Exception { - doBehaviourDrivenTest(new BehaviourDrivenTest(false) + doBehaviourDrivenTest(new BehaviourDrivenTest(null, false) { NodeRef recordFolder; NodeRef destinationRecordCategory; @@ -141,7 +141,7 @@ public class MoveRecordFolderTest extends BaseRMTestCase */ public void testMoveRecordFolderBeforeCutOffIntoAFolderWithNoDisposition() throws Exception { - doBehaviourDrivenTest(new BehaviourDrivenTest(false) + doBehaviourDrivenTest(new BehaviourDrivenTest(null, false) { NodeRef recordFolder; NodeRef destinationRecordCategory; @@ -217,7 +217,7 @@ public class MoveRecordFolderTest extends BaseRMTestCase */ public void testMoveRecordFolderWithRecordsBeforeCutOffRecordLevelDisposition() throws Exception { - doBehaviourDrivenTest(new BehaviourDrivenTest(false) + doBehaviourDrivenTest(new BehaviourDrivenTest(null, false) { NodeRef record; NodeRef recordFolder; diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AdHocRecordableVersions.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AdHocRecordableVersions.java index b7a0df85c1..e04fa328b9 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AdHocRecordableVersions.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AdHocRecordableVersions.java @@ -22,93 +22,153 @@ import java.io.Serializable; import java.util.HashMap; import java.util.Map; -import org.alfresco.model.ContentModel; -import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; -import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionModel; +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.module.org_alfresco_module_rm.test.util.TestModel; import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionServiceImpl; import org.alfresco.repo.version.VersionModel; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.version.Version; import org.alfresco.service.cmr.version.VersionType; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.service.namespace.QName; -import org.alfresco.util.PropertyMap; /** - * - * + * AdHoc Recordable Versions Integration Test + * * @author Roy Wetherall * @since 2.3 */ -public class AdHocRecordableVersions extends BaseRMTestCase implements RecordableVersionModel +public class AdHocRecordableVersions extends RecordableVersionsBaseTest { - private static final QName QNAME_PUBLISHER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "publisher"); - private static final QName QNAME_SUBJECT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "subject"); - - private static final String DESCRIPTION = "description"; - private static final String PUBLISHER = "publisher"; - private static final String SUBJECT = "subject"; - - @Override - protected boolean isCollaborationSiteTest() - { - return true; - } - + /** + * Adhoc recorded version creation, with no policy defined as site collaborator + */ public void testRecordAdHocVersionNoPolicy() { - doBehaviourDrivenTest(new BehaviourDrivenTest() + doBehaviourDrivenTest(new BehaviourDrivenTest(dmCollaborator) { - private Version version; - private Map versionProperties; + private Map versionProperties; public void given() throws Exception { - // add Dublin core aspect - PropertyMap dublinCoreProperties = new PropertyMap(2); - dublinCoreProperties.put(QNAME_PUBLISHER, PUBLISHER); - dublinCoreProperties.put(QNAME_SUBJECT, SUBJECT); - nodeService.addAspect(documentLibrary, ContentModel.ASPECT_DUBLINCORE, dublinCoreProperties); - // setup version properties versionProperties = new HashMap(4); versionProperties.put(Version.PROP_DESCRIPTION, DESCRIPTION); versionProperties.put(VersionModel.PROP_VERSION_TYPE, VersionType.MINOR); versionProperties.put(RecordableVersionServiceImpl.KEY_RECORDABLE_VERSION, true); versionProperties.put(RecordableVersionServiceImpl.KEY_FILE_PLAN, filePlan); - } public void when() { // create version - version = versionService.createVersion(dmDocument, versionProperties); + versionService.createVersion(dmDocument, versionProperties); } public void then() { - // version has been created - assertNotNull(version); + // check that the record has been recorded + checkRecordedVersion(dmDocument, DESCRIPTION, "0.1"); + } + }); + } + + /** + * Adhoc recordable version with recordable set as false + */ + public void testRecordableVersionFalseNoPolicy() + { + doBehaviourDrivenTest(new BehaviourDrivenTest(dmCollaborator) + { + private Map versionProperties; + + public void given() throws Exception + { + // setup version properties + versionProperties = new HashMap(4); + versionProperties.put(Version.PROP_DESCRIPTION, DESCRIPTION); + versionProperties.put(VersionModel.PROP_VERSION_TYPE, VersionType.MINOR); + versionProperties.put(RecordableVersionServiceImpl.KEY_RECORDABLE_VERSION, false); + versionProperties.put(RecordableVersionServiceImpl.KEY_FILE_PLAN, filePlan); + } + + public void when() + { + // create version + versionService.createVersion(dmDocument, versionProperties); + } + + public void then() + { + // check that the record has been recorded + checkNotRecordedAspect(dmDocument, DESCRIPTION, "0.1"); + } + }); + } + + /** + * Test no file plan specified (and no default available) + */ + public void testNoFilePlan() + { + doBehaviourDrivenTest(new BehaviourDrivenTest(AlfrescoRuntimeException.class, dmCollaborator) + { + private Map versionProperties; + + public void given() throws Exception + { + // setup version properties + versionProperties = new HashMap(4); + versionProperties.put(Version.PROP_DESCRIPTION, DESCRIPTION); + versionProperties.put(VersionModel.PROP_VERSION_TYPE, VersionType.MINOR); + versionProperties.put(RecordableVersionServiceImpl.KEY_RECORDABLE_VERSION, true); + } + + public void when() + { + // create version + versionService.createVersion(dmDocument, versionProperties); + } + + }); + } + + /** + * Test recorded version with record metadata aspect (want to ensure additional non-rm URI properties and aspects + * don't find their way into the frozen state) + */ + public void testRecordedVersionWithRecordMetadataAspect() + { + doBehaviourDrivenTest(new BehaviourDrivenTest(dmCollaborator) + { + private Map versionProperties; + + public void given() throws Exception + { + // setup version properties + versionProperties = new HashMap(4); + versionProperties.put(Version.PROP_DESCRIPTION, DESCRIPTION); + versionProperties.put(VersionModel.PROP_VERSION_TYPE, VersionType.MINOR); + versionProperties.put(RecordableVersionServiceImpl.KEY_RECORDABLE_VERSION, true); + versionProperties.put(RecordableVersionServiceImpl.KEY_FILE_PLAN, filePlan); + } + + public void when() + { + // create version + Version version = versionService.createVersion(dmDocument, versionProperties); - // check the version properties - assertEquals(DESCRIPTION, version.getDescription()); - assertEquals("0.1", version.getVersionLabel()); - - assertEquals(NAME_DM_DOCUMENT, nodeService.getProperty(dmDocument, ContentModel.PROP_NAME)); - - NodeRef frozen = version.getFrozenStateNodeRef(); - assertEquals(NAME_DM_DOCUMENT, nodeService.getProperty(frozen, ContentModel.PROP_NAME)); - - // record version node reference is available on version + // add custom meta-data to record NodeRef record = (NodeRef)version.getVersionProperties().get("RecordVersion"); assertNotNull(record); - - // record version is an unfiled record - assertTrue(recordService.isRecord(record)); - assertFalse(recordService.isFiled(record)); + recordService.addRecordType(record, TestModel.ASPECT_RECORD_METADATA); + nodeService.setProperty(record, TestModel.PROPERTY_RECORD_METADATA, "Peter Wetherall"); + } + + public void then() + { + // check that the record has been recorded + checkRecordedVersion(dmDocument, DESCRIPTION, "0.1"); } - }); - + }); } } diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AutoRecordableVersions.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AutoRecordableVersions.java new file mode 100644 index 0000000000..7ee66178ed --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/AutoRecordableVersions.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.integration.version; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionModel; +import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionPolicy; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.util.PropertyMap; + +/** + * Auto Recordable Versions Integration Test + * + * @author Roy Wetherall + * @since 2.3 + */ +public class AutoRecordableVersions extends RecordableVersionsBaseTest +{ + public final static String MY_NEW_CONTENT = "this is some new content that I have changed to trigger auto version"; + + public void testAutoVersionRecordAllRevisions() + { + doBehaviourDrivenTest(new BehaviourDrivenTest(dmCollaborator) + { + public void given() throws Exception + { + // make the node versionable + PropertyMap versionableProperties = new PropertyMap(1); + versionableProperties.put(ContentModel.PROP_INITIAL_VERSION, false); + nodeService.addAspect(dmDocument, ContentModel.ASPECT_VERSIONABLE, versionableProperties); + + // set the recordable version policy + PropertyMap recordableVersionProperties = new PropertyMap(1); + recordableVersionProperties.put(PROP_RECORDABLE_VERSION_POLICY, RecordableVersionPolicy.ALL); + recordableVersionProperties.put(PROP_FILE_PLAN, filePlan); + nodeService.addAspect(dmDocument, RecordableVersionModel.ASPECT_VERSIONABLE, recordableVersionProperties); + } + + public void when() + { + // generate new version by updating content + ContentWriter writer = contentService.getWriter(dmDocument, ContentModel.PROP_CONTENT, true); + writer.putContent(MY_NEW_CONTENT); + } + + public void then() + { + // check that the record has been recorded + checkRecordedVersion(dmDocument, null, "0.2"); + } + }); + } + +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/RecordableVersionsBaseTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/RecordableVersionsBaseTest.java new file mode 100644 index 0000000000..a7dd545955 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/RecordableVersionsBaseTest.java @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.integration.version; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase; +import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionModel; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.version.Version; +import org.alfresco.service.cmr.version.VersionHistory; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.PropertyMap; + +/** + * @author Roy Wetherall + * @since 2.3 + */ +public abstract class RecordableVersionsBaseTest extends BaseRMTestCase implements RecordableVersionModel +{ + protected static final QName QNAME_PUBLISHER = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "publisher"); + protected static final QName QNAME_SUBJECT = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "subject"); + + protected static final String DESCRIPTION = "description"; + protected static final String PUBLISHER = "publisher"; + protected static final String SUBJECT = "subject"; + protected static final String OWNER = "Grace Wetherall"; + + protected static final String CONTENT = + "Simple + Smart. A smarter way to build, a smarter way to deploy. Its simple because we focus on the end " + + "user and smart because we support more open standards than any other ECM platform, while delivering all " + + "the value a traditional platform provides."; + + @Override + protected boolean isCollaborationSiteTest() + { + return true; + } + + @Override + protected void setupCollaborationSiteTestDataImpl() + { + super.setupCollaborationSiteTestDataImpl(); + + // add titled aspect + PropertyMap titledProperties = new PropertyMap(2); + titledProperties.put(ContentModel.PROP_TITLE, "document title"); + titledProperties.put(ContentModel.PROP_DESCRIPTION, "document description"); + nodeService.addAspect(dmDocument, ContentModel.ASPECT_TITLED, titledProperties); + + // add ownable aspect + PropertyMap ownableProperties = new PropertyMap(1); + ownableProperties.put(ContentModel.PROP_OWNER, OWNER); + nodeService.addAspect(dmDocument, ContentModel.ASPECT_OWNABLE, ownableProperties); + + // add Dublin core aspect + PropertyMap dublinCoreProperties = new PropertyMap(2); + dublinCoreProperties.put(QNAME_PUBLISHER, PUBLISHER); + dublinCoreProperties.put(QNAME_SUBJECT, SUBJECT); + nodeService.addAspect(dmDocument, ContentModel.ASPECT_DUBLINCORE, dublinCoreProperties); + + // add content + ContentWriter writer = contentService.getWriter(dmDocument, ContentModel.PROP_CONTENT, true); + writer.setEncoding("UTF-8"); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.putContent(CONTENT); + } + + /** + * Helper to check that the current version is recorded + */ + protected void checkRecordedVersion(NodeRef document, String description, String versionLabel) + { + // double check that the document is not a record + assertFalse(recordService.isRecord(document)); + + // store document state + Map beforeProperties = nodeService.getProperties(document); + Set beforeAspects = nodeService.getAspects(dmDocument); + + // get the current version + Version version = versionService.getCurrentVersion(document); + + // version has been created + assertNotNull(version); + + // check the version properties + assertEquals(description, version.getDescription()); + assertEquals(versionLabel, version.getVersionLabel()); + + // get the frozen state + NodeRef frozen = version.getFrozenStateNodeRef(); + + // check the properties + checkProperties(frozen, beforeProperties); + + // compare aspects + checkAspects(frozen, beforeAspects); + + // record version node reference is available on version + NodeRef record = (NodeRef)version.getVersionProperties().get("RecordVersion"); + assertNotNull(record); + + // record version is an unfiled record + assertTrue(recordService.isRecord(record)); + assertFalse(recordService.isFiled(record)); + + // check the version history + VersionHistory versionHistory = versionService.getVersionHistory(document); + assertNotNull(versionHistory); + Version headVersion = versionHistory.getHeadVersion(); + assertNotNull(headVersion); + } + + protected void checkNotRecordedAspect(NodeRef document, String description, String versionLabel) + { + // double check that the document is not a record + assertFalse(recordService.isRecord(document)); + + // get the current version + Version version = versionService.getCurrentVersion(document); + + // version has been created + assertNotNull(version); + + // check the version properties + assertEquals(description, version.getDescription()); + assertEquals(versionLabel, version.getVersionLabel()); + + // record version node reference is available on version + NodeRef record = (NodeRef)version.getVersionProperties().get("RecordVersion"); + assertNull(record); + + // check the version history + VersionHistory versionHistory = versionService.getVersionHistory(document); + assertNotNull(versionHistory); + Version headVersion = versionHistory.getHeadVersion(); + assertNotNull(headVersion); + } + + /** + * Helper to check the properties of a recorded version + */ + protected void checkProperties(NodeRef frozen, Map beforeProperies) + { + Map frozenProperties = nodeService.getProperties(frozen); + Map cloneFrozenProperties = new HashMap(frozenProperties); + for (Map.Entry entry : beforeProperies.entrySet()) + { + if (frozenProperties.containsKey(entry.getKey())) + { + Serializable frozenValue = frozenProperties.get(entry.getKey()); + assertEquals("Frozen property " + entry.getKey().getLocalName() + " value is incorrect.", entry.getValue(), frozenValue); + cloneFrozenProperties.remove(entry.getKey()); + } + else + { + fail("Property missing from frozen state .. " + entry.getKey()); + } + } + + // frozen properties should be empty + assertTrue("Properties in frozen state, but not in origional. " + cloneFrozenProperties.keySet(), cloneFrozenProperties.isEmpty()); + } + + /** + * Helper to check the aspects of a recorded version + */ + protected void checkAspects(NodeRef frozen, Set beforeAspects) + { + Set cloneBeforeAspects = new HashSet(beforeAspects); + + // compare origional and frozen aspects + Set frozenAspects = nodeService.getAspects(frozen); + cloneBeforeAspects.removeAll(frozenAspects); + if (!cloneBeforeAspects.isEmpty()) + { + fail("Aspects not present in frozen state. " + cloneBeforeAspects.toString()); + } + + frozenAspects.removeAll(beforeAspects); + if (!frozenAspects.isEmpty()) + { + fail("Aspects in the frozen state, but not in origional. " + frozenAspects.toString()); + } + } + +} diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/VersionTestSuite.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/VersionTestSuite.java index 18cf965658..2f4a360ae5 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/VersionTestSuite.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/version/VersionTestSuite.java @@ -31,7 +31,8 @@ import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) @SuiteClasses( { - AdHocRecordableVersions.class + AdHocRecordableVersions.class, + AutoRecordableVersions.class }) public class VersionTestSuite { diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java index 5f8906475b..0aa92eb510 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java @@ -823,10 +823,18 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase */ protected abstract class BehaviourDrivenTest { + /** run in transaction */ protected boolean runInTransactionTests = true; + + /** run as user */ + protected String runAsUser = AuthenticationUtil.getAdminUserName(); + /** expected exception */ protected Class expectedException; + /** + * Default constructor + */ public BehaviourDrivenTest() { } @@ -835,10 +843,25 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase { this.expectedException = expectedException; } + + public BehaviourDrivenTest(Class expectedException, String runAsUser) + { + this.expectedException = expectedException; + this.runAsUser = runAsUser; + } - public BehaviourDrivenTest(boolean runInTransactionTests) + public BehaviourDrivenTest(String runAsUser) + { + this.runAsUser = runAsUser; + } + + public BehaviourDrivenTest(String runAsUser, boolean runInTransactionTests) { this.runInTransactionTests = runInTransactionTests; + if (runAsUser != null) + { + this.runAsUser = runAsUser; + } } public void given() throws Exception { /** empty implementation */ } @@ -862,7 +885,7 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase { given(); } - }); + }, runAsUser); } else { @@ -880,7 +903,7 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase { when(); } - }); + }, runAsUser); doTestInTransaction(new VoidTest() { @@ -889,7 +912,7 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase { then(); } - }); + }, runAsUser); } else { @@ -906,7 +929,7 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase { when(); } - }); + }, runAsUser); } } finally @@ -920,7 +943,7 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase { after(); } - }); + }, runAsUser); } else { diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/TestModel.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/TestModel.java new file mode 100644 index 0000000000..31b2690545 --- /dev/null +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/TestModel.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.test.util; + +import org.alfresco.service.namespace.QName; + +/** + * @author Roy Wetherall + * @since 2.3 + */ +public interface TestModel +{ + public static final String TEST_URI = "http://www.alfresco.org/model/rmtest/1.0"; + public static final String TEST_PREFIX = "rmt"; + + public static final QName ASPECT_RECORD_METADATA = QName.createQName(TEST_URI, "recordMetaData"); + public static final QName PROPERTY_RECORD_METADATA = QName.createQName(TEST_URI, "recordMetaDataProperty"); +} diff --git a/rm-server/test/resources/test-model.xml b/rm-server/test/resources/test-model.xml index 6007fd32ae..2f7b137c3a 100644 --- a/rm-server/test/resources/test-model.xml +++ b/rm-server/test/resources/test-model.xml @@ -47,7 +47,12 @@ - + + + + d:text + + diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/security/FilePlanPermissionServiceImplUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/security/FilePlanPermissionServiceImplUnitTest.java index 3935ebb7c3..430999961f 100644 --- a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/security/FilePlanPermissionServiceImplUnitTest.java +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/security/FilePlanPermissionServiceImplUnitTest.java @@ -87,9 +87,6 @@ public class FilePlanPermissionServiceImplUnitTest extends BaseUnitTest { super.before(); - // mock up run as methods - mockRunAsMethods(filePlanPermissionService); - // initialize node's unfiledRecordContainer = generateContainerNodeRef(TYPE_UNFILED_RECORD_CONTAINER); unfiledRecordFolder = generateContainerNodeRef(TYPE_UNFILED_RECORD_FOLDER); diff --git a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseUnitTest.java b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseUnitTest.java index fe20b2ebc6..cd9e175aaa 100644 --- a/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseUnitTest.java +++ b/rm-server/unit-test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseUnitTest.java @@ -41,10 +41,12 @@ import org.alfresco.module.org_alfresco_module_rm.record.RecordService; import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderService; import org.alfresco.module.org_alfresco_module_rm.report.ReportService; import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService; -import org.alfresco.module.org_alfresco_module_rm.util.ServiceBaseImpl; +import org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService; +import org.alfresco.module.org_alfresco_module_rm.util.AuthenticationUtil; import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.security.permissions.impl.ExtendedPermissionService; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.dictionary.DictionaryService; @@ -114,6 +116,9 @@ public class BaseUnitTest implements RecordsManagementModel, ContentModel @Mock(name="filePlanRoleService") protected FilePlanRoleService mockedFilePlanRoleService; @Mock(name="recordsManagementAuditService") protected RecordsManagementAuditService mockedRecordsManagementAuditService; @Mock(name="policyBehaviourFilter") protected BehaviourFilter mockedBehaviourFilter; + @Mock(name="authenticationUtil") protected AuthenticationUtil mockedAuthenticationUtil; + @Mock(name="extendedPermissionService") protected ExtendedPermissionService mockedExtendedPermissionService; + @Mock(name="extendedSecurityService") protected ExtendedSecurityService mockedExtendedSecurityService; /** application context mock */ @Mock(name="applicationContext") protected ApplicationContext mockedApplicationContext; @@ -146,6 +151,9 @@ public class BaseUnitTest implements RecordsManagementModel, ContentModel } }; doAnswer(doInTransactionAnswer).when(mockedRetryingTransactionHelper).doInTransaction(any(RetryingTransactionCallback.class)); + + // setup mocked authentication util + setupAuthenticationUtilMock(); // setup file plan filePlan = generateNodeRef(TYPE_FILE_PLAN); @@ -173,6 +181,43 @@ public class BaseUnitTest implements RecordsManagementModel, ContentModel doReturn(Collections.singletonList(record)).when(mockedRecordService).getRecords(recordFolder); } + /** + * Setup authentication util mock + */ + @SuppressWarnings("unchecked") + private void setupAuthenticationUtilMock() + { + // just do the work + doAnswer(new Answer() + { + @SuppressWarnings("rawtypes") + @Override + public Object answer(InvocationOnMock invocation) throws Throwable + { + RunAsWork work = (RunAsWork)invocation.getArguments()[0]; + return work.doWork(); + } + + }).when(mockedAuthenticationUtil).runAsSystem(any(RunAsWork.class)); + + // just do the work + doAnswer(new Answer() + { + @SuppressWarnings("rawtypes") + @Override + public Object answer(InvocationOnMock invocation) throws Throwable + { + RunAsWork work = (RunAsWork)invocation.getArguments()[0]; + return work.doWork(); + } + + }).when(mockedAuthenticationUtil).runAs(any(RunAsWork.class), anyString()); + + // assume admin + doReturn("admin").when(mockedAuthenticationUtil).getAdminUserName(); + doReturn("admin").when(mockedAuthenticationUtil).getFullyAuthenticatedUser(); + } + /** * Helper method to generate a qname. * @@ -349,40 +394,6 @@ public class BaseUnitTest implements RecordsManagementModel, ContentModel doReturn(assocs).when(mockedNodeService).getChildAssocs(parent, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); } - /** - * Helper method to mock up calls to 'run as' methods - * on base service implementation. - * - * @param service - */ - @SuppressWarnings("unchecked") - protected void mockRunAsMethods(ServiceBaseImpl service) - { - doAnswer(new Answer() - { - @SuppressWarnings("rawtypes") - @Override - public Object answer(InvocationOnMock invocation) throws Throwable - { - RunAsWork work = (RunAsWork)invocation.getArguments()[0]; - return work.doWork(); - } - - }).when(service).runAsSystem(any(RunAsWork.class)); - - doAnswer(new Answer() - { - @SuppressWarnings("rawtypes") - @Override - public Object answer(InvocationOnMock invocation) throws Throwable - { - RunAsWork work = (RunAsWork)invocation.getArguments()[0]; - return work.doWork(); - } - - }).when(service).runAs(any(RunAsWork.class), anyString()); - } - @SuppressWarnings("unchecked") protected List buildList(T ... values) {