mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-24 17:32:48 +00:00
Scheduled Actions
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2673 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -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<NodeRef> 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<Object>()
|
||||
{
|
||||
public Object doWork()
|
||||
{
|
||||
// Get the list of nodes
|
||||
List<NodeRef> nodes = abstractScheduledAction.getNodes();
|
||||
if (s_logger.isDebugEnabled())
|
||||
{
|
||||
s_logger.debug("Found " + nodes.size());
|
||||
}
|
||||
|
||||
// Individual transactions
|
||||
if (abstractScheduledAction.getTransactionModeEnum() == TransactionMode.ONE_TRANSACTION)
|
||||
{
|
||||
if (s_logger.isDebugEnabled())
|
||||
{
|
||||
s_logger.debug("Executing in one transaction");
|
||||
}
|
||||
runTransactionalActions(nodes);
|
||||
return null;
|
||||
}
|
||||
// Single global transaction
|
||||
else
|
||||
{
|
||||
|
||||
if (s_logger.isDebugEnabled())
|
||||
{
|
||||
s_logger.debug("Executing in individual transaction");
|
||||
}
|
||||
for (NodeRef nodeRef : nodes)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
runTransactionalAction(nodeRef);
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
if (abstractScheduledAction.getTransactionModeEnum() == TransactionMode.ISOLATED_TRANSACTIONS)
|
||||
{
|
||||
s_logger
|
||||
.error(
|
||||
"Error in scheduled action executed in isolated transactions (other actions will continue",
|
||||
t);
|
||||
}
|
||||
else
|
||||
{
|
||||
throwRuntimeException(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the action to all nodes in one overall transaction
|
||||
*
|
||||
* @param nodes
|
||||
*/
|
||||
public void runTransactionalActions(final List<NodeRef> nodes)
|
||||
{
|
||||
boolean runCompensatingActions = abstractScheduledAction.getCompensatingActionModeEnum() == CompensatingActionMode.RUN_COMPENSATING_ACTIONS_ON_FAILURE;
|
||||
|
||||
try
|
||||
{
|
||||
TransactionUtil.executeInUserTransaction(abstractScheduledAction.getTransactionService(),
|
||||
new TransactionUtil.TransactionWork<Object>()
|
||||
{
|
||||
public Object doWork() throws Exception
|
||||
{
|
||||
// Build the full list of compensating actions
|
||||
// If anything goes wrong we need to do all these instead
|
||||
List<Pair<Action, NodeRef>> compensatingActions = new ArrayList<Pair<Action, NodeRef>>(
|
||||
nodes.size());
|
||||
|
||||
for (NodeRef nodeRef : nodes)
|
||||
{
|
||||
Action action = abstractScheduledAction.getAction(nodeRef);
|
||||
Action compensatingAction = action.getCompensatingAction();
|
||||
if (compensatingAction != null)
|
||||
{
|
||||
compensatingActions.add(new Pair<Action, NodeRef>(compensatingAction,
|
||||
nodeRef));
|
||||
}
|
||||
}
|
||||
|
||||
// Run all the actions
|
||||
try
|
||||
{
|
||||
|
||||
for (NodeRef nodeRef : nodes)
|
||||
{
|
||||
Action action = abstractScheduledAction.getAction(nodeRef);
|
||||
abstractScheduledAction.getActionService().executeAction(action,
|
||||
nodeRef);
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
// Throw exception to trigger compensating actions
|
||||
throw new CompensatingActionException("Requires compensating action", t,
|
||||
compensatingActions);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
// Do compensation if required
|
||||
doCompensation(runCompensatingActions, true, t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run compensating actions.
|
||||
*
|
||||
* These are always in their own transaction. We try to run all compensating actions.
|
||||
*
|
||||
* @param runCompensatingActions
|
||||
* @param rethrow
|
||||
* @param t
|
||||
*/
|
||||
private void doCompensation(boolean runCompensatingActions, boolean rethrow, Throwable t)
|
||||
{
|
||||
// If the error triggers compensation, and they should be processed.
|
||||
if (runCompensatingActions && (t instanceof CompensatingActionException))
|
||||
{
|
||||
CompensatingActionException cae = (CompensatingActionException) t.getCause();
|
||||
for (Pair<Action, NodeRef> pair : cae.getCompensatingActions())
|
||||
if ((pair != null) && (pair.getFirst() != null) && (pair.getSecond() != null))
|
||||
{
|
||||
try
|
||||
|
||||
{
|
||||
// try the compensating action in its own tx
|
||||
runTransactionalCompensatingAction(pair);
|
||||
}
|
||||
catch (Throwable cat)
|
||||
{
|
||||
s_logger.error("Error executing compensating action", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rethrow)
|
||||
{
|
||||
throwRuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a single transaction in its own tx
|
||||
*
|
||||
* @param nodeRef
|
||||
*/
|
||||
public void runTransactionalAction(final NodeRef nodeRef)
|
||||
{
|
||||
boolean runCompensatingActions = abstractScheduledAction.getCompensatingActionModeEnum() == CompensatingActionMode.RUN_COMPENSATING_ACTIONS_ON_FAILURE;
|
||||
boolean rethrow = abstractScheduledAction.getTransactionModeEnum() != TransactionMode.ISOLATED_TRANSACTIONS;
|
||||
|
||||
try
|
||||
{
|
||||
TransactionUtil.executeInUserTransaction(abstractScheduledAction.getTransactionService(),
|
||||
new TransactionUtil.TransactionWork<Object>()
|
||||
{
|
||||
public Object doWork() throws Exception
|
||||
{
|
||||
// try action - failure triggers compensation
|
||||
Action action = abstractScheduledAction.getAction(nodeRef);
|
||||
Action compensatingAction = action.getCompensatingAction();
|
||||
try
|
||||
{
|
||||
abstractScheduledAction.getActionService().executeAction(action, nodeRef);
|
||||
return null;
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
if (compensatingAction != null)
|
||||
{
|
||||
throw new CompensatingActionException(
|
||||
"Requires compensating action",
|
||||
t,
|
||||
Collections
|
||||
.<Pair<Action, NodeRef>> singletonList(new Pair<Action, NodeRef>(
|
||||
action.getCompensatingAction(), nodeRef)));
|
||||
}
|
||||
else
|
||||
{
|
||||
return throwRuntimeException(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
// Run compensating action if required
|
||||
doCompensation(runCompensatingActions, rethrow, t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage running a compensating action and chaining all its compensating actions until done
|
||||
*
|
||||
* @param pair
|
||||
*/
|
||||
public void runTransactionalCompensatingAction(final Pair<Action, NodeRef> pair)
|
||||
{
|
||||
boolean runCompensatingActions = abstractScheduledAction.getCompensatingActionModeEnum() == CompensatingActionMode.RUN_COMPENSATING_ACTIONS_ON_FAILURE;
|
||||
|
||||
try
|
||||
{
|
||||
TransactionUtil.executeInUserTransaction(abstractScheduledAction.getTransactionService(),
|
||||
new TransactionUtil.TransactionWork<Object>()
|
||||
{
|
||||
public Object doWork() throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
abstractScheduledAction.getActionService().executeAction(pair.getFirst(),
|
||||
pair.getSecond());
|
||||
return null;
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
List<Pair<Action, NodeRef>> compensatingActions = new ArrayList<Pair<Action, NodeRef>>(
|
||||
1);
|
||||
if (pair.getFirst().getCompensatingAction() != null)
|
||||
{
|
||||
compensatingActions.add(new Pair<Action, NodeRef>(pair.getFirst()
|
||||
.getCompensatingAction(), pair.getSecond()));
|
||||
}
|
||||
throw new CompensatingActionException("Requires compensating action", t,
|
||||
compensatingActions);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
// Run compensation
|
||||
doCompensation(runCompensatingActions, true, t);
|
||||
}
|
||||
}
|
||||
|
||||
}, abstractScheduledAction.getRunAsUser());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple class to hold to related objects
|
||||
*
|
||||
* @author Andy Hind
|
||||
*/
|
||||
public static class Pair<FIRST, SECOND>
|
||||
{
|
||||
FIRST first;
|
||||
|
||||
SECOND second;
|
||||
|
||||
Pair(FIRST first, SECOND second)
|
||||
{
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
FIRST getFirst()
|
||||
{
|
||||
return first;
|
||||
}
|
||||
|
||||
SECOND getSecond()
|
||||
{
|
||||
return second;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Support method to translate exceptions to runtime exceptions.
|
||||
*
|
||||
* @param t
|
||||
* @return
|
||||
*/
|
||||
private static Object throwRuntimeException(Throwable t)
|
||||
{
|
||||
if (t instanceof RuntimeException)
|
||||
{
|
||||
throw (RuntimeException) t;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new RuntimeException("Error during execution of transaction.", t);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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 org.alfresco.service.cmr.action.ActionService;
|
||||
import org.alfresco.service.cmr.repository.TemplateService;
|
||||
|
||||
/**
|
||||
* Common attributes for template action definitions.
|
||||
*
|
||||
* @author Andy Hind
|
||||
*/
|
||||
public abstract class AbstractTemplateActionDefinition implements TemplateActionDefinition
|
||||
{
|
||||
/*
|
||||
* The action service
|
||||
*/
|
||||
public ActionService actionService;
|
||||
|
||||
/*
|
||||
* The template service
|
||||
*/
|
||||
public TemplateService templateService;
|
||||
|
||||
/*
|
||||
* The compensating action
|
||||
*/
|
||||
protected TemplateActionDefinition compensatingTemplateActionDefinition;
|
||||
|
||||
/**
|
||||
* Simple construction
|
||||
*/
|
||||
public AbstractTemplateActionDefinition()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the action service.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public ActionService getActionService()
|
||||
{
|
||||
return actionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the action service - IOC.
|
||||
*
|
||||
* @param actionService
|
||||
*/
|
||||
public void setActionService(ActionService actionService)
|
||||
{
|
||||
this.actionService = actionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the template service.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public TemplateService getTemplateService()
|
||||
{
|
||||
return templateService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the template service - IOC.
|
||||
*
|
||||
* @param templateService
|
||||
*/
|
||||
public void setTemplateService(TemplateService templateService)
|
||||
{
|
||||
this.templateService = templateService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the template to define the compensating action.
|
||||
*
|
||||
* @param compensatingTemplateActionDefinition
|
||||
*/
|
||||
public void setCompensatingTemplateCompositeActionDefinition(
|
||||
TemplateActionDefinition compensatingTemplateActionDefinition)
|
||||
{
|
||||
this.compensatingTemplateActionDefinition = compensatingTemplateActionDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the template that defines the conpensating action.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public TemplateActionDefinition getCompensatingTemplateCompositeActionDefinition()
|
||||
{
|
||||
return compensatingTemplateActionDefinition;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.List;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.repo.action.scheduled.AbstractScheduledAction.Pair;
|
||||
import org.alfresco.service.cmr.action.Action;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
|
||||
/**
|
||||
* Error that triggers the execution of compensating actions.
|
||||
*
|
||||
* The required compensating actions are contained by the exception thrown.
|
||||
*
|
||||
* @author Andy Hind
|
||||
*/
|
||||
public class CompensatingActionException extends AlfrescoRuntimeException
|
||||
{
|
||||
|
||||
/**
|
||||
* Comment for <code>serialVersionUID</code>
|
||||
*/
|
||||
private static final long serialVersionUID = 2144573075007116603L;
|
||||
|
||||
List<Pair<Action, NodeRef>> compensatingActions;
|
||||
|
||||
public CompensatingActionException(String msgId)
|
||||
{
|
||||
super(msgId);
|
||||
}
|
||||
|
||||
public CompensatingActionException(String msgId, Throwable cause, List<Pair<Action, NodeRef>> compensatingActions)
|
||||
{
|
||||
super(msgId, cause);
|
||||
this.compensatingActions = compensatingActions;
|
||||
}
|
||||
|
||||
public List<Pair<Action, NodeRef>> getCompensatingActions()
|
||||
{
|
||||
return compensatingActions;
|
||||
}
|
||||
|
||||
public CompensatingActionException(String msgId, Object[] msgParams)
|
||||
{
|
||||
super(msgId, msgParams);
|
||||
}
|
||||
|
||||
public CompensatingActionException(String msgId, Throwable cause)
|
||||
{
|
||||
super(msgId, cause);
|
||||
}
|
||||
|
||||
public CompensatingActionException(String msgId, Object[] msgParams, Throwable cause)
|
||||
{
|
||||
super(msgId, msgParams, cause);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.List;
|
||||
|
||||
import org.alfresco.service.cmr.action.Action;
|
||||
import org.alfresco.service.cmr.action.CompositeAction;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
|
||||
/**
|
||||
* The template to define a composite action.
|
||||
*
|
||||
* @author Andy Hind
|
||||
*/
|
||||
public class CompositeTemplateActionDefinition extends AbstractTemplateActionDefinition
|
||||
{
|
||||
|
||||
/*
|
||||
* The list of action templates that define this composite
|
||||
*/
|
||||
private List<TemplateActionDefinition> templateActionDefinitions;
|
||||
|
||||
public CompositeTemplateActionDefinition()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the action templates - IOC.
|
||||
*
|
||||
* @param templateActionDefinitions
|
||||
*/
|
||||
public void setTemplateActionDefinitions(List<TemplateActionDefinition> templateActionDefinitions)
|
||||
{
|
||||
this.templateActionDefinitions = templateActionDefinitions;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of template actions.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public List<TemplateActionDefinition> templateActionDefinitions()
|
||||
{
|
||||
return templateActionDefinitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the composite action in the context of the given node.
|
||||
*
|
||||
*/
|
||||
public Action getAction(NodeRef nodeRef)
|
||||
{
|
||||
CompositeAction compositeAction = getActionService().createCompositeAction();
|
||||
for(TemplateActionDefinition tad : templateActionDefinitions)
|
||||
{
|
||||
compositeAction.addAction(tad.getAction(nodeRef));
|
||||
}
|
||||
|
||||
if (getCompensatingTemplateCompositeActionDefinition() != null)
|
||||
{
|
||||
compositeAction.setCompensatingAction(getCompensatingTemplateCompositeActionDefinition().getAction(nodeRef));
|
||||
}
|
||||
|
||||
return compositeAction;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,309 @@
|
||||
/*
|
||||
* 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.text.ParseException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.alfresco.service.cmr.action.Action;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.repository.TemplateService;
|
||||
import org.alfresco.service.cmr.search.ResultSet;
|
||||
import org.alfresco.service.cmr.search.ResultSetRow;
|
||||
import org.alfresco.service.cmr.search.SearchParameters;
|
||||
import org.alfresco.service.cmr.search.SearchService;
|
||||
import org.quartz.CronTrigger;
|
||||
import org.quartz.Scheduler;
|
||||
import org.quartz.Trigger;
|
||||
|
||||
/**
|
||||
* A scheduled action for which the trigger is defined in the standard cron format and the nodes to which the
|
||||
* action should be run is defined from the nodes selected by query.
|
||||
*
|
||||
* @author Andy Hind
|
||||
*/
|
||||
public class CronScheduledQueryBasedTemplateActionDefinition extends AbstractScheduledAction
|
||||
{
|
||||
/*
|
||||
* The search service.
|
||||
*/
|
||||
private SearchService searchService;
|
||||
|
||||
/*
|
||||
* The template service.
|
||||
*/
|
||||
private TemplateService templateService;
|
||||
|
||||
/*
|
||||
* The query language to use
|
||||
*/
|
||||
private String queryLanguage;
|
||||
|
||||
/*
|
||||
* The stores against which the query should run
|
||||
*/
|
||||
private List<String> stores;
|
||||
|
||||
/*
|
||||
* The template for the query
|
||||
*/
|
||||
private String queryTemplate;
|
||||
|
||||
/*
|
||||
* The cron expression
|
||||
*/
|
||||
private String cronExpression;
|
||||
|
||||
/*
|
||||
* The name of the job
|
||||
*/
|
||||
private String jobName;
|
||||
|
||||
/*
|
||||
* The job group
|
||||
*/
|
||||
private String jobGroup;
|
||||
|
||||
/*
|
||||
* The name of the trigger
|
||||
*/
|
||||
private String triggerName;
|
||||
|
||||
/*
|
||||
* The name of the trigger group
|
||||
*/
|
||||
private String triggerGroup;
|
||||
|
||||
/*
|
||||
* The scheduler
|
||||
*/
|
||||
private Scheduler scheduler;
|
||||
|
||||
/*
|
||||
* The templateModelFactory
|
||||
*
|
||||
* This defines in which template language the query template is defined
|
||||
*/
|
||||
private TemplateActionModelFactory templateActionModelFactory;
|
||||
|
||||
public CronScheduledQueryBasedTemplateActionDefinition()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
//
|
||||
// IOC
|
||||
//
|
||||
|
||||
public SearchService getSearchService()
|
||||
{
|
||||
return searchService;
|
||||
}
|
||||
|
||||
public void setSearchService(SearchService searchService)
|
||||
{
|
||||
this.searchService = searchService;
|
||||
}
|
||||
|
||||
public TemplateService getTemplateService()
|
||||
{
|
||||
return templateService;
|
||||
}
|
||||
|
||||
public void setTemplateService(TemplateService templateService)
|
||||
{
|
||||
this.templateService = templateService;
|
||||
}
|
||||
|
||||
public Scheduler getScheduler()
|
||||
{
|
||||
return scheduler;
|
||||
}
|
||||
|
||||
public void setScheduler(Scheduler scheduler)
|
||||
{
|
||||
this.scheduler = scheduler;
|
||||
}
|
||||
|
||||
public TemplateActionModelFactory getTemplateActionModelFactory()
|
||||
{
|
||||
return templateActionModelFactory;
|
||||
}
|
||||
|
||||
public void setTemplateActionModelFactory(TemplateActionModelFactory templateActionModelFactory)
|
||||
{
|
||||
this.templateActionModelFactory = templateActionModelFactory;
|
||||
}
|
||||
|
||||
//
|
||||
// End of IOC
|
||||
//
|
||||
|
||||
@Override
|
||||
public Trigger getTrigger()
|
||||
{
|
||||
try
|
||||
{
|
||||
return new CronTrigger(getTriggerName(), getTriggerGroup(), getCronExpression());
|
||||
}
|
||||
catch (ParseException e)
|
||||
{
|
||||
throw new InvalidCronExpression("Invalid chron expression: n" + getCronExpression());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NodeRef> getNodes()
|
||||
{
|
||||
LinkedList<NodeRef> nodeRefs = new LinkedList<NodeRef>();
|
||||
|
||||
// Build the actual query string
|
||||
String queryTemplate = getQueryTemplate();
|
||||
String query = templateService.processTemplateString(getTemplateActionModelFactory().getTemplateEngine(),
|
||||
queryTemplate, getTemplateActionModelFactory().getModel());
|
||||
|
||||
// Execute the query
|
||||
SearchParameters sp = new SearchParameters();
|
||||
sp.setLanguage(getQueryLanguage());
|
||||
sp.setQuery(query);
|
||||
for (String storeRef : getStores())
|
||||
{
|
||||
sp.addStore(new StoreRef(storeRef));
|
||||
}
|
||||
|
||||
// Transform the reults into a node list
|
||||
ResultSet results = null;
|
||||
try
|
||||
{
|
||||
results = searchService.query(sp);
|
||||
for (ResultSetRow row : results)
|
||||
{
|
||||
nodeRefs.add(row.getNodeRef());
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (results != null)
|
||||
{
|
||||
results.close();
|
||||
}
|
||||
}
|
||||
return nodeRefs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Action getAction(NodeRef nodeRef)
|
||||
{
|
||||
// Use the template to build its action
|
||||
return getTemplateActionDefinition().getAction(nodeRef);
|
||||
}
|
||||
|
||||
//
|
||||
// IOC/Getters/Setters for instance variables
|
||||
//
|
||||
|
||||
public void setQueryLanguage(String queryLanguage)
|
||||
{
|
||||
this.queryLanguage = queryLanguage;
|
||||
}
|
||||
|
||||
public String getQueryLanguage()
|
||||
{
|
||||
return queryLanguage;
|
||||
}
|
||||
|
||||
public void setStores(List<String> stores)
|
||||
{
|
||||
this.stores = stores;
|
||||
}
|
||||
|
||||
public List<String> getStores()
|
||||
{
|
||||
return stores;
|
||||
}
|
||||
|
||||
public void setQueryTemplate(String queryTemplate)
|
||||
{
|
||||
this.queryTemplate = queryTemplate;
|
||||
}
|
||||
|
||||
public String getQueryTemplate()
|
||||
{
|
||||
return queryTemplate;
|
||||
}
|
||||
|
||||
public void setCronExpression(String cronExpression)
|
||||
{
|
||||
this.cronExpression = cronExpression;
|
||||
}
|
||||
|
||||
public String getCronExpression()
|
||||
{
|
||||
return cronExpression;
|
||||
}
|
||||
|
||||
public void setJobName(String jobName)
|
||||
{
|
||||
this.jobName = jobName;
|
||||
}
|
||||
|
||||
public String getJobName()
|
||||
{
|
||||
return jobName;
|
||||
}
|
||||
|
||||
public void setJobGroup(String jobGroup)
|
||||
{
|
||||
this.jobGroup = jobGroup;
|
||||
}
|
||||
|
||||
public String getJobGroup()
|
||||
{
|
||||
return jobGroup;
|
||||
}
|
||||
|
||||
public void setTriggerName(String triggerName)
|
||||
{
|
||||
this.triggerName = triggerName;
|
||||
}
|
||||
|
||||
public String getTriggerName()
|
||||
{
|
||||
return triggerName;
|
||||
}
|
||||
|
||||
public void setTriggerGroup(String triggerGroup)
|
||||
{
|
||||
this.triggerGroup = triggerGroup;
|
||||
}
|
||||
|
||||
public String getTriggerGroup()
|
||||
{
|
||||
return this.triggerGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register with the scheduler.
|
||||
*/
|
||||
public void afterPropertiesSet() throws Exception
|
||||
{
|
||||
register(getScheduler());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
import javax.transaction.UserTransaction;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.alfresco.repo.security.authentication.AuthenticationComponent;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.repository.datatype.Duration;
|
||||
import org.alfresco.service.transaction.TransactionService;
|
||||
import org.alfresco.util.ApplicationContextHelper;
|
||||
import org.alfresco.util.ISO8601DateFormat;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
/**
|
||||
* Test that the correct date ranges are generated for lucene
|
||||
*
|
||||
* @author Andy Hind
|
||||
*/
|
||||
public class FreeMarkerModelLuceneFunctionTest extends TestCase
|
||||
{
|
||||
//private static SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.sssZ");
|
||||
private static SimpleDateFormat SDF2 = new SimpleDateFormat("yyyy-MM-dd");
|
||||
|
||||
private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
|
||||
private AuthenticationComponent authenticationComponent;
|
||||
private ServiceRegistry serviceRegistry;
|
||||
private UserTransaction tx;
|
||||
|
||||
private Date today;
|
||||
|
||||
public FreeMarkerModelLuceneFunctionTest()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
public FreeMarkerModelLuceneFunctionTest(String arg0)
|
||||
{
|
||||
super(arg0);
|
||||
}
|
||||
|
||||
public void setUp() throws Exception
|
||||
{
|
||||
|
||||
authenticationComponent = (AuthenticationComponent) ctx.getBean("authenticationComponentImpl");
|
||||
serviceRegistry = (ServiceRegistry) ctx.getBean("ServiceRegistry");
|
||||
|
||||
this.authenticationComponent.setSystemUserAsCurrentUser();
|
||||
|
||||
TransactionService transactionService = (TransactionService) ctx.getBean(ServiceRegistry.TRANSACTION_SERVICE
|
||||
.getLocalName());
|
||||
tx = transactionService.getUserTransaction();
|
||||
tx.begin();
|
||||
|
||||
GregorianCalendar cal = new GregorianCalendar();
|
||||
cal.set(Calendar.HOUR, 0);
|
||||
cal.set(Calendar.MINUTE, 0);
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
today = cal.getTime();
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception
|
||||
{
|
||||
authenticationComponent.clearCurrentSecurityContext();
|
||||
tx.rollback();
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
public void testDate()
|
||||
{
|
||||
String template = "${date?date?string(\"yyyy-MM-dd\")}";
|
||||
FreeMarkerWithLuceneExtensionsModelFactory mf = new FreeMarkerWithLuceneExtensionsModelFactory();
|
||||
mf.setServiceRegistry(serviceRegistry);
|
||||
String result = serviceRegistry.getTemplateService().processTemplateString("freemarker", template, mf.getModel());
|
||||
assertEquals(result, SDF2.format(new Date()));
|
||||
}
|
||||
|
||||
public void testLuceneDateRangeFunction()
|
||||
{
|
||||
String template = "${luceneDateRange(\"2000-01-01T00:00:00.000Z\", \"P1D\")}";
|
||||
FreeMarkerWithLuceneExtensionsModelFactory mf = new FreeMarkerWithLuceneExtensionsModelFactory();
|
||||
mf.setServiceRegistry(serviceRegistry);
|
||||
String result = serviceRegistry.getTemplateService().processTemplateString("freemarker", template, mf.getModel());
|
||||
assertEquals(result, "[2000-01-01T00:00:00.000Z TO 2000-01-02T00:00:00.000Z]");
|
||||
}
|
||||
|
||||
public void testLuceneDateRangeFunctionToAdte()
|
||||
{
|
||||
String template = "${luceneDateRange(\"2000-01-01T00:00:00.000Z\", \"2000-01-05T00:00:00.000Z\")}";
|
||||
FreeMarkerWithLuceneExtensionsModelFactory mf = new FreeMarkerWithLuceneExtensionsModelFactory();
|
||||
mf.setServiceRegistry(serviceRegistry);
|
||||
String result = serviceRegistry.getTemplateService().processTemplateString("freemarker", template, mf.getModel());
|
||||
assertEquals(result, "[2000-01-01T00:00:00.000Z TO 2000-01-05T00:00:00.000Z]");
|
||||
}
|
||||
|
||||
public void testLuceneDateRangeFunctionTodayPlus4()
|
||||
{
|
||||
String template = "${luceneDateRange(today, \"P4D\")}";
|
||||
FreeMarkerWithLuceneExtensionsModelFactory mf = new FreeMarkerWithLuceneExtensionsModelFactory();
|
||||
mf.setServiceRegistry(serviceRegistry);
|
||||
String result = serviceRegistry.getTemplateService().processTemplateString("freemarker", template, mf.getModel());
|
||||
assertNotNull(result);
|
||||
assertEquals(result, "["+ISO8601DateFormat.format(today) + " TO " + ISO8601DateFormat.format(Duration.add(today, new Duration("P4D"))) + "]");
|
||||
}
|
||||
|
||||
public void testLuceneDateRangeFunctionTodayMinus4()
|
||||
{
|
||||
String template = "${luceneDateRange(today, \"-P4D\")}";
|
||||
FreeMarkerWithLuceneExtensionsModelFactory mf = new FreeMarkerWithLuceneExtensionsModelFactory();
|
||||
mf.setServiceRegistry(serviceRegistry);
|
||||
String result = serviceRegistry.getTemplateService().processTemplateString("freemarker", template, mf.getModel());
|
||||
assertEquals(result, "["+ ISO8601DateFormat.format(Duration.add(today, new Duration("-P4D"))) + " TO " + ISO8601DateFormat.format(today) + "]");
|
||||
}
|
||||
|
||||
|
||||
public void testLuceneDateRangeFunctionTodayToday()
|
||||
{
|
||||
String template = "${luceneDateRange(today, today)}";
|
||||
FreeMarkerWithLuceneExtensionsModelFactory mf = new FreeMarkerWithLuceneExtensionsModelFactory();
|
||||
mf.setServiceRegistry(serviceRegistry);
|
||||
String result = serviceRegistry.getTemplateService().processTemplateString("freemarker", template, mf.getModel());
|
||||
assertEquals(result, "["+ISO8601DateFormat.format(today) + " TO " + ISO8601DateFormat.format(today) + "]");
|
||||
}
|
||||
}
|
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* 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.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.repository.TemplateNode;
|
||||
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
|
||||
import org.alfresco.service.cmr.repository.datatype.Duration;
|
||||
import org.alfresco.service.cmr.search.ResultSet;
|
||||
import org.alfresco.service.cmr.search.SearchParameters;
|
||||
import org.alfresco.util.ISO8601DateFormat;
|
||||
|
||||
import freemarker.template.TemplateDateModel;
|
||||
import freemarker.template.TemplateMethodModelEx;
|
||||
import freemarker.template.TemplateModelException;
|
||||
import freemarker.template.TemplateScalarModel;
|
||||
|
||||
/**
|
||||
* A factory implementation to build suitable models for the freemarker templating language.
|
||||
*
|
||||
* @author Andy Hind
|
||||
*/
|
||||
public class FreeMarkerWithLuceneExtensionsModelFactory implements TemplateActionModelFactory
|
||||
{
|
||||
/*
|
||||
* Service registry
|
||||
*/
|
||||
private ServiceRegistry serviceRegistry;
|
||||
|
||||
public FreeMarkerWithLuceneExtensionsModelFactory()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
// IOC
|
||||
|
||||
public void setServiceRegistry(ServiceRegistry serviceRegistry)
|
||||
{
|
||||
this.serviceRegistry = serviceRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the non-contextual model.
|
||||
*
|
||||
* This defines:
|
||||
* <ol>
|
||||
* <li>dates: date, today, yesterday, tomorrow
|
||||
* <li>functions: luceneDateRange, selectSingleNode
|
||||
* </ol>
|
||||
*/
|
||||
public Map<String, Object> getModel()
|
||||
{
|
||||
GregorianCalendar cal = new GregorianCalendar();
|
||||
cal.set(Calendar.HOUR, 0);
|
||||
cal.set(Calendar.MINUTE, 0);
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
HashMap<String, Object> model = new HashMap<String, Object>();
|
||||
|
||||
model.put("date", new Date());
|
||||
|
||||
Date today = cal.getTime();
|
||||
model.put("today", today);
|
||||
|
||||
model.put("yesterday", Duration.add(today, new Duration("-P1D")));
|
||||
|
||||
model.put("tomorrow", Duration.add(today, new Duration("P1D")));
|
||||
|
||||
model.put("luceneDateRange", new LuceneDateRangeFunction());
|
||||
|
||||
model.put("selectSingleNode", new QueryForSingleNodeFunction());
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a non-contextual nod model + the contextual node
|
||||
*/
|
||||
public Map<String, Object> getModel(NodeRef nodeRef)
|
||||
{
|
||||
Map<String, Object> model = getModel();
|
||||
|
||||
TemplateNode companyRootNode = new TemplateNode(nodeRef, serviceRegistry, null);
|
||||
model.put("node", companyRootNode);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to find a single node by query
|
||||
*
|
||||
* @author Andy Hind
|
||||
*/
|
||||
private class QueryForSingleNodeFunction implements TemplateMethodModelEx
|
||||
{
|
||||
public Object exec(List args) throws TemplateModelException
|
||||
{
|
||||
if (args.size() == 3)
|
||||
{
|
||||
Object arg0 = args.get(0);
|
||||
Object arg1 = args.get(1);
|
||||
Object arg2 = args.get(2);
|
||||
StoreRef storeRef;
|
||||
String language;
|
||||
String query;
|
||||
|
||||
if (arg0 instanceof TemplateScalarModel)
|
||||
{
|
||||
storeRef = new StoreRef(((TemplateScalarModel) arg0).getAsString());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new TemplateModelException("Invalid store string");
|
||||
}
|
||||
|
||||
if (arg1 instanceof TemplateScalarModel)
|
||||
{
|
||||
language = ((TemplateScalarModel) arg1).getAsString();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new TemplateModelException("Invalid language string");
|
||||
}
|
||||
|
||||
if (arg2 instanceof TemplateScalarModel)
|
||||
{
|
||||
query = ((TemplateScalarModel) arg2).getAsString();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new TemplateModelException("Invalid query string");
|
||||
}
|
||||
|
||||
SearchParameters sp = new SearchParameters();
|
||||
sp.addStore(storeRef);
|
||||
sp.setLanguage(language);
|
||||
sp.setQuery(query);
|
||||
|
||||
ResultSet results = serviceRegistry.getSearchService().query(sp);
|
||||
|
||||
if (results.length() == 0)
|
||||
{
|
||||
throw new TemplateModelException("No nodes selected");
|
||||
}
|
||||
|
||||
else if (results.length() == 1)
|
||||
{
|
||||
return results.getNodeRef(0).toString();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new TemplateModelException("More than one node selected");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new TemplateModelException("Incorrect arguments");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to generate the date range portion of a lucene query
|
||||
*
|
||||
* @author Andy Hind
|
||||
*/
|
||||
private static class LuceneDateRangeFunction implements TemplateMethodModelEx
|
||||
{
|
||||
|
||||
public Object exec(List args) throws TemplateModelException
|
||||
{
|
||||
if (args.size() == 2)
|
||||
{
|
||||
|
||||
Object arg0 = args.get(0);
|
||||
Object arg1 = args.get(1);
|
||||
|
||||
Date startDate = null;
|
||||
Date endDate = null;
|
||||
|
||||
if (arg0 instanceof TemplateDateModel)
|
||||
{
|
||||
startDate = (Date) ((TemplateDateModel) arg0).getAsDate();
|
||||
}
|
||||
else if (arg0 instanceof TemplateScalarModel)
|
||||
{
|
||||
String startDateString = ((TemplateScalarModel) arg0).getAsString();
|
||||
startDate = ISO8601DateFormat.parse(startDateString);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new TemplateModelException("Invalid date entry");
|
||||
}
|
||||
|
||||
if (arg1 instanceof TemplateDateModel)
|
||||
{
|
||||
endDate = (Date) ((TemplateDateModel) arg0).getAsDate();
|
||||
}
|
||||
else if (arg1 instanceof TemplateScalarModel)
|
||||
{
|
||||
|
||||
String valueString = ((TemplateScalarModel) arg1).getAsString();
|
||||
try
|
||||
{
|
||||
Duration duration = new Duration(valueString);
|
||||
endDate = Duration.add(startDate, duration);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
endDate = ISO8601DateFormat.parse(valueString);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new TemplateModelException("Invalid date entry");
|
||||
}
|
||||
|
||||
if (startDate.compareTo(endDate) > 0)
|
||||
{
|
||||
Date temp = startDate;
|
||||
startDate = endDate;
|
||||
endDate = temp;
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("[");
|
||||
builder.append(DefaultTypeConverter.INSTANCE.convert(String.class, startDate));
|
||||
builder.append(" TO ");
|
||||
builder.append(DefaultTypeConverter.INSTANCE.convert(String.class, endDate));
|
||||
builder.append("]");
|
||||
|
||||
return builder.toString();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new TemplateModelException("Invalid date entry");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the template engine for which this model applies.
|
||||
* In this case, "freemarker".
|
||||
*/
|
||||
public String getTemplateEngine()
|
||||
{
|
||||
return "freemarker";
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
public class InvalidCronExpression extends ScheduledActionException
|
||||
{
|
||||
|
||||
/**
|
||||
* Comment for <code>serialVersionUID</code>
|
||||
*/
|
||||
private static final long serialVersionUID = -6618964886875008727L;
|
||||
|
||||
public InvalidCronExpression(String msgId)
|
||||
{
|
||||
super(msgId);
|
||||
}
|
||||
|
||||
public InvalidCronExpression(String msgId, Object[] msgParams)
|
||||
{
|
||||
super(msgId, msgParams);
|
||||
}
|
||||
|
||||
public InvalidCronExpression(String msgId, Throwable cause)
|
||||
{
|
||||
super(msgId, cause);
|
||||
}
|
||||
|
||||
public InvalidCronExpression(String msgId, Object[] msgParams, Throwable cause)
|
||||
{
|
||||
super(msgId, msgParams, cause);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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 org.quartz.Scheduler;
|
||||
import org.quartz.SchedulerException;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
/**
|
||||
* The information needed to schedule a job.
|
||||
*
|
||||
* The implementation is responsible for creating job details, triggers and registering with a scheduler.
|
||||
*
|
||||
* This is not used anywhere at the moment. When we have a service then scheduled actions will be registered with the service.
|
||||
*
|
||||
* @author Andy Hind
|
||||
*/
|
||||
public interface ScheduledActionDefinition extends InitializingBean
|
||||
{
|
||||
/**
|
||||
* Set the template action definition that is used to build the Action to execute.
|
||||
*
|
||||
* @param templateActionDefinition
|
||||
*/
|
||||
public void setTemplateActionDefinition(TemplateActionDefinition templateActionDefinition);
|
||||
|
||||
/**
|
||||
* Get the template action definition that is used to build the Action to execute.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public TemplateActionDefinition getTemplateActionDefinition();
|
||||
|
||||
/**
|
||||
* Register with a scheduler. This should be called in the implementation afterPropertiesSet() method of InitializingBean
|
||||
*
|
||||
* @param scheduler
|
||||
* @throws SchedulerException
|
||||
*/
|
||||
public void register(Scheduler scheduler) throws SchedulerException;
|
||||
|
||||
/**
|
||||
* Set the name of the job - used for job admin
|
||||
*
|
||||
* @param jobName
|
||||
*/
|
||||
public void setJobName(String jobName);
|
||||
|
||||
/**
|
||||
* Get the name of the job - used for job admin
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getJobName();
|
||||
|
||||
/**
|
||||
* Set the job group - used for job admin
|
||||
*
|
||||
* @param jobGroup
|
||||
*/
|
||||
public void setJobGroup(String jobGroup);
|
||||
|
||||
/**
|
||||
* Get the job group - used for job admin
|
||||
* @return
|
||||
*/
|
||||
public String getJobGroup();
|
||||
|
||||
/**
|
||||
* Set the trigger name - used for job admin
|
||||
*
|
||||
* @param triggerName
|
||||
*/
|
||||
public void setTriggerName(String triggerName);
|
||||
|
||||
/**
|
||||
* Get the trigger name - used for job admin
|
||||
* @return
|
||||
*/
|
||||
public String getTriggerName();
|
||||
|
||||
/**
|
||||
* Set the trigger group - used for job admin
|
||||
*
|
||||
* @param triggerGroup
|
||||
*/
|
||||
public void setTriggerGroup(String triggerGroup);
|
||||
|
||||
/**
|
||||
* Get the trigger group - used for job admin
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getTriggerGroup();
|
||||
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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 org.alfresco.error.AlfrescoRuntimeException;
|
||||
|
||||
/**
|
||||
* Base exception for sceduled actions.
|
||||
*
|
||||
* @author Andy Hind
|
||||
*/
|
||||
public class ScheduledActionException extends AlfrescoRuntimeException
|
||||
{
|
||||
|
||||
/**
|
||||
* Comment for <code>serialVersionUID</code>
|
||||
*/
|
||||
private static final long serialVersionUID = -543079391770744598L;
|
||||
|
||||
public ScheduledActionException(String msgId)
|
||||
{
|
||||
super(msgId);
|
||||
}
|
||||
|
||||
public ScheduledActionException(String msgId, Object[] msgParams)
|
||||
{
|
||||
super(msgId, msgParams);
|
||||
}
|
||||
|
||||
public ScheduledActionException(String msgId, Throwable cause)
|
||||
{
|
||||
super(msgId, cause);
|
||||
}
|
||||
|
||||
public ScheduledActionException(String msgId, Object[] msgParams, Throwable cause)
|
||||
{
|
||||
super(msgId, msgParams, cause);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* 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.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.repo.action.executer.ActionExecuter;
|
||||
import org.alfresco.service.cmr.action.Action;
|
||||
import org.alfresco.service.cmr.action.ActionDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
|
||||
/**
|
||||
* This class defines the template used to build a single action.
|
||||
*
|
||||
* @author Andy Hind
|
||||
*/
|
||||
public class SimpleTemplateActionDefinition extends AbstractTemplateActionDefinition implements ApplicationContextAware
|
||||
{
|
||||
/*
|
||||
* The name of the action
|
||||
*/
|
||||
private String actionName;
|
||||
|
||||
/*
|
||||
* The parameters used by the action
|
||||
*/
|
||||
private Map<String, String> parameterTemplates;
|
||||
|
||||
/*
|
||||
* The model factory to build models appropriate to the template language used to define
|
||||
* templated parameters.
|
||||
*/
|
||||
private TemplateActionModelFactory templateActionModelFactory;
|
||||
|
||||
/*
|
||||
* The dictionary service.
|
||||
*/
|
||||
private DictionaryService dictionaryService;
|
||||
|
||||
/*
|
||||
* The application context
|
||||
* (Some actions are not publicly exposed via the action service.
|
||||
* They can always be obtained via the appropriate action excecutor.)
|
||||
*/
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
/**
|
||||
* Simple constructor.
|
||||
*
|
||||
*/
|
||||
public SimpleTemplateActionDefinition()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the template model factory.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public TemplateActionModelFactory getTemplateActionModelFactory()
|
||||
{
|
||||
return templateActionModelFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the template model factory IOC.
|
||||
*
|
||||
* @param templateActionModelFactory
|
||||
*/
|
||||
public void setTemplateActionModelFactory(TemplateActionModelFactory templateActionModelFactory)
|
||||
{
|
||||
this.templateActionModelFactory = templateActionModelFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the dictionary service.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public DictionaryService getDictionaryService()
|
||||
{
|
||||
return dictionaryService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the dictionary service - IOC.
|
||||
*
|
||||
* @param dictionaryService
|
||||
*/
|
||||
public void setDictionaryService(DictionaryService dictionaryService)
|
||||
{
|
||||
this.dictionaryService = dictionaryService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the action.
|
||||
*
|
||||
* @param actionName
|
||||
*/
|
||||
public void setActionName(String actionName)
|
||||
{
|
||||
this.actionName = actionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the action.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getActionName()
|
||||
{
|
||||
return actionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the map of parameters used by the template.
|
||||
* These are processed via the template service to produce the actual poarameters.
|
||||
*
|
||||
* @param parameterTemplates
|
||||
*/
|
||||
public void setParameterTemplates(Map<String, String> parameterTemplates)
|
||||
{
|
||||
this.parameterTemplates = parameterTemplates;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the templates that define the parameters for the action.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Map<String, String> getParameterTemplates()
|
||||
{
|
||||
return parameterTemplates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the action from the template using the context node.
|
||||
*/
|
||||
public Action getAction(NodeRef nodeRef)
|
||||
{
|
||||
// Get the action definition. We can not go to the service are some are not exposed.
|
||||
// So we find them from the application context.
|
||||
ActionExecuter actionExecutor = (ActionExecuter)applicationContext.getBean(getActionName());
|
||||
ActionDefinition actionDefinition = actionExecutor.getActionDefinition();
|
||||
|
||||
|
||||
// Build the base action
|
||||
Action action = actionService.createAction(getActionName());
|
||||
|
||||
// Go through the template definitions and set the values.
|
||||
for (String paramName : parameterTemplates.keySet())
|
||||
{
|
||||
// Transform the template
|
||||
String template = parameterTemplates.get(paramName);
|
||||
String stringValue = templateService.processTemplateString(getTemplateActionModelFactory()
|
||||
.getTemplateEngine(), template, getTemplateActionModelFactory().getModel(nodeRef));
|
||||
|
||||
// Find the data type from the action defintion
|
||||
DataTypeDefinition dataTypeDef;
|
||||
if (actionDefinition.getParameterDefintion(paramName) != null)
|
||||
{
|
||||
dataTypeDef = dictionaryService
|
||||
.getDataType(actionDefinition.getParameterDefintion(paramName).getType());
|
||||
}
|
||||
// Fall back to the DD using the property name of it is not defined
|
||||
// This is sometimes used for setting a property to a value.
|
||||
// There can be no definition for such an ad hoc property.
|
||||
else
|
||||
{
|
||||
dataTypeDef = dictionaryService.getProperty(QName.createQName(paramName)).getDataType();
|
||||
}
|
||||
|
||||
// Convert the template result into the correct type and set the parameter
|
||||
Object value = DefaultTypeConverter.INSTANCE.convert(dataTypeDef, stringValue);
|
||||
if (value instanceof Serializable)
|
||||
{
|
||||
action.setParameterValue(paramName, (Serializable) value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If there is a compensating action then set it.
|
||||
if (getCompensatingTemplateCompositeActionDefinition() != null)
|
||||
{
|
||||
action.setCompensatingAction(getCompensatingTemplateCompositeActionDefinition().getAction(nodeRef));
|
||||
}
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* ApplciationContextAware - get the application context.
|
||||
*/
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
|
||||
{
|
||||
this.applicationContext = applicationContext;
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 org.alfresco.service.cmr.action.Action;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
|
||||
/**
|
||||
* A template action definition is used to generate an action from a template style
|
||||
* definition.
|
||||
*
|
||||
* @author Andy Hind
|
||||
*/
|
||||
public interface TemplateActionDefinition
|
||||
{
|
||||
/**
|
||||
* Generate an action definition for the action defined by this template.
|
||||
*
|
||||
* @param nodeRef
|
||||
* @return
|
||||
*/
|
||||
public Action getAction(NodeRef nodeRef);
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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 org.alfresco.service.cmr.repository.NodeRef;
|
||||
|
||||
/**
|
||||
* A factory that builds models to use with a particular template engine for use with scheduled actions built
|
||||
* from action templates.
|
||||
*
|
||||
* @author Andy Hind
|
||||
*/
|
||||
public interface TemplateActionModelFactory
|
||||
{
|
||||
/**
|
||||
* Get the name of the template engine for which this factory applies
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getTemplateEngine();
|
||||
|
||||
/**
|
||||
* Build a model with no default node context.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Object getModel();
|
||||
|
||||
/**
|
||||
* Build a model with a default node context.
|
||||
*
|
||||
* @param nodeRef
|
||||
* @return
|
||||
*/
|
||||
public Object getModel(NodeRef nodeRef);
|
||||
}
|
@@ -23,9 +23,11 @@ import org.alfresco.service.cmr.repository.ContentService;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.TemplateException;
|
||||
import org.alfresco.service.cmr.repository.TemplateProcessor;
|
||||
import org.alfresco.util.ISO9075;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import freemarker.cache.MruCacheStorage;
|
||||
import freemarker.cache.StringTemplateLoader;
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.template.Template;
|
||||
import freemarker.template.TemplateExceptionHandler;
|
||||
@@ -98,6 +100,28 @@ public class FreeMarkerProcessor implements TemplateProcessor
|
||||
return this.config;
|
||||
}
|
||||
|
||||
private Configuration getStringConfig(String path, String template)
|
||||
{
|
||||
|
||||
Configuration config = new Configuration();
|
||||
|
||||
// setup template cache
|
||||
config.setCacheStorage(new MruCacheStorage(20, 0));
|
||||
|
||||
// use our custom loader to find templates on the ClassPath
|
||||
StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
|
||||
stringTemplateLoader.putTemplate(path, template);
|
||||
config.setTemplateLoader(stringTemplateLoader);
|
||||
|
||||
// use our custom object wrapper that can deal with QNameMap objects directly
|
||||
config.setObjectWrapper(new QNameAwareObjectWrapper());
|
||||
|
||||
// rethrow any exception so we can deal with them
|
||||
config.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.service.cmr.repository.TemplateProcessor#process(java.lang.String, java.lang.Object, java.io.Writer)
|
||||
*/
|
||||
@@ -144,4 +168,50 @@ public class FreeMarkerProcessor implements TemplateProcessor
|
||||
throw new TemplateException(MSG_ERROR_TEMPLATE_IO, new Object[] {template}, ioerr);
|
||||
}
|
||||
}
|
||||
|
||||
private static final String PATH = "string://fixed";
|
||||
|
||||
public void processString(String template, Object model, Writer out)
|
||||
{
|
||||
if (template == null || template.length() == 0)
|
||||
{
|
||||
throw new IllegalArgumentException("Template is mandatory.");
|
||||
}
|
||||
if (model == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Model is mandatory.");
|
||||
}
|
||||
if (out == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Output Writer is mandatory.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Executing template: " + template + " on model: " + model);
|
||||
|
||||
Template t = getStringConfig(PATH, template).getTemplate(PATH);
|
||||
if (t != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// perform the template processing against supplied data model
|
||||
t.process(model, out);
|
||||
}
|
||||
catch (Throwable err)
|
||||
{
|
||||
throw new TemplateException(MSG_ERROR_TEMPLATE_FAIL, new Object[] {err.getMessage()}, err);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new TemplateException(MSG_ERROR_NO_TEMPLATE, new Object[] {template});
|
||||
}
|
||||
}
|
||||
catch (IOException ioerr)
|
||||
{
|
||||
throw new TemplateException(MSG_ERROR_TEMPLATE_IO, new Object[] {template}, ioerr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -48,7 +48,7 @@ public class TemplateServiceImpl implements TemplateService, ApplicationContextA
|
||||
private Map<String, String> templateEngines;
|
||||
|
||||
/** Threadlocal instance for template processor cache */
|
||||
private static ThreadLocal<Map<String, TemplateProcessor>> processors = new ThreadLocal();
|
||||
private static ThreadLocal<Map<String, TemplateProcessor>> processors = new ThreadLocal<Map<String, TemplateProcessor>>();
|
||||
|
||||
/**
|
||||
* Set the application context
|
||||
@@ -127,6 +127,36 @@ public class TemplateServiceImpl implements TemplateService, ApplicationContextA
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
public void processTemplateString(String engine, String template, Object model, Writer out)
|
||||
throws TemplateException
|
||||
{
|
||||
try
|
||||
{
|
||||
// execute template processor
|
||||
TemplateProcessor processor = getTemplateProcessorImpl(engine);
|
||||
processor.processString(template, model, out);
|
||||
}
|
||||
catch (TemplateException terr)
|
||||
{
|
||||
throw terr;
|
||||
}
|
||||
catch (Throwable err)
|
||||
{
|
||||
throw new TemplateException(err.getMessage(), err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String processTemplateString(String engine, String template, Object model)
|
||||
throws TemplateException
|
||||
{
|
||||
Writer out = new StringWriter(1024);
|
||||
processTemplateString(engine, template, model, out);
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Return the TemplateProcessor implementation for the named template engine
|
||||
*
|
||||
|
@@ -91,6 +91,8 @@ public final class TemplateNode implements Serializable
|
||||
private Long size = null;
|
||||
private TemplateImageResolver imageResolver = null;
|
||||
private TemplateNode parent = null;
|
||||
|
||||
private ChildAssociationRef primaryParentAssoc;
|
||||
|
||||
|
||||
/**
|
||||
@@ -491,6 +493,19 @@ public final class TemplateNode implements Serializable
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the primary parent association so we can get at the association QName and the association type QName.
|
||||
*/
|
||||
public ChildAssociationRef getPrimaryParentAssoc()
|
||||
{
|
||||
if (primaryParentAssoc == null)
|
||||
{
|
||||
primaryParentAssoc = this.services.getNodeService().getPrimaryParent(nodeRef);
|
||||
}
|
||||
return primaryParentAssoc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the content String for this node from the default content property
|
||||
* (@see ContentModel.PROP_CONTENT)
|
||||
|
@@ -35,4 +35,13 @@ public interface TemplateProcessor
|
||||
* @param out Writer object to send output too
|
||||
*/
|
||||
public void process(String template, Object model, Writer out);
|
||||
|
||||
/**
|
||||
* Process a string template against the supplied data model and write to the out.
|
||||
*
|
||||
* @param template Template string
|
||||
* @param model Object model to process template against
|
||||
* @param out Writer object to send output too
|
||||
*/
|
||||
public void processString(String template, Object model, Writer out);
|
||||
}
|
||||
|
@@ -55,6 +55,34 @@ public interface TemplateService
|
||||
public void processTemplate(String engine, String template, Object model, Writer out)
|
||||
throws TemplateException;
|
||||
|
||||
/**
|
||||
* Process a given template, provided as a string, against the supplied data model and return the result as a String
|
||||
*
|
||||
* @param engine Name of the template engine to use
|
||||
* @param template Template string
|
||||
* @param model Object model to process template against
|
||||
*
|
||||
* @return output of the template process as a String
|
||||
*
|
||||
* @throws TemplateException
|
||||
*/
|
||||
public String processTemplateString(String engine, String template, Object model)
|
||||
throws TemplateException;
|
||||
|
||||
/**
|
||||
* Process a given template, provided as a string, against the supplied data model and report the
|
||||
* result back in the provided writer.
|
||||
*
|
||||
* @param engine Name of the template engine to use
|
||||
* @param template Template string
|
||||
* @param model Object model to process template against
|
||||
* @param out Writer object to send output too
|
||||
*
|
||||
* @throws TemplateException
|
||||
*/
|
||||
public void processTemplateString(String engine, String template, Object model, Writer out)
|
||||
throws TemplateException;
|
||||
|
||||
/**
|
||||
* Return a TemplateProcessor instance for the specified engine name.
|
||||
* Note that the processor instance is NOT thread safe!
|
||||
|
Reference in New Issue
Block a user