alfresco-community-repo/source/java/org/alfresco/repo/action/ActionTrackingServiceImplTest.java
Nick Burch 4425a2b4db Ability to cancel replication actions (replication 94)
Cance ability, which feeds down into the transfer service, and feeds it back up to the action tracking service. Also, refactor of the replication service unit tests to do the transactions itself, which is needed to reliably test async actions


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@21387 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
2010-07-23 16:52:54 +00:00

905 lines
39 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 static org.alfresco.repo.action.ActionServiceImplTest.assertBefore;
import java.util.Date;
import javax.transaction.UserTransaction;
import junit.framework.TestCase;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ActionServiceImplTest.CancellableSleepAction;
import org.alfresco.repo.action.ActionServiceImplTest.SleepActionExecuter;
import org.alfresco.repo.action.executer.MoveActionExecuter;
import org.alfresco.repo.cache.EhCacheAdapter;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
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.ActionTrackingService;
import org.alfresco.service.cmr.action.ExecutionDetails;
import org.alfresco.service.cmr.action.ExecutionSummary;
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 tracking service tests. These mostly need
* careful control over the transactions they use.
*
* @author Nick Burch
*/
public class ActionTrackingServiceImplTest 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 ActionTrackingService actionTrackingService;
private EhCacheAdapter<String, ExecutionDetails> executingActionsCache;
@Override
protected void setUp() throws Exception {
this.nodeService = (NodeService)ctx.getBean("nodeService");
this.actionService = (ActionService)ctx.getBean("actionService");
this.runtimeActionService = (RuntimeActionService)ctx.getBean("actionService");
this.actionTrackingService = (ActionTrackingService)ctx.getBean("actionTrackingService");
this.transactionService = (TransactionService)ctx.getBean("transactionService");
this.executingActionsCache = (EhCacheAdapter<String, ExecutionDetails>)ctx.getBean("executingActionsSharedCache");
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();
// Cache should start empty each time
for(String key : executingActionsCache.getKeys()) {
executingActionsCache.remove(key);
}
// Reset the execution instance IDs, so we
// can predict what they'll be
((ActionTrackingServiceImpl)actionTrackingService).resetNextExecutionId();
// Register the test executor, if needed
SleepActionExecuter.registerIfNeeded(ctx);
}
/** Creating cache keys */
public void testCreateCacheKeys() throws Exception
{
ActionImpl action = (ActionImpl)createWorkingSleepAction("1234");
assertEquals("sleep-action", action.getActionDefinitionName());
assertEquals("1234", action.getId());
assertEquals(-1, action.getExecutionInstance());
// Give it a predictable execution instance
action.setExecutionInstance(1);
// From an action
String key = ActionTrackingServiceImpl.generateCacheKey(action);
assertEquals("sleep-action=1234=1", key);
// From an ExecutionSummary
ExecutionSummary s = new ExecutionSummary("sleep-action", "1234", 1);
key = ActionTrackingServiceImpl.generateCacheKey(s);
assertEquals("sleep-action=1234=1", key);
}
/** Creating ExecutionDetails and ExecutionSummary */
public void testExecutionDetailsSummary() throws Exception
{
// Create an action with a known execution instance
Action action = createWorkingSleepAction("1234");
((ActionImpl)action).setExecutionInstance(1);
// Create the ExecutionSummary from an action
String key = ActionTrackingServiceImpl.generateCacheKey(action);
ExecutionSummary s = ActionTrackingServiceImpl.buildExecutionSummary(action);
assertEquals("sleep-action", s.getActionType());
assertEquals("1234", s.getActionId());
assertEquals(1, s.getExecutionInstance());
// Create the ExecutionSummery from a key
s = ActionTrackingServiceImpl.buildExecutionSummary(key);
assertEquals("sleep-action", s.getActionType());
assertEquals("1234", s.getActionId());
assertEquals(1, s.getExecutionInstance());
// Now create ExecutionDetails
ExecutionDetails d = ActionTrackingServiceImpl.buildExecutionDetails(action);
assertNotNull(d.getExecutionSummary());
assertEquals("sleep-action", d.getActionType());
assertEquals("1234", d.getActionId());
assertEquals(1, d.getExecutionInstance());
assertEquals(null, d.getPersistedActionRef());
assertEquals(null, d.getStartedAt());
// TODO Check machine details
}
/** Running an action gives it an execution ID */
public void testExecutionInstanceAssignment()
{
// TODO
}
/**
* The correct things happen with the cache
* when you mark things as working / failed / etc
*/
public void testInOutCache() throws Exception
{
Action action = createWorkingSleepAction("1234");
assertEquals(ActionStatus.New, action.getExecutionStatus());
String key = ActionTrackingServiceImpl.generateCacheKey(action);
assertEquals(null, executingActionsCache.get(key));
// Can complete or fail, won't be there
actionTrackingService.recordActionComplete(action);
key = ActionTrackingServiceImpl.generateCacheKey(action);
assertEquals(ActionStatus.Completed, action.getExecutionStatus());
assertEquals(null, executingActionsCache.get(key));
actionTrackingService.recordActionFailure(action, new Exception("Testing"));
key = ActionTrackingServiceImpl.generateCacheKey(action);
assertEquals(ActionStatus.Failed, action.getExecutionStatus());
assertEquals("Testing", action.getExecutionFailureMessage());
assertEquals(null, executingActionsCache.get(key));
// Pending won't add it in either
actionTrackingService.recordActionPending(action);
key = ActionTrackingServiceImpl.generateCacheKey(action);
assertEquals(ActionStatus.Pending, action.getExecutionStatus());
assertEquals(null, executingActionsCache.get(key));
// Run it, will go into the cache
actionTrackingService.recordActionExecuting(action);
key = ActionTrackingServiceImpl.generateCacheKey(action);
assertEquals(ActionStatus.Running, action.getExecutionStatus());
assertNotNull(null, executingActionsCache.get(key));
ExecutionSummary s = ActionTrackingServiceImpl.buildExecutionSummary(action);
ExecutionDetails d = actionTrackingService.getExecutionDetails(s);
assertNotNull(d.getExecutionSummary());
assertEquals("sleep-action", d.getActionType());
assertEquals("1234", d.getActionId());
assertEquals(1, d.getExecutionInstance());
assertEquals(null, d.getPersistedActionRef());
assertNotNull(null, d.getStartedAt());
// Completion removes it
actionTrackingService.recordActionComplete(action);
key = ActionTrackingServiceImpl.generateCacheKey(action);
assertEquals(ActionStatus.Completed, action.getExecutionStatus());
assertEquals(null, executingActionsCache.get(key));
// Failure removes it
actionTrackingService.recordActionExecuting(action);
key = ActionTrackingServiceImpl.generateCacheKey(action);
assertNotNull(null, executingActionsCache.get(key));
actionTrackingService.recordActionFailure(action, new Exception("Testing"));
key = ActionTrackingServiceImpl.generateCacheKey(action);
assertEquals(ActionStatus.Failed, action.getExecutionStatus());
assertEquals("Testing", action.getExecutionFailureMessage());
assertEquals(null, executingActionsCache.get(key));
}
/** Working actions go into the cache, then out */
public void testWorkingActions() throws Exception
{
final SleepActionExecuter sleepActionExec =
(SleepActionExecuter)ctx.getBean(SleepActionExecuter.NAME);
sleepActionExec.setSleepMs(10000);
// Have it run asynchronously
UserTransaction txn = transactionService.getUserTransaction();
txn.begin();
Action action = createWorkingSleepAction("54321");
assertNull(action.getExecutionStartDate());
assertNull(action.getExecutionEndDate());
assertNull(action.getExecutionFailureMessage());
assertEquals(ActionStatus.New, action.getExecutionStatus());
String key = ActionTrackingServiceImpl.generateCacheKey(action);
assertEquals(null, executingActionsCache.get(key));
this.actionService.executeAction(action, this.nodeRef, false, true);
// End the transaction. Should allow the async action
// to be started
txn.commit();
Thread.sleep(150);
// Will get an execution instance id, so a new key
key = ActionTrackingServiceImpl.generateCacheKey(action);
// Check it's in the cache
System.out.println("Checking the cache for " + key);
assertNotNull(executingActionsCache.get(key));
ExecutionSummary s = ActionTrackingServiceImpl.buildExecutionSummary(action);
ExecutionDetails d = actionTrackingService.getExecutionDetails(s);
assertNotNull(d.getExecutionSummary());
assertEquals("sleep-action", d.getActionType());
assertEquals("54321", d.getActionId());
assertEquals(1, d.getExecutionInstance());
assertEquals(null, d.getPersistedActionRef());
assertNotNull(null, d.getStartedAt());
// Tell it to stop sleeping
sleepActionExec.getExecutingThread().interrupt();
Thread.sleep(100);
// Ensure it went away again
assertEquals(ActionStatus.Completed, action.getExecutionStatus());
assertEquals(null, executingActionsCache.get(key));
d = actionTrackingService.getExecutionDetails(s);
assertEquals(null, d);
}
/** Failing actions go into the cache, then out */
public void testFailingActions() throws Exception
{
final SleepActionExecuter sleepActionExec =
(SleepActionExecuter)ctx.getBean(SleepActionExecuter.NAME);
sleepActionExec.setSleepMs(10000);
// Have it run asynchronously
UserTransaction txn = transactionService.getUserTransaction();
txn.begin();
Action action = createFailingSleepAction("54321");
assertNull(action.getExecutionStartDate());
assertNull(action.getExecutionEndDate());
assertNull(action.getExecutionFailureMessage());
assertEquals(ActionStatus.New, action.getExecutionStatus());
String key = ActionTrackingServiceImpl.generateCacheKey(action);
assertEquals(null, executingActionsCache.get(key));
this.actionService.executeAction(action, this.nodeRef, false, true);
// End the transaction. Should allow the async action
// to be started
txn.commit();
Thread.sleep(150);
// Will get an execution instance id, so a new key
key = ActionTrackingServiceImpl.generateCacheKey(action);
// Check it's in the cache
System.out.println("Checking the cache for " + key);
assertNotNull(executingActionsCache.get(key));
ExecutionSummary s = ActionTrackingServiceImpl.buildExecutionSummary(action);
ExecutionDetails d = actionTrackingService.getExecutionDetails(s);
assertNotNull(d.getExecutionSummary());
assertEquals("sleep-action", d.getActionType());
assertEquals("54321", d.getActionId());
assertEquals(1, d.getExecutionInstance());
assertEquals(null, d.getPersistedActionRef());
assertNotNull(null, d.getStartedAt());
// Tell it to stop sleeping
sleepActionExec.getExecutingThread().interrupt();
Thread.sleep(100);
// Ensure it went away again
assertEquals(ActionStatus.Failed, action.getExecutionStatus());
assertEquals("Bang!", action.getExecutionFailureMessage());
assertEquals(null, executingActionsCache.get(key));
d = actionTrackingService.getExecutionDetails(s);
assertEquals(null, d);
}
/** Ensure that the listing functions work */
public void testListings() throws Exception
{
// All listings start blank
assertEquals(
0, actionTrackingService.getAllExecutingActions().size()
);
assertEquals(
0, actionTrackingService.getExecutingActions("test").size()
);
assertEquals(
0, actionTrackingService.getExecutingActions(SleepActionExecuter.NAME).size()
);
assertEquals(
0, actionTrackingService.getExecutingActions(createWorkingSleepAction(null)).size()
);
// Create some actions
Action sleepAction1 = createWorkingSleepAction("12345");
Action sleepAction2 = createWorkingSleepAction("54321");
Action moveAction = createFailingMoveAction();
// Start putting them in
actionTrackingService.recordActionExecuting(sleepAction1);
assertEquals(
1, actionTrackingService.getAllExecutingActions().size()
);
assertEquals(
0, actionTrackingService.getExecutingActions("test").size()
);
assertEquals(
1, actionTrackingService.getExecutingActions(SleepActionExecuter.NAME).size()
);
assertEquals(
1, actionTrackingService.getExecutingActions(sleepAction1).size()
);
assertEquals(
0, actionTrackingService.getExecutingActions(sleepAction2).size()
);
assertEquals(
0, actionTrackingService.getExecutingActions(moveAction).size()
);
actionTrackingService.recordActionExecuting(moveAction);
assertEquals(
2, actionTrackingService.getAllExecutingActions().size()
);
assertEquals(
0, actionTrackingService.getExecutingActions("test").size()
);
assertEquals(
1, actionTrackingService.getExecutingActions(SleepActionExecuter.NAME).size()
);
assertEquals(
1, actionTrackingService.getExecutingActions(sleepAction1).size()
);
assertEquals(
0, actionTrackingService.getExecutingActions(sleepAction2).size()
);
assertEquals(
1, actionTrackingService.getExecutingActions(moveAction).size()
);
actionTrackingService.recordActionExecuting(sleepAction2);
assertEquals(
3, actionTrackingService.getAllExecutingActions().size()
);
assertEquals(
0, actionTrackingService.getExecutingActions("test").size()
);
assertEquals(
2, actionTrackingService.getExecutingActions(SleepActionExecuter.NAME).size()
);
assertEquals(
1, actionTrackingService.getExecutingActions(sleepAction1).size()
);
assertEquals(
1, actionTrackingService.getExecutingActions(sleepAction2).size()
);
assertEquals(
1, actionTrackingService.getExecutingActions(moveAction).size()
);
// Now have some finish, should leave the cache
actionTrackingService.recordActionComplete(sleepAction2);
assertEquals(
2, actionTrackingService.getAllExecutingActions().size()
);
assertEquals(
0, actionTrackingService.getExecutingActions("test").size()
);
assertEquals(
1, actionTrackingService.getExecutingActions(SleepActionExecuter.NAME).size()
);
assertEquals(
1, actionTrackingService.getExecutingActions(sleepAction1).size()
);
assertEquals(
0, actionTrackingService.getExecutingActions(sleepAction2).size()
);
assertEquals(
1, actionTrackingService.getExecutingActions(moveAction).size()
);
actionTrackingService.recordActionComplete(sleepAction1);
assertEquals(
1, actionTrackingService.getAllExecutingActions().size()
);
assertEquals(
0, actionTrackingService.getExecutingActions("test").size()
);
assertEquals(
0, actionTrackingService.getExecutingActions(SleepActionExecuter.NAME).size()
);
assertEquals(
0, actionTrackingService.getExecutingActions(sleepAction1).size()
);
assertEquals(
0, actionTrackingService.getExecutingActions(sleepAction2).size()
);
assertEquals(
1, actionTrackingService.getExecutingActions(moveAction).size()
);
// TODO Multiple actions of the same instance
}
/** Cancel related */
public void testCancellation() throws Exception {
// Ensure we get the right answers checking
CancellableSleepAction sleepAction1 = (CancellableSleepAction)createWorkingSleepAction(null);
CancellableSleepAction sleepAction2 = (CancellableSleepAction)createWorkingSleepAction(null);
actionTrackingService.recordActionExecuting(sleepAction1);
actionTrackingService.recordActionExecuting(sleepAction2);
assertEquals(false, actionTrackingService.isCancellationRequested(sleepAction1));
assertEquals(false, actionTrackingService.isCancellationRequested(sleepAction2));
// Cancel with the action
actionTrackingService.requestActionCancellation(sleepAction1);
assertEquals(true, actionTrackingService.isCancellationRequested(sleepAction1));
assertEquals(false, actionTrackingService.isCancellationRequested(sleepAction2));
// Cancel with the summary
ExecutionSummary s2 = ActionTrackingServiceImpl.buildExecutionSummary(sleepAction2);
actionTrackingService.requestActionCancellation(s2);
assertEquals(true, actionTrackingService.isCancellationRequested(sleepAction1));
assertEquals(true, actionTrackingService.isCancellationRequested(sleepAction2));
// If the action had gone missing from the cache,
// then a check will put it back
CancellableSleepAction sleepAction3 = (CancellableSleepAction)createWorkingSleepAction(null);
String key3 = ActionTrackingServiceImpl.generateCacheKey(sleepAction3);
assertNull(executingActionsCache.get(key3));
assertEquals(false, actionTrackingService.isCancellationRequested(sleepAction3));
assertNotNull(executingActionsCache.get(key3));
executingActionsCache.remove(key3);
assertNull(executingActionsCache.get(key3));
assertEquals(false, actionTrackingService.isCancellationRequested(sleepAction3));
assertNotNull(executingActionsCache.get(key3));
actionTrackingService.requestActionCancellation(sleepAction3);
assertEquals(true, actionTrackingService.isCancellationRequested(sleepAction3));
assertNotNull(executingActionsCache.get(key3));
// Now have one execute and cancel it, ensure it does
final SleepActionExecuter sleepActionExec =
(SleepActionExecuter)ctx.getBean(SleepActionExecuter.NAME);
sleepActionExec.setSleepMs(10000);
UserTransaction txn = transactionService.getUserTransaction();
txn.begin();
executingActionsCache.remove(key3);
this.actionService.executeAction(sleepAction3, this.nodeRef, false, true);
// End the transaction. Should allow the async action
// to be started
txn.commit();
Thread.sleep(150);
// Get the updated key, and check
key3 = ActionTrackingServiceImpl.generateCacheKey(sleepAction3);
assertEquals(false, actionTrackingService.isCancellationRequested(sleepAction3));
assertNotNull(executingActionsCache.get(key3));
actionTrackingService.requestActionCancellation(sleepAction3);
assertEquals(true, actionTrackingService.isCancellationRequested(sleepAction3));
assertNotNull(executingActionsCache.get(key3));
// Have it finish sleeping, will have been cancelled
sleepActionExec.getExecutingThread().interrupt();
Thread.sleep(150);
// Ensure the proper cancelled tracking
assertEquals(ActionStatus.Cancelled, sleepAction3.getExecutionStatus());
assertEquals(null, sleepAction3.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 =
(SleepActionExecuter)ctx.getBean(SleepActionExecuter.NAME);
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(null);
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(null);
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(150);
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(null);
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(150);
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(150);
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(null);
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(150);
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 createFailingSleepAction(String id) throws Exception {
return ActionServiceImplTest.createFailingSleepAction(id, actionService);
}
private Action createWorkingSleepAction(String id) throws Exception {
return ActionServiceImplTest.createWorkingSleepAction(id, actionService);
}
}