From 595b2c44db74b5ba00db9e19e487b8fa201c5841 Mon Sep 17 00:00:00 2001 From: tiagosalvado10 <9038083+tiagosalvado10@users.noreply.github.com> Date: Wed, 16 Sep 2020 11:38:10 +0100 Subject: [PATCH] Merge pull request #1220 from Alfresco/hotfix-3.2/MNT-21818_fix_tests [MNT-21818] Created records management root cache to avoid performing the same query multiple times (cherry picked from commit 2538c12a92c6d7b25a8853c6e597941c09b59018) --- .../rm-model-context.xml | 2 + .../rm-service-context.xml | 9 + .../fileplan/FilePlanService.java | 2 +- .../fileplan/FilePlanServiceImpl.java | 49 ++++-- .../type/RecordsManagementContainerType.java | 52 +++++- .../model/rma/type/RmSiteType.java | 61 ++++++- .../util/RMContainerCacheManager.java | 162 ++++++++++++++++++ .../test/util/BaseRMTestCase.java | 14 +- 8 files changed, 333 insertions(+), 18 deletions(-) create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/util/RMContainerCacheManager.java diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml index 9119c2f01c..e3010b1eb5 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-model-context.xml @@ -71,6 +71,7 @@ + @@ -123,6 +124,7 @@ + diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml index 3b5c7eee06..0b1b335fef 100644 --- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml +++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml @@ -389,6 +389,14 @@ + + + + + + + + @@ -397,6 +405,7 @@ parent="baseService" class="org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanServiceImpl"> + diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/fileplan/FilePlanService.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/fileplan/FilePlanService.java index f8b784d342..41bb9576b8 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/fileplan/FilePlanService.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/fileplan/FilePlanService.java @@ -346,4 +346,4 @@ public interface FilePlanService */ NodeRef createRecordCategory(NodeRef parent, String name, Map properties); -} +} \ No newline at end of file diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/fileplan/FilePlanServiceImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/fileplan/FilePlanServiceImpl.java index 1194805168..a700b9dce9 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/fileplan/FilePlanServiceImpl.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/fileplan/FilePlanServiceImpl.java @@ -42,6 +42,7 @@ import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService; +import org.alfresco.module.org_alfresco_module_rm.util.RMContainerCacheManager; import org.alfresco.module.org_alfresco_module_rm.util.ServiceBaseImpl; import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.domain.node.NodeDAO; @@ -101,6 +102,9 @@ public class FilePlanServiceImpl extends ServiceBaseImpl /** Site service */ private SiteService siteService; + /** RM container cache manager **/ + private RMContainerCacheManager rmContainerCacheManager; + /** * Gets the file plan role service * @@ -174,6 +178,15 @@ public class FilePlanServiceImpl extends ServiceBaseImpl this.rootContainerCache = rootContainerCache; } + /** + * @param rmContainerCacheManager RM container cache manager + * + */ + public void setRmContainerCacheManager(RMContainerCacheManager rmContainerCacheManager) + { + this.rmContainerCacheManager = rmContainerCacheManager; + } + /** * @see org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService#getFilePlans(org.alfresco.service.cmr.repository.StoreRef) */ @@ -185,20 +198,30 @@ public class FilePlanServiceImpl extends ServiceBaseImpl final Set results = new HashSet<>(); Set aspects = new HashSet<>(1); aspects.add(ASPECT_RECORDS_MANAGEMENT_ROOT); - getNodeDAO().getNodesWithAspects(aspects, Long.MIN_VALUE, Long.MAX_VALUE, new NodeDAO.NodeRefQueryCallback() - { - @Override - public boolean handle(Pair nodePair) - { - NodeRef nodeRef = nodePair.getSecond(); - if (storeRef.equals(nodeRef.getStoreRef())) - { - results.add(nodeRef); - } - return true; - } - }); + if (!rmContainerCacheManager.isCached(storeRef)) + { + getNodeDAO().getNodesWithAspects(aspects, Long.MIN_VALUE, Long.MAX_VALUE, new NodeDAO.NodeRefQueryCallback() + { + @Override + public boolean handle(Pair nodePair) + { + NodeRef nodeRef = nodePair.getSecond(); + if (storeRef.equals(nodeRef.getStoreRef())) + { + results.add(nodeRef); + rmContainerCacheManager.add(nodeRef); + } + + return true; + } + }); + } + else + { + return rmContainerCacheManager.get(storeRef); + } + return results; } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RecordsManagementContainerType.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RecordsManagementContainerType.java index 72a7894901..582c6ec1d6 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RecordsManagementContainerType.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RecordsManagementContainerType.java @@ -33,6 +33,7 @@ import org.alfresco.module.org_alfresco_module_rm.model.BaseBehaviourBean; 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.recordfolder.RecordFolderService; +import org.alfresco.module.org_alfresco_module_rm.util.RMContainerCacheManager; import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.policy.Behaviour.NotificationFrequency; import org.alfresco.repo.policy.annotation.Behaviour; @@ -55,7 +56,8 @@ import org.alfresco.service.namespace.QName; defaultType = "rma:recordsManagementContainer" ) public class RecordsManagementContainerType extends BaseBehaviourBean - implements NodeServicePolicies.OnCreateChildAssociationPolicy + implements NodeServicePolicies.OnCreateChildAssociationPolicy, + NodeServicePolicies.OnDeleteChildAssociationPolicy { /** behaviour name */ private static final String BEHAVIOUR_NAME = "onCreateContainerType"; @@ -69,9 +71,21 @@ public class RecordsManagementContainerType extends BaseBehaviourBean /** record folder service */ protected RecordFolderService recordFolderService; + /** RM container cache manager **/ + private RMContainerCacheManager rmContainerCacheManager; + /** I18N */ private static final String MSG_CANNOT_CAST_TO_RM_TYPE = "rm.action.cast-to-rm-type"; + /** + * @param rmContainerCacheManager RM container cache manager + * + */ + public void setRmContainerCacheManager(RMContainerCacheManager rmContainerCacheManager) + { + this.rmContainerCacheManager = rmContainerCacheManager; + } + /** * @param identifierService identifier service */ @@ -194,12 +208,48 @@ public class RecordsManagementContainerType extends BaseBehaviourBean setIdenifierProperty(child); } } + + if (rmContainerCacheManager != null) + { + rmContainerCacheManager.add(child); + } } return null; } }); + } + /** + * Attempts to remove a deleted node from records management root cache + * + * @see org.alfresco.repo.node.NodeServicePolicies.OnDeleteAssociationPolicy#onDeleteAssociation(org.alfresco.service.cmr.repository.AssociationRef) + */ + @Override + @Behaviour + ( + kind = BehaviourKind.ASSOCIATION, + notificationFrequency = NotificationFrequency.TRANSACTION_COMMIT + ) + public void onDeleteChildAssociation(ChildAssociationRef childAssocRef) + { + + AuthenticationUtil.runAsSystem(new RunAsWork() + { + @Override + public Void doWork() + { + // Get the elements of the deleted association + final NodeRef child = childAssocRef.getChildRef(); + + if (rmContainerCacheManager != null) + { + rmContainerCacheManager.remove(child); + } + + return null; + } + }); } /** diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RmSiteType.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RmSiteType.java index 76b31f5250..c9f6e22ef4 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RmSiteType.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/model/rma/type/RmSiteType.java @@ -39,6 +39,7 @@ import org.alfresco.model.ContentModel; import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService; import org.alfresco.module.org_alfresco_module_rm.model.BaseBehaviourBean; import org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchService; +import org.alfresco.module.org_alfresco_module_rm.util.RMContainerCacheManager; import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.policy.Behaviour.NotificationFrequency; import org.alfresco.repo.policy.annotation.Behaviour; @@ -73,7 +74,8 @@ public class RmSiteType extends BaseBehaviourBean implements NodeServicePolicies.OnCreateNodePolicy, NodeServicePolicies.OnUpdatePropertiesPolicy, NodeServicePolicies.BeforeDeleteNodePolicy, - NodeServicePolicies.OnCreateChildAssociationPolicy + NodeServicePolicies.OnCreateChildAssociationPolicy, + NodeServicePolicies.OnDeleteChildAssociationPolicy { /** Constant values */ public static final String COMPONENT_DOCUMENT_LIBRARY = "documentLibrary"; @@ -95,6 +97,9 @@ public class RmSiteType extends BaseBehaviourBean private FilePlanType filePlanType; + /** RM container cache manager **/ + private RMContainerCacheManager rmContainerCacheManager; + /** Map of file plan type's key'ed by corresponding site types */ protected Map mapFilePlanType = new HashMap<>(3); @@ -105,7 +110,7 @@ public class RmSiteType extends BaseBehaviourBean public void setSiteService(SiteService siteService) { this.siteService = siteService; - } + } /** * @param recordsManagementSearchService records management search service @@ -136,6 +141,15 @@ public class RmSiteType extends BaseBehaviourBean this.filePlanType = filePlanType; } + /** + * @param rmContainerCacheManager RM container cache manager + * + */ + public void setRmContainerCacheManager(RMContainerCacheManager rmContainerCacheManager) + { + this.rmContainerCacheManager = rmContainerCacheManager; + } + /** * Registers a file plan type for a specific site type. * @@ -310,6 +324,36 @@ public class RmSiteType extends BaseBehaviourBean } } + /** + * Handles site deletion in order to reset the records management root cache + * + * @param childAssocRef + * + * @see org.alfresco.repo.node.NodeServicePolicies.OnDeleteAssociationPolicy#onDeleteAssociation(org.alfresco.service.cmr.repository.AssociationRef) + */ + @Override + @Behaviour + ( + kind = BehaviourKind.ASSOCIATION + ) + public void onDeleteChildAssociation(ChildAssociationRef childAssocRef) + { + AuthenticationUtil.runAsSystem(new RunAsWork() + { + @Override + public Void doWork() + { + // Resets RM Container Cache Manager + if (rmContainerCacheManager != null) + { + rmContainerCacheManager.reset(); + } + + return null; + } + }); + } + /** * Add the limitation of creating only one rma:filePlan or one dod:filePlan depending on the type of rm site. * Let multiple cm:folder type be created under rm site. @@ -339,6 +383,13 @@ public class RmSiteType extends BaseBehaviourBean }); } + /** + * Handles the deletion node policy (alf:onDeleteNode), resetting the records management root cache + * and enabling file plan behavior as well + * + * @param childAssocRef + * @param isNodeArchived + */ @Behaviour ( kind = BehaviourKind.CLASS, @@ -347,6 +398,12 @@ public class RmSiteType extends BaseBehaviourBean ) public void onDeleteNodeOnCommit(ChildAssociationRef childAssocRef, boolean isNodeArchived) { + // Resets RM Container Cache Manager + if (rmContainerCacheManager != null) + { + rmContainerCacheManager.reset(); + } + filePlanType.enable(); } } diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/util/RMContainerCacheManager.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/util/RMContainerCacheManager.java new file mode 100644 index 0000000000..2517198e02 --- /dev/null +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/util/RMContainerCacheManager.java @@ -0,0 +1,162 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2020 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 . + * #L% + */ +package org.alfresco.module.org_alfresco_module_rm.util; + +import java.util.HashSet; +import java.util.Set; + +import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; +import org.alfresco.repo.cache.SimpleCache; +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.namespace.QName; +import org.alfresco.util.Pair; + +/** + * Provides operations to manipulate the records management root cache + * + * @author Tiago Salvado + * + * @see RecordsManagementModel + */ +public class RMContainerCacheManager implements RecordsManagementModel +{ + /** node service */ + private NodeService nodeService; + + /** root records management cache */ + private SimpleCache, Set> cache; + + /** + * @param nodeService + * node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @param cache + */ + public void setCache(SimpleCache, Set> cache) + { + this.cache = cache; + } + + /** + * Verifies if there is cached nodes for supplied storeRef + * + * @param storeRef + * @return true if there are cached nodes, false otherwise + */ + public boolean isCached(StoreRef storeRef) + { + return cache.contains(getKey(storeRef)); + } + + /** + * Obtains the cached nodes for supplied storeRef + * + * @param storeRef + * @return a set containing the cached nodes + */ + public Set get(StoreRef storeRef) + { + return cache.get(getKey(storeRef)); + } + + /** + * Caches the supplied node + * + * @param nodeRef + */ + public void add(NodeRef nodeRef) + { + if (nodeRef != null && nodeService.hasAspect(nodeRef, ASPECT_RECORDS_MANAGEMENT_ROOT)) + { + Set entries; + Pair key = getKey(nodeRef.getStoreRef()); + + if (cache.contains(key)) + { + entries = this.cache.get(key); + } + else + { + entries = new HashSet<>(); + } + + if (!entries.contains(nodeRef)) + { + entries.add(nodeRef); + } + + cache.put(key, entries); + } + } + + /** + * Removes the supplied entry from the cache + * + * @param nodeRef + */ + public void remove(NodeRef nodeRef) + { + if (nodeRef != null) + { + if (nodeService.hasAspect(nodeRef, ASPECT_RECORDS_MANAGEMENT_ROOT)) + { + Pair key = getKey(nodeRef.getStoreRef()); + if (cache.contains(key)) + { + cache.get(key).remove(nodeRef); + } + } + } + } + + /** + * Resets the cache entries + */ + public void reset() + { + this.cache.clear(); + } + + /** + * Builds the cache key using the supplied storeRef + * + * @param storeRef + * @return a pair corresponding to the cache key + */ + private Pair getKey(StoreRef storeRef) + { + return new Pair(storeRef, ASPECT_RECORDS_MANAGEMENT_ROOT.toString()); + } +} diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java index a8b31ff55e..1d81ab517c 100644 --- a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java +++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/util/BaseRMTestCase.java @@ -57,6 +57,7 @@ import org.alfresco.module.org_alfresco_module_rm.role.FilePlanRoleService; import org.alfresco.module.org_alfresco_module_rm.search.RecordsManagementSearchService; import org.alfresco.module.org_alfresco_module_rm.security.ExtendedSecurityService; import org.alfresco.module.org_alfresco_module_rm.security.FilePlanPermissionService; +import org.alfresco.module.org_alfresco_module_rm.util.RMContainerCacheManager; import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordService; import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.repo.policy.PolicyComponent; @@ -173,6 +174,9 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase protected InplaceRecordService inplaceRecordService; protected RelationshipService relationshipService; + /** RM Container Cache Manager */ + protected RMContainerCacheManager rmContainerCacheManager; + /** test utils */ protected UserAndGroupsUtils userAndGroupsUtils; @@ -424,6 +428,9 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase holdService = (HoldService) applicationContext.getBean("HoldService"); inplaceRecordService = (InplaceRecordService) applicationContext.getBean("InplaceRecordService"); relationshipService = (RelationshipService) applicationContext.getBean("RelationshipService"); + + // RM Container Cache Manager + rmContainerCacheManager = (RMContainerCacheManager) applicationContext.getBean("rmContainerCacheManager"); } /** @@ -484,6 +491,11 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase { siteService.deleteSite(collabSiteId); } + + if (rmContainerCacheManager != null) + { + rmContainerCacheManager.reset(); + } } finally { @@ -936,7 +948,7 @@ public abstract class BaseRMTestCase extends RetryingTransactionHelperTestCase public void then() throws Exception { /** empty implementation */ } public void after() throws Exception { /** empty implementation */ } - + public void run() throws Exception { try