mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-06-30 18:15:39 +00:00
40238: CLOUD-37 - Initial Commit to test Merged BRANCHES/DEV/AMILLER/CLOUD1_SPRINT1 to BRANCHES/DEV/CLOUD1_SPRINT1: 40077: CLOUD-37: Initial commit. 40101: CLOUD-37: Fix build error. 40114: CLOUD-37: Fix path names and missing files. 40122: CLOUD-37: Initial drop of UI code for investigation of progress issues 40124: CLOUD-37: A couple of minor UI tweaks (set icon and hide panel before archive download) 40125: CLOUD-37: Download files and folders as zip 40134: CLOUD-37: Updates to UI (javascript doc, CSS tweaks, intervals for requests, labels, etc). 40143: CLOUD-37: Error messages for failures, more JavaScript doc, archive naming, code tidy 40157: CLOUD-37 - Download files and folders as zip 40202: CLOUD-37: UI tweaks following UX review 40217: CLOUD-37: Add file count to status reports. 40222: CLOUD-37: Added information to download dialog to report on the number of files added to the zip 40240: CLOUD-37: Remove extraneous file, breaking build 40513: CLOUD-37: Add Action Service Metrics Merged BRANCHES/DEV/AMILLER/CLOUD1_SPRINT1 to BRANCHES/DEV/CLOUD1_SPRINT1: 40260: CLOUD-37: Add action service metrics 40309: CLOUD-37: Fix JMX configuration, pointing at renamed class. 40514: CLOUD-37: Enable the execution of the zip creation process on a remote transformation node Merged BRANCHES/DEV/AMILLER/CLOUD1_SPRINT1 to BRANCHES/DEV/CLOUD1_SPRINT1: 40369: CLOUD-37: Enable the execution of the zip creation process on a remote transformation node 40516: CLOUD-37: Implement clean up job. Merged BRANCHES/DEV/AMILLER/CLOUD1_SPRINT1 to BRANCHES/DEV/CLOUD1_SPRINT1: 40462: CLOUD-37: Implement clean up job. 40517: CLOUD-505: Add entries for folders. Merged BRANCHES/DEV/AMILLER/CLOUD1_SPRINT1 to BRANCHES/DEV/CLOUD1_SPRINT1: 40493: CLOUD-505: Add entries for folders. 40547: CLOUD-37: Fix broken test 40595: CLOUD-518: Add working copy/locked file filtering 40642: CLOUD-508: Prevent problems occurring when cancelling and restarting the same download 40643: CLOUD-507: When a single item is selected for download it the item name gets used for the archive name 41442: CLOUD-590: Limit the total size of the content which can be downloaded. This can be set via the property, download.maxContentSize. The default is 2GB. 41472: CLOUD-589: Added cancelled flag to download type and added checks in Zip creation action to act upon the setting of this flag. Also added webscript for canceling the download. 41692: Adds support to Alfresco.util.formatFileSize for file sizes with commas (as needed by zip download) 41693: Zip Download enhancements: CLOUD-590: Notifies the user when they've exceeded the maximum file size limit. CLOUD-626: Better handling when there are errors during zipping. (WIP) 41713: Zip Download Updates: CLOUD-589: A cancel download UI action now triggers a delete of the archive on the server. CLOUD-626: The UI now triggers a full download cancel (with node delete) in event of an error. 41737: Updates Alfresco.util.formatFileSize to support an optional decimal places param. (For CLOUD-685) 41739: CLOUD-685: Display total file size of files for download to two decimal places when there is an error. 41832: Fixes: CLOUD-704: new CANCELLED status is now handled correctly. 41887: CLOUD-686: Updated maximum download content size to 2152852358 bytes (2.005GB) 41965: CLOUD-703: Upload content now runs as system user, and Quota Service returns unlimited quota for system user. 42025: CLOUD-703: Fix test failures and ensure S3 content store works in the clustered and non-clustered environments git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@42146 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
1799 lines
69 KiB
Java
1799 lines
69 KiB
Java
/*
|
|
* Copyright (C) 2005-2010 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.repo.action;
|
|
|
|
import java.io.Serializable;
|
|
import java.util.ArrayList;
|
|
import java.util.Date;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
|
|
import org.alfresco.model.ContentModel;
|
|
import org.alfresco.repo.action.evaluator.ActionConditionEvaluator;
|
|
import org.alfresco.repo.action.executer.ActionExecuter;
|
|
import org.alfresco.repo.copy.CopyBehaviourCallback;
|
|
import org.alfresco.repo.copy.CopyDetails;
|
|
import org.alfresco.repo.copy.CopyServicePolicies;
|
|
import org.alfresco.repo.copy.DefaultCopyBehaviourCallback;
|
|
import org.alfresco.repo.policy.JavaBehaviour;
|
|
import org.alfresco.repo.policy.PolicyComponent;
|
|
import org.alfresco.repo.security.authentication.AuthenticationContext;
|
|
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
|
import org.alfresco.service.cmr.action.Action;
|
|
import org.alfresco.service.cmr.action.ActionCondition;
|
|
import org.alfresco.service.cmr.action.ActionConditionDefinition;
|
|
import org.alfresco.service.cmr.action.ActionDefinition;
|
|
import org.alfresco.service.cmr.action.ActionList;
|
|
import org.alfresco.service.cmr.action.ActionService;
|
|
import org.alfresco.service.cmr.action.ActionServiceException;
|
|
import org.alfresco.service.cmr.action.ActionServiceTransientException;
|
|
import org.alfresco.service.cmr.action.ActionStatus;
|
|
import org.alfresco.service.cmr.action.ActionTrackingService;
|
|
import org.alfresco.service.cmr.action.CompositeAction;
|
|
import org.alfresco.service.cmr.action.CompositeActionCondition;
|
|
import org.alfresco.service.cmr.action.ParameterConstraint;
|
|
import org.alfresco.service.cmr.action.ParameterizedItem;
|
|
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
|
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.search.SearchService;
|
|
import org.alfresco.service.namespace.DynamicNamespacePrefixResolver;
|
|
import org.alfresco.service.namespace.NamespaceService;
|
|
import org.alfresco.service.namespace.QName;
|
|
import org.alfresco.service.namespace.RegexQNamePattern;
|
|
import org.alfresco.util.GUID;
|
|
import org.alfresco.util.PropertyCheck;
|
|
import org.apache.commons.logging.Log;
|
|
import org.apache.commons.logging.LogFactory;
|
|
import org.springframework.beans.BeansException;
|
|
import org.springframework.context.ApplicationContext;
|
|
import org.springframework.context.ApplicationContextAware;
|
|
|
|
/**
|
|
* Action service implementation
|
|
*
|
|
* @author Roy Wetherall
|
|
*/
|
|
public class ActionServiceImpl implements ActionService, RuntimeActionService, ApplicationContextAware,
|
|
CopyServicePolicies.OnCopyNodePolicy, CopyServicePolicies.OnCopyCompletePolicy
|
|
{
|
|
/**
|
|
* Transaction resource name
|
|
*/
|
|
private static final String POST_TRANSACTION_PENDING_ACTIONS = "postTransactionPendingActions";
|
|
|
|
/**
|
|
* Error message
|
|
*/
|
|
private static final String ERR_FAIL = "The action failed to execute due to an error.";
|
|
|
|
/**
|
|
* The logger
|
|
*/
|
|
private static Log logger = LogFactory.getLog(ActionServiceImpl.class);
|
|
|
|
/**
|
|
* Thread local containing the current action chain
|
|
*/
|
|
ThreadLocal<Set<String>> currentActionChain = new ThreadLocal<Set<String>>();
|
|
|
|
/**
|
|
* The application context used to retrieve defined actions
|
|
*/
|
|
private ApplicationContext applicationContext;
|
|
private NodeService nodeService;
|
|
private SearchService searchService;
|
|
private DictionaryService dictionaryService;
|
|
private AuthenticationContext authenticationContext;
|
|
private ActionTrackingService actionTrackingService;
|
|
private PolicyComponent policyComponent;
|
|
private ActionServiceMonitor monitor;
|
|
|
|
/**
|
|
* The asynchronous action execution queues map of name, queue
|
|
*/
|
|
private Map<String, AsynchronousActionExecutionQueue> asynchronousActionExecutionQueues;
|
|
|
|
/**
|
|
* Action transaction listener
|
|
*/
|
|
private ActionTransactionListener transactionListener = new ActionTransactionListener(this);
|
|
|
|
/**
|
|
* All the condition definitions currently registered
|
|
*/
|
|
private Map<String, ActionConditionDefinition> conditionDefinitions = new HashMap<String, ActionConditionDefinition>();
|
|
|
|
/**
|
|
* All the action definitions currently registered
|
|
*/
|
|
private Map<String, ActionDefinition> actionDefinitions = new HashMap<String, ActionDefinition>();
|
|
|
|
/**
|
|
* All the parameter constraints
|
|
*/
|
|
private Map<String, ParameterConstraint> parameterConstraints = new HashMap<String, ParameterConstraint>();
|
|
|
|
/**
|
|
* Set the application context
|
|
*
|
|
* @param applicationContext the application context
|
|
*/
|
|
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
|
|
{
|
|
this.applicationContext = applicationContext;
|
|
}
|
|
|
|
/**
|
|
* Set the node service
|
|
*
|
|
* @param nodeService the node service
|
|
*/
|
|
public void setNodeService(NodeService nodeService)
|
|
{
|
|
this.nodeService = nodeService;
|
|
}
|
|
|
|
/**
|
|
* Set the search service
|
|
*
|
|
* @param searchService the search service
|
|
*/
|
|
public void setSearchService(SearchService searchService)
|
|
{
|
|
this.searchService = searchService;
|
|
}
|
|
|
|
/**
|
|
* Set the authentication component
|
|
*
|
|
* @param authenticationContext the authentication component
|
|
*/
|
|
public void setAuthenticationContext(AuthenticationContext authenticationContext)
|
|
{
|
|
this.authenticationContext = authenticationContext;
|
|
}
|
|
|
|
/**
|
|
* Set the action tracking service
|
|
*
|
|
* @param actionTrackingService the action tracking service
|
|
*/
|
|
public void setActionTrackingService(ActionTrackingService actionTrackingService)
|
|
{
|
|
this.actionTrackingService = actionTrackingService;
|
|
}
|
|
|
|
/**
|
|
* Set the dictionary service
|
|
*
|
|
* @param dictionaryService the dictionary service
|
|
*/
|
|
public void setDictionaryService(DictionaryService dictionaryService)
|
|
{
|
|
this.dictionaryService = dictionaryService;
|
|
}
|
|
|
|
/**
|
|
* @param policyComponent used to set up the action-based policy behaviour
|
|
*/
|
|
public void setPolicyComponent(PolicyComponent policyComponent)
|
|
{
|
|
this.policyComponent = policyComponent;
|
|
}
|
|
|
|
/**
|
|
* @param monitor used to monitor running actions and execution times
|
|
*/
|
|
public void setMonitor(ActionServiceMonitor monitor)
|
|
{
|
|
this.monitor = monitor;
|
|
}
|
|
|
|
/**
|
|
* Set the asynchronous action execution queues
|
|
*
|
|
* @param asynchronousActionExecutionQueue the asynchronous action execution
|
|
* queues
|
|
*/
|
|
public void setAsynchronousActionExecutionQueues(
|
|
Map<String, AsynchronousActionExecutionQueue> asynchronousActionExecutionQueues)
|
|
{
|
|
this.asynchronousActionExecutionQueues = asynchronousActionExecutionQueues;
|
|
}
|
|
|
|
public void init()
|
|
{
|
|
PropertyCheck.mandatory(this, "policyComponent", policyComponent);
|
|
|
|
this.policyComponent.bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"),
|
|
ActionModel.TYPE_ACTION_PARAMETER, new JavaBehaviour(this, "getCopyCallback"));
|
|
this.policyComponent.bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "onCopyComplete"),
|
|
ActionModel.TYPE_ACTION_PARAMETER, new JavaBehaviour(this, "onCopyComplete"));
|
|
}
|
|
|
|
/**
|
|
* Gets the saved action folder reference
|
|
*
|
|
* @param nodeRef the node reference
|
|
* @return the node reference
|
|
*/
|
|
private NodeRef getSavedActionFolderRef(NodeRef nodeRef)
|
|
{
|
|
List<ChildAssociationRef> assocs = this.nodeService.getChildAssocs(nodeRef, RegexQNamePattern.MATCH_ALL,
|
|
ActionModel.ASSOC_ACTION_FOLDER);
|
|
if (assocs.size() != 1)
|
|
{
|
|
throw new ActionServiceException("Unable to retrieve the saved action folder reference.");
|
|
}
|
|
|
|
return assocs.get(0).getChildRef();
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.action.ActionService#getActionDefinition(java.lang.String)
|
|
*/
|
|
public ActionDefinition getActionDefinition(String name)
|
|
{
|
|
// get direct access to action definition (i.e. ignoring public flag of
|
|
// executer)
|
|
ActionDefinition definition = null;
|
|
Object bean = this.applicationContext.getBean(name);
|
|
if (bean != null && bean instanceof ActionExecuter)
|
|
{
|
|
ActionExecuter executer = (ActionExecuter) bean;
|
|
definition = executer.getActionDefinition();
|
|
}
|
|
return definition;
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.action.ActionService#getActionDefinitions()
|
|
*/
|
|
public List<ActionDefinition> getActionDefinitions()
|
|
{
|
|
return new ArrayList<ActionDefinition>(this.actionDefinitions.values());
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.action.ActionService#getActionDefinitions(org.alfresco.service.cmr.repository.NodeRef)
|
|
*/
|
|
public List<ActionDefinition> getActionDefinitions(NodeRef nodeRef)
|
|
{
|
|
if (nodeRef == null)
|
|
{
|
|
return getActionDefinitions();
|
|
}
|
|
else
|
|
{
|
|
// TODO for now we will only filter by type, we will introduce
|
|
// filtering by aspect later
|
|
QName nodeType = this.nodeService.getType(nodeRef);
|
|
List<ActionDefinition> result = new ArrayList<ActionDefinition>();
|
|
for (ActionDefinition actionDefinition : getActionDefinitions())
|
|
{
|
|
Set<QName> applicableTypes = actionDefinition.getApplicableTypes();
|
|
if (applicableTypes != null && applicableTypes.isEmpty() == false)
|
|
{
|
|
// First do a short-cut check directly against the type
|
|
if (applicableTypes.contains(nodeType))
|
|
{
|
|
result.add(actionDefinition);
|
|
}
|
|
else
|
|
{
|
|
// Have to do a check for all subtypes of the applicable types
|
|
for (QName applicableType : actionDefinition.getApplicableTypes())
|
|
{
|
|
if (this.dictionaryService.isSubClass(nodeType, applicableType))
|
|
{
|
|
result.add(actionDefinition);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result.add(actionDefinition);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.action.ActionService#getActionConditionDefinition(java.lang.String)
|
|
*/
|
|
public ActionConditionDefinition getActionConditionDefinition(String name)
|
|
{
|
|
return this.conditionDefinitions.get(name);
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.action.ActionService#getActionConditionDefinitions()
|
|
*/
|
|
public List<ActionConditionDefinition> getActionConditionDefinitions()
|
|
{
|
|
return new ArrayList<ActionConditionDefinition>(this.conditionDefinitions.values());
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.action.ActionService#getParameterConstraint(java.lang.String)
|
|
*/
|
|
public ParameterConstraint getParameterConstraint(String name)
|
|
{
|
|
return this.parameterConstraints.get(name);
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.action.ActionService#getParameterConstraints()
|
|
*/
|
|
public List<ParameterConstraint> getParameterConstraints()
|
|
{
|
|
return new ArrayList<ParameterConstraint>(this.parameterConstraints.values());
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.action.ActionService#createActionCondition(java.lang.String)
|
|
*/
|
|
public ActionCondition createActionCondition(String name)
|
|
{
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("Creating Action Condition - [" + name + "]");
|
|
|
|
if (CompositeActionCondition.COMPOSITE_CONDITION.equals(name))
|
|
{
|
|
return new CompositeActionConditionImpl(GUID.generate());
|
|
}
|
|
|
|
return new ActionConditionImpl(GUID.generate(), name);
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.action.ActionService#createActionCondition(java.lang.String,
|
|
* java.util.Map)
|
|
*/
|
|
public ActionCondition createActionCondition(String name, Map<String, Serializable> params)
|
|
{
|
|
ActionCondition condition = createActionCondition(name);
|
|
condition.setParameterValues(params);
|
|
return condition;
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.action.ActionService#createAction()
|
|
*/
|
|
public Action createAction(String name)
|
|
{
|
|
return new ActionImpl(null, GUID.generate(), name, null);
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.action.ActionService#createAction(java.lang.String,
|
|
* java.util.Map)
|
|
*/
|
|
public Action createAction(String name, Map<String, Serializable> params)
|
|
{
|
|
Action action = createAction(name);
|
|
action.setParameterValues(params);
|
|
return action;
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.action.ActionService#createCompositeAction()
|
|
*/
|
|
public CompositeAction createCompositeAction()
|
|
{
|
|
return new CompositeActionImpl(null, GUID.generate());
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.action.ActionService#createCompositeActionCondition()
|
|
*/
|
|
public CompositeActionCondition createCompositeActionCondition()
|
|
{
|
|
return new CompositeActionConditionImpl(GUID.generate());
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.action.ActionService#evaluateAction(org.alfresco.service.cmr.action.Action,
|
|
* org.alfresco.service.cmr.repository.NodeRef)
|
|
*/
|
|
public boolean evaluateAction(Action action, NodeRef actionedUponNodeRef)
|
|
{
|
|
boolean result = true;
|
|
|
|
if (action.hasActionConditions() == true)
|
|
{
|
|
List<ActionCondition> actionConditions = action.getActionConditions();
|
|
for (ActionCondition condition : actionConditions)
|
|
{
|
|
boolean tempresult = evaluateActionCondition(condition, actionedUponNodeRef);
|
|
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("\tCondition " + condition.getActionConditionDefinitionName() + " Result - "
|
|
+ tempresult);
|
|
|
|
result = result && tempresult;
|
|
}
|
|
}
|
|
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("\tAll Condition Evaluation Result - " + result);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Evaluates the actions by finding corresponding actionEvaluators in
|
|
* applicationContext (registered through Spring). Composite conditions are
|
|
* evaluated here as well. It is also possible to have composite actions
|
|
* inside composite actions.
|
|
*
|
|
* @see org.alfresco.service.cmr.action.ActionService#evaluateActionCondition(org.alfresco.service.cmr.action.ActionCondition,
|
|
* org.alfresco.service.cmr.repository.NodeRef)
|
|
*/
|
|
public boolean evaluateActionCondition(ActionCondition condition, NodeRef actionedUponNodeRef)
|
|
{
|
|
if (condition instanceof CompositeActionCondition)
|
|
{
|
|
CompositeActionCondition compositeCondition = (CompositeActionCondition) condition;
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
logger.debug("Evaluating Composite Condition - BOOLEAN CONDITION IS "
|
|
+ (compositeCondition.isORCondition() ? "OR" : "AND"));
|
|
}
|
|
|
|
if (!compositeCondition.hasActionConditions())
|
|
{
|
|
throw new IllegalStateException("CompositeActionCondition has no subconditions.");
|
|
}
|
|
|
|
boolean result;
|
|
if (compositeCondition.isORCondition())
|
|
{
|
|
result = false;
|
|
}
|
|
else
|
|
{
|
|
result = true;
|
|
}
|
|
|
|
for (ActionCondition simplecondition : compositeCondition.getActionConditions())
|
|
{
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
logger.debug(
|
|
"Evaluating composite condition " + simplecondition.getActionConditionDefinitionName());
|
|
}
|
|
|
|
if (compositeCondition.isORCondition())
|
|
{
|
|
result = result || evaluateSimpleCondition(simplecondition, actionedUponNodeRef);
|
|
|
|
// Short circuit for the OR condition
|
|
if (result)
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
result = result && evaluateSimpleCondition(simplecondition, actionedUponNodeRef);
|
|
// Short circuit for the AND condition
|
|
if (!result)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (compositeCondition.getInvertCondition())
|
|
{
|
|
return !result;
|
|
}
|
|
else
|
|
{
|
|
return result;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return evaluateSimpleCondition(condition, actionedUponNodeRef);
|
|
}
|
|
}
|
|
|
|
private boolean evaluateSimpleCondition(ActionCondition condition, NodeRef actionedUponNodeRef)
|
|
{
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
logger.debug("Evaluating simple condition " + condition.getActionConditionDefinitionName());
|
|
}
|
|
// Evaluate the condition
|
|
ActionConditionEvaluator evaluator = (ActionConditionEvaluator) this.applicationContext.getBean(condition
|
|
.getActionConditionDefinitionName());
|
|
return evaluator.evaluate(condition, actionedUponNodeRef);
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.action.ActionService#executeAction(org.alfresco.service.cmr.action.Action,
|
|
* org.alfresco.service.cmr.repository.NodeRef, boolean)
|
|
*/
|
|
public void executeAction(Action action, NodeRef actionedUponNodeRef, boolean checkConditions)
|
|
{
|
|
executeAction(action, actionedUponNodeRef, checkConditions, action.getExecuteAsychronously());
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.action.ActionService#executeAction(org.alfresco.service.cmr.action.Action,
|
|
* org.alfresco.service.cmr.repository.NodeRef, boolean)
|
|
*/
|
|
public void executeAction(Action action, NodeRef actionedUponNodeRef, boolean checkConditions,
|
|
boolean executeAsychronously)
|
|
{
|
|
Set<String> actionChain = this.currentActionChain.get();
|
|
|
|
if (executeAsychronously == false)
|
|
{
|
|
executeActionImpl(action, actionedUponNodeRef, checkConditions, false, actionChain);
|
|
}
|
|
else
|
|
{
|
|
// Add to the post transaction pending action list
|
|
addPostTransactionPendingAction(action, actionedUponNodeRef, checkConditions, actionChain);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* called by transaction service.
|
|
*/
|
|
public void postCommit()
|
|
{
|
|
for (PendingAction pendingAction : getPostTransactionPendingActions())
|
|
{
|
|
queueAction(pendingAction);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
private void queueAction(PendingAction action)
|
|
{
|
|
// Get the right queue
|
|
AsynchronousActionExecutionQueue queue = getQueue(action.action);
|
|
|
|
// Queue the action for execution
|
|
queue.executeAction(this, action.getAction(), action.getActionedUponNodeRef(), action.getCheckConditions(),
|
|
action.getActionChain());
|
|
}
|
|
|
|
/**
|
|
* Queue a compensating action for execution against a specific node
|
|
*/
|
|
private void queueAction(Action compensatingAction, NodeRef actionedUponNodeRef)
|
|
{
|
|
// Get the right queue
|
|
AsynchronousActionExecutionQueue queue = getQueue(compensatingAction);
|
|
|
|
// Queue the action for execution
|
|
queue.executeAction(this, compensatingAction, actionedUponNodeRef, false, null);
|
|
}
|
|
|
|
/**
|
|
* Get the queue to use for asynchronous execution of the given action.
|
|
*/
|
|
private AsynchronousActionExecutionQueue getQueue(Action action)
|
|
{
|
|
ActionExecuter executer = (ActionExecuter) this.applicationContext.getBean(action.getActionDefinitionName());
|
|
AsynchronousActionExecutionQueue queue = null;
|
|
|
|
String queueName = executer.getQueueName();
|
|
if (queueName == null)
|
|
{
|
|
queue = asynchronousActionExecutionQueues.get("");
|
|
}
|
|
else
|
|
{
|
|
queue = asynchronousActionExecutionQueues.get(queueName);
|
|
}
|
|
if (queue == null)
|
|
{
|
|
// can't get queue
|
|
throw new ActionServiceException("Unable to get AsynchronousActionExecutionQueue name: " + queueName);
|
|
}
|
|
|
|
return queue;
|
|
}
|
|
|
|
/**
|
|
* Get whether the action should be tracked by the {@link ActionTrackingService} or not.
|
|
*
|
|
* @param action the action, which may or may not have any say about the status tracking
|
|
* @return <tt>true</tt> if the status must be tracked, otherwise <tt>false</tt>
|
|
*/
|
|
private boolean getTrackStatus(Action action)
|
|
{
|
|
Boolean trackStatusManual = action.getTrackStatus();
|
|
if (trackStatusManual != null)
|
|
{
|
|
return trackStatusManual.booleanValue();
|
|
}
|
|
// The flag has not be changed for the specific action, so use the value from the
|
|
// action definition.
|
|
ActionDefinition actionDef = getActionDefinition(action.getActionDefinitionName());
|
|
if (actionDef == null)
|
|
{
|
|
return false; // default to 'false' if the definition has disappeared
|
|
}
|
|
else
|
|
{
|
|
return actionDef.getTrackStatus();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void executeActionImpl(Action action, NodeRef actionedUponNodeRef, boolean checkConditions,
|
|
boolean executedAsynchronously, Set<String> actionChain)
|
|
{
|
|
if (logger.isDebugEnabled() == true)
|
|
{
|
|
StringBuilder builder = new StringBuilder("Execute action impl action chain = ");
|
|
if (actionChain == null)
|
|
{
|
|
builder.append("null");
|
|
}
|
|
else
|
|
{
|
|
for (String value : actionChain)
|
|
{
|
|
builder.append(value).append(" ");
|
|
}
|
|
}
|
|
logger.debug(builder.toString());
|
|
logger.debug("Current action = " + action.getId());
|
|
}
|
|
|
|
// get the current user early in case the process fails and we are
|
|
// unable to do it later
|
|
String currentUserName = this.authenticationContext.getCurrentUserName();
|
|
|
|
if (actionChain == null || actionChain.contains(action.getId()) == false)
|
|
{
|
|
if (logger.isDebugEnabled() == true)
|
|
{
|
|
logger.debug("Doing executeActionImpl");
|
|
}
|
|
|
|
try
|
|
{
|
|
Set<String> origActionChain = null;
|
|
|
|
if (actionChain == null)
|
|
{
|
|
actionChain = new HashSet<String>();
|
|
}
|
|
else
|
|
{
|
|
origActionChain = new HashSet<String>(actionChain);
|
|
}
|
|
actionChain.add(action.getId());
|
|
this.currentActionChain.set(actionChain);
|
|
|
|
if (logger.isDebugEnabled() == true)
|
|
{
|
|
logger.debug("Adding " + action.getActionDefinitionName() + ", " + action.getId() + " to action chain.");
|
|
}
|
|
|
|
try
|
|
{
|
|
// Check and execute now
|
|
if (checkConditions == false || evaluateAction(action, actionedUponNodeRef) == true)
|
|
{
|
|
if (getTrackStatus(action))
|
|
{
|
|
// Mark the action as starting
|
|
actionTrackingService.recordActionExecuting(action);
|
|
}
|
|
|
|
RunningAction runningAction = monitor.actionStarted(action);
|
|
|
|
try
|
|
{
|
|
// Execute the action
|
|
directActionExecution(action, actionedUponNodeRef);
|
|
}
|
|
catch (Throwable e)
|
|
{
|
|
runningAction.setException(e);
|
|
throw e;
|
|
}
|
|
finally
|
|
{
|
|
monitor.actionCompleted(runningAction);
|
|
}
|
|
|
|
if (getTrackStatus(action))
|
|
{
|
|
// Mark it as having worked
|
|
actionTrackingService.recordActionComplete(action);
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (origActionChain == null)
|
|
{
|
|
this.currentActionChain.remove();
|
|
}
|
|
else
|
|
{
|
|
this.currentActionChain.set(origActionChain);
|
|
}
|
|
|
|
if (logger.isDebugEnabled() == true)
|
|
{
|
|
logger.debug("Resetting the action chain.");
|
|
}
|
|
}
|
|
}
|
|
catch (ActionServiceTransientException transientException)
|
|
{
|
|
// This is a non-fatal exception which will be recorded as a failed action,
|
|
// but which will not lead to the execution of any compensating action
|
|
if (getTrackStatus(action))
|
|
{
|
|
actionTrackingService.recordActionFailure(action, transientException);
|
|
}
|
|
}
|
|
catch (Throwable exception)
|
|
{
|
|
// DH: No logging of the exception. Leave the logging decision
|
|
// to the client code,
|
|
// which can handle the rethrown exception.
|
|
if (executedAsynchronously == true)
|
|
{
|
|
// If one is specified, queue the compensating action ready
|
|
// for execution
|
|
Action compensatingAction = action.getCompensatingAction();
|
|
if (compensatingAction != null)
|
|
{
|
|
// Set the current user
|
|
((ActionImpl) compensatingAction).setRunAsUser(currentUserName);
|
|
queueAction(compensatingAction, actionedUponNodeRef);
|
|
}
|
|
}
|
|
|
|
if (getTrackStatus(action))
|
|
{
|
|
// Have the failure logged on the action
|
|
actionTrackingService.recordActionFailure(action, exception);
|
|
}
|
|
|
|
// Rethrow the exception
|
|
if (exception instanceof RuntimeException)
|
|
{
|
|
throw (RuntimeException) exception;
|
|
}
|
|
else
|
|
{
|
|
throw new ActionServiceException(ERR_FAIL, exception);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.repo.action.RuntimeActionService#directActionExecution(org.alfresco.service.cmr.action.Action,
|
|
* org.alfresco.service.cmr.repository.NodeRef)
|
|
*/
|
|
public void directActionExecution(Action action, NodeRef actionedUponNodeRef)
|
|
{
|
|
// Debug output
|
|
if (logger.isDebugEnabled() == true)
|
|
{
|
|
logger
|
|
.debug("The action is being executed as the user: "
|
|
+ this.authenticationContext.getCurrentUserName());
|
|
}
|
|
|
|
// Get the action executer and execute
|
|
ActionExecuter executer = (ActionExecuter) this.applicationContext.getBean(action.getActionDefinitionName());
|
|
executer.execute(action, actionedUponNodeRef);
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.action.ActionService#executeAction(org.alfresco.service.cmr.action.Action,
|
|
* NodeRef)
|
|
*/
|
|
public void executeAction(Action action, NodeRef actionedUponNodeRef)
|
|
{
|
|
executeAction(action, actionedUponNodeRef, true);
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.repo.action.RuntimeActionService#registerActionConditionEvaluator(org.alfresco.repo.action.evaluator.ActionConditionEvaluator)
|
|
*/
|
|
public void registerActionConditionEvaluator(ActionConditionEvaluator actionConditionEvaluator)
|
|
{
|
|
ActionConditionDefinition cond = actionConditionEvaluator.getActionConditionDefintion();
|
|
this.conditionDefinitions.put(cond.getName(), cond);
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.repo.action.RuntimeActionService#registerActionExecuter(org.alfresco.repo.action.executer.ActionExecuter)
|
|
*/
|
|
public void registerActionExecuter(ActionExecuter actionExecuter)
|
|
{
|
|
ActionDefinition action = actionExecuter.getActionDefinition();
|
|
this.actionDefinitions.put(action.getName(), action);
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.repo.action.RuntimeActionService#registerParameterConstraint(org.alfresco.service.cmr.action.ParameterConstraint)
|
|
*/
|
|
public void registerParameterConstraint(ParameterConstraint parameterConstraint)
|
|
{
|
|
this.parameterConstraints.put(parameterConstraint.getName(), parameterConstraint);
|
|
}
|
|
|
|
/**
|
|
* Gets the action node ref from the action id
|
|
*
|
|
* @param nodeRef the node reference
|
|
* @param actionId the action id
|
|
* @return the action node reference
|
|
*/
|
|
private NodeRef getActionNodeRefFromId(NodeRef nodeRef, String actionId)
|
|
{
|
|
NodeRef result = null;
|
|
|
|
if (this.nodeService.hasAspect(nodeRef, ActionModel.ASPECT_ACTIONS) == true)
|
|
{
|
|
DynamicNamespacePrefixResolver namespacePrefixResolver = new DynamicNamespacePrefixResolver();
|
|
namespacePrefixResolver.registerNamespace(NamespaceService.SYSTEM_MODEL_PREFIX,
|
|
NamespaceService.SYSTEM_MODEL_1_0_URI);
|
|
|
|
List<NodeRef> nodeRefs = searchService.selectNodes(getSavedActionFolderRef(nodeRef), "*[@sys:"
|
|
+ ContentModel.PROP_NODE_UUID.getLocalName() + "='" + actionId + "']", null,
|
|
namespacePrefixResolver, false);
|
|
if (nodeRefs.size() != 0)
|
|
{
|
|
result = nodeRefs.get(0);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.action.ActionService#saveAction(org.alfresco.service.cmr.repository.NodeRef,
|
|
* org.alfresco.service.cmr.action.Action)
|
|
*/
|
|
public void saveAction(NodeRef nodeRef, Action action)
|
|
{
|
|
NodeRef actionNodeRef = getActionNodeRefFromId(nodeRef, action.getId());
|
|
if (actionNodeRef == null)
|
|
{
|
|
if (this.nodeService.hasAspect(nodeRef, ActionModel.ASPECT_ACTIONS) == false)
|
|
{
|
|
// Apply the actionable aspect
|
|
this.nodeService.addAspect(nodeRef, ActionModel.ASPECT_ACTIONS, null);
|
|
}
|
|
|
|
// Create the action and reference
|
|
actionNodeRef = createActionNodeRef(action, getSavedActionFolderRef(nodeRef), ContentModel.ASSOC_CONTAINS,
|
|
ActionModel.ASSOC_NAME_ACTIONS);
|
|
}
|
|
saveActionImpl(actionNodeRef, action);
|
|
}
|
|
|
|
public NodeRef createActionNodeRef(Action action, NodeRef parentNodeRef, QName assocTypeName, QName assocName)
|
|
{
|
|
Map<QName, Serializable> props = new HashMap<QName, Serializable>(2);
|
|
props.put(ActionModel.PROP_DEFINITION_NAME, action.getActionDefinitionName());
|
|
props.put(ContentModel.PROP_NODE_UUID, action.getId());
|
|
|
|
QName actionType = ActionModel.TYPE_ACTION;
|
|
if (action instanceof ActionList<?>)
|
|
{
|
|
actionType = ActionModel.TYPE_COMPOSITE_ACTION;
|
|
}
|
|
|
|
// Create the action node
|
|
NodeRef actionNodeRef = this.nodeService.createNode(parentNodeRef, assocTypeName, assocName, actionType, props)
|
|
.getChildRef();
|
|
|
|
// Update the created details and the node reference
|
|
((ActionImpl) action).setCreator((String) this.nodeService
|
|
.getProperty(actionNodeRef, ContentModel.PROP_CREATOR));
|
|
((ActionImpl) action).setCreatedDate((Date) this.nodeService.getProperty(actionNodeRef,
|
|
ContentModel.PROP_CREATED));
|
|
((ActionImpl) action).setNodeRef(actionNodeRef);
|
|
|
|
return actionNodeRef;
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.repo.action.RuntimeActionService#saveActionImpl(org.alfresco.service.cmr.repository.NodeRef,
|
|
* org.alfresco.service.cmr.action.Action)
|
|
*/
|
|
public void saveActionImpl(NodeRef actionNodeRef, Action action)
|
|
{
|
|
// Save action properties
|
|
saveActionProperties(actionNodeRef, action);
|
|
|
|
// Update the parameters of the action
|
|
saveParameters(actionNodeRef, action);
|
|
|
|
// Update the conditions of the action
|
|
saveConditions(actionNodeRef, action);
|
|
|
|
if (action instanceof ActionList<?>)
|
|
{
|
|
// Update composite action
|
|
saveCompositeActions(actionNodeRef, (ActionList<?>) action);
|
|
}
|
|
|
|
// Update the modified details
|
|
((ActionImpl) action).setModifier((String) this.nodeService.getProperty(actionNodeRef,
|
|
ContentModel.PROP_MODIFIER));
|
|
((ActionImpl) action).setModifiedDate((Date) this.nodeService.getProperty(actionNodeRef,
|
|
ContentModel.PROP_MODIFIED));
|
|
}
|
|
|
|
/**
|
|
* Save the action property values
|
|
*
|
|
* @param actionNodeRef the action node reference
|
|
* @param action the action
|
|
*/
|
|
private void saveActionProperties(NodeRef actionNodeRef, Action action)
|
|
{
|
|
// Update the action property values
|
|
Map<QName, Serializable> props = this.nodeService.getProperties(actionNodeRef);
|
|
props.put(ActionModel.PROP_ACTION_TITLE, action.getTitle());
|
|
props.put(ActionModel.PROP_ACTION_DESCRIPTION, action.getDescription());
|
|
if (action.getTrackStatus() != null)
|
|
{
|
|
props.put(ActionModel.PROP_TRACK_STATUS, action.getTrackStatus());
|
|
}
|
|
props.put(ActionModel.PROP_EXECUTE_ASYNCHRONOUSLY, action.getExecuteAsychronously());
|
|
|
|
props.put(ActionModel.PROP_EXECUTION_START_DATE, action.getExecutionStartDate());
|
|
props.put(ActionModel.PROP_EXECUTION_END_DATE, action.getExecutionEndDate());
|
|
props.put(ActionModel.PROP_EXECUTION_ACTION_STATUS, action.getExecutionStatus());
|
|
props.put(ActionModel.PROP_EXECUTION_FAILURE_MESSAGE, action.getExecutionFailureMessage());
|
|
|
|
this.nodeService.setProperties(actionNodeRef, props);
|
|
|
|
// Update the compensating action (model should enforce the singularity
|
|
// of this association)
|
|
Action compensatingAction = action.getCompensatingAction();
|
|
List<ChildAssociationRef> assocs = this.nodeService.getChildAssocs(actionNodeRef, RegexQNamePattern.MATCH_ALL,
|
|
ActionModel.ASSOC_COMPENSATING_ACTION);
|
|
if (assocs.size() == 0)
|
|
{
|
|
if (compensatingAction != null)
|
|
{
|
|
// Map<QName, Serializable> props2 = new HashMap<QName,
|
|
// Serializable>(2);
|
|
// props2.put(ActionModel.PROP_DEFINITION_NAME,
|
|
// compensatingAction.getActionDefinitionName());
|
|
// props2.put(ContentModel.PROP_NODE_UUID,
|
|
// compensatingAction.getId());
|
|
|
|
// NodeRef compensatingActionNodeRef =
|
|
// this.nodeService.createNode(
|
|
// / actionNodeRef,
|
|
// ActionModel.ASSOC_COMPENSATING_ACTION,
|
|
// ActionModel.ASSOC_COMPENSATING_ACTION,
|
|
// ActionModel.TYPE_ACTION,
|
|
// props2).getChildRef();
|
|
|
|
// Create the compensating node reference
|
|
NodeRef compensatingActionNodeRef = createActionNodeRef(compensatingAction, actionNodeRef,
|
|
ActionModel.ASSOC_COMPENSATING_ACTION, ActionModel.ASSOC_COMPENSATING_ACTION);
|
|
saveActionImpl(compensatingActionNodeRef, compensatingAction);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ChildAssociationRef assoc = assocs.get(0);
|
|
if (compensatingAction == null)
|
|
{
|
|
this.nodeService.removeChild(actionNodeRef, assoc.getChildRef());
|
|
}
|
|
else
|
|
{
|
|
saveActionImpl(assoc.getChildRef(), compensatingAction);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Save the actions of a composite action
|
|
*
|
|
* @param compositeActionNodeRef the node reference of the composite action
|
|
* @param action2 the composite action
|
|
*/
|
|
private void saveCompositeActions(NodeRef compositeActionNodeRef, ActionList<?> action2)
|
|
{
|
|
// TODO Need a way of sorting the order of the actions
|
|
|
|
Map<String, Action> idToAction = new HashMap<String, Action>();
|
|
List<String> orderedIds = new ArrayList<String>();
|
|
for (Action action : action2.getActions())
|
|
{
|
|
idToAction.put(action.getId(), action);
|
|
orderedIds.add(action.getId());
|
|
}
|
|
|
|
List<ChildAssociationRef> actionRefs = this.nodeService.getChildAssocs(compositeActionNodeRef,
|
|
RegexQNamePattern.MATCH_ALL, ActionModel.ASSOC_ACTIONS);
|
|
for (ChildAssociationRef actionRef : actionRefs)
|
|
{
|
|
NodeRef actionNodeRef = actionRef.getChildRef();
|
|
if (idToAction.containsKey(actionNodeRef.getId()) == false)
|
|
{
|
|
// Delete the action
|
|
this.nodeService.removeChild(compositeActionNodeRef, actionNodeRef);
|
|
}
|
|
else
|
|
{
|
|
// Update the action
|
|
Action action = idToAction.get(actionNodeRef.getId());
|
|
saveActionImpl(actionNodeRef, action);
|
|
orderedIds.remove(actionNodeRef.getId());
|
|
}
|
|
|
|
}
|
|
|
|
// Create the actions remaining
|
|
for (String actionId : orderedIds)
|
|
{
|
|
Action action = idToAction.get(actionId);
|
|
|
|
Map<QName, Serializable> props = new HashMap<QName, Serializable>(2);
|
|
props.put(ActionModel.PROP_DEFINITION_NAME, action.getActionDefinitionName());
|
|
props.put(ContentModel.PROP_NODE_UUID, action.getId());
|
|
|
|
NodeRef actionNodeRef = this.nodeService.createNode(compositeActionNodeRef, ActionModel.ASSOC_ACTIONS,
|
|
ActionModel.ASSOC_ACTIONS, ActionModel.TYPE_ACTION, props).getChildRef();
|
|
|
|
// Update the created details and the node reference
|
|
((ActionImpl) action).setCreator((String) this.nodeService.getProperty(actionNodeRef,
|
|
ContentModel.PROP_CREATOR));
|
|
((ActionImpl) action).setCreatedDate((Date) this.nodeService.getProperty(actionNodeRef,
|
|
ContentModel.PROP_CREATED));
|
|
((ActionImpl) action).setNodeRef(actionNodeRef);
|
|
|
|
saveActionImpl(actionNodeRef, action);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Saves the conditions associated with an action.
|
|
*
|
|
* @param actionNodeRef the action node reference
|
|
* @param action the action
|
|
*/
|
|
private void saveConditions(NodeRef actionNodeRef, Action action)
|
|
{
|
|
// TODO Need a way of sorting out the order of the conditions
|
|
List<ActionCondition> actionConditionsList = action.getActionConditions();
|
|
saveActionConditionList(actionNodeRef, actionConditionsList, false);
|
|
}
|
|
|
|
private void saveActionConditionList(NodeRef actionNodeRef, List<ActionCondition> actionConditionsList,
|
|
boolean isComposite)
|
|
{
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("SaveActionCondition list, " + actionConditionsList.size() + (isComposite ? " Composite" : "")
|
|
+ " conditions to be saved");
|
|
|
|
Map<String, ActionCondition> idToCondition = new HashMap<String, ActionCondition>();
|
|
List<String> orderedIds = new ArrayList<String>();
|
|
|
|
for (ActionCondition actionCondition : actionConditionsList)
|
|
{
|
|
idToCondition.put(actionCondition.getId(), actionCondition);
|
|
orderedIds.add(actionCondition.getId());
|
|
}
|
|
|
|
List<ChildAssociationRef> conditionRefs = this.nodeService.getChildAssocs(actionNodeRef,
|
|
RegexQNamePattern.MATCH_ALL, !isComposite ? ActionModel.ASSOC_CONDITIONS
|
|
: ActionModel.ASSOC_COMPOSITE_ACTION_CONDITION);
|
|
|
|
for (ChildAssociationRef conditionRef : conditionRefs)
|
|
{
|
|
NodeRef conditionNodeRef = conditionRef.getChildRef();
|
|
if (idToCondition.containsKey(conditionNodeRef.getId()) == false)
|
|
{
|
|
// Delete the condition
|
|
this.nodeService.removeChild(actionNodeRef, conditionNodeRef);
|
|
}
|
|
else
|
|
{
|
|
saveConditionProperties(conditionNodeRef, idToCondition.get(conditionNodeRef.getId()));
|
|
// Update the conditions parameters
|
|
saveParameters(conditionNodeRef, idToCondition.get(conditionNodeRef.getId()));
|
|
orderedIds.remove(conditionNodeRef.getId());
|
|
}
|
|
}
|
|
|
|
// Create the conditions remaining
|
|
for (String nextId : orderedIds)
|
|
{
|
|
ActionCondition actionCondition = idToCondition.get(nextId);
|
|
|
|
if (!isComposite && actionCondition instanceof CompositeActionCondition)
|
|
{
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("Saving Composite Condition");
|
|
|
|
NodeRef conditionNodeRef = saveActionCondition(actionNodeRef, actionCondition,
|
|
ActionModel.ASSOC_CONDITIONS, ActionModel.TYPE_COMPOSITE_ACTION_CONDITION);
|
|
saveActionConditionList(conditionNodeRef, ((CompositeActionCondition) actionCondition)
|
|
.getActionConditions(), true);
|
|
}
|
|
else
|
|
{
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("Saving Condition " + actionCondition.getActionConditionDefinitionName());
|
|
|
|
@SuppressWarnings("unused")
|
|
NodeRef conditionNodeRef = saveActionCondition(actionNodeRef, actionCondition,
|
|
!isComposite ? ActionModel.ASSOC_CONDITIONS : ActionModel.ASSOC_COMPOSITE_ACTION_CONDITION,
|
|
ActionModel.TYPE_ACTION_CONDITION);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* private void saveCompositeActionConditionList(NodeRef
|
|
* compositeConditionRef, List<ActionCondition> actionConditionsList) { if
|
|
* (logger.isDebugEnabled())
|
|
* logger.debug("SaveActionCondition list Composite, "+
|
|
* actionConditionsList.size() + " conditions to be saved"); Map<String,
|
|
* ActionCondition> idToCondition = new HashMap<String, ActionCondition>();
|
|
* List<String> orderedIds = new ArrayList<String>(); for (ActionCondition
|
|
* actionCondition : actionConditionsList) {
|
|
* idToCondition.put(actionCondition.getId(), actionCondition);
|
|
* orderedIds.add(actionCondition.getId()); } List<ChildAssociationRef>
|
|
* conditionRefs = this.nodeService.getChildAssocs(compositeConditionRef,
|
|
* RegexQNamePattern.MATCH_ALL, ActionModel.ASSOC_CONDITIONS); for
|
|
* (ChildAssociationRef conditionRef : conditionRefs) { NodeRef
|
|
* conditionNodeRef = conditionRef.getChildRef(); if
|
|
* (idToCondition.containsKey(conditionNodeRef.getId()) == false) { //
|
|
* Delete the condition this.nodeService.removeChild(compositeConditionRef,
|
|
* conditionNodeRef); } else { saveConditionProperties(conditionNodeRef,
|
|
* idToCondition.get(conditionNodeRef.getId())); // Update the conditions
|
|
* parameters saveParameters(conditionNodeRef,
|
|
* idToCondition.get(conditionNodeRef.getId()));
|
|
* orderedIds.remove(conditionNodeRef.getId()); } } // Create the conditions
|
|
* remaining for (String nextId : orderedIds) { ActionCondition
|
|
* actionCondition = idToCondition.get(nextId); NodeRef conditionNodeRef =
|
|
* saveActionCondition(compositeConditionRef, actionCondition,
|
|
* ActionModel.ASSOC_CONDITIONS, ActionModel.TYPE_ACTION_CONDITION); } }
|
|
*/
|
|
|
|
private NodeRef saveActionCondition(NodeRef actionNodeRef, ActionCondition actionCondition, QName AssociationQName,
|
|
QName typeName)
|
|
{
|
|
Map<QName, Serializable> props = new HashMap<QName, Serializable>(2);
|
|
props.put(ActionModel.PROP_DEFINITION_NAME, actionCondition.getActionConditionDefinitionName());
|
|
props.put(ContentModel.PROP_NODE_UUID, actionCondition.getId());
|
|
|
|
NodeRef conditionNodeRef = this.nodeService.createNode(actionNodeRef, AssociationQName, AssociationQName,
|
|
typeName, props).getChildRef();
|
|
|
|
saveConditionProperties(conditionNodeRef, actionCondition);
|
|
saveParameters(conditionNodeRef, actionCondition);
|
|
return conditionNodeRef;
|
|
}
|
|
|
|
/**
|
|
* Save the condition properties
|
|
*
|
|
* @param conditionNodeRef
|
|
* @param condition
|
|
*/
|
|
private void saveConditionProperties(NodeRef conditionNodeRef, ActionCondition condition)
|
|
{
|
|
this.nodeService.setProperty(conditionNodeRef, ActionModel.PROP_CONDITION_INVERT, condition
|
|
.getInvertCondition());
|
|
|
|
if (condition instanceof CompositeActionCondition)
|
|
{
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
logger.debug("SAVING OR = " + ((CompositeActionCondition) condition).isORCondition());
|
|
}
|
|
this.nodeService.setProperty(conditionNodeRef, ActionModel.PROP_CONDITION_ANDOR, new Boolean(
|
|
((CompositeActionCondition) condition).isORCondition()));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Saves the parameters associated with an action or condition
|
|
*
|
|
* @param parameterizedNodeRef the parameterized item node reference
|
|
* @param item the parameterized item
|
|
*/
|
|
private void saveParameters(NodeRef parameterizedNodeRef, ParameterizedItem item)
|
|
{
|
|
Map<String, Serializable> parameterMap = new HashMap<String, Serializable>();
|
|
parameterMap.putAll(item.getParameterValues());
|
|
|
|
List<ChildAssociationRef> parameters = this.nodeService.getChildAssocs(parameterizedNodeRef,
|
|
ActionModel.ASSOC_PARAMETERS, ActionModel.ASSOC_PARAMETERS);
|
|
for (ChildAssociationRef ref : parameters)
|
|
{
|
|
NodeRef paramNodeRef = ref.getChildRef();
|
|
Map<QName, Serializable> nodeRefParameterMap = this.nodeService.getProperties(paramNodeRef);
|
|
String paramName = (String) nodeRefParameterMap.get(ActionModel.PROP_PARAMETER_NAME);
|
|
if (parameterMap.containsKey(paramName) == false)
|
|
{
|
|
// Delete parameter from node ref
|
|
this.nodeService.removeChild(parameterizedNodeRef, paramNodeRef);
|
|
}
|
|
else
|
|
{
|
|
// Update the parameter value
|
|
nodeRefParameterMap.put(ActionModel.PROP_PARAMETER_VALUE, parameterMap.get(paramName));
|
|
this.nodeService.setProperties(paramNodeRef, nodeRefParameterMap);
|
|
parameterMap.remove(paramName);
|
|
}
|
|
}
|
|
|
|
// Add any remaining parameters
|
|
for (Map.Entry<String, Serializable> entry : parameterMap.entrySet())
|
|
{
|
|
Map<QName, Serializable> nodeRefProperties = new HashMap<QName, Serializable>(2);
|
|
nodeRefProperties.put(ActionModel.PROP_PARAMETER_NAME, entry.getKey());
|
|
nodeRefProperties.put(ActionModel.PROP_PARAMETER_VALUE, entry.getValue());
|
|
|
|
this.nodeService.createNode(parameterizedNodeRef, ActionModel.ASSOC_PARAMETERS,
|
|
ActionModel.ASSOC_PARAMETERS, ActionModel.TYPE_ACTION_PARAMETER, nodeRefProperties);
|
|
}
|
|
}
|
|
|
|
// TODO: Add copy behaviour
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.action.ActionService#getActions(org.alfresco.service.cmr.repository.NodeRef)
|
|
*/
|
|
public List<Action> getActions(NodeRef nodeRef)
|
|
{
|
|
List<Action> result = new ArrayList<Action>();
|
|
|
|
if (this.nodeService.exists(nodeRef) == true
|
|
&& this.nodeService.hasAspect(nodeRef, ActionModel.ASPECT_ACTIONS) == true)
|
|
{
|
|
List<ChildAssociationRef> actions = this.nodeService.getChildAssocs(getSavedActionFolderRef(nodeRef),
|
|
RegexQNamePattern.MATCH_ALL, ActionModel.ASSOC_NAME_ACTIONS);
|
|
for (ChildAssociationRef action : actions)
|
|
{
|
|
NodeRef actionNodeRef = action.getChildRef();
|
|
result.add(createAction(actionNodeRef));
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Create an action from the action node reference
|
|
*
|
|
* @param actionNodeRef the action node reference
|
|
* @return the action
|
|
*/
|
|
public Action createAction(NodeRef actionNodeRef)
|
|
{
|
|
Action result = null;
|
|
|
|
Map<QName, Serializable> properties = this.nodeService.getProperties(actionNodeRef);
|
|
|
|
QName actionType = this.nodeService.getType(actionNodeRef);
|
|
if (ActionModel.TYPE_COMPOSITE_ACTION.equals(actionType) == true)
|
|
{
|
|
// Create a composite action
|
|
result = new CompositeActionImpl(actionNodeRef, actionNodeRef.getId());
|
|
populateCompositeAction(actionNodeRef, (CompositeAction) result);
|
|
}
|
|
else
|
|
{
|
|
// Create an action
|
|
result = new ActionImpl(actionNodeRef, actionNodeRef.getId(), (String) properties
|
|
.get(ActionModel.PROP_DEFINITION_NAME));
|
|
populateAction(actionNodeRef, result);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Populate the details of the action from the node reference
|
|
*
|
|
* @param actionNodeRef the action node reference
|
|
* @param action the action
|
|
*/
|
|
private void populateAction(NodeRef actionNodeRef, Action action)
|
|
{
|
|
// Populate the action properties
|
|
populateActionProperties(actionNodeRef, action);
|
|
|
|
// Set the parameters
|
|
populateParameters(actionNodeRef, action);
|
|
|
|
// Set the conditions
|
|
List<ChildAssociationRef> conditions = this.nodeService.getChildAssocs(actionNodeRef,
|
|
RegexQNamePattern.MATCH_ALL, ActionModel.ASSOC_CONDITIONS);
|
|
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("Retrieving " + (conditions == null ? " null" : conditions.size()) + " conditions");
|
|
|
|
if (conditions != null)
|
|
{
|
|
for (ChildAssociationRef condition : conditions)
|
|
{
|
|
NodeRef conditionNodeRef = condition.getChildRef();
|
|
action.addActionCondition(createActionCondition(conditionNodeRef));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Populates the action properties from the node reference
|
|
*
|
|
* @param actionNodeRef the action node reference
|
|
* @param action the action
|
|
*/
|
|
private void populateActionProperties(NodeRef actionNodeRef, Action action)
|
|
{
|
|
Map<QName, Serializable> props = this.nodeService.getProperties(actionNodeRef);
|
|
|
|
action.setTitle((String) props.get(ActionModel.PROP_ACTION_TITLE));
|
|
action.setDescription((String) props.get(ActionModel.PROP_ACTION_DESCRIPTION));
|
|
|
|
Boolean trackStatusObj = (Boolean) props.get(ActionModel.PROP_TRACK_STATUS);
|
|
action.setTrackStatus(trackStatusObj); // Allowed to be null
|
|
|
|
Boolean executeAsynchObj = (Boolean) props.get(ActionModel.PROP_EXECUTE_ASYNCHRONOUSLY);
|
|
boolean executeAsynch = executeAsynchObj == null ? false : executeAsynchObj.booleanValue();
|
|
action.setExecuteAsynchronously(executeAsynch);
|
|
|
|
((ActionImpl) action).setCreator((String) props.get(ContentModel.PROP_CREATOR));
|
|
((ActionImpl) action).setCreatedDate((Date) props.get(ContentModel.PROP_CREATED));
|
|
((ActionImpl) action).setModifier((String) props.get(ContentModel.PROP_MODIFIER));
|
|
((ActionImpl) action).setModifiedDate((Date) props.get(ContentModel.PROP_MODIFIED));
|
|
|
|
((ActionImpl) action).setExecutionStartDate((Date) props.get(ActionModel.PROP_EXECUTION_START_DATE));
|
|
((ActionImpl) action).setExecutionEndDate((Date) props.get(ActionModel.PROP_EXECUTION_END_DATE));
|
|
((ActionImpl) action).setExecutionStatus(ActionStatus.valueOf(props.get(ActionModel.PROP_EXECUTION_ACTION_STATUS)));
|
|
((ActionImpl) action).setExecutionFailureMessage((String) props.get(ActionModel.PROP_EXECUTION_FAILURE_MESSAGE));
|
|
|
|
// Get the compensating action
|
|
List<ChildAssociationRef> assocs = this.nodeService.getChildAssocs(actionNodeRef, RegexQNamePattern.MATCH_ALL,
|
|
ActionModel.ASSOC_COMPENSATING_ACTION);
|
|
if (assocs.size() != 0)
|
|
{
|
|
Action compensatingAction = createAction(assocs.get(0).getChildRef());
|
|
action.setCompensatingAction(compensatingAction);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Populate the parameters of a parameterized item from the parameterized
|
|
* item node reference
|
|
*
|
|
* @param parameterizedItemNodeRef the parameterized item node reference
|
|
* @param parameterizedItem the parameterized item
|
|
*/
|
|
private void populateParameters(NodeRef parameterizedItemNodeRef, ParameterizedItem parameterizedItem)
|
|
{
|
|
List<ChildAssociationRef> parameters = this.nodeService.getChildAssocs(parameterizedItemNodeRef,
|
|
RegexQNamePattern.MATCH_ALL, ActionModel.ASSOC_PARAMETERS);
|
|
for (ChildAssociationRef parameter : parameters)
|
|
{
|
|
NodeRef parameterNodeRef = parameter.getChildRef();
|
|
Map<QName, Serializable> properties = this.nodeService.getProperties(parameterNodeRef);
|
|
parameterizedItem.setParameterValue((String) properties.get(ActionModel.PROP_PARAMETER_NAME), properties
|
|
.get(ActionModel.PROP_PARAMETER_VALUE));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates an action condition from an action condition node reference
|
|
*
|
|
* @param conditionNodeRef the condition node reference
|
|
* @return the action condition
|
|
*/
|
|
private ActionCondition createActionCondition(NodeRef conditionNodeRef)
|
|
{
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("\tCreateActionCondition: Retrieving Conditions from repository");
|
|
|
|
Map<QName, Serializable> properties = this.nodeService.getProperties(conditionNodeRef);
|
|
QName conditionType = this.nodeService.getType(conditionNodeRef);
|
|
|
|
ActionCondition condition = null;
|
|
if (ActionModel.TYPE_COMPOSITE_ACTION_CONDITION.equals(conditionType) == false)
|
|
{
|
|
condition = new ActionConditionImpl(conditionNodeRef.getId(), (String) properties
|
|
.get(ActionModel.PROP_DEFINITION_NAME));
|
|
}
|
|
else
|
|
{
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
logger.debug("\tRetrieving Composite Condition from repository");
|
|
}
|
|
|
|
// Create a composite condition
|
|
CompositeActionCondition compositeCondition = new CompositeActionConditionImpl(GUID.generate());
|
|
populateCompositeActionCondition(conditionNodeRef, compositeCondition);
|
|
|
|
condition = compositeCondition;
|
|
}
|
|
|
|
Boolean invert = (Boolean) this.nodeService.getProperty(conditionNodeRef, ActionModel.PROP_CONDITION_INVERT);
|
|
condition.setInvertCondition(invert == null ? false : invert.booleanValue());
|
|
|
|
populateParameters(conditionNodeRef, condition);
|
|
return condition;
|
|
}
|
|
|
|
private void populateCompositeActionCondition(NodeRef compositeNodeRef,
|
|
CompositeActionCondition compositeActionCondition)
|
|
{
|
|
List<ChildAssociationRef> conditions = this.nodeService.getChildAssocs(compositeNodeRef,
|
|
RegexQNamePattern.MATCH_ALL, ActionModel.ASSOC_COMPOSITE_ACTION_CONDITION);
|
|
|
|
Boolean OR = (Boolean) this.nodeService.getProperty(compositeNodeRef, ActionModel.PROP_CONDITION_ANDOR);
|
|
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
logger.debug("\tPopulating Composite Condition with subconditions, Condition OR = " + OR);
|
|
}
|
|
|
|
compositeActionCondition.setORCondition(OR == null ? false : OR.booleanValue());
|
|
|
|
for (ChildAssociationRef conditionNodeRef : conditions)
|
|
{
|
|
NodeRef actionNodeRef = conditionNodeRef.getChildRef();
|
|
ActionCondition currentCondition = createActionCondition(actionNodeRef);
|
|
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("\t\tAdding subcondition " + currentCondition.getActionConditionDefinitionName());
|
|
|
|
compositeActionCondition.addActionCondition(currentCondition);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Populates a composite action from a composite action node reference
|
|
*
|
|
* @param compositeNodeRef the composite action node reference
|
|
* @param compositeAction the composite action
|
|
*/
|
|
public void populateCompositeAction(NodeRef compositeNodeRef, CompositeAction compositeAction)
|
|
{
|
|
populateAction(compositeNodeRef, compositeAction);
|
|
|
|
List<ChildAssociationRef> actions = this.nodeService.getChildAssocs(compositeNodeRef,
|
|
RegexQNamePattern.MATCH_ALL, ActionModel.ASSOC_ACTIONS);
|
|
for (ChildAssociationRef action : actions)
|
|
{
|
|
NodeRef actionNodeRef = action.getChildRef();
|
|
compositeAction.addAction(createAction(actionNodeRef));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.action.ActionService#getAction(org.alfresco.service.cmr.repository.NodeRef,
|
|
* java.lang.String)
|
|
*/
|
|
public Action getAction(NodeRef nodeRef, String actionId)
|
|
{
|
|
Action result = null;
|
|
|
|
if (this.nodeService.exists(nodeRef) == true
|
|
&& this.nodeService.hasAspect(nodeRef, ActionModel.ASPECT_ACTIONS) == true)
|
|
{
|
|
NodeRef actionNodeRef = getActionNodeRefFromId(nodeRef, actionId);
|
|
if (actionNodeRef != null)
|
|
{
|
|
result = createAction(actionNodeRef);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.action.ActionService#removeAction(org.alfresco.service.cmr.repository.NodeRef,
|
|
* org.alfresco.service.cmr.action.Action)
|
|
*/
|
|
public void removeAction(NodeRef nodeRef, Action action)
|
|
{
|
|
if (this.nodeService.exists(nodeRef) == true
|
|
&& this.nodeService.hasAspect(nodeRef, ActionModel.ASPECT_ACTIONS) == true)
|
|
{
|
|
NodeRef actionNodeRef = getActionNodeRefFromId(nodeRef, action.getId());
|
|
if (actionNodeRef != null)
|
|
{
|
|
this.nodeService.removeChild(getSavedActionFolderRef(nodeRef), actionNodeRef);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.service.cmr.action.ActionService#removeAllActions(org.alfresco.service.cmr.repository.NodeRef)
|
|
*/
|
|
public void removeAllActions(NodeRef nodeRef)
|
|
{
|
|
if (this.nodeService.exists(nodeRef) == true
|
|
&& this.nodeService.hasAspect(nodeRef, ActionModel.ASPECT_ACTIONS) == true)
|
|
{
|
|
List<ChildAssociationRef> actions = new ArrayList<ChildAssociationRef>(this.nodeService.getChildAssocs(
|
|
getSavedActionFolderRef(nodeRef), RegexQNamePattern.MATCH_ALL, ActionModel.ASSOC_NAME_ACTIONS));
|
|
for (ChildAssociationRef action : actions)
|
|
{
|
|
this.nodeService.removeChild(getSavedActionFolderRef(nodeRef), action.getChildRef());
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add a pending action to the list to be queued for execution once the
|
|
* transaction is completed.
|
|
*
|
|
* @param action the action
|
|
* @param actionedUponNodeRef the actioned upon node reference
|
|
* @param checkConditions indicates whether to check the conditions before
|
|
* execution
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
private void addPostTransactionPendingAction(Action action, NodeRef actionedUponNodeRef, boolean checkConditions,
|
|
Set<String> actionChain)
|
|
{
|
|
if (logger.isDebugEnabled() == true)
|
|
{
|
|
StringBuilder builder = new StringBuilder("addPostTransactionPendingAction action chain = ");
|
|
if (actionChain == null)
|
|
{
|
|
builder.append("null");
|
|
}
|
|
else
|
|
{
|
|
for (String value : actionChain)
|
|
{
|
|
builder.append(value).append(" ");
|
|
}
|
|
}
|
|
logger.debug(builder.toString());
|
|
logger.debug("Current action = " + action.getId());
|
|
}
|
|
|
|
// Don't continue if the action is already in the action chain
|
|
if (actionChain == null || actionChain.contains(action.getId()) == false)
|
|
{
|
|
if (logger.isDebugEnabled() == true)
|
|
{
|
|
logger.debug("Doing addPostTransactionPendingAction");
|
|
}
|
|
|
|
// Set the run as user to the current user
|
|
if (logger.isDebugEnabled() == true)
|
|
{
|
|
logger.debug("The current user is: " + this.authenticationContext.getCurrentUserName());
|
|
}
|
|
((ActionImpl) action).setRunAsUser(this.authenticationContext.getCurrentUserName());
|
|
|
|
// Ensure that the transaction listener is bound to the transaction
|
|
AlfrescoTransactionSupport.bindListener(this.transactionListener);
|
|
|
|
// Add the pending action to the transaction resource
|
|
List<PendingAction> pendingActions = (List<PendingAction>) AlfrescoTransactionSupport
|
|
.getResource(POST_TRANSACTION_PENDING_ACTIONS);
|
|
if (pendingActions == null)
|
|
{
|
|
pendingActions = new ArrayList<PendingAction>();
|
|
AlfrescoTransactionSupport.bindResource(POST_TRANSACTION_PENDING_ACTIONS, pendingActions);
|
|
}
|
|
|
|
// Check that action has only been added to the list once
|
|
PendingAction pendingAction = new PendingAction(action, actionedUponNodeRef, checkConditions, actionChain);
|
|
if (pendingActions.contains(pendingAction) == false)
|
|
{
|
|
pendingActions.add(pendingAction);
|
|
|
|
if (getTrackStatus(action))
|
|
{
|
|
actionTrackingService.recordActionPending(action);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @see org.alfresco.repo.action.RuntimeActionService#getPostTransactionPendingActions()
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
private List<PendingAction> getPostTransactionPendingActions()
|
|
{
|
|
return (List<PendingAction>) AlfrescoTransactionSupport.getResource(POST_TRANSACTION_PENDING_ACTIONS);
|
|
}
|
|
|
|
/**
|
|
* Pending action details class
|
|
*/
|
|
private class PendingAction
|
|
{
|
|
/**
|
|
* The action
|
|
*/
|
|
private Action action;
|
|
|
|
/**
|
|
* The actioned upon node reference
|
|
*/
|
|
private NodeRef actionedUponNodeRef;
|
|
|
|
/**
|
|
* Indicates whether the conditions should be checked before the action
|
|
* is executed
|
|
*/
|
|
private boolean checkConditions;
|
|
|
|
private Set<String> actionChain;
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param action the action
|
|
* @param actionedUponNodeRef the actioned upon node reference
|
|
* @param checkConditions indicated whether the conditions need to be
|
|
* checked
|
|
*/
|
|
public PendingAction(Action action, NodeRef actionedUponNodeRef, boolean checkConditions,
|
|
Set<String> actionChain)
|
|
{
|
|
this.action = action;
|
|
this.actionedUponNodeRef = actionedUponNodeRef;
|
|
this.checkConditions = checkConditions;
|
|
this.actionChain = actionChain;
|
|
}
|
|
|
|
/**
|
|
* Get the action
|
|
*
|
|
* @return the action
|
|
*/
|
|
public Action getAction()
|
|
{
|
|
return action;
|
|
}
|
|
|
|
/**
|
|
* Get the actioned upon node reference
|
|
*
|
|
* @return the actioned upon node reference
|
|
*/
|
|
public NodeRef getActionedUponNodeRef()
|
|
{
|
|
return actionedUponNodeRef;
|
|
}
|
|
|
|
/**
|
|
* Get the check conditions value
|
|
*
|
|
* @return indicates whether the condition should be checked
|
|
*/
|
|
public boolean getCheckConditions()
|
|
{
|
|
return this.checkConditions;
|
|
}
|
|
|
|
public Set<String> getActionChain()
|
|
{
|
|
return this.actionChain;
|
|
}
|
|
|
|
/**
|
|
* @see java.lang.Object#hashCode()
|
|
*/
|
|
@Override
|
|
public int hashCode()
|
|
{
|
|
int hashCode = 37 * this.actionedUponNodeRef.hashCode();
|
|
hashCode += 37 * this.action.hashCode();
|
|
return hashCode;
|
|
}
|
|
|
|
/**
|
|
* @see java.lang.Object#equals(java.lang.Object)
|
|
*/
|
|
@Override
|
|
public boolean equals(Object obj)
|
|
{
|
|
if (this == obj)
|
|
{
|
|
return true;
|
|
}
|
|
if (obj instanceof PendingAction)
|
|
{
|
|
PendingAction that = (PendingAction) obj;
|
|
return (this.action.equals(that.action) && this.actionedUponNodeRef.equals(that.actionedUponNodeRef));
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return Returns {@link AdctionParameterTypeCopyBehaviourCallback}
|
|
*/
|
|
public CopyBehaviourCallback getCopyCallback(QName classRef, CopyDetails copyDetails)
|
|
{
|
|
return ActionParameterTypeCopyBehaviourCallback.INSTANCE;
|
|
}
|
|
|
|
/**
|
|
* Records any potential <b>d:noderef</d> properties for once the copy is
|
|
* done.
|
|
*
|
|
* @author Derek Hulley
|
|
* @since 3.2
|
|
*/
|
|
private static class ActionParameterTypeCopyBehaviourCallback extends DefaultCopyBehaviourCallback
|
|
{
|
|
private static final ActionParameterTypeCopyBehaviourCallback INSTANCE = new ActionParameterTypeCopyBehaviourCallback();
|
|
|
|
@Override
|
|
public Map<QName, Serializable> getCopyProperties(QName classQName, CopyDetails copyDetails,
|
|
Map<QName, Serializable> properties)
|
|
{
|
|
NodeRef sourceNodeRef = copyDetails.getSourceNodeRef();
|
|
recordNodeRefsForRepointing(sourceNodeRef, properties, ActionModel.PROP_PARAMETER_VALUE);
|
|
// Don't modify the properties
|
|
return properties;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ensures that <b>d:noderef</b> properties are repointed if the target was
|
|
* also copied as part of the hierarchy.
|
|
*/
|
|
public void onCopyComplete(QName classRef, NodeRef sourceNodeRef, NodeRef targetNodeRef, boolean copyToNewNode,
|
|
Map<NodeRef, NodeRef> copyMap)
|
|
{
|
|
ActionParameterTypeCopyBehaviourCallback.INSTANCE.repointNodeRefs(sourceNodeRef, targetNodeRef,
|
|
ActionModel.PROP_PARAMETER_VALUE, copyMap, nodeService);
|
|
}
|
|
}
|