Merge pull request #1347 from Alfresco/feature/APPS-832_IntermittentStartupFailure

APPS-832: prevent multiple nodes from initialising the custom properties
This commit is contained in:
Elena Hardon
2021-03-05 14:58:27 +02:00
committed by GitHub
3 changed files with 135 additions and 24 deletions

View File

@@ -806,6 +806,7 @@
<bean id="recordsManagementAdminService" class="org.alfresco.module.org_alfresco_module_rm.admin.RecordsManagementAdminServiceImpl" parent="recordsManagementAdminBase">
<property name="relationshipService" ref="RelationshipService" />
<property name="transactionService" ref="transactionService"/>
<property name="jobLockService" ref="jobLockService"/>
<property name="customisableTypes">
<list>
<value>rma:recordCategory</value>

View File

@@ -53,6 +53,8 @@ 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.LockAcquisitionException;
import org.alfresco.repo.node.NodeServicePolicies;
import org.alfresco.repo.policy.Behaviour.NotificationFrequency;
import org.alfresco.repo.policy.annotation.Behaviour;
@@ -71,10 +73,14 @@ 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;
import org.alfresco.util.GUID;
import org.alfresco.util.LockCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.Ordered;
@@ -96,6 +102,9 @@ public class RecordsManagementAdminServiceImpl extends RecordsManagementAdminBas
ApplicationListener<ContextRefreshedEvent>,
Ordered
{
private static final Logger LOGGER = LoggerFactory.getLogger(RecordsManagementAdminServiceImpl.class);
/** I18N messages*/
private static final String MSG_SERVICE_NOT_INIT = "rm.admin.service-not-init";
private static final String MSG_PROP_EXIST = "rm.admin.prop-exist";
@@ -108,17 +117,23 @@ public class RecordsManagementAdminServiceImpl extends RecordsManagementAdminBas
/** Constants */
private static final String CUSTOM_CONSTRAINT_TYPE = org.alfresco.module.org_alfresco_module_rm.caveat.RMListOfValuesConstraint.class.getName();
private static final String CAPATIBILITY_CUSTOM_CONTRAINT_TYPE = org.alfresco.module.org_alfresco_module_dod5015.caveat.RMListOfValuesConstraint.class.getName();
private static final String CAPABILITY_CUSTOM_CONSTRAINT_TYPE = org.alfresco.module.org_alfresco_module_dod5015.caveat.RMListOfValuesConstraint.class.getName();
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 QName LOCK_QNAME = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "RecordsManagementAdminServiceImpl");
private static final long DEFAULT_TIME = 30000L;
/** Relationship service */
private RelationshipService relationshipService;
/** Transaction service */
private TransactionService transactionService;
/** Job Lock service */
private JobLockService jobLockService;
/** List of types that can be customisable */
private List<QName> pendingCustomisableTypes;
private Map<QName, QName> customisableTypes;
@@ -152,7 +167,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 +195,63 @@ 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<Object>()
{
public Object doWork()
{
RetryingTransactionCallback<Void> callback = new RetryingTransactionCallback<Void>()
{
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, LOCK_QNAME, DEFAULT_TIME, lockCallback);
if (!isCustomMapInit && getDictionaryService().getAllModels().contains(RM_CUSTOM_MODEL))
{
// run as System on bootstrap
AuthenticationUtil.runAsSystem((RunAsWork<Void>) () -> {
transactionService.getRetryingTransactionHelper()
.doInTransaction((RetryingTransactionCallback<Void>) () -> {
// initialise custom properties
initCustomMap();
return null;
});
return null;
});
}
}
}, AuthenticationUtil.getSystemUserName());
}
finally
{
try
{
lockCallback.setIsRunning(false);
jobLockService.releaseLock(lockToken, LOCK_QNAME);
}
catch (LockAcquisitionException e)
{
LOGGER.debug("Lock release failed: {}: {}", LOCK_QNAME, lockToken, e);
}
}
}
}
/**
* Attempts to get the lock. If the lock couldn't be taken, then <tt>null</tt> is returned.
*
* @return Returns the lock token or <tt>null</tt>
*/
private String getLock()
{
try
{
return jobLockService.getLock(LOCK_QNAME, DEFAULT_TIME);
}
catch (LockAcquisitionException e)
{
return null;
}
}
/**
* Helper method to indicate whether the custom map is initialised or not.
*
@@ -1198,7 +1254,7 @@ public class RecordsManagementAdminServiceImpl extends RecordsManagementAdminBas
String type = customConstraint.getType();
if (type == null ||
(!type.equals(CUSTOM_CONSTRAINT_TYPE) &&
!type.equals(CAPATIBILITY_CUSTOM_CONTRAINT_TYPE)))
!type.equals(CAPABILITY_CUSTOM_CONSTRAINT_TYPE)))
{
throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_UNEXPECTED_TYPE_CONSTRAINT, type, constraintNameAsPrefixString, CUSTOM_CONSTRAINT_TYPE));
}

View File

@@ -0,0 +1,54 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.util;
import java.util.concurrent.atomic.AtomicBoolean;
import org.alfresco.repo.lock.JobLockService.JobLockRefreshCallback;
public class LockCallback implements JobLockRefreshCallback
{
private final AtomicBoolean running = new AtomicBoolean(true);
@Override
public boolean isActive()
{
return running.get();
}
@Override
public void lockReleased()
{
running.set(false);
}
public void setIsRunning(boolean isRunning)
{
this.running.set(isRunning);
}
}