diff --git a/config/alfresco/action-services-context.xml b/config/alfresco/action-services-context.xml
index 8d7f46ddc4..774707b6fb 100644
--- a/config/alfresco/action-services-context.xml
+++ b/config/alfresco/action-services-context.xml
@@ -124,7 +124,7 @@
-
+
diff --git a/config/alfresco/cache-context.xml b/config/alfresco/cache-context.xml
index ee4dae8245..da70994d43 100644
--- a/config/alfresco/cache-context.xml
+++ b/config/alfresco/cache-context.xml
@@ -50,7 +50,7 @@
-
+
@@ -401,6 +401,10 @@
+
+
+
+
@@ -1044,7 +1048,7 @@
- org.alfresco.cache.routingContentStoreSharedCache
+ org.alfresco.cache.routingContentStoreCache
@@ -1068,7 +1072,7 @@
-
+
@@ -1077,10 +1081,24 @@
- org.alfresco.cache.executingActionsSharedCache
+ org.alfresco.cache.executingActionsCache
+
+
+
+
+
+
+
+ org.alfresco.cache.executingActionsTransactionalCache
+
+
+ 1000
+
+
+
diff --git a/config/alfresco/ehcache-default.xml b/config/alfresco/ehcache-default.xml
index 17f04ca246..6faf50ec8b 100644
--- a/config/alfresco/ehcache-default.xml
+++ b/config/alfresco/ehcache-default.xml
@@ -370,4 +370,14 @@
statistics="false"
/>
+
+
+
+
diff --git a/config/alfresco/extension/ehcache-custom.xml.sample.cluster b/config/alfresco/extension/ehcache-custom.xml.sample.cluster
index 6e9a0cfc1a..f52a886d18 100644
--- a/config/alfresco/extension/ehcache-custom.xml.sample.cluster
+++ b/config/alfresco/extension/ehcache-custom.xml.sample.cluster
@@ -806,4 +806,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/source/java/org/alfresco/repo/action/ActionTrackingServiceImpl.java b/source/java/org/alfresco/repo/action/ActionTrackingServiceImpl.java
index e069aec2c3..a8e99b5c1b 100644
--- a/source/java/org/alfresco/repo/action/ActionTrackingServiceImpl.java
+++ b/source/java/org/alfresco/repo/action/ActionTrackingServiceImpl.java
@@ -1,511 +1,512 @@
-/*
- * 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 .
- */
-package org.alfresco.repo.action;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Date;
-import java.util.List;
-import java.util.StringTokenizer;
-
-import org.alfresco.repo.cache.EhCacheAdapter;
-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.ActionStatus;
-import org.alfresco.service.cmr.action.ActionTrackingService;
-import org.alfresco.service.cmr.action.CancellableAction;
-import org.alfresco.service.cmr.action.ExecutionDetails;
-import org.alfresco.service.cmr.action.ExecutionSummary;
-import org.alfresco.service.cmr.repository.NodeRef;
-import org.alfresco.service.transaction.TransactionService;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
-/**
- * Action execution tracking service implementation
- *
- * @author Nick Burch
- */
-public class ActionTrackingServiceImpl implements ActionTrackingService
-{
- /**
- * The logger
- */
- private static Log logger = LogFactory.getLog(ActionTrackingServiceImpl.class);
-
- private EhCacheAdapter executingActionsCache;
-
- private TransactionService transactionService;
- private RuntimeActionService runtimeActionService;
-
- /**
- * Doesn't need to be cluster unique, is just used
- * to try to reduce the chance of clashes in the
- * quickest and easiest way.
- */
- private short nextExecutionId = 1;
- private short wrapExecutionIdAfter = Short.MAX_VALUE/2;
-
- /** How we separate bits of the cache key */
- private static final char cacheKeyPartSeparator = '=';
-
- /**
- * Set the transaction service
- *
- * @param transactionService the transaction service
- */
- public void setTransactionService(TransactionService transactionService)
- {
- this.transactionService = transactionService;
- }
-
- /**
- * Set the runtime action service
- *
- * @param runtimeActionService the runtime action service
- */
- public void setRuntimeActionService(RuntimeActionService runtimeActionService)
- {
- this.runtimeActionService = runtimeActionService;
- }
-
- /**
- * Sets the cache used to store details of
- * currently executing actions, cluster wide.
- */
- public void setExecutingActionsCache(EhCacheAdapter executingActionsCache)
- {
- this.executingActionsCache = executingActionsCache;
- }
-
-
- /** Used by unit tests only */
- protected void resetNextExecutionId() {
- this.nextExecutionId = 1;
- }
-
-
- public void recordActionPending(Action action)
- {
- recordActionPending((ActionImpl)action);
- }
- public void recordActionPending(ActionImpl action)
- {
- // Set the status
- action.setExecutionStatus(ActionStatus.Pending);
-
- // Mark it as not having started quite yet
- action.setExecutionStartDate(null);
-
- // Have it put into the cache, so we can tell it
- // is waiting to be run
- placeActionInCache(action);
- }
-
- public void recordActionComplete(Action action)
- {
- recordActionComplete((ActionImpl)action);
- }
- private void recordActionComplete(ActionImpl action)
- {
- if (logger.isDebugEnabled() == true)
- {
- logger.debug("Action " + action + " has completed execution");
- }
-
- // Mark it as having worked
- action.setExecutionEndDate(new Date());
- action.setExecutionStatus(ActionStatus.Completed);
- action.setExecutionFailureMessage(null);
- if(action.getNodeRef() != null)
- {
- runtimeActionService.saveActionImpl(action.getNodeRef(), action);
- }
-
- // Remove it from the cache, as it's finished
- String key = generateCacheKey(action);
- executingActionsCache.remove(key);
- }
-
- public void recordActionExecuting(Action action)
- {
- recordActionExecuting((ActionImpl)action);
- }
- private void recordActionExecuting(ActionImpl action)
- {
- if (logger.isDebugEnabled() == true)
- {
- logger.debug("Action " + action + " with provisional key " + generateCacheKey(action) + " has begun exection");
- }
-
- // Grab what status it was before
- ActionStatus previousStatus = action.getExecutionStatus();
-
- // Mark the action as starting
- action.setExecutionStartDate(new Date());
- action.setExecutionStatus(ActionStatus.Running);
-
- // If it's a synchronous execution, put it into the cache
- if(previousStatus != ActionStatus.Pending)
- {
- placeActionInCache(action);
- }
- else
- {
- // If it's async, update the existing cache entry
- String key = generateCacheKey(action);
- ExecutionDetails details = executingActionsCache.get(key);
-
- // Check it's really there, warn + fix if not
- if(details == null) {
- logger.warn(
- "Went to mark the start of execution of " +
- action + " with key " + key +
- " but it wasn't in the running actions cache! " +
- "Your running actions cache is probably too small"
- );
- }
-
- // Update and save into the cache
- details = buildExecutionDetails(action);
- executingActionsCache.put(key, details);
- }
- }
-
- /**
- * For an action that needs to go into the cache
- * (async action that is pending, or sync action
- * that is running), assign an execution instance
- * and put into the cache
- */
- private void placeActionInCache(ActionImpl action)
- {
- // Assign it a (unique) execution ID
- // (Keep checking to see if the key is used as we
- // increase nextExecutionId until it isn't)
- String key = null;
- boolean assigned = false;
- while(!assigned) {
- // Try
- action.setExecutionInstance(nextExecutionId++);
- key = generateCacheKey(action);
-
- // Is it ok?
- if(executingActionsCache.get(key) == null) {
- assigned = true;
- }
-
- // Do we need to wrap?
- // (Wrap before absolutely needed, makes things simpler)
- if(nextExecutionId > wrapExecutionIdAfter) {
- synchronized (this) {
- while(nextExecutionId > wrapExecutionIdAfter) {
- nextExecutionId -= wrapExecutionIdAfter;
- }
- }
- }
- }
-
- // Put it into the cache
- ExecutionDetails details = buildExecutionDetails(action);
- executingActionsCache.put(key, details);
-
- if (logger.isDebugEnabled() == true)
- {
- logger.debug("Action " + action + " with key " + key + " placed into execution cache");
- }
- }
-
- /**
- * Schedule the recording of the action failure to occur
- * in another transaction
- */
- public void recordActionFailure(Action action, Throwable exception)
- {
- 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());
- }
- }
-
- // Record when it finished
- ((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).setExecutionFailureMessage(exception.getMessage());
- }
-
- // Remove it from the cache, as it's no longer running
- String key = generateCacheKey(action);
- executingActionsCache.remove(key);
-
- // Do we need to update the persisted details?
- if(action.getNodeRef() != null)
- {
- // 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