diff --git a/config/alfresco/extension/scheduled-action-services-context.xml b/config/alfresco/extension/scheduled-action-services-context.xml
new file mode 100644
index 0000000000..674aa84b5b
--- /dev/null
+++ b/config/alfresco/extension/scheduled-action-services-context.xml
@@ -0,0 +1,442 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ add-features
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ remove-features
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ copy
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ set-property-value
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ UNTIL_FIRST_FAILURE
+
+
+ IGNORE
+
+
+
+
+
+
+
+
+ lucene
+
+
+
+ workspace://SpacesStore
+
+
+
+
+ PATH:"//\*" -ASPECT:"{http://www.alfresco.org/model/content/1.0}generalclassifiable"
+
+
+ 0 50 * * * ?
+
+
+ jobA
+
+
+ jobGroup
+
+
+ triggerA
+
+
+ triggerGroup
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ System
+
+
+
+
+
+
+ UNTIL_FIRST_FAILURE
+
+
+ IGNORE
+
+
+
+
+
+
+
+
+ lucene
+
+
+
+ workspace://SpacesStore
+
+
+
+ ASPECT:"{http://www.alfresco.org/model/content/1.0}generalclassifiable"
+
+
+ 0 55 * * * ?
+
+
+ jobB
+
+
+ jobGroup
+
+
+ triggerB
+
+
+ triggerGroup
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ System
+
+
+
+
+
+
+ UNTIL_FIRST_FAILURE
+
+
+ IGNORE
+
+
+
+
+
+
+
+
+ lucene
+
+
+
+ workspace://SpacesStore
+
+
+
+ +PATH:"/app:company_home/*//*" +TEXT:"tutorial"
+
+
+ 0 40 * * * ?
+
+
+ jobC
+
+
+ jobGroup
+
+
+ triggerC
+
+
+ triggerGroup
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ System
+
+
+
+
+
+
+ UNTIL_FIRST_FAILURE
+
+
+ IGNORE
+
+
+
+
+
+
+
+
+ lucene
+
+
+
+ workspace://SpacesStore
+
+
+
+ @cm\:created:${luceneDateRange(yesterday, "-P10Y")}
+
+
+ 0 0/1 * * * ?
+
+
+ jobD
+
+
+ jobGroup
+
+
+ triggerD
+
+
+ triggerGroup
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ System
+
+
+
+
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/action/scheduled/AbstractScheduledAction.java b/source/java/org/alfresco/repo/action/scheduled/AbstractScheduledAction.java
new file mode 100644
index 0000000000..231ac14223
--- /dev/null
+++ b/source/java/org/alfresco/repo/action/scheduled/AbstractScheduledAction.java
@@ -0,0 +1,645 @@
+/*
+ * Copyright (C) 2005 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the
+ * License.
+ */
+package org.alfresco.repo.action.scheduled;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.repo.transaction.TransactionUtil;
+import org.alfresco.service.cmr.action.Action;
+import org.alfresco.service.cmr.action.ActionService;
+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;
+import org.quartz.Job;
+import org.quartz.JobDataMap;
+import org.quartz.JobDetail;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.quartz.Trigger;
+
+/**
+ * Abstract action support.
+ *
+ * Each action applies to a set of nodes.
+ *
+ * These actions may be executed in one overall transaction or one individual transaction. If actions are in individual transactions an error may halt subsequent execution or
+ * processing can try and invoke the action for each node.
+ *
+ * @author Andy Hind
+ */
+public abstract class AbstractScheduledAction implements ScheduledActionDefinition
+{
+ /**
+ * Logging
+ */
+ private static Log s_logger = LogFactory.getLog(AbstractScheduledAction.class);
+
+ /**
+ * Enum to define the transaction mode.
+ *
+ * @author Andy Hind
+ */
+ public enum TransactionMode
+ {
+ ISOLATED_TRANSACTIONS, UNTIL_FIRST_FAILURE, ONE_TRANSACTION;
+
+ public static TransactionMode getTransactionMode(String transactionModeString)
+ {
+ TransactionMode transactionMode;
+ if (transactionModeString.equalsIgnoreCase("ISOLATED_TRANSACTIONS"))
+ {
+ transactionMode = TransactionMode.ISOLATED_TRANSACTIONS;
+ }
+ else if (transactionModeString.equalsIgnoreCase("UNTIL_FIRST_FAILURE"))
+ {
+ transactionMode = TransactionMode.UNTIL_FIRST_FAILURE;
+ }
+ else if (transactionModeString.equalsIgnoreCase("ONE_TRANSACTION"))
+ {
+ transactionMode = TransactionMode.ONE_TRANSACTION;
+ }
+ else
+ {
+ // The default ....
+ transactionMode = TransactionMode.ISOLATED_TRANSACTIONS;
+ }
+ return transactionMode;
+ }
+ }
+
+ /**
+ * Enum to define if compensating actions are run.
+ *
+ * @author Andy Hind
+ */
+ public enum CompensatingActionMode
+ {
+ RUN_COMPENSATING_ACTIONS_ON_FAILURE, IGNORE;
+
+ public static CompensatingActionMode getCompensatingActionMode(String compensatingActionModeString)
+ {
+ CompensatingActionMode compensatingActionMode;
+ if (compensatingActionModeString.equalsIgnoreCase("RUN_COMPENSATING_ACTIONS_ON_FAILURE"))
+ {
+ compensatingActionMode = CompensatingActionMode.RUN_COMPENSATING_ACTIONS_ON_FAILURE;
+ }
+ else if (compensatingActionModeString.equalsIgnoreCase("IGNORE"))
+ {
+ compensatingActionMode = CompensatingActionMode.IGNORE;
+ }
+ else
+ {
+ // The default ....
+ compensatingActionMode = CompensatingActionMode.IGNORE;
+ }
+ return compensatingActionMode;
+ }
+ }
+
+ /*
+ * Key used to pass the action in the quartz job definition
+ */
+ private static final String ACTION_JOB_DATA_MAP_KEY = "Action";
+
+ /*
+ * The Action service.
+ */
+ private ActionService actionService;
+
+ /*
+ * The user in whose name the action will run.
+ */
+ private String runAsUser;
+
+ /*
+ * The template definition of the action.
+ */
+ private TemplateActionDefinition templateActionDefinition;
+
+ /*
+ * The transaction mode in which all the nodes found by this sceduled action will be treated.
+ */
+ private TransactionMode transactionMode = TransactionMode.ISOLATED_TRANSACTIONS;
+
+ /*
+ * Control if compensating actions will be used. The default is not to apply compensating actions.
+ */
+ private CompensatingActionMode compensatingActionMode = CompensatingActionMode.IGNORE;
+
+ /*
+ * The transaction service
+ */
+ private TransactionService transactionService;
+
+ /**
+ * Simple constructor
+ */
+ public AbstractScheduledAction()
+ {
+ super();
+ }
+
+ /**
+ * Get the user in whose name to run the action.
+ *
+ * @return
+ */
+ public String getRunAsUser()
+ {
+ return runAsUser;
+ }
+
+ /**
+ * Set the user in whose name to run the action.
+ *
+ * @param runAsUser
+ */
+ public void setRunAsUser(String runAsUser)
+ {
+ this.runAsUser = runAsUser;
+ }
+
+ /**
+ * Get the template definition.
+ */
+ public TemplateActionDefinition getTemplateActionDefinition()
+ {
+ return templateActionDefinition;
+ }
+
+ /**
+ * Set the action service - IOC.
+ *
+ * @param actionService
+ */
+ public void setActionService(ActionService actionService)
+ {
+ this.actionService = actionService;
+ }
+
+ /**
+ * Get the actions service.
+ *
+ * @return
+ */
+ public ActionService getActionService()
+ {
+ return actionService;
+ }
+
+ /**
+ * Set the behaviour for compensating actiions.
+ *
+ * @param compensatingActionModeString
+ */
+ public void setCompensatingActionMode(String compensatingActionModeString)
+ {
+ this.compensatingActionMode = CompensatingActionMode.getCompensatingActionMode(compensatingActionModeString);
+ }
+
+ /**
+ * Set transactional behaviour.
+ *
+ * @param transactionModeString
+ */
+ public void setTransactionMode(String transactionModeString)
+ {
+ this.transactionMode = TransactionMode.getTransactionMode(transactionModeString);
+ }
+
+ /**
+ * Get the transaction service.
+ *
+ * @return
+ */
+ public TransactionService getTransactionService()
+ {
+ return transactionService;
+ }
+
+ /**
+ * Set the transactions service - IOC.
+ *
+ * @param transactionService
+ */
+ public void setTransactionService(TransactionService transactionService)
+ {
+ this.transactionService = transactionService;
+ }
+
+ /**
+ * Set the template action that is used to generate the real action for each node.
+ */
+ public void setTemplateActionDefinition(TemplateActionDefinition templateActionDefinition)
+ {
+ this.templateActionDefinition = templateActionDefinition;
+ }
+
+ /**
+ * Get the behaviour for compensating actions.
+ *
+ * @return
+ */
+ public CompensatingActionMode getCompensatingActionModeEnum()
+ {
+ return compensatingActionMode;
+ }
+
+ /**
+ * Get the transaction mode.
+ *
+ * @return
+ */
+ public TransactionMode getTransactionModeEnum()
+ {
+ return transactionMode;
+ }
+
+ public void register(Scheduler scheduler) throws SchedulerException
+ {
+ JobDetail jobDetail = getJobDetail();
+ Trigger trigger = getTrigger();
+
+ if (s_logger.isDebugEnabled())
+ {
+ s_logger.debug(("Registering job: " + jobDetail));
+ s_logger.debug(("With trigger: " + trigger));
+ }
+ scheduler.scheduleJob(jobDetail, trigger);
+ }
+
+ /**
+ * Get the trigger definition for this job. Used to register with the injected scheduler.
+ *
+ * @return
+ */
+ public abstract Trigger getTrigger();
+
+ /**
+ * Get the list of nodes against which this action should run.
+ *
+ * @return
+ */
+ public abstract List getNodes();
+
+ /**
+ * Generate the actual action for the given node from the action template.
+ *
+ * @param nodeRef
+ * @return
+ */
+ public abstract Action getAction(NodeRef nodeRef);
+
+ /**
+ * Get the job detail.
+ *
+ * @return
+ */
+ private JobDetail getJobDetail()
+ {
+ JobDataMap jobDataMap = new JobDataMap();
+ jobDataMap.put(ACTION_JOB_DATA_MAP_KEY, this);
+
+ JobDetail jobDetail = new JobDetail();
+ jobDetail.setName(getJobName());
+ jobDetail.setGroup(getJobGroup());
+ jobDetail.setJobDataMap(jobDataMap);
+ jobDetail.setJobClass(JobDefinition.class);
+ return jobDetail;
+ }
+
+ /**
+ * Job definition to run scheduled action
+ *
+ * @author Andy Hind
+ */
+ public static class JobDefinition implements Job
+ {
+
+ public void execute(JobExecutionContext ctx) throws JobExecutionException
+ {
+ final AbstractScheduledAction abstractScheduledAction = (AbstractScheduledAction) ctx.getJobDetail()
+ .getJobDataMap().get(ACTION_JOB_DATA_MAP_KEY);
+
+ // Run as the required user
+ AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork