Action tracking of execution status (Replication 78)

Support for getting the start & end execution times, status and failure reason for actions, persisted if the action is
(Execution doesn't update these yet though)


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@21135 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Nick Burch
2010-07-13 15:10:47 +00:00
parent 4cd5e90110
commit 297309f1aa
8 changed files with 340 additions and 3 deletions

View File

@@ -28,6 +28,7 @@ import java.util.Set;
import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionCondition; import org.alfresco.service.cmr.action.ActionCondition;
import org.alfresco.service.cmr.action.ActionStatus;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
/** /**
@@ -104,6 +105,29 @@ public class ActionImpl extends ParameterizedItemImpl implements Action
* Action conditions * Action conditions
*/ */
private List<ActionCondition> actionConditions = new ArrayList<ActionCondition>(); private List<ActionCondition> actionConditions = new ArrayList<ActionCondition>();
/**
* When the action started executing,
* or null if it hasn't yet.
*/
private Date executionStartDate;
/**
* When the action finished executing,
* or null if it hasn't yet.
*/
private Date executionEndDate;
/**
* The status of the action's execution
*/
private ActionStatus executionStatus = ActionStatus.New;
/**
* Why the action failed to execute fully,
* if exists.
*/
private String executionFailureMessage;
/** /**
* Constructor * Constructor
@@ -146,6 +170,10 @@ public class ActionImpl extends ParameterizedItemImpl implements Action
this.modifier = action.getModifier(); this.modifier = action.getModifier();
this.nodeRef = action.getNodeRef(); this.nodeRef = action.getNodeRef();
this.title = action.getTitle(); this.title = action.getTitle();
this.executionStartDate = action.getExecutionStartDate();
this.executionEndDate = action.getExecutionEndDate();
this.executionStatus = action.getExecutionStatus();
this.executionFailureMessage = action.getExecutionFailureMessage();
if (action instanceof ActionImpl) if (action instanceof ActionImpl)
{ {
ActionImpl actionImpl = (ActionImpl) action; ActionImpl actionImpl = (ActionImpl) action;
@@ -437,5 +465,55 @@ public class ActionImpl extends ParameterizedItemImpl implements Action
{ {
getParameterValues().putAll(values); getParameterValues().putAll(values);
} }
public Date getExecutionStartDate() {
return executionStartDate;
}
/**
* Records the date when the action began execution,
* normally called by the {@link ActionService} when
* it starts running the action.
*/
public void setExecutionStartDate(Date startDate) {
this.executionStartDate = startDate;
}
public Date getExecutionEndDate() {
return executionEndDate;
}
/**
* Records the date when the action finished execution,
* normally called by the {@link ActionService} when
* the action completes or fails.
*/
public void setExecutionEndDate(Date endDate) {
this.executionEndDate = endDate;
}
public ActionStatus getExecutionStatus() {
return executionStatus;
}
/**
* Updates the current execution status. This is
* normally called by the {@link ActionService} as
* it progresses the Action's execution.
*/
public void setExecutionStatus(ActionStatus status) {
this.executionStatus = status;
}
public String getExecutionFailureMessage() {
return executionFailureMessage;
}
/**
* Records the message of the exception which caused the
* Action to fail, if any.
*/
public void setExecutionFailureMessage(String message) {
this.executionFailureMessage = message;
}
} }

View File

@@ -30,6 +30,10 @@ public interface ActionModel
static final QName PROP_ACTION_TITLE = QName.createQName(ACTION_MODEL_URI, "actionTitle"); static final QName PROP_ACTION_TITLE = QName.createQName(ACTION_MODEL_URI, "actionTitle");
static final QName PROP_ACTION_DESCRIPTION = QName.createQName(ACTION_MODEL_URI, "actionDescription"); static final QName PROP_ACTION_DESCRIPTION = QName.createQName(ACTION_MODEL_URI, "actionDescription");
static final QName PROP_EXECUTE_ASYNCHRONOUSLY = QName.createQName(ACTION_MODEL_URI, "executeAsynchronously"); static final QName PROP_EXECUTE_ASYNCHRONOUSLY = QName.createQName(ACTION_MODEL_URI, "executeAsynchronously");
static final QName PROP_EXECUTION_START_DATE = QName.createQName(ACTION_MODEL_URI, "executionStartDate");
static final QName PROP_EXECUTION_END_DATE = QName.createQName(ACTION_MODEL_URI, "executionEndDate");
static final QName PROP_EXECUTION_ACTION_STATUS = QName.createQName(ACTION_MODEL_URI, "executionActionStatus");
static final QName PROP_EXECUTION_FAILURE_MESSAGE = QName.createQName(ACTION_MODEL_URI, "executionFailureMessage");
static final QName ASSOC_CONDITIONS = QName.createQName(ACTION_MODEL_URI, "conditions"); static final QName ASSOC_CONDITIONS = QName.createQName(ACTION_MODEL_URI, "conditions");
static final QName ASSOC_COMPENSATING_ACTION = QName.createQName(ACTION_MODEL_URI, "compensatingAction"); static final QName ASSOC_COMPENSATING_ACTION = QName.createQName(ACTION_MODEL_URI, "compensatingAction");

View File

@@ -46,6 +46,7 @@ import org.alfresco.service.cmr.action.ActionDefinition;
import org.alfresco.service.cmr.action.ActionList; import org.alfresco.service.cmr.action.ActionList;
import org.alfresco.service.cmr.action.ActionService; import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.action.ActionServiceException; import org.alfresco.service.cmr.action.ActionServiceException;
import org.alfresco.service.cmr.action.ActionStatus;
import org.alfresco.service.cmr.action.CompositeAction; import org.alfresco.service.cmr.action.CompositeAction;
import org.alfresco.service.cmr.action.CompositeActionCondition; import org.alfresco.service.cmr.action.CompositeActionCondition;
import org.alfresco.service.cmr.action.ParameterConstraint; import org.alfresco.service.cmr.action.ParameterConstraint;
@@ -879,6 +880,12 @@ public class ActionServiceImpl implements ActionService, RuntimeActionService, A
props.put(ActionModel.PROP_ACTION_TITLE, action.getTitle()); props.put(ActionModel.PROP_ACTION_TITLE, action.getTitle());
props.put(ActionModel.PROP_ACTION_DESCRIPTION, action.getDescription()); props.put(ActionModel.PROP_ACTION_DESCRIPTION, action.getDescription());
props.put(ActionModel.PROP_EXECUTE_ASYNCHRONOUSLY, action.getExecuteAsychronously()); 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); this.nodeService.setProperties(actionNodeRef, props);
// Update the compensating action (model should enforce the singularity // Update the compensating action (model should enforce the singularity
@@ -1283,6 +1290,11 @@ public class ActionServiceImpl implements ActionService, RuntimeActionService, A
((ActionImpl) action).setCreatedDate((Date) props.get(ContentModel.PROP_CREATED)); ((ActionImpl) action).setCreatedDate((Date) props.get(ContentModel.PROP_CREATED));
((ActionImpl) action).setModifier((String) props.get(ContentModel.PROP_MODIFIER)); ((ActionImpl) action).setModifier((String) props.get(ContentModel.PROP_MODIFIER));
((ActionImpl) action).setModifiedDate((Date) props.get(ContentModel.PROP_MODIFIED)); ((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 // Get the compensating action
List<ChildAssociationRef> assocs = this.nodeService.getChildAssocs(actionNodeRef, RegexQNamePattern.MATCH_ALL, List<ChildAssociationRef> assocs = this.nodeService.getChildAssocs(actionNodeRef, RegexQNamePattern.MATCH_ALL,

View File

@@ -19,8 +19,7 @@
package org.alfresco.repo.action; package org.alfresco.repo.action;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.Date;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -45,6 +44,7 @@ import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionCondition; import org.alfresco.service.cmr.action.ActionCondition;
import org.alfresco.service.cmr.action.ActionConditionDefinition; import org.alfresco.service.cmr.action.ActionConditionDefinition;
import org.alfresco.service.cmr.action.ActionDefinition; import org.alfresco.service.cmr.action.ActionDefinition;
import org.alfresco.service.cmr.action.ActionStatus;
import org.alfresco.service.cmr.action.CompositeAction; import org.alfresco.service.cmr.action.CompositeAction;
import org.alfresco.service.cmr.action.CompositeActionCondition; import org.alfresco.service.cmr.action.CompositeActionCondition;
import org.alfresco.service.cmr.action.ParameterDefinition; import org.alfresco.service.cmr.action.ParameterDefinition;
@@ -55,7 +55,6 @@ import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.alfresco.service.transaction.TransactionService; import org.alfresco.service.transaction.TransactionService;
import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.BaseAlfrescoSpringTest; import org.alfresco.util.BaseAlfrescoSpringTest;
/** /**
@@ -1140,6 +1139,83 @@ public class ActionServiceImplTest extends BaseAlfrescoSpringTest
} }
/**
* Tests that we can read, save, load etc the various
* execution related details such as started at,
* ended at, status and exception
*/
public void testExecutionTrackingDetails() {
Action action = this.actionService.createAction(AddFeaturesActionExecuter.NAME);
String actionId = action.getId();
assertNull(action.getExecutionStartDate());
assertNull(action.getExecutionEndDate());
assertNull(action.getExecutionFailureMessage());
assertEquals(ActionStatus.New, action.getExecutionStatus());
// Save and load, details shouldn't have changed
this.actionService.saveAction(this.nodeRef, action);
action = (Action)this.actionService.getAction(this.nodeRef, actionId);
assertNull(action.getExecutionStartDate());
assertNull(action.getExecutionEndDate());
assertNull(action.getExecutionFailureMessage());
assertEquals(ActionStatus.New, action.getExecutionStatus());
// Set some details, ensure they survive a save/load
((ActionImpl)action).setExecutionStatus(ActionStatus.Running);
((ActionImpl)action).setExecutionStartDate(new Date(12345));
this.actionService.saveAction(this.nodeRef, action);
action = (Action)this.actionService.getAction(this.nodeRef, actionId);
assertEquals(ActionStatus.Running, action.getExecutionStatus());
assertEquals(12345, action.getExecutionStartDate().getTime());
assertNull(action.getExecutionEndDate());
assertNull(action.getExecutionFailureMessage());
// Set the rest, and change some, ensure they survive a save/load
((ActionImpl)action).setExecutionStatus(ActionStatus.Failed);
((ActionImpl)action).setExecutionStartDate(new Date(123450));
((ActionImpl)action).setExecutionEndDate(new Date(123455));
((ActionImpl)action).setExecutionFailureMessage("Testing");
this.actionService.saveAction(this.nodeRef, action);
action = (Action)this.actionService.getAction(this.nodeRef, actionId);
assertEquals(ActionStatus.Failed, action.getExecutionStatus());
assertEquals(123450, action.getExecutionStartDate().getTime());
assertEquals(123455, action.getExecutionEndDate().getTime());
assertEquals("Testing", action.getExecutionFailureMessage());
// Unset a few, ensure they survive a save/load
((ActionImpl)action).setExecutionStatus(null);
((ActionImpl)action).setExecutionStartDate(new Date(123450));
((ActionImpl)action).setExecutionFailureMessage(null);
this.actionService.saveAction(this.nodeRef, action);
action = (Action)this.actionService.getAction(this.nodeRef, actionId);
assertEquals(ActionStatus.New, action.getExecutionStatus()); // Default
assertEquals(123450, action.getExecutionStartDate().getTime());
assertEquals(123455, action.getExecutionEndDate().getTime());
assertEquals(null, action.getExecutionFailureMessage());
}
/**
* Tests that when we run an action, either
* synchronously or asynchronously, with it
* working or failing, that the action execution
* service correctly sets the flags
*/
public void testExecutionTrackingOnExecution() {
// TODO
}
/** /**
* This class is only used during JUnit testing. * This class is only used during JUnit testing.
* *

View File

@@ -15,6 +15,20 @@
<namespace uri="http://www.alfresco.org/model/action/1.0" prefix="act"/> <namespace uri="http://www.alfresco.org/model/action/1.0" prefix="act"/>
</namespaces> </namespaces>
<constraints>
<constraint name="act:allowedExecutionstatuses" type="LIST">
<parameter name="allowedValues">
<list>
<value>New</value>
<value>Pending</value>
<value>Running</value>
<value>Completed</value>
<value>Cancelled</value>
<value>Failed</value>
</list>
</parameter>
</constraint>
</constraints>
<types> <types>
@@ -64,6 +78,25 @@
<type>d:text</type> <type>d:text</type>
<mandatory>false</mandatory> <mandatory>false</mandatory>
</property> </property>
<property name="act:executionStartDate">
<type>d:date</type>
<mandatory>false</mandatory>
</property>
<property name="act:executionEndDate">
<type>d:date</type>
<mandatory>false</mandatory>
</property>
<property name="act:executionActionStatus">
<type>d:text</type>
<mandatory>false</mandatory>
<constraints>
<constraint ref="act:allowedExecutionstatuses" />
</constraints>
</property>
<property name="act:executionFailureMessage">
<type>d:text</type>
<mandatory>false</mandatory>
</property>
</properties> </properties>
<associations> <associations>
<child-association name="act:conditions"> <child-association name="act:conditions">

View File

@@ -208,4 +208,38 @@ public interface Action extends ParameterizedItem
* @param values A map of values to be added * @param values A map of values to be added
*/ */
void addParameterValues(Map<String, Serializable> values); void addParameterValues(Map<String, Serializable> values);
/**
* Gets the date that the action (last) began executing at.
* Null if the action has not yet been run.
* For a saved action, this will be the last time at ran.
* @return The date the action (last) began executing at, or null.
*/
Date getExecutionStartDate();
/**
* Gets the date that the action (last) finished
* execution at.
* Null if the action has not yet been run, or is
* currently running.
* For a saved action, this will normally be the last
* time it finished running.
* @return The date the action last finished exeucting at, or null.
*/
Date getExecutionEndDate();
/**
* Gets the current execution status of the action,
* such as Running or Completed.
* @return The current execution status
*/
ActionStatus getExecutionStatus();
/**
* Gets the message of the exception which caused the
* Action execution failure, or null if the Action
* hasn't failed / has been retried.
* @return The exception message, if the action has failed
*/
String getExecutionFailureMessage();
} }

View File

@@ -0,0 +1,69 @@
/*
* 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.service.cmr.action;
import java.io.Serializable;
/**
* The various states an Action can be in.
*
* @author Nick Burch
*/
public enum ActionStatus
{
/**
* A new Action, which has never been run
*/
New,
/**
* An Action which has been scheduled for
* Asynchronous execution, but not yet run.
*/
Pending,
/**
* Indicates that the Action is currently being
* executed by the {@link ActionService}
*/
Running,
/**
* The Action was run without error
*/
Completed,
/**
* The Action, which must implement
* {@link CancellableAction}, detected that a
* cancellation was requested and cancelled itself.
*/
Cancelled,
/**
* The Action failed to run to completion. Call
* {@link Action#getExecutionFailureCause()} to find
* out why.
*/
Failed
;
public static ActionStatus valueOf(Serializable s)
{
if(s == null) return New;
return valueOf((String)s);
}
}

View File

@@ -0,0 +1,31 @@
/*
* 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.service.cmr.action;
/**
* A marker interface that forms part of the Cancel Action contract.
* An action that implements this interface commits to periodically
* asking the {@link ActionService} if a cancel of it has been
* requested, and orderly terminating itself if so.
*
* @author Nick Burch
*/
public interface CancellableAction extends Action
{
}