/*
* 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.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.RetryingTransactionHelper.RetryingTransactionCallback;
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
{
/**
* Run Each action in an isolated transaction
*/
ISOLATED_TRANSACTIONS,
/**
* Run each action in anisolated transaction, but stop at the first failure
*/
UNTIL_FIRST_FAILURE,
/**
* Run in one big transaction. Any failure rolls the whole lot b ack
*/
ONE_TRANSACTION;
/**
* Generate a mode from a string.
*
* @param transactionModeString
* @return - the transaction mode.
*/
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
{
/**
* If failure occurs run the comensating actions.
*/
RUN_COMPENSATING_ACTIONS_ON_FAILURE,
/**
* Ignore compensating actions
*/
IGNORE;
/**
* Parse a string to a compensating action mode - used in reading the config.
*
* @param compensatingActionModeString
* @return - the compensating action mode.
*/
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 - the user as whom to run the action
*/
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.
* @return - the template action 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 - the action service
*/
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 - the transaction service.
*/
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.
* @param templateActionDefinition
*/
public void setTemplateActionDefinition(TemplateActionDefinition templateActionDefinition)
{
this.templateActionDefinition = templateActionDefinition;
}
/**
* Get the behaviour for compensating actions.
*
* @return - the compensating action mode.
*/
public CompensatingActionMode getCompensatingActionModeEnum()
{
return compensatingActionMode;
}
/**
* Get the transaction mode.
*
* @return - the transaction mode.
*/
public TransactionMode getTransactionModeEnum()
{
return transactionMode;
}
/**
* Register with teh scheduler.
*
* @param scheduler
* @throws SchedulerException
*/
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 - the trigger definition for this scheduled action.
*/
public abstract Trigger getTrigger();
/**
* Get the list of nodes against which this action should run.
*
* @return - the list of node refs for which to run this action.
*/
public abstract List getNodes();
/**
* Generate the actual action for the given node from the action template.
*
* @param nodeRef
* @return - the action to execute.
*/
public abstract Action getAction(NodeRef nodeRef);
/**
* Get the job detail.
*
* @return - the job detail.
*/
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
{
/**
* Execute the job
*
* @param ctx
* @throws JobExecutionException
*
*/
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