mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
ActionService support for recording why persisted actions fail, on the action definition, plus tests
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@21206 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -98,6 +98,9 @@
|
||||
<property name="dictionaryService">
|
||||
<ref bean="DictionaryService" />
|
||||
</property>
|
||||
<property name="transactionService">
|
||||
<ref bean="TransactionService" />
|
||||
</property>
|
||||
|
||||
<property name="asynchronousActionExecutionQueues">
|
||||
<map>
|
||||
|
@@ -38,7 +38,11 @@ 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.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
||||
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||
import org.alfresco.repo.transaction.TransactionListenerAdapter;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||
import org.alfresco.service.cmr.action.Action;
|
||||
import org.alfresco.service.cmr.action.ActionCondition;
|
||||
import org.alfresco.service.cmr.action.ActionConditionDefinition;
|
||||
@@ -60,6 +64,7 @@ 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.service.transaction.TransactionService;
|
||||
import org.alfresco.util.GUID;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -103,6 +108,7 @@ public class ActionServiceImpl implements ActionService, RuntimeActionService, A
|
||||
private NodeService nodeService;
|
||||
private SearchService searchService;
|
||||
private DictionaryService dictionaryService;
|
||||
private TransactionService transactionService;
|
||||
private AuthenticationContext authenticationContext;
|
||||
private PolicyComponent policyComponent;
|
||||
|
||||
@@ -181,6 +187,16 @@ public class ActionServiceImpl implements ActionService, RuntimeActionService, A
|
||||
this.dictionaryService = dictionaryService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the transaction service
|
||||
*
|
||||
* @param transactionService the transaction service
|
||||
*/
|
||||
public void setTransactionService(TransactionService transactionService)
|
||||
{
|
||||
this.transactionService = transactionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param policyComponent used to set up the action-based policy behaviour
|
||||
*/
|
||||
@@ -732,7 +748,7 @@ public class ActionServiceImpl implements ActionService, RuntimeActionService, A
|
||||
{
|
||||
if (logger.isDebugEnabled() == true)
|
||||
{
|
||||
logger.debug("Recording failure of action " + action + " due to " + exception.getMessage());
|
||||
logger.debug("Will shortly record failure of action " + action + " due to " + exception.getMessage());
|
||||
}
|
||||
|
||||
((ActionImpl)action).setExecutionEndDate(new Date());
|
||||
@@ -741,7 +757,56 @@ public class ActionServiceImpl implements ActionService, RuntimeActionService, A
|
||||
|
||||
if(action.getNodeRef() != null)
|
||||
{
|
||||
// TODO
|
||||
// Take a local copy of the details
|
||||
// (That way, if someone has a reference to the
|
||||
// action and plays with it, we still save the
|
||||
// correct information)
|
||||
final String actionId = action.getId();
|
||||
final Date startedAt = action.getExecutionStartDate();
|
||||
final Date endedAt = action.getExecutionEndDate();
|
||||
final String message = action.getExecutionFailureMessage();
|
||||
final NodeRef actionNode = action.getNodeRef();
|
||||
|
||||
// Have the details updated on the action as soon
|
||||
// as the transaction has finished rolling back
|
||||
AlfrescoTransactionSupport.bindListener(
|
||||
new TransactionListenerAdapter() {
|
||||
public void afterRollback()
|
||||
{
|
||||
transactionService.getRetryingTransactionHelper().doInTransaction(
|
||||
new RetryingTransactionCallback<Object>()
|
||||
{
|
||||
public Object execute() throws Throwable
|
||||
{
|
||||
// Update the action as the system user
|
||||
return AuthenticationUtil.runAs(new RunAsWork<Action>() {
|
||||
public Action doWork() throws Exception
|
||||
{
|
||||
// Grab the latest version of the action
|
||||
ActionImpl action = (ActionImpl)createAction(actionNode);
|
||||
|
||||
// Update it
|
||||
action.setExecutionStartDate(startedAt);
|
||||
action.setExecutionEndDate(endedAt);
|
||||
action.setExecutionStatus(ActionStatus.Failed);
|
||||
action.setExecutionFailureMessage(message);
|
||||
saveActionImpl(actionNode, action);
|
||||
|
||||
if (logger.isDebugEnabled() == true)
|
||||
{
|
||||
logger.debug("Recorded failure of action " + actionId + ", node " + actionNode + " due to " + message);
|
||||
}
|
||||
|
||||
// All done
|
||||
return action;
|
||||
}
|
||||
}, AuthenticationUtil.SYSTEM_USER_NAME);
|
||||
}
|
||||
}, false, true
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -68,7 +68,6 @@ public class ActionServiceImplTest extends BaseAlfrescoSpringTest
|
||||
|
||||
private NodeRef nodeRef;
|
||||
private NodeRef folder;
|
||||
private RuntimeActionService runtimeActionService;
|
||||
private RetryingTransactionHelper transactionHelper;
|
||||
|
||||
// @Override
|
||||
@@ -90,7 +89,6 @@ public class ActionServiceImplTest extends BaseAlfrescoSpringTest
|
||||
super.onSetUpInTransaction();
|
||||
|
||||
this.transactionHelper = (RetryingTransactionHelper)this.applicationContext.getBean("retryingTransactionHelper");
|
||||
this.runtimeActionService = (RuntimeActionService)this.applicationContext.getBean("actionService");
|
||||
|
||||
// Create the node used for tests
|
||||
this.nodeRef = this.nodeService.createNode(
|
||||
@@ -1206,294 +1204,7 @@ public class ActionServiceImplTest extends BaseAlfrescoSpringTest
|
||||
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() throws Exception {
|
||||
final SleepActionExecuter sleepActionExec = new SleepActionExecuter();
|
||||
sleepActionExec.setSleepMs(10);
|
||||
Action action;
|
||||
NodeRef actionNode;
|
||||
|
||||
|
||||
// ===========================================================
|
||||
// Execute a transient Action that works, synchronously
|
||||
// ===========================================================
|
||||
action = createWorkingSleepAction();
|
||||
assertNull(action.getExecutionStartDate());
|
||||
assertNull(action.getExecutionEndDate());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.New, action.getExecutionStatus());
|
||||
|
||||
this.actionService.executeAction(action, this.nodeRef);
|
||||
|
||||
assertNotNull(action.getExecutionStartDate());
|
||||
assertNotNull(action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionStartDate(), action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionEndDate(), new Date());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Completed, action.getExecutionStatus());
|
||||
|
||||
|
||||
// ===========================================================
|
||||
// Execute a transient Action that fails, synchronously
|
||||
// ===========================================================
|
||||
action = createFailingMoveAction();
|
||||
assertNull(action.getExecutionStartDate());
|
||||
assertNull(action.getExecutionEndDate());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.New, action.getExecutionStatus());
|
||||
|
||||
try {
|
||||
this.actionService.executeAction(action, this.nodeRef);
|
||||
fail("Action should have failed, and the error been thrown");
|
||||
} catch(Exception e) {}
|
||||
|
||||
assertNotNull(action.getExecutionStartDate());
|
||||
assertNotNull(action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionStartDate(), action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionEndDate(), new Date());
|
||||
assertNotNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Failed, action.getExecutionStatus());
|
||||
|
||||
// Tidy up from the action failure
|
||||
endTransaction();
|
||||
startNewTransaction();
|
||||
onSetUpInTransaction();
|
||||
|
||||
|
||||
// ===========================================================
|
||||
// Execute a stored Action that works, synchronously
|
||||
// ===========================================================
|
||||
action = createWorkingSleepAction();
|
||||
this.actionService.saveAction(this.nodeRef, action);
|
||||
actionNode = action.getNodeRef();
|
||||
assertNotNull(actionNode);
|
||||
|
||||
assertNull(action.getExecutionStartDate());
|
||||
assertNull(action.getExecutionEndDate());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.New, action.getExecutionStatus());
|
||||
|
||||
this.actionService.executeAction(action, this.nodeRef);
|
||||
|
||||
// Check our copy
|
||||
assertNotNull(action.getExecutionStartDate());
|
||||
assertNotNull(action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionStartDate(), action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionEndDate(), new Date());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Completed, action.getExecutionStatus());
|
||||
|
||||
// Now re-load and check the stored one
|
||||
action = runtimeActionService.createAction(actionNode);
|
||||
assertNotNull(action.getExecutionStartDate());
|
||||
assertNotNull(action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionStartDate(), action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionEndDate(), new Date());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Completed, action.getExecutionStatus());
|
||||
|
||||
|
||||
// ===========================================================
|
||||
// Execute a stored Action that fails, synchronously
|
||||
// ===========================================================
|
||||
action = createFailingMoveAction();
|
||||
this.actionService.saveAction(this.nodeRef, action);
|
||||
actionNode = action.getNodeRef();
|
||||
assertNotNull(actionNode);
|
||||
|
||||
assertNull(action.getExecutionStartDate());
|
||||
assertNull(action.getExecutionEndDate());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.New, action.getExecutionStatus());
|
||||
|
||||
try {
|
||||
this.actionService.executeAction(action, this.nodeRef);
|
||||
fail("Action should have failed, and the error been thrown");
|
||||
} catch(Exception e) {}
|
||||
|
||||
// Check our copy
|
||||
assertNotNull(action.getExecutionStartDate());
|
||||
assertNotNull(action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionStartDate(), action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionEndDate(), new Date());
|
||||
assertNotNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Failed, action.getExecutionStatus());
|
||||
|
||||
// Now re-load and check the stored one
|
||||
action = runtimeActionService.createAction(actionNode);
|
||||
// TODO - Fix these
|
||||
// assertNotNull(action.getExecutionStartDate());
|
||||
// assertNotNull(action.getExecutionEndDate());
|
||||
// assertBefore(action.getExecutionStartDate(), action.getExecutionEndDate());
|
||||
// assertBefore(action.getExecutionEndDate(), new Date());
|
||||
// assertNotNull(action.getExecutionFailureMessage());
|
||||
// assertEquals(ActionStatus.Failed, action.getExecutionStatus());
|
||||
|
||||
// Tidy up from the action failure
|
||||
endTransaction();
|
||||
startNewTransaction();
|
||||
onSetUpInTransaction();
|
||||
|
||||
|
||||
// ===========================================================
|
||||
// Execute a transient Action that works, asynchronously
|
||||
// ===========================================================
|
||||
action = createWorkingSleepAction();
|
||||
assertNull(action.getExecutionStartDate());
|
||||
assertNull(action.getExecutionEndDate());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.New, action.getExecutionStatus());
|
||||
|
||||
this.actionService.executeAction(action, this.nodeRef, false, true);
|
||||
assertNull(action.getExecutionStartDate());
|
||||
assertNull(action.getExecutionEndDate());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Pending, action.getExecutionStatus());
|
||||
setComplete();
|
||||
// End the transaction. When run from a test, this call will
|
||||
// block until the "async" action has finished running
|
||||
endTransaction();
|
||||
Thread.sleep(100);
|
||||
|
||||
assertNotNull(action.getExecutionStartDate());
|
||||
assertNotNull(action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionStartDate(), action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionEndDate(), new Date());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Completed, action.getExecutionStatus());
|
||||
|
||||
// Put things back ready for the next check
|
||||
startNewTransaction();
|
||||
onSetUpInTransaction();
|
||||
|
||||
|
||||
// ===========================================================
|
||||
// Execute a transient Action that fails, asynchronously
|
||||
// ===========================================================
|
||||
action = createFailingMoveAction();
|
||||
assertNull(action.getExecutionStartDate());
|
||||
assertNull(action.getExecutionEndDate());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.New, action.getExecutionStatus());
|
||||
|
||||
this.actionService.executeAction(action, this.nodeRef, false, true);
|
||||
assertNull(action.getExecutionStartDate());
|
||||
assertNull(action.getExecutionEndDate());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Pending, action.getExecutionStatus());
|
||||
setComplete();
|
||||
// End the transaction. When run from a test, this call will
|
||||
// block until the "async" action has finished running
|
||||
endTransaction();
|
||||
Thread.sleep(100);
|
||||
|
||||
assertNotNull(action.getExecutionStartDate());
|
||||
assertNotNull(action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionStartDate(), action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionEndDate(), new Date());
|
||||
assertNotNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Failed, action.getExecutionStatus());
|
||||
|
||||
// Put things back ready for the next check
|
||||
startNewTransaction();
|
||||
onSetUpInTransaction();
|
||||
|
||||
|
||||
// ===========================================================
|
||||
// Execute a stored Action that works, asynchronously
|
||||
// ===========================================================
|
||||
action = createWorkingSleepAction();
|
||||
this.actionService.saveAction(this.nodeRef, action);
|
||||
actionNode = action.getNodeRef();
|
||||
assertNotNull(actionNode);
|
||||
|
||||
assertNull(action.getExecutionStartDate());
|
||||
assertNull(action.getExecutionEndDate());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.New, action.getExecutionStatus());
|
||||
|
||||
this.actionService.executeAction(action, this.nodeRef, false, true);
|
||||
assertNull(action.getExecutionStartDate());
|
||||
assertNull(action.getExecutionEndDate());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Pending, action.getExecutionStatus());
|
||||
setComplete();
|
||||
// End the transaction. When run from a test, this call will
|
||||
// block until the "async" action has finished running
|
||||
endTransaction();
|
||||
Thread.sleep(100);
|
||||
|
||||
// Check our copy
|
||||
assertNotNull(action.getExecutionStartDate());
|
||||
assertNotNull(action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionStartDate(), action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionEndDate(), new Date());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Completed, action.getExecutionStatus());
|
||||
|
||||
// Now re-load and check the stored one
|
||||
action = runtimeActionService.createAction(actionNode);
|
||||
assertNotNull(action.getExecutionStartDate());
|
||||
assertNotNull(action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionStartDate(), action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionEndDate(), new Date());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Completed, action.getExecutionStatus());
|
||||
|
||||
// Put things back ready for the next check
|
||||
startNewTransaction();
|
||||
onSetUpInTransaction();
|
||||
|
||||
|
||||
// ===========================================================
|
||||
// Execute a stored Action that fails, asynchronously
|
||||
// ===========================================================
|
||||
action = createFailingMoveAction();
|
||||
this.actionService.saveAction(this.nodeRef, action);
|
||||
actionNode = action.getNodeRef();
|
||||
assertNotNull(actionNode);
|
||||
|
||||
assertNull(action.getExecutionStartDate());
|
||||
assertNull(action.getExecutionEndDate());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.New, action.getExecutionStatus());
|
||||
|
||||
this.actionService.executeAction(action, this.nodeRef, false, true);
|
||||
assertNull(action.getExecutionStartDate());
|
||||
assertNull(action.getExecutionEndDate());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Pending, action.getExecutionStatus());
|
||||
setComplete();
|
||||
// End the transaction. When run from a test, this call will
|
||||
// block until the "async" action has finished running
|
||||
endTransaction();
|
||||
Thread.sleep(100);
|
||||
|
||||
// Check our copy
|
||||
assertNotNull(action.getExecutionStartDate());
|
||||
assertNotNull(action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionStartDate(), action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionEndDate(), new Date());
|
||||
assertNotNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Failed, action.getExecutionStatus());
|
||||
|
||||
// Now re-load and check the stored one
|
||||
action = runtimeActionService.createAction(actionNode);
|
||||
// TODO - Fix these
|
||||
// assertNotNull(action.getExecutionStartDate());
|
||||
// assertNotNull(action.getExecutionEndDate());
|
||||
// assertBefore(action.getExecutionStartDate(), action.getExecutionEndDate());
|
||||
// assertBefore(action.getExecutionEndDate(), new Date());
|
||||
// assertNotNull(action.getExecutionFailureMessage());
|
||||
// assertEquals(ActionStatus.Failed, action.getExecutionStatus());
|
||||
}
|
||||
|
||||
private Action createFailingMoveAction() {
|
||||
protected Action createFailingMoveAction() {
|
||||
Action failingAction = this.actionService.createAction(MoveActionExecuter.NAME);
|
||||
failingAction.setParameterValue(MoveActionExecuter.PARAM_ASSOC_TYPE_QNAME, ContentModel.ASSOC_CHILDREN);
|
||||
failingAction.setParameterValue(MoveActionExecuter.PARAM_ASSOC_QNAME, ContentModel.ASSOC_CHILDREN);
|
||||
@@ -1503,7 +1214,7 @@ public class ActionServiceImplTest extends BaseAlfrescoSpringTest
|
||||
|
||||
return failingAction;
|
||||
}
|
||||
private Action createWorkingSleepAction() {
|
||||
protected Action createWorkingSleepAction() {
|
||||
Action workingAction = actionService.createAction(SleepActionExecuter.NAME);
|
||||
return workingAction;
|
||||
}
|
||||
|
@@ -0,0 +1,500 @@
|
||||
/*
|
||||
* 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 static org.alfresco.repo.action.ActionServiceImplTest.assertBefore;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.transaction.UserTransaction;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
|
||||
import org.alfresco.repo.action.executer.MoveActionExecuter;
|
||||
import org.alfresco.repo.content.MimetypeMap;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||
import org.alfresco.service.cmr.action.Action;
|
||||
import org.alfresco.service.cmr.action.ActionService;
|
||||
import org.alfresco.service.cmr.action.ActionStatus;
|
||||
import org.alfresco.service.cmr.action.ParameterDefinition;
|
||||
import org.alfresco.service.cmr.repository.ContentData;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.service.transaction.TransactionService;
|
||||
import org.alfresco.util.ApplicationContextHelper;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
/**
|
||||
* Action service tests which need careful control
|
||||
* over the transactions they use.
|
||||
*
|
||||
* @author Nick Burch
|
||||
*/
|
||||
public class ActionServiceImplTransactionalTest extends TestCase
|
||||
{
|
||||
private static ConfigurableApplicationContext ctx =
|
||||
(ConfigurableApplicationContext)ApplicationContextHelper.getApplicationContext();
|
||||
|
||||
private StoreRef storeRef;
|
||||
private NodeRef rootNodeRef;
|
||||
|
||||
private NodeRef nodeRef;
|
||||
private NodeRef folder;
|
||||
private NodeService nodeService;
|
||||
private ActionService actionService;
|
||||
private TransactionService transactionService;
|
||||
private RuntimeActionService runtimeActionService;
|
||||
private RetryingTransactionHelper transactionHelper;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
this.transactionHelper = (RetryingTransactionHelper)ctx.getBean("retryingTransactionHelper");
|
||||
this.nodeService = (NodeService)ctx.getBean("nodeService");
|
||||
this.actionService = (ActionService)ctx.getBean("actionService");
|
||||
this.runtimeActionService = (RuntimeActionService)ctx.getBean("actionService");
|
||||
this.transactionService = (TransactionService)ctx.getBean("transactionService");
|
||||
|
||||
AuthenticationUtil.setRunAsUserSystem();
|
||||
|
||||
UserTransaction txn = transactionService.getUserTransaction();
|
||||
txn.begin();
|
||||
|
||||
// Where to put things
|
||||
this.storeRef = this.nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis());
|
||||
this.rootNodeRef = this.nodeService.getRootNode(this.storeRef);
|
||||
|
||||
// Create the node used for tests
|
||||
this.nodeRef = this.nodeService.createNode(
|
||||
this.rootNodeRef,
|
||||
ContentModel.ASSOC_CHILDREN,
|
||||
QName.createQName("{test}testnode"),
|
||||
ContentModel.TYPE_CONTENT).getChildRef();
|
||||
this.nodeService.setProperty(
|
||||
this.nodeRef,
|
||||
ContentModel.PROP_CONTENT,
|
||||
new ContentData(null, MimetypeMap.MIMETYPE_TEXT_PLAIN, 0L, null));
|
||||
this.folder = this.nodeService.createNode(
|
||||
this.rootNodeRef,
|
||||
ContentModel.ASSOC_CHILDREN,
|
||||
QName.createQName("{test}testFolder"),
|
||||
ContentModel.TYPE_FOLDER).getChildRef();
|
||||
|
||||
txn.commit();
|
||||
|
||||
// Register the test executor, if needed
|
||||
if(!ctx.containsBean(SleepActionExecuter.NAME))
|
||||
{
|
||||
ctx.getBeanFactory().registerSingleton(
|
||||
SleepActionExecuter.NAME,
|
||||
new SleepActionExecuter()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() throws Exception {
|
||||
final SleepActionExecuter sleepActionExec = new SleepActionExecuter();
|
||||
sleepActionExec.setSleepMs(10);
|
||||
Action action;
|
||||
NodeRef actionNode;
|
||||
|
||||
// We need real transactions
|
||||
UserTransaction txn = transactionService.getUserTransaction();
|
||||
txn.begin();
|
||||
|
||||
|
||||
// ===========================================================
|
||||
// Execute a transient Action that works, synchronously
|
||||
// ===========================================================
|
||||
action = createWorkingSleepAction();
|
||||
assertNull(action.getExecutionStartDate());
|
||||
assertNull(action.getExecutionEndDate());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.New, action.getExecutionStatus());
|
||||
|
||||
this.actionService.executeAction(action, this.nodeRef);
|
||||
|
||||
assertNotNull(action.getExecutionStartDate());
|
||||
assertNotNull(action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionStartDate(), action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionEndDate(), new Date());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Completed, action.getExecutionStatus());
|
||||
|
||||
|
||||
// ===========================================================
|
||||
// Execute a transient Action that fails, synchronously
|
||||
// ===========================================================
|
||||
action = createFailingMoveAction();
|
||||
assertNull(action.getExecutionStartDate());
|
||||
assertNull(action.getExecutionEndDate());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.New, action.getExecutionStatus());
|
||||
|
||||
try {
|
||||
this.actionService.executeAction(action, this.nodeRef);
|
||||
fail("Action should have failed, and the error been thrown");
|
||||
} catch(Exception e) {}
|
||||
|
||||
assertNotNull(action.getExecutionStartDate());
|
||||
assertNotNull(action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionStartDate(), action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionEndDate(), new Date());
|
||||
assertNotNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Failed, action.getExecutionStatus());
|
||||
|
||||
// Tidy up from the action failure
|
||||
txn.rollback();
|
||||
txn = transactionService.getUserTransaction();
|
||||
txn.begin();
|
||||
|
||||
|
||||
// ===========================================================
|
||||
// Execute a stored Action that works, synchronously
|
||||
// ===========================================================
|
||||
action = createWorkingSleepAction();
|
||||
this.actionService.saveAction(this.nodeRef, action);
|
||||
actionNode = action.getNodeRef();
|
||||
assertNotNull(actionNode);
|
||||
|
||||
assertNull(action.getExecutionStartDate());
|
||||
assertNull(action.getExecutionEndDate());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.New, action.getExecutionStatus());
|
||||
|
||||
this.actionService.executeAction(action, this.nodeRef);
|
||||
|
||||
// Check our copy
|
||||
assertNotNull(action.getExecutionStartDate());
|
||||
assertNotNull(action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionStartDate(), action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionEndDate(), new Date());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Completed, action.getExecutionStatus());
|
||||
|
||||
// Now re-load and check the stored one
|
||||
action = runtimeActionService.createAction(actionNode);
|
||||
assertNotNull(action.getExecutionStartDate());
|
||||
assertNotNull(action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionStartDate(), action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionEndDate(), new Date());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Completed, action.getExecutionStatus());
|
||||
|
||||
|
||||
// ===========================================================
|
||||
// Execute a stored Action that fails, synchronously
|
||||
// ===========================================================
|
||||
action = createFailingMoveAction();
|
||||
this.actionService.saveAction(this.nodeRef, action);
|
||||
actionNode = action.getNodeRef();
|
||||
String actionId = action.getId();
|
||||
assertNotNull(actionNode);
|
||||
|
||||
assertNull(action.getExecutionStartDate());
|
||||
assertNull(action.getExecutionEndDate());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.New, action.getExecutionStatus());
|
||||
|
||||
// Save this
|
||||
txn.commit();
|
||||
txn = transactionService.getUserTransaction();
|
||||
txn.begin();
|
||||
|
||||
// Run the action - will fail and trigger a rollback
|
||||
try {
|
||||
this.actionService.executeAction(action, this.nodeRef);
|
||||
fail("Action should have failed, and the error been thrown");
|
||||
} catch(Exception e) {}
|
||||
|
||||
// Check our copy
|
||||
assertNotNull(action.getExecutionStartDate());
|
||||
assertNotNull(action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionStartDate(), action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionEndDate(), new Date());
|
||||
assertNotNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Failed, action.getExecutionStatus());
|
||||
|
||||
// Wait for the post-rollback update to complete
|
||||
// (The stored one gets updated asynchronously)
|
||||
txn.rollback();
|
||||
Thread.sleep(100);
|
||||
txn = transactionService.getUserTransaction();
|
||||
txn.begin();
|
||||
|
||||
// Now re-load and check the stored one
|
||||
action = runtimeActionService.createAction(actionNode);
|
||||
assertEquals(actionId, action.getId());
|
||||
assertNotNull(action.getExecutionStartDate());
|
||||
assertNotNull(action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionStartDate(), action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionEndDate(), new Date());
|
||||
assertNotNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Failed, action.getExecutionStatus());
|
||||
|
||||
// Tidy up from the action failure
|
||||
txn.commit();
|
||||
txn = transactionService.getUserTransaction();
|
||||
txn.begin();
|
||||
|
||||
|
||||
// ===========================================================
|
||||
// Execute a transient Action that works, asynchronously
|
||||
// ===========================================================
|
||||
action = createWorkingSleepAction();
|
||||
assertNull(action.getExecutionStartDate());
|
||||
assertNull(action.getExecutionEndDate());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.New, action.getExecutionStatus());
|
||||
|
||||
this.actionService.executeAction(action, this.nodeRef, false, true);
|
||||
assertNull(action.getExecutionStartDate());
|
||||
assertNull(action.getExecutionEndDate());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Pending, action.getExecutionStatus());
|
||||
|
||||
// End the transaction. Should allow the async action
|
||||
// to be executed
|
||||
txn.commit();
|
||||
Thread.sleep(100);
|
||||
|
||||
assertNotNull(action.getExecutionStartDate());
|
||||
assertNotNull(action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionStartDate(), action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionEndDate(), new Date());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Completed, action.getExecutionStatus());
|
||||
|
||||
// Put things back ready for the next check
|
||||
txn = transactionService.getUserTransaction();
|
||||
txn.begin();
|
||||
|
||||
|
||||
// ===========================================================
|
||||
// Execute a transient Action that fails, asynchronously
|
||||
// ===========================================================
|
||||
action = createFailingMoveAction();
|
||||
assertNull(action.getExecutionStartDate());
|
||||
assertNull(action.getExecutionEndDate());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.New, action.getExecutionStatus());
|
||||
|
||||
this.actionService.executeAction(action, this.nodeRef, false, true);
|
||||
assertNull(action.getExecutionStartDate());
|
||||
assertNull(action.getExecutionEndDate());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Pending, action.getExecutionStatus());
|
||||
|
||||
// End the transaction. Should allow the async action
|
||||
// to be executed
|
||||
txn.commit();
|
||||
Thread.sleep(100);
|
||||
|
||||
assertNotNull(action.getExecutionStartDate());
|
||||
assertNotNull(action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionStartDate(), action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionEndDate(), new Date());
|
||||
assertNotNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Failed, action.getExecutionStatus());
|
||||
|
||||
// Put things back ready for the next check
|
||||
txn = transactionService.getUserTransaction();
|
||||
txn.begin();
|
||||
|
||||
|
||||
// ===========================================================
|
||||
// Execute a stored Action that works, asynchronously
|
||||
// ===========================================================
|
||||
action = createWorkingSleepAction();
|
||||
this.actionService.saveAction(this.nodeRef, action);
|
||||
actionNode = action.getNodeRef();
|
||||
assertNotNull(actionNode);
|
||||
|
||||
assertNull(action.getExecutionStartDate());
|
||||
assertNull(action.getExecutionEndDate());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.New, action.getExecutionStatus());
|
||||
|
||||
this.actionService.executeAction(action, this.nodeRef, false, true);
|
||||
assertNull(action.getExecutionStartDate());
|
||||
assertNull(action.getExecutionEndDate());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Pending, action.getExecutionStatus());
|
||||
|
||||
// End the transaction. Should allow the async action
|
||||
// to be executed
|
||||
txn.commit();
|
||||
Thread.sleep(100);
|
||||
txn = transactionService.getUserTransaction();
|
||||
txn.begin();
|
||||
|
||||
// Check our copy
|
||||
assertNotNull(action.getExecutionStartDate());
|
||||
assertNotNull(action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionStartDate(), action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionEndDate(), new Date());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Completed, action.getExecutionStatus());
|
||||
|
||||
// Now re-load and check the stored one
|
||||
action = runtimeActionService.createAction(actionNode);
|
||||
assertNotNull(action.getExecutionStartDate());
|
||||
assertNotNull(action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionStartDate(), action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionEndDate(), new Date());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Completed, action.getExecutionStatus());
|
||||
|
||||
|
||||
// ===========================================================
|
||||
// Execute a stored Action that fails, asynchronously
|
||||
// ===========================================================
|
||||
action = createFailingMoveAction();
|
||||
this.actionService.saveAction(this.nodeRef, action);
|
||||
actionNode = action.getNodeRef();
|
||||
assertNotNull(actionNode);
|
||||
|
||||
assertNull(action.getExecutionStartDate());
|
||||
assertNull(action.getExecutionEndDate());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.New, action.getExecutionStatus());
|
||||
|
||||
this.actionService.executeAction(action, this.nodeRef, false, true);
|
||||
assertNull(action.getExecutionStartDate());
|
||||
assertNull(action.getExecutionEndDate());
|
||||
assertNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Pending, action.getExecutionStatus());
|
||||
|
||||
// End the transaction. Should allow the async action
|
||||
// to be executed
|
||||
// Need to wait longer, as we have two async actions
|
||||
// that need to occur - action + record
|
||||
txn.commit();
|
||||
Thread.sleep(250);
|
||||
txn = transactionService.getUserTransaction();
|
||||
txn.begin();
|
||||
|
||||
// Check our copy
|
||||
assertNotNull(action.getExecutionStartDate());
|
||||
assertNotNull(action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionStartDate(), action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionEndDate(), new Date());
|
||||
assertNotNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Failed, action.getExecutionStatus());
|
||||
|
||||
// Now re-load and check the stored one
|
||||
action = runtimeActionService.createAction(actionNode);
|
||||
assertNotNull(action.getExecutionStartDate());
|
||||
assertNotNull(action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionStartDate(), action.getExecutionEndDate());
|
||||
assertBefore(action.getExecutionEndDate(), new Date());
|
||||
assertNotNull(action.getExecutionFailureMessage());
|
||||
assertEquals(ActionStatus.Failed, action.getExecutionStatus());
|
||||
}
|
||||
|
||||
private Action createFailingMoveAction() {
|
||||
Action failingAction = this.actionService.createAction(MoveActionExecuter.NAME);
|
||||
failingAction.setParameterValue(MoveActionExecuter.PARAM_ASSOC_TYPE_QNAME, ContentModel.ASSOC_CHILDREN);
|
||||
failingAction.setParameterValue(MoveActionExecuter.PARAM_ASSOC_QNAME, ContentModel.ASSOC_CHILDREN);
|
||||
// Create a bad node ref
|
||||
NodeRef badNodeRef = new NodeRef(this.storeRef, "123123");
|
||||
failingAction.setParameterValue(MoveActionExecuter.PARAM_DESTINATION_FOLDER, badNodeRef);
|
||||
|
||||
return failingAction;
|
||||
}
|
||||
private Action createWorkingSleepAction() {
|
||||
Action workingAction = actionService.createAction(SleepActionExecuter.NAME);
|
||||
return workingAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is only used during JUnit testing.
|
||||
*
|
||||
* @author Neil Mc Erlean
|
||||
*/
|
||||
public static class SleepActionFilter extends AbstractAsynchronousActionFilter
|
||||
{
|
||||
public int compare(OngoingAsyncAction sae1, OngoingAsyncAction sae2)
|
||||
{
|
||||
// Sleep actions are always equivalent.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is only intended for use in JUnit tests.
|
||||
*
|
||||
* @author Neil McErlean.
|
||||
*/
|
||||
public static class SleepActionExecuter extends ActionExecuterAbstractBase
|
||||
{
|
||||
public static final String NAME = "sleep-action";
|
||||
private int sleepMs;
|
||||
|
||||
private int timesExecuted = 0;
|
||||
private void incrementTimesExecutedCount() {timesExecuted++;}
|
||||
public int getTimesExecuted() {return timesExecuted;}
|
||||
|
||||
public int getSleepMs()
|
||||
{
|
||||
return sleepMs;
|
||||
}
|
||||
|
||||
public void setSleepMs(int sleepMs)
|
||||
{
|
||||
this.sleepMs = sleepMs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add parameter definitions
|
||||
*/
|
||||
@Override
|
||||
protected void addParameterDefinitions(List<ParameterDefinition> paramList)
|
||||
{
|
||||
// Intentionally empty
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void executeImpl(Action action, NodeRef actionedUponNodeRef) {
|
||||
try
|
||||
{
|
||||
Thread.sleep(sleepMs);
|
||||
}
|
||||
catch (InterruptedException ignored)
|
||||
{
|
||||
// Intentionally empty
|
||||
}
|
||||
finally
|
||||
{
|
||||
incrementTimesExecutedCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -69,6 +69,9 @@ public class ActionTestSuite extends TestSuite
|
||||
suite.addTestSuite(SpecialiseTypeActionExecuterTest.class);
|
||||
suite.addTestSuite(RemoveFeaturesActionExecuterTest.class);
|
||||
|
||||
// Tests which care about transactions
|
||||
suite.addTestSuite(ActionServiceImplTransactionalTest.class);
|
||||
|
||||
return suite;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user