From e19101823169032e4e915442ee095e66cc96c1cc Mon Sep 17 00:00:00 2001 From: ehardon Date: Wed, 3 Mar 2021 15:10:33 +0200 Subject: [PATCH] APPS-832: prevent multiple nodes from initializing the custom properties --- .../rm-service-context.xml | 1 + .../RecordsManagementAdminServiceImpl.java | 129 +++++++++++++++--- 2 files changed, 108 insertions(+), 22 deletions(-) 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 29b5a0e785..3d46d6a87a 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 @@ -806,6 +806,7 @@ + rma:recordCategory diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/admin/RecordsManagementAdminServiceImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/admin/RecordsManagementAdminServiceImpl.java index 9cd8e0c096..0e8da44a36 100644 --- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/admin/RecordsManagementAdminServiceImpl.java +++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/admin/RecordsManagementAdminServiceImpl.java @@ -37,6 +37,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; @@ -53,6 +54,9 @@ import org.alfresco.repo.dictionary.M2Aspect; import org.alfresco.repo.dictionary.M2Constraint; import org.alfresco.repo.dictionary.M2Model; import org.alfresco.repo.dictionary.M2Property; +import org.alfresco.repo.lock.JobLockService; +import org.alfresco.repo.lock.JobLockService.JobLockRefreshCallback; +import org.alfresco.repo.lock.LockAcquisitionException; import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.policy.Behaviour.NotificationFrequency; import org.alfresco.repo.policy.annotation.Behaviour; @@ -71,6 +75,7 @@ import org.alfresco.service.cmr.dictionary.TypeDefinition; 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.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.service.transaction.TransactionService; @@ -112,6 +117,7 @@ public class RecordsManagementAdminServiceImpl extends RecordsManagementAdminBas private static final String PARAM_ALLOWED_VALUES = "allowedValues"; private static final String PARAM_CASE_SENSITIVE = "caseSensitive"; private static final String PARAM_MATCH_LOGIC = "matchLogic"; + private static final long DEFAULT_TIME = 30000L; /** Relationship service */ private RelationshipService relationshipService; @@ -119,6 +125,9 @@ public class RecordsManagementAdminServiceImpl extends RecordsManagementAdminBas /** Transaction service */ private TransactionService transactionService; + /** Job Lock service */ + private JobLockService jobLockService; + /** List of types that can be customisable */ private List pendingCustomisableTypes; private Map customisableTypes; @@ -152,7 +161,15 @@ public class RecordsManagementAdminServiceImpl extends RecordsManagementAdminBas return this.relationshipService; } - /** + /** + * @param jobLockService The Job Lock service + */ + public void setJobLockService(JobLockService jobLockService) + { + this.jobLockService = jobLockService; + } + + /** * Indicate that this application content listener must be executed with the lowest * precedence. (ie last) * @@ -172,30 +189,98 @@ public class RecordsManagementAdminServiceImpl extends RecordsManagementAdminBas @Override public void onApplicationEvent(ContextRefreshedEvent event) { - if(!isCustomMapInit && getDictionaryService().getAllModels().contains(RM_CUSTOM_MODEL)) - { - // run as System on bootstrap - AuthenticationUtil.runAs(new RunAsWork() - { - public Object doWork() - { - RetryingTransactionCallback callback = new RetryingTransactionCallback() - { - public Void execute() - { - // initialise custom properties - initCustomMap(); - return null; - } - }; - transactionService.getRetryingTransactionHelper().doInTransaction(callback); + final LockCallback lockCallback = new LockCallback(); - return null; + // try and get the lock + String lockToken = getLock(); + if (lockToken != null) + { + try + { + jobLockService.refreshLock(lockToken, getLockQName(), DEFAULT_TIME, lockCallback); + + if (!isCustomMapInit && getDictionaryService().getAllModels().contains(RM_CUSTOM_MODEL)) + { + // run as System on bootstrap + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() + { + RetryingTransactionCallback callback = new RetryingTransactionCallback() + { + public Void execute() + { + // initialise custom properties + initCustomMap(); + return null; + } + }; + transactionService.getRetryingTransactionHelper().doInTransaction(callback); + + return null; + } + }, AuthenticationUtil.getSystemUserName()); + } } - }, AuthenticationUtil.getSystemUserName()); - } + finally + { + try + { + lockCallback.running.set(false); + jobLockService.releaseLock(lockToken, getLockQName()); + } + catch (LockAcquisitionException e) + { + // Ignore + if (logger.isDebugEnabled()) + { + logger.debug("Lock release failed: " + getLockQName() + ": " + lockToken + "(" + + e.getMessage() + ")"); + } + } + } + } } - + + private QName getLockQName() + { + return QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, this.getClass().getName()); + } + + /** + * Attempts to get the lock. If the lock couldn't be taken, then null is returned. + * + * @return Returns the lock token or null + */ + private String getLock() + { + try + { + return jobLockService.getLock(getLockQName(), DEFAULT_TIME); + } + catch (LockAcquisitionException e) + { + return null; + } + } + + private class LockCallback implements JobLockRefreshCallback + { + final AtomicBoolean running = new AtomicBoolean(true); + + @Override + public boolean isActive() + { + return running.get(); + } + + @Override + public void lockReleased() + { + running.set(false); + } + } + /** * Helper method to indicate whether the custom map is initialised or not. *