RM-273 - RM: PublishUpdatesJob allows concurrent execution

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/BRANCHES/V2.0@36435 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Roy Wetherall
2012-05-16 04:51:20 +00:00
parent 6a4bfb4609
commit 54a12c4174
10 changed files with 448 additions and 279 deletions

View File

@@ -10,27 +10,25 @@
--> -->
<bean id="scheduledNotifyOfRecordsDueForReviewJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean"> <bean id="scheduledNotifyOfRecordsDueForReviewJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass"> <property name="jobClass">
<value>org.alfresco.module.org_alfresco_module_rm.job.NotifyOfRecordsDueForReviewJob</value> <value>org.alfresco.module.org_alfresco_module_rm.job.RecordsManagementJob</value>
</property> </property>
<property name="jobDataAsMap"> <property name="jobDataAsMap">
<map> <map>
<entry key="recordsManagementService"> <entry key="jobName" value="scheduledNotifyOfRecordsDueForReview"/>
<ref bean="recordsManagementService"/> <entry key="jobLockService">
<ref bean="jobLockService" />
</entry> </entry>
<entry key="recordsManagementNotificationHelper"> <entry key="jobExecuter">
<ref bean="recordsManagementNotificationHelper"/> <ref bean="scheduledNotifyOfRecordsDueForReviewJobExecuter" />
</entry>
<entry key="nodeService">
<ref bean="nodeService" />
</entry> </entry>
<entry key="searchService">
<ref bean="searchService" />
</entry>
<entry key="transactionService">
<ref bean="transactionService" />
</entry>
</map> </map>
</property> </property>
</bean>
<bean id="scheduledNotifyOfRecordsDueForReviewJobExecuter" class="org.alfresco.module.org_alfresco_module_rm.job.NotifyOfRecordsDueForReviewJobExecuter">
<property name="nodeService" ref="nodeService" />
<property name="searchService" ref="searchService" />
<property name="recordsManagementNotificationHelper" ref="recordsManagementNotificationHelper" />
</bean> </bean>
<bean id="scheduledNotifyOfRecordsDueForReviewJobTrigger" class="org.alfresco.util.CronTriggerBean"> <bean id="scheduledNotifyOfRecordsDueForReviewJobTrigger" class="org.alfresco.util.CronTriggerBean">
@@ -49,25 +47,27 @@
<!-- Disposition Lifecycle Job --> <!-- Disposition Lifecycle Job -->
<bean id="scheduledDispositionLifecyceleJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean"> <bean id="scheduledDispositionLifecyceleJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass"> <property name="jobClass">
<value>org.alfresco.module.org_alfresco_module_rm.job.DispositionLifecycleJob</value> <value>org.alfresco.module.org_alfresco_module_rm.job.RecordsManagementJob</value>
</property> </property>
<property name="jobDataAsMap"> <property name="jobDataAsMap">
<map> <map>
<entry key="nodeService"> <entry key="jobName" value="dispositionLifecycle"/>
<ref bean="nodeService" /> <entry key="jobLockService">
</entry> <ref bean="jobLockService" />
<entry key="searchService">
<ref bean="searchService" />
</entry>
<entry key="recordsManagementActionService">
<ref bean="recordsManagementActionService" />
</entry>
<entry key="transactionService">
<ref bean="transactionService" />
</entry> </entry>
<entry key="jobExecuter">
<ref bean="dispositionLifecycleJobExecuter" />
</entry>
</map> </map>
</property> </property>
</bean> </bean>
<bean id="dispositionLifecycleJobExecuter" class="org.alfresco.module.org_alfresco_module_rm.job.DispositionLifecycleJobExecuter">
<property name="nodeService" ref="nodeService" />
<property name="searchService" ref="searchService" />
<property name="retryingTransactionHelper" ref="retryingTransactionHelper" />
<property name="recordsManagementActionService" ref="recordsManagementActionService" />
</bean>
<bean id="scheduledDispositionLifecyceleJobTrigger" class="org.alfresco.util.CronTriggerBean"> <bean id="scheduledDispositionLifecyceleJobTrigger" class="org.alfresco.util.CronTriggerBean">
<property name="jobDetail"> <property name="jobDetail">
@@ -84,42 +84,38 @@
<bean id="scheduledPublishUpdatesJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean"> <bean id="scheduledPublishUpdatesJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass"> <property name="jobClass">
<value>org.alfresco.module.org_alfresco_module_rm.job.PublishUpdatesJob</value> <value>org.alfresco.module.org_alfresco_module_rm.job.RecordsManagementJob</value>
</property> </property>
<property name="jobDataAsMap"> <property name="jobDataAsMap">
<map> <map>
<entry key="nodeService"> <entry key="jobName" value="publishUpdates"/>
<ref bean="nodeService" /> <entry key="jobLockService">
</entry> <ref bean="jobLockService" />
<entry key="searchService">
<ref bean="searchService" />
</entry>
<entry key="retryingTransactionHelper">
<ref bean="retryingTransactionHelper" />
</entry>
<entry key="publishExecutorRegistry">
<ref bean="publishExecutorRegistry" />
</entry>
<entry key="behaviourFilter">
<ref bean="policyBehaviourFilter" />
</entry> </entry>
<entry key="jobExecuter">
<ref bean="publishUpdatesJobExecuter" />
</entry>
</map> </map>
</property> </property>
</bean> </bean>
<bean id="scheduledPublishUpdatesJobTrigger" class="org.alfresco.util.CronTriggerBean"> <bean id="scheduledPublishUpdatesJobTrigger" class="org.alfresco.util.CronTriggerBean">
<property name="jobDetail"> <property name="jobDetail" ref="scheduledPublishUpdatesJobDetail" />
<ref bean="scheduledPublishUpdatesJobDetail" /> <property name="scheduler" ref="schedulerFactory" />
</property>
<property name="scheduler">
<ref bean="schedulerFactory" />
</property>
<property name="cronExpression"> <property name="cronExpression">
<!-- <value>0 30 3 * * ?</value> --> <!-- <value>0 30 3 * * ?</value> -->
<value>0/30 * * * * ?</value> <value>0/30 * * * * ?</value>
</property> </property>
</bean> </bean>
<bean id="publishUpdatesJobExecuter" class="org.alfresco.module.org_alfresco_module_rm.job.PublishUpdatesJobExecuter">
<property name="nodeService" ref="nodeService" />
<property name="searchService" ref="searchService" />
<property name="retryingTransactionHelper" ref="retryingTransactionHelper" />
<property name="publishExecutorRegistry" ref="publishExecutorRegistry" />
<property name="behaviourFilter" ref="policyBehaviourFilter" />
</bean>
<bean id="publishExecutorRegistry" class="org.alfresco.module.org_alfresco_module_rm.job.publish.PublishExecutorRegistry"/> <bean id="publishExecutorRegistry" class="org.alfresco.module.org_alfresco_module_rm.job.publish.PublishExecutorRegistry"/>
<bean id="dispositionActionDefintionPublishExecutor" <bean id="dispositionActionDefintionPublishExecutor"

View File

@@ -87,12 +87,16 @@
</property> </property>
</bean> </bean>
<bean id="rmEntryVoter" class="org.alfresco.module.org_alfresco_module_rm.capability.RMEntryVoter" lazy-init="false" parent="RMSecurityCommon"> <bean id="rmEntryVoter"
class="org.alfresco.module.org_alfresco_module_rm.capability.RMEntryVoter"
lazy-init="false"
parent="RMSecurityCommon"
depends-on="CapabilityService">
<!-- Services --> <!-- Services -->
<property name="namespacePrefixResolver" ref="namespaceService"/> <property name="namespacePrefixResolver" ref="namespaceService"/>
<property name="dictionaryService" ref="dictionaryService"/> <property name="dictionaryService" ref="dictionaryService"/>
<property name="dispositionService" ref="dispositionService"/> <property name="dispositionService" ref="dispositionService"/>
<property name="capabilityService" ref="CapabilityService"/> <property name="capabilityService" ref="capabilityService"/>
</bean> </bean>

View File

@@ -229,10 +229,9 @@
<!-- Capability Service --> <!-- Capability Service -->
<bean id="CapabilityService" class="org.alfresco.module.org_alfresco_module_rm.capability.CapabilityServiceImpl"> <bean id="capabilityService" class="org.alfresco.module.org_alfresco_module_rm.capability.CapabilityServiceImpl">
</bean> </bean>
<!--
<bean id="CapabilityService" class="org.springframework.aop.framework.ProxyFactoryBean"> <bean id="CapabilityService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces"> <property name="proxyInterfaces">
<value>org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService</value> <value>org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService</value>
@@ -244,7 +243,7 @@
<list> <list>
<idref local="CapabilityService_transaction"/> <idref local="CapabilityService_transaction"/>
<idref bean="exceptionTranslator"/> <idref bean="exceptionTranslator"/>
<idref local="CapabilityService_security"/> <!-- <idref local="CapabilityService_security"/> -->
</list> </list>
</property> </property>
</bean> </bean>
@@ -273,15 +272,11 @@
<property name="objectDefinitionSource"> <property name="objectDefinitionSource">
<value> <value>
<![CDATA[ <![CDATA[
org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService.registerCapability=RM_ALLOW org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService.*=RM_ALLOW
org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService.getCapability=RM_ALLOW
org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService.getCapabilities=RM_ALLOW
org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService.getCapabilitiesAccessState=RM_ALLOW
org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService.*=RM_DENY
]]> ]]>
</value> </value>
</property> </property>
</bean> --> </bean>
<!-- Records Management Search Service --> <!-- Records Management Search Service -->

View File

@@ -49,21 +49,24 @@ public interface CapabilityService
Capability getCapability(String name); Capability getCapability(String name);
/** /**
* Get a list of all the assignable capabilities.
* *
* @return * @return {@link Set}<{@link Capability}> set of all the assignable capabilities
*/ */
Set<Capability> getCapabilities(); Set<Capability> getCapabilities();
/** /**
* Get a list of all the capabilities, optionally including those that are non-assignable.
* *
* @param includePrivate * @param includePrivate indicates that the private, or non-assignable capabilities are included in the result
* @return * @return {@link Set}<{@link Capability}> set of capabilities
*/ */
Set<Capability> getCapabilities(boolean includePrivate); Set<Capability> getCapabilities(boolean includePrivate);
/** /**
* Get all the capabilities access state based on the current user.
* *
* @param nodeRef * @param nodeRef node reference
* @return * @return
*/ */
Map<Capability, AccessStatus> getCapabilitiesAccessState(NodeRef nodeRef); Map<Capability, AccessStatus> getCapabilitiesAccessState(NodeRef nodeRef);

View File

@@ -1,150 +0,0 @@
/*
* Copyright (C) 2009-2011 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.module.org_alfresco_module_rm.job;
import java.util.List;
import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction;
import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService;
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.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.transaction.TransactionService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/**
* The Disposition Lifecycle Job Finds all disposition action nodes which are
* for "retain" or "cutOff" actions Where asOf > now OR
* dispositionEventsEligible = true;
*
* Runs the cut off or retain action for
* elligible records.
*
* @author mrogers
*/
public class DispositionLifecycleJob implements Job
{
private static Log logger = LogFactory.getLog(DispositionLifecycleJob.class);
/**
* @see org.quartz.Job#execute(org.quartz.JobExecutionContext)
*/
public void execute(JobExecutionContext context) throws JobExecutionException
{
final RecordsManagementActionService rmActionService = (RecordsManagementActionService) context
.getJobDetail().getJobDataMap().get("recordsManagementActionService");
final NodeService nodeService = (NodeService) context.getJobDetail().getJobDataMap().get(
"nodeService");
final SearchService search = (SearchService) context.getJobDetail().getJobDataMap().get(
"searchService");
final TransactionService trxService = (TransactionService) context.getJobDetail()
.getJobDataMap().get("transactionService");
logger.debug("Job Starting");
AuthenticationUtil.runAs(new RunAsWork<Object>()
{
public Object doWork() throws Exception
{
StringBuilder sb = new StringBuilder();
sb.append("+TYPE:\"rma:dispositionAction\" ");
sb.append("+(@rma\\:dispositionAction:(\"cutoff\" OR \"retain\"))");
sb.append("+ISNULL:\"rma:dispositionActionCompletedAt\" ");
sb.append("+( ");
sb.append("@rma\\:dispositionEventsEligible:true ");
sb.append("OR @rma\\:dispositionAsOf:[MIN TO NOW] ");
sb.append(") ");
String query = sb.toString();
ResultSet results = search.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE,
SearchService.LANGUAGE_LUCENE, query);
List<NodeRef> resultNodes = results.getNodeRefs();
results.close();
RetryingTransactionHelper trn = trxService.getRetryingTransactionHelper();
for (NodeRef node : resultNodes)
{
final NodeRef currentNode = node;
RetryingTransactionCallback<Boolean> processTranCB = new RetryingTransactionCallback<Boolean>()
{
public Boolean execute() throws Throwable
{
final String dispAction = (String) nodeService.getProperty(currentNode,
RecordsManagementModel.PROP_DISPOSITION_ACTION);
// Run "retain" and "cutoff" actions.
if (dispAction != null)
{
if (dispAction.equalsIgnoreCase("cutoff") ||
dispAction.equalsIgnoreCase("retain"))
{
ChildAssociationRef parent = nodeService.getPrimaryParent(currentNode);
if (parent.getTypeQName().equals(RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION))
{
// Check that the action is executable
RecordsManagementAction rmAction = rmActionService.getDispositionAction(dispAction);
if (rmAction.isExecutable(parent.getParentRef(), null) == true)
{
rmActionService.executeRecordsManagementAction(parent.getParentRef(), dispAction);
if (logger.isDebugEnabled())
{
logger.debug("Processed action: " + dispAction + "on" + parent);
}
}
else
{
logger.debug("The disposition action " + dispAction + " is not executable.");
}
}
return null;
}
}
return Boolean.TRUE;
}
};
/**
* Now do the work, one action in each transaction
*/
trn.doInTransaction(processTranCB);
}
return null;
};
}, AuthenticationUtil.getSystemUserName());
logger.debug("Job Finished");
}
}

View File

@@ -0,0 +1,147 @@
/*
* Copyright (C) 2009-2011 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.module.org_alfresco_module_rm.job;
import java.util.List;
import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction;
import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* The Disposition Lifecycle Job Finds all disposition action nodes which are
* for "retain" or "cutOff" actions Where asOf > now OR
* dispositionEventsEligible = true;
*
* Runs the cut off or retain action for
* elligible records.
*
* @author mrogers
*/
public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecuter
{
private static Log logger = LogFactory.getLog(DispositionLifecycleJobExecuter.class);
private RecordsManagementActionService recordsManagementActionService;
private NodeService nodeService;
private SearchService searchService;
public void setRecordsManagementActionService(RecordsManagementActionService recordsManagementActionService)
{
this.recordsManagementActionService = recordsManagementActionService;
}
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public void setSearchService(SearchService searchService)
{
this.searchService = searchService;
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.job.RecordsManagementJobExecuter#execute()
*/
public void execute()
{
logger.debug("Job Starting");
StringBuilder sb = new StringBuilder();
sb.append("+TYPE:\"rma:dispositionAction\" ");
sb.append("+(@rma\\:dispositionAction:(\"cutoff\" OR \"retain\"))");
sb.append("+ISNULL:\"rma:dispositionActionCompletedAt\" ");
sb.append("+( ");
sb.append("@rma\\:dispositionEventsEligible:true ");
sb.append("OR @rma\\:dispositionAsOf:[MIN TO NOW] ");
sb.append(") ");
String query = sb.toString();
ResultSet results = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE,
SearchService.LANGUAGE_LUCENE, query);
List<NodeRef> resultNodes = results.getNodeRefs();
results.close();
for (NodeRef node : resultNodes)
{
final NodeRef currentNode = node;
RetryingTransactionCallback<Boolean> processTranCB = new RetryingTransactionCallback<Boolean>()
{
public Boolean execute() throws Throwable
{
final String dispAction = (String) nodeService.getProperty(currentNode,
RecordsManagementModel.PROP_DISPOSITION_ACTION);
// Run "retain" and "cutoff" actions.
if (dispAction != null)
{
if (dispAction.equalsIgnoreCase("cutoff") ||
dispAction.equalsIgnoreCase("retain"))
{
ChildAssociationRef parent = nodeService.getPrimaryParent(currentNode);
if (parent.getTypeQName().equals(RecordsManagementModel.ASSOC_NEXT_DISPOSITION_ACTION))
{
// Check that the action is executable
RecordsManagementAction rmAction = recordsManagementActionService.getDispositionAction(dispAction);
if (rmAction.isExecutable(parent.getParentRef(), null) == true)
{
recordsManagementActionService.executeRecordsManagementAction(parent.getParentRef(), dispAction);
if (logger.isDebugEnabled())
{
logger.debug("Processed action: " + dispAction + "on" + parent);
}
}
else
{
logger.debug("The disposition action " + dispAction + " is not executable.");
}
}
return null;
}
}
return Boolean.TRUE;
}
};
/**
* Now do the work, one action in each transaction
*/
retryingTransactionHelper.doInTransaction(processTranCB);
}
logger.debug("Job Finished");
}
}

View File

@@ -25,19 +25,14 @@ import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.module.org_alfresco_module_rm.notification.RecordsManagementNotificationHelper; import org.alfresco.module.org_alfresco_module_rm.notification.RecordsManagementNotificationHelper;
import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.transaction.TransactionService;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/** /**
* This job finds all Vital Records which are due for review, optionally * This job finds all Vital Records which are due for review, optionally
@@ -45,20 +40,37 @@ import org.quartz.JobExecutionException;
* *
* @author Neil McErlean * @author Neil McErlean
*/ */
public class NotifyOfRecordsDueForReviewJob implements Job public class NotifyOfRecordsDueForReviewJobExecuter extends RecordsManagementJobExecuter
{ {
private static Log logger = LogFactory.getLog(NotifyOfRecordsDueForReviewJob.class); private static Log logger = LogFactory.getLog(NotifyOfRecordsDueForReviewJobExecuter.class);
private RecordsManagementNotificationHelper recordsManagementNotificationHelper;
private NodeService nodeService;
private SearchService searchService;
public void setRecordsManagementNotificationHelper(
RecordsManagementNotificationHelper recordsManagementNotificationHelper)
{
this.recordsManagementNotificationHelper = recordsManagementNotificationHelper;
}
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public void setSearchService(SearchService searchService)
{
this.searchService = searchService;
}
/** /**
* @see org.quartz.Job#execute(org.quartz.JobExecutionContext) * @see org.alfresco.module.org_alfresco_module_rm.job.RecordsManagementJobExecuter#execute()
*/ */
public void execute(JobExecutionContext context) throws JobExecutionException public void execute()
{ {
final RecordsManagementNotificationHelper notificationHelper = (RecordsManagementNotificationHelper)context.getJobDetail().getJobDataMap().get("recordsManagementNotificationHelper");
final NodeService nodeService = (NodeService) context.getJobDetail().getJobDataMap().get("nodeService");
final SearchService searchService = (SearchService) context.getJobDetail().getJobDataMap().get("searchService");
final TransactionService trxService = (TransactionService) context.getJobDetail().getJobDataMap().get("transactionService");
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
{ {
logger.debug("Job " + this.getClass().getSimpleName() + " starting."); logger.debug("Job " + this.getClass().getSimpleName() + " starting.");
@@ -91,8 +103,6 @@ public class NotifyOfRecordsDueForReviewJob implements Job
//If we have something to do and a template to do it with //If we have something to do and a template to do it with
if(resultNodes.size() != 0) if(resultNodes.size() != 0)
{ {
RetryingTransactionHelper trn = trxService.getRetryingTransactionHelper();
//Send the email message - but we must not retry since email is not transactional //Send the email message - but we must not retry since email is not transactional
RetryingTransactionCallback<Boolean> txCallbackSendEmail = new RetryingTransactionCallback<Boolean>() RetryingTransactionCallback<Boolean> txCallbackSendEmail = new RetryingTransactionCallback<Boolean>()
{ {
@@ -100,7 +110,7 @@ public class NotifyOfRecordsDueForReviewJob implements Job
public Boolean execute() throws Throwable public Boolean execute() throws Throwable
{ {
// Send notification // Send notification
notificationHelper.recordsDueForReviewEmailNotification(resultNodes); recordsManagementNotificationHelper.recordsDueForReviewEmailNotification(resultNodes);
return null; return null;
} }
@@ -122,10 +132,10 @@ public class NotifyOfRecordsDueForReviewJob implements Job
/** /**
* Now do the work, one action in each transaction * Now do the work, one action in each transaction
*/ */
trn.setMaxRetries(0); // don't retry the send email retryingTransactionHelper.setMaxRetries(0); // don't retry the send email
trn.doInTransaction(txCallbackSendEmail); retryingTransactionHelper.doInTransaction(txCallbackSendEmail);
trn.setMaxRetries(10); retryingTransactionHelper.setMaxRetries(10);
trn.doInTransaction(txUpdateNodesCallback); retryingTransactionHelper.doInTransaction(txUpdateNodesCallback);
} }
return null; return null;
} }

View File

@@ -24,7 +24,6 @@ import java.util.List;
import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.module.org_alfresco_module_rm.job.publish.PublishExecutor; import org.alfresco.module.org_alfresco_module_rm.job.publish.PublishExecutor;
import org.alfresco.module.org_alfresco_module_rm.job.publish.PublishExecutorRegistry; import org.alfresco.module.org_alfresco_module_rm.job.publish.PublishExecutorRegistry;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.repo.policy.BehaviourFilter;
import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
@@ -37,47 +36,51 @@ import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.search.SearchService;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/** /**
* Job to publish any pending updates on marked node references. * Job to publish any pending updates on marked node references.
* *
* @author Roy Wetherall * @author Roy Wetherall
*/ */
public class PublishUpdatesJob implements Job, RecordsManagementModel public class PublishUpdatesJobExecuter extends RecordsManagementJobExecuter
{ {
/** Logger */ /** Logger */
private static Log logger = LogFactory.getLog(PublishUpdatesJob.class); private static Log logger = LogFactory.getLog(PublishUpdatesJobExecuter.class);
/** Node service */ /** Node service */
private NodeService nodeService; private NodeService nodeService;
/** Search service */ /** Search service */
private SearchService searchService; private SearchService searchService;
/** Retrying transaction helper */
private RetryingTransactionHelper retryingTransactionHelper;
/** Publish executor register */ /** Publish executor register */
private PublishExecutorRegistry register; private PublishExecutorRegistry publishExecutorRegistry;
/** Behaviour filter */ /** Behaviour filter */
private BehaviourFilter behaviourFilter; private BehaviourFilter behaviourFilter;
/** Indicates whether the job bean has been initialised or not */ public void setNodeService(NodeService nodeService)
private boolean initialised = false;
/**
* @see org.quartz.Job#execute(org.quartz.JobExecutionContext)
*/
public void execute(JobExecutionContext context) throws JobExecutionException
{ {
// Initialise the service references this.nodeService = nodeService;
initServices(context); }
public void setSearchService(SearchService searchService)
{
this.searchService = searchService;
}
public void setPublishExecutorRegistry(PublishExecutorRegistry publishExecutorRegistry)
{
this.publishExecutorRegistry = publishExecutorRegistry;
}
public void setBehaviourFilter(BehaviourFilter behaviourFilter)
{
this.behaviourFilter = behaviourFilter;
}
public void execute()
{
if (logger.isDebugEnabled() == true) if (logger.isDebugEnabled() == true)
{ {
logger.debug("Job Starting"); logger.debug("Job Starting");
@@ -156,25 +159,6 @@ public class PublishUpdatesJob implements Job, RecordsManagementModel
return retryingTransactionHelper.doInTransaction(execution, true); return retryingTransactionHelper.doInTransaction(execution, true);
} }
/**
* Initialise service based on the job execution context
* @param context job execution context
*/
private void initServices(JobExecutionContext context)
{
if (initialised == false)
{
// Get references to the required services
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
nodeService = (NodeService)jobDataMap.get("nodeService");
searchService = (SearchService)jobDataMap.get("searchService");
retryingTransactionHelper = (RetryingTransactionHelper)jobDataMap.get("retryingTransactionHelper");
register = (PublishExecutorRegistry)jobDataMap.get("publishExecutorRegistry");
behaviourFilter = (BehaviourFilter)jobDataMap.get("behaviourFilter");
initialised = true;
}
}
/** /**
* Mark the node as publish in progress. This is often used as a marker to prevent any further updates * Mark the node as publish in progress. This is often used as a marker to prevent any further updates
* to a node. * to a node.
@@ -235,7 +219,7 @@ public class PublishUpdatesJob implements Job, RecordsManagementModel
} }
// Get the publish executor // Get the publish executor
PublishExecutor executor = register.get(updateTo); PublishExecutor executor = publishExecutorRegistry.get(updateTo);
if (executor == null) if (executor == null)
{ {
if (logger.isDebugEnabled() == true) if (logger.isDebugEnabled() == true)

View File

@@ -0,0 +1,143 @@
/*
* Copyright (C) 2009-2011 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.module.org_alfresco_module_rm.job;
import java.util.concurrent.atomic.AtomicBoolean;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.lock.JobLockService;
import org.alfresco.repo.lock.LockAcquisitionException;
import org.alfresco.repo.lock.JobLockService.JobLockRefreshCallback;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/**
* Base records management job implementation.
* <p>
* Delegates job execution and ensures locking
* is enforced.
*
* @author Roy Wetherall
*/
public class RecordsManagementJob implements Job
{
private static long DEFAULT_TIME = 2000L;
private JobLockService jobLockService;
private RecordsManagementJobExecuter jobExecuter;
private String jobName;
private QName getLockQName()
{
return QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, jobName);
}
/**
* 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(getLockQName(), DEFAULT_TIME);
}
catch (LockAcquisitionException e)
{
return null;
}
}
@Override
public void execute(JobExecutionContext context) throws JobExecutionException
{
// get the job lock service
jobLockService = (JobLockService)context.getJobDetail().getJobDataMap().get("jobLockService");
if (jobLockService == null)
{
throw new AlfrescoRuntimeException("Job lock service has not been specified.");
}
// get the job executer
jobExecuter = (RecordsManagementJobExecuter)context.getJobDetail().getJobDataMap().get("jobExecuter");
if (jobExecuter == null)
{
throw new AlfrescoRuntimeException("Job executer has not been specified.");
}
// get the job name
jobName = (String)context.getJobDetail().getJobDataMap().get("jobName");
if (jobName == null)
{
throw new AlfrescoRuntimeException("Job name has not been specified.");
}
AuthenticationUtil.runAs(new RunAsWork<Void>()
{
public Void doWork() throws Exception
{
// try and get the lock
String lockToken = getLock();
if (lockToken == null)
{
// exit
return null;
}
// use a flag to keep track of the running job
final AtomicBoolean running = new AtomicBoolean(true);
jobLockService.refreshLock(lockToken, getLockQName(), DEFAULT_TIME, new JobLockRefreshCallback()
{
@Override
public boolean isActive()
{
return running.get();
}
@Override
public void lockReleased()
{
running.set(false);
}
});
try
{
// do work
jobExecuter.execute();
}
finally
{
// The lock will self-release if answer isActive in the negative
running.set(false);
}
// return
return null;
}
}, AuthenticationUtil.getSystemUserName());
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (C) 2009-2011 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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.module.org_alfresco_module_rm.job;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
/**
* @author Roy Wetherall
*/
public abstract class RecordsManagementJobExecuter implements RecordsManagementModel
{
protected RetryingTransactionHelper retryingTransactionHelper;
public void setRetryingTransactionHelper(RetryingTransactionHelper retryingTransactionHelper)
{
this.retryingTransactionHelper = retryingTransactionHelper;
}
public abstract void execute();
}