Merged BRANCHES/V3.4 to HEAD:

24356: It's possible for a persisted action to be removed during the transaction, so have the ActionTrackingService ensure the node still exists before doing the async update of the details (ALF-5745)
   24374: ALF-5744 - Retry the add aspect to avoid failures due to concurrent node updates


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@24931 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Nick Burch
2011-01-19 15:25:28 +00:00
parent 2b8e3837ce
commit 8c500a3cd8
3 changed files with 86 additions and 29 deletions

View File

@@ -114,6 +114,9 @@
<property name="transactionService"> <property name="transactionService">
<ref bean="TransactionService" /> <ref bean="TransactionService" />
</property> </property>
<property name="nodeService">
<ref bean="NodeService" />
</property>
<property name="runtimeActionService"> <property name="runtimeActionService">
<ref bean="actionService" /> <ref bean="actionService" />
</property> </property>
@@ -330,6 +333,12 @@
<property name="nodeService"> <property name="nodeService">
<ref bean="NodeService" /> <ref bean="NodeService" />
</property> </property>
<property name="transactionService">
<ref bean="TransactionService" />
</property>
<property name="ignoreLock">
<value>false</value>
</property>
</bean> </bean>
<bean id="remove-features" class="org.alfresco.repo.action.executer.RemoveFeaturesActionExecuter" parent="action-executer"> <bean id="remove-features" class="org.alfresco.repo.action.executer.RemoveFeaturesActionExecuter" parent="action-executer">

View File

@@ -39,6 +39,7 @@ import org.alfresco.service.cmr.action.CancellableAction;
import org.alfresco.service.cmr.action.ExecutionDetails; import org.alfresco.service.cmr.action.ExecutionDetails;
import org.alfresco.service.cmr.action.ExecutionSummary; import org.alfresco.service.cmr.action.ExecutionSummary;
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.transaction.TransactionService; 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;
@@ -56,6 +57,7 @@ public class ActionTrackingServiceImpl implements ActionTrackingService
private static Log logger = LogFactory.getLog(ActionTrackingServiceImpl.class); private static Log logger = LogFactory.getLog(ActionTrackingServiceImpl.class);
private SimpleCache<String, ExecutionDetails> executingActionsCache; private SimpleCache<String, ExecutionDetails> executingActionsCache;
private NodeService nodeService;
private TransactionService transactionService; private TransactionService transactionService;
private RuntimeActionService runtimeActionService; private RuntimeActionService runtimeActionService;
@@ -78,6 +80,16 @@ public class ActionTrackingServiceImpl implements ActionTrackingService
this.transactionService = transactionService; this.transactionService = transactionService;
} }
/**
* Set the node service
*
* @param nodeService the node service
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/** /**
* Set the runtime action service * Set the runtime action service
* *
@@ -139,7 +151,7 @@ public class ActionTrackingServiceImpl implements ActionTrackingService
action.setExecutionFailureMessage(null); action.setExecutionFailureMessage(null);
// Do we need to update the persisted details? // Do we need to update the persisted details?
if (action.getNodeRef() != null) if (action.getNodeRef() != null && nodeService.exists(action.getNodeRef()))
{ {
// Make sure we re-fetch the latest action details and save // Make sure we re-fetch the latest action details and save
// this version back into the repository // this version back into the repository
@@ -164,8 +176,15 @@ public class ActionTrackingServiceImpl implements ActionTrackingService
{ {
public Action doWork() throws Exception public Action doWork() throws Exception
{ {
// Grab the latest version of the // Ensure the action persisted node still exists, and wasn't deleted
// action // between when it loaded running and now
if( !nodeService.exists(actionNode) )
{
// Persisted node has gone, nothing to update
return null;
}
// Grab the latest version of the action
ActionImpl action = (ActionImpl) runtimeActionService ActionImpl action = (ActionImpl) runtimeActionService
.createAction(actionNode); .createAction(actionNode);

View File

@@ -24,12 +24,14 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import org.alfresco.repo.action.ParameterDefinitionImpl; import org.alfresco.repo.action.ParameterDefinitionImpl;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ParameterDefinition; import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
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.namespace.QName; import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService;
/** /**
* Add features action executor implementation. * Add features action executor implementation.
@@ -49,16 +51,29 @@ public class AddFeaturesActionExecuter extends ActionExecuterAbstractBase
*/ */
private NodeService nodeService; private NodeService nodeService;
/** Transaction Service, used for retrying operations */
private TransactionService transactionService;
/** /**
* Set the node service * Set the node service
* *
* @param nodeService the node service * @param nodeService the node service
*/ */
public void setNodeService(NodeService nodeService) public void setNodeService(NodeService nodeService)
{ {
this.nodeService = nodeService; this.nodeService = nodeService;
} }
/**
* Set the transaction service
*
* @param transactionService the transaction service
*/
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
/** /**
* Adhoc properties are allowed for this executor * Adhoc properties are allowed for this executor
*/ */
@@ -71,32 +86,46 @@ public class AddFeaturesActionExecuter extends ActionExecuterAbstractBase
/** /**
* @see org.alfresco.repo.action.executer.ActionExecuter#execute(org.alfresco.service.cmr.repository.NodeRef, NodeRef) * @see org.alfresco.repo.action.executer.ActionExecuter#execute(org.alfresco.service.cmr.repository.NodeRef, NodeRef)
*/ */
public void executeImpl(Action ruleAction, NodeRef actionedUponNodeRef) public void executeImpl(final Action ruleAction, final NodeRef actionedUponNodeRef)
{ {
if (this.nodeService.exists(actionedUponNodeRef) == true) if (this.nodeService.exists(actionedUponNodeRef))
{ {
Map<QName, Serializable> properties = new HashMap<QName, Serializable>(); transactionService.getRetryingTransactionHelper().doInTransaction(
QName aspectQName = null; new RetryingTransactionCallback<Void>() {
public Void execute() throws Throwable {
Map<String, Serializable> paramValues = ruleAction.getParameterValues(); Map<QName, Serializable> properties = new HashMap<QName, Serializable>();
for (Map.Entry<String, Serializable> entry : paramValues.entrySet()) QName aspectQName = null;
{
if (entry.getKey().equals(PARAM_ASPECT_NAME) == true) if(! nodeService.exists(actionedUponNodeRef))
{ {
aspectQName = (QName)entry.getValue(); // Node has gone away, skip
} return null;
else }
{
// Must be an adhoc property // Build the aspect details
QName propertyQName = QName.createQName(entry.getKey()); Map<String, Serializable> paramValues = ruleAction.getParameterValues();
Serializable propertyValue = entry.getValue(); for (Map.Entry<String, Serializable> entry : paramValues.entrySet())
properties.put(propertyQName, propertyValue); {
} if (entry.getKey().equals(PARAM_ASPECT_NAME) == true)
} {
aspectQName = (QName)entry.getValue();
// Add the aspect }
this.nodeService.addAspect(actionedUponNodeRef, aspectQName, properties); else
} {
// Must be an adhoc property
QName propertyQName = QName.createQName(entry.getKey());
Serializable propertyValue = entry.getValue();
properties.put(propertyQName, propertyValue);
}
}
// Add the aspect
nodeService.addAspect(actionedUponNodeRef, aspectQName, properties);
return null;
}
}
);
}
} }
/** /**