mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
ActionTrackingService work
Initial cancel support, and some duplicate instance work (mostly updating tests to handle it coming along). Duplicate id assignment work still remains git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@21340 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* 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 org.alfresco.error.AlfrescoRuntimeException;
|
||||||
|
import org.alfresco.service.cmr.action.CancellableAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Exception thrown when a {@link CancellableAction} detects
|
||||||
|
* that a cancel was requested, and needs the transaction it
|
||||||
|
* is in to be wound back as part of the cancellation.
|
||||||
|
*/
|
||||||
|
public class ActionCancelledException extends AlfrescoRuntimeException
|
||||||
|
{
|
||||||
|
public ActionCancelledException(CancellableAction action)
|
||||||
|
{
|
||||||
|
super(action.toString());
|
||||||
|
}
|
||||||
|
}
|
@@ -106,6 +106,16 @@ public class ActionImpl extends ParameterizedItemImpl implements Action
|
|||||||
*/
|
*/
|
||||||
private List<ActionCondition> actionConditions = new ArrayList<ActionCondition>();
|
private List<ActionCondition> actionConditions = new ArrayList<ActionCondition>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When there is more than one instance of the
|
||||||
|
* action executing, both with the same ID,
|
||||||
|
* which one is this?
|
||||||
|
* This crops up most often with persisted
|
||||||
|
* actions, with two copies running, one on
|
||||||
|
* each of two different target nodes.
|
||||||
|
*/
|
||||||
|
private int executionInstance = -1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When the action started executing,
|
* When the action started executing,
|
||||||
* or null if it hasn't yet.
|
* or null if it hasn't yet.
|
||||||
@@ -466,6 +476,27 @@ public class ActionImpl extends ParameterizedItemImpl implements Action
|
|||||||
getParameterValues().putAll(values);
|
getParameterValues().putAll(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When there is more than one instance of the
|
||||||
|
* action executing, both with the same ID,
|
||||||
|
* which one is this?
|
||||||
|
* This crops up most often with persisted
|
||||||
|
* actions, with two copies running, one on
|
||||||
|
* each of two different target nodes.
|
||||||
|
*/
|
||||||
|
public int getExecutionInstance()
|
||||||
|
{
|
||||||
|
return executionInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by the ActionService when the action
|
||||||
|
* begins running.
|
||||||
|
*/
|
||||||
|
public void setExecutionInstance(int instance) {
|
||||||
|
executionInstance = instance;
|
||||||
|
}
|
||||||
|
|
||||||
public Date getExecutionStartDate() {
|
public Date getExecutionStartDate() {
|
||||||
return executionStartDate;
|
return executionStartDate;
|
||||||
}
|
}
|
||||||
|
@@ -795,6 +795,7 @@ public class ActionServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
|
|
||||||
final SleepActionExecuter sleepAction = (SleepActionExecuter)applicationContext.getBean("sleep-action");
|
final SleepActionExecuter sleepAction = (SleepActionExecuter)applicationContext.getBean("sleep-action");
|
||||||
assertNotNull(sleepAction);
|
assertNotNull(sleepAction);
|
||||||
|
sleepAction.setSleepMs(10);
|
||||||
|
|
||||||
final int actionSubmissonCount = 4; // Rather arbitrary count.
|
final int actionSubmissonCount = 4; // Rather arbitrary count.
|
||||||
for (int i = 0; i < actionSubmissonCount; i ++)
|
for (int i = 0; i < actionSubmissonCount; i ++)
|
||||||
@@ -883,7 +884,7 @@ public class ActionServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
|
|
||||||
postAsyncActionTest(
|
postAsyncActionTest(
|
||||||
this.transactionService,
|
this.transactionService,
|
||||||
1000,
|
1000l,
|
||||||
10,
|
10,
|
||||||
new AsyncTest()
|
new AsyncTest()
|
||||||
{
|
{
|
||||||
@@ -1344,7 +1345,7 @@ public class ActionServiceImplTest extends BaseAlfrescoSpringTest
|
|||||||
boolean cancelled = actionTrackingService.isCancellationRequested(ca);
|
boolean cancelled = actionTrackingService.isCancellationRequested(ca);
|
||||||
if(cancelled)
|
if(cancelled)
|
||||||
{
|
{
|
||||||
throw new RuntimeException("Cancelled!");
|
throw new ActionCancelledException(ca);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -98,6 +98,11 @@ public class ActionTrackingServiceImpl implements ActionTrackingService
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Used by unit tests only */
|
||||||
|
protected void resetNextExecutionId() {
|
||||||
|
this.nextExecutionId = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void recordActionPending(Action action)
|
public void recordActionPending(Action action)
|
||||||
{
|
{
|
||||||
@@ -139,6 +144,7 @@ public class ActionTrackingServiceImpl implements ActionTrackingService
|
|||||||
// TODO assign it a (unique) execution ID
|
// TODO assign it a (unique) execution ID
|
||||||
// (Keep checking to see if the key is used as we
|
// (Keep checking to see if the key is used as we
|
||||||
// increase nextExecutionId until it isn't)
|
// increase nextExecutionId until it isn't)
|
||||||
|
((ActionImpl)action).setExecutionInstance(nextExecutionId++); // TODO
|
||||||
String key = generateCacheKey(action);
|
String key = generateCacheKey(action);
|
||||||
|
|
||||||
// Put it into the cache
|
// Put it into the cache
|
||||||
@@ -153,13 +159,31 @@ public class ActionTrackingServiceImpl implements ActionTrackingService
|
|||||||
public void recordActionFailure(Action action, Throwable exception)
|
public void recordActionFailure(Action action, Throwable exception)
|
||||||
{
|
{
|
||||||
if (logger.isDebugEnabled() == true)
|
if (logger.isDebugEnabled() == true)
|
||||||
|
{
|
||||||
|
if(exception instanceof ActionCancelledException)
|
||||||
|
{
|
||||||
|
logger.debug("Will shortly record completed cancellation of action " + action);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
logger.debug("Will shortly record failure of action " + action + " due to " + exception.getMessage());
|
logger.debug("Will shortly record failure of action " + action + " due to " + exception.getMessage());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record when it finished
|
||||||
((ActionImpl)action).setExecutionEndDate(new Date());
|
((ActionImpl)action).setExecutionEndDate(new Date());
|
||||||
|
|
||||||
|
// Record it as Failed or Cancelled, depending on the exception
|
||||||
|
if(exception instanceof ActionCancelledException)
|
||||||
|
{
|
||||||
|
((ActionImpl)action).setExecutionStatus(ActionStatus.Cancelled);
|
||||||
|
((ActionImpl)action).setExecutionFailureMessage(null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
((ActionImpl)action).setExecutionStatus(ActionStatus.Failed);
|
((ActionImpl)action).setExecutionStatus(ActionStatus.Failed);
|
||||||
((ActionImpl)action).setExecutionFailureMessage(exception.getMessage());
|
((ActionImpl)action).setExecutionFailureMessage(exception.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
// Remove it from the cache, as it's no longer running
|
// Remove it from the cache, as it's no longer running
|
||||||
String key = generateCacheKey(action);
|
String key = generateCacheKey(action);
|
||||||
@@ -334,7 +358,7 @@ public class ActionTrackingServiceImpl implements ActionTrackingService
|
|||||||
return
|
return
|
||||||
action.getActionDefinitionName() + cacheKeyPartSeparator +
|
action.getActionDefinitionName() + cacheKeyPartSeparator +
|
||||||
action.getId() + cacheKeyPartSeparator +
|
action.getId() + cacheKeyPartSeparator +
|
||||||
"1"//action.getExecutionInstance // TODO
|
((ActionImpl)action).getExecutionInstance()
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
protected static String generateCacheKey(ExecutionSummary summary)
|
protected static String generateCacheKey(ExecutionSummary summary)
|
||||||
@@ -381,7 +405,7 @@ public class ActionTrackingServiceImpl implements ActionTrackingService
|
|||||||
return new ExecutionSummary(
|
return new ExecutionSummary(
|
||||||
action.getActionDefinitionName(),
|
action.getActionDefinitionName(),
|
||||||
action.getId(),
|
action.getId(),
|
||||||
1 // TODO
|
((ActionImpl)action).getExecutionInstance()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -113,6 +113,10 @@ public class ActionTrackingServiceImplTest extends TestCase
|
|||||||
executingActionsCache.remove(key);
|
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
|
// Register the test executor, if needed
|
||||||
SleepActionExecuter.registerIfNeeded(ctx);
|
SleepActionExecuter.registerIfNeeded(ctx);
|
||||||
}
|
}
|
||||||
@@ -120,10 +124,13 @@ public class ActionTrackingServiceImplTest extends TestCase
|
|||||||
/** Creating cache keys */
|
/** Creating cache keys */
|
||||||
public void testCreateCacheKeys() throws Exception
|
public void testCreateCacheKeys() throws Exception
|
||||||
{
|
{
|
||||||
Action action = createWorkingSleepAction("1234");
|
ActionImpl action = (ActionImpl)createWorkingSleepAction("1234");
|
||||||
assertEquals("sleep-action", action.getActionDefinitionName());
|
assertEquals("sleep-action", action.getActionDefinitionName());
|
||||||
assertEquals("1234", action.getId());
|
assertEquals("1234", action.getId());
|
||||||
// assertNull(action.getExecutionInstance()); // TODO
|
assertEquals(-1, action.getExecutionInstance());
|
||||||
|
|
||||||
|
// Give it a predictable execution instance
|
||||||
|
action.setExecutionInstance(1);
|
||||||
|
|
||||||
// From an action
|
// From an action
|
||||||
String key = ActionTrackingServiceImpl.generateCacheKey(action);
|
String key = ActionTrackingServiceImpl.generateCacheKey(action);
|
||||||
@@ -138,8 +145,11 @@ public class ActionTrackingServiceImplTest extends TestCase
|
|||||||
/** Creating ExecutionDetails and ExecutionSummary */
|
/** Creating ExecutionDetails and ExecutionSummary */
|
||||||
public void testExecutionDetailsSummary() throws Exception
|
public void testExecutionDetailsSummary() throws Exception
|
||||||
{
|
{
|
||||||
// Create the ExecutionSummary from an action
|
// Create an action with a known execution instance
|
||||||
Action action = createWorkingSleepAction("1234");
|
Action action = createWorkingSleepAction("1234");
|
||||||
|
((ActionImpl)action).setExecutionInstance(1);
|
||||||
|
|
||||||
|
// Create the ExecutionSummary from an action
|
||||||
String key = ActionTrackingServiceImpl.generateCacheKey(action);
|
String key = ActionTrackingServiceImpl.generateCacheKey(action);
|
||||||
|
|
||||||
ExecutionSummary s = ActionTrackingServiceImpl.buildExecutionSummary(action);
|
ExecutionSummary s = ActionTrackingServiceImpl.buildExecutionSummary(action);
|
||||||
@@ -161,10 +171,15 @@ public class ActionTrackingServiceImplTest extends TestCase
|
|||||||
assertEquals(1, d.getExecutionInstance());
|
assertEquals(1, d.getExecutionInstance());
|
||||||
assertEquals(null, d.getPersistedActionRef());
|
assertEquals(null, d.getPersistedActionRef());
|
||||||
assertEquals(null, d.getStartedAt());
|
assertEquals(null, d.getStartedAt());
|
||||||
|
|
||||||
|
// TODO Check machine details
|
||||||
}
|
}
|
||||||
|
|
||||||
// Running an action gives it an execution ID
|
/** Running an action gives it an execution ID */
|
||||||
|
public void testExecutionInstanceAssignment()
|
||||||
|
{
|
||||||
// TODO
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The correct things happen with the cache
|
* The correct things happen with the cache
|
||||||
@@ -181,10 +196,12 @@ public class ActionTrackingServiceImplTest extends TestCase
|
|||||||
|
|
||||||
// Can complete or fail, won't be there
|
// Can complete or fail, won't be there
|
||||||
actionTrackingService.recordActionComplete(action);
|
actionTrackingService.recordActionComplete(action);
|
||||||
|
key = ActionTrackingServiceImpl.generateCacheKey(action);
|
||||||
assertEquals(ActionStatus.Completed, action.getExecutionStatus());
|
assertEquals(ActionStatus.Completed, action.getExecutionStatus());
|
||||||
assertEquals(null, executingActionsCache.get(key));
|
assertEquals(null, executingActionsCache.get(key));
|
||||||
|
|
||||||
actionTrackingService.recordActionFailure(action, new Exception("Testing"));
|
actionTrackingService.recordActionFailure(action, new Exception("Testing"));
|
||||||
|
key = ActionTrackingServiceImpl.generateCacheKey(action);
|
||||||
assertEquals(ActionStatus.Failed, action.getExecutionStatus());
|
assertEquals(ActionStatus.Failed, action.getExecutionStatus());
|
||||||
assertEquals("Testing", action.getExecutionFailureMessage());
|
assertEquals("Testing", action.getExecutionFailureMessage());
|
||||||
assertEquals(null, executingActionsCache.get(key));
|
assertEquals(null, executingActionsCache.get(key));
|
||||||
@@ -192,12 +209,14 @@ public class ActionTrackingServiceImplTest extends TestCase
|
|||||||
|
|
||||||
// Pending won't add it in either
|
// Pending won't add it in either
|
||||||
actionTrackingService.recordActionPending(action);
|
actionTrackingService.recordActionPending(action);
|
||||||
|
key = ActionTrackingServiceImpl.generateCacheKey(action);
|
||||||
assertEquals(ActionStatus.Pending, action.getExecutionStatus());
|
assertEquals(ActionStatus.Pending, action.getExecutionStatus());
|
||||||
assertEquals(null, executingActionsCache.get(key));
|
assertEquals(null, executingActionsCache.get(key));
|
||||||
|
|
||||||
|
|
||||||
// Run it, will go in
|
// Run it, will go into the cache
|
||||||
actionTrackingService.recordActionExecuting(action);
|
actionTrackingService.recordActionExecuting(action);
|
||||||
|
key = ActionTrackingServiceImpl.generateCacheKey(action);
|
||||||
assertEquals(ActionStatus.Running, action.getExecutionStatus());
|
assertEquals(ActionStatus.Running, action.getExecutionStatus());
|
||||||
assertNotNull(null, executingActionsCache.get(key));
|
assertNotNull(null, executingActionsCache.get(key));
|
||||||
|
|
||||||
@@ -213,14 +232,17 @@ public class ActionTrackingServiceImplTest extends TestCase
|
|||||||
|
|
||||||
// Completion removes it
|
// Completion removes it
|
||||||
actionTrackingService.recordActionComplete(action);
|
actionTrackingService.recordActionComplete(action);
|
||||||
|
key = ActionTrackingServiceImpl.generateCacheKey(action);
|
||||||
assertEquals(ActionStatus.Completed, action.getExecutionStatus());
|
assertEquals(ActionStatus.Completed, action.getExecutionStatus());
|
||||||
assertEquals(null, executingActionsCache.get(key));
|
assertEquals(null, executingActionsCache.get(key));
|
||||||
|
|
||||||
// Failure removes it
|
// Failure removes it
|
||||||
actionTrackingService.recordActionExecuting(action);
|
actionTrackingService.recordActionExecuting(action);
|
||||||
|
key = ActionTrackingServiceImpl.generateCacheKey(action);
|
||||||
assertNotNull(null, executingActionsCache.get(key));
|
assertNotNull(null, executingActionsCache.get(key));
|
||||||
|
|
||||||
actionTrackingService.recordActionFailure(action, new Exception("Testing"));
|
actionTrackingService.recordActionFailure(action, new Exception("Testing"));
|
||||||
|
key = ActionTrackingServiceImpl.generateCacheKey(action);
|
||||||
assertEquals(ActionStatus.Failed, action.getExecutionStatus());
|
assertEquals(ActionStatus.Failed, action.getExecutionStatus());
|
||||||
assertEquals("Testing", action.getExecutionFailureMessage());
|
assertEquals("Testing", action.getExecutionFailureMessage());
|
||||||
assertEquals(null, executingActionsCache.get(key));
|
assertEquals(null, executingActionsCache.get(key));
|
||||||
@@ -254,6 +276,10 @@ public class ActionTrackingServiceImplTest extends TestCase
|
|||||||
Thread.sleep(150);
|
Thread.sleep(150);
|
||||||
|
|
||||||
|
|
||||||
|
// Will get an execution instance id, so a new key
|
||||||
|
key = ActionTrackingServiceImpl.generateCacheKey(action);
|
||||||
|
|
||||||
|
|
||||||
// Check it's in the cache
|
// Check it's in the cache
|
||||||
System.out.println("Checking the cache for " + key);
|
System.out.println("Checking the cache for " + key);
|
||||||
assertNotNull(executingActionsCache.get(key));
|
assertNotNull(executingActionsCache.get(key));
|
||||||
@@ -309,6 +335,10 @@ public class ActionTrackingServiceImplTest extends TestCase
|
|||||||
Thread.sleep(150);
|
Thread.sleep(150);
|
||||||
|
|
||||||
|
|
||||||
|
// Will get an execution instance id, so a new key
|
||||||
|
key = ActionTrackingServiceImpl.generateCacheKey(action);
|
||||||
|
|
||||||
|
|
||||||
// Check it's in the cache
|
// Check it's in the cache
|
||||||
System.out.println("Checking the cache for " + key);
|
System.out.println("Checking the cache for " + key);
|
||||||
assertNotNull(executingActionsCache.get(key));
|
assertNotNull(executingActionsCache.get(key));
|
||||||
@@ -521,6 +551,9 @@ public class ActionTrackingServiceImplTest extends TestCase
|
|||||||
txn.commit();
|
txn.commit();
|
||||||
Thread.sleep(150);
|
Thread.sleep(150);
|
||||||
|
|
||||||
|
// Get the updated key, and check
|
||||||
|
key3 = ActionTrackingServiceImpl.generateCacheKey(sleepAction3);
|
||||||
|
|
||||||
assertEquals(false, actionTrackingService.isCancellationRequested(sleepAction3));
|
assertEquals(false, actionTrackingService.isCancellationRequested(sleepAction3));
|
||||||
assertNotNull(executingActionsCache.get(key3));
|
assertNotNull(executingActionsCache.get(key3));
|
||||||
|
|
||||||
@@ -531,11 +564,11 @@ public class ActionTrackingServiceImplTest extends TestCase
|
|||||||
|
|
||||||
// Have it finish sleeping, will have been cancelled
|
// Have it finish sleeping, will have been cancelled
|
||||||
sleepActionExec.getExecutingThread().interrupt();
|
sleepActionExec.getExecutingThread().interrupt();
|
||||||
Thread.sleep(100);
|
Thread.sleep(150);
|
||||||
|
|
||||||
// TODO Proper cancelled exception and tracking
|
// Ensure the proper cancelled tracking
|
||||||
assertEquals(ActionStatus.Failed, sleepAction3.getExecutionStatus());
|
assertEquals(ActionStatus.Cancelled, sleepAction3.getExecutionStatus());
|
||||||
assertEquals("Cancelled!", sleepAction3.getExecutionFailureMessage());
|
assertEquals(null, sleepAction3.getExecutionFailureMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -18,11 +18,23 @@
|
|||||||
*/
|
*/
|
||||||
package org.alfresco.service.cmr.action;
|
package org.alfresco.service.cmr.action;
|
||||||
|
|
||||||
|
import org.alfresco.repo.action.ActionCancelledException;
|
||||||
|
import org.alfresco.repo.action.executer.ActionExecuter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A marker interface that forms part of the Cancel Action contract.
|
* A marker interface that forms part of the Cancel Action contract.
|
||||||
* An action that implements this interface commits to periodically
|
* An action that implements this interface commits to periodically
|
||||||
* asking the {@link ActionService} if a cancel of it has been
|
* asking the {@link ActionTrackingService} if a cancel of it has
|
||||||
* requested, and orderly terminating itself if so.
|
* been requested, and orderly terminating itself if so.
|
||||||
|
*
|
||||||
|
* Actions implementing this should, via their
|
||||||
|
* {@link ActionExecuter}, periodically call
|
||||||
|
* {@link ActionTrackingService#isCancellationRequested(CancellableAction)}
|
||||||
|
* to check if a cancel has been requested for them.
|
||||||
|
* If it has, they should tidy up as much as possible, and then throw
|
||||||
|
* a {@link ActionCancelledException} to indicate to the
|
||||||
|
* {@link ActionService} that they ceased running due to a
|
||||||
|
* cancel.
|
||||||
*
|
*
|
||||||
* @author Nick Burch
|
* @author Nick Burch
|
||||||
*/
|
*/
|
||||||
|
Reference in New Issue
Block a user