mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-10-08 14:51:49 +00:00
Merged 5.1.N (5.1.2) to 5.2.N (5.2.1)
125605 rmunteanu: Merged 5.1.1 (5.1.1) to 5.1.N (5.1.2) 125498 slanglois: MNT-16155 Update source headers - remove svn:eol-style property on Java and JSP source files git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@125783 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -1,245 +1,245 @@
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.jscript.ScriptNode;
|
||||
import org.alfresco.repo.security.authority.AuthorityDAO;
|
||||
import org.alfresco.repo.workflow.WorkflowNotificationUtils;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.workflow.WorkflowException;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.dom4j.Element;
|
||||
import org.jbpm.graph.exe.ExecutionContext;
|
||||
import org.jbpm.taskmgmt.exe.Assignable;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Assignment Handler for assigning Alfresco People and Groups to Tasks
|
||||
* and Swimlanes
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class AlfrescoAssignment extends JBPMSpringAssignmentHandler
|
||||
{
|
||||
private static final long serialVersionUID = 1025667849552265719L;
|
||||
private ServiceRegistry services;
|
||||
private DictionaryService dictionaryService;
|
||||
private AuthorityDAO authorityDAO;
|
||||
private WorkflowNotificationUtils workflowNotificationUtils;
|
||||
|
||||
private Element actor;
|
||||
private Element pooledactors;
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.workflow.jbpm.JBPMSpringActionHandler#initialiseHandler(org.springframework.beans.factory.BeanFactory)
|
||||
*/
|
||||
@Override
|
||||
protected void initialiseHandler(BeanFactory factory)
|
||||
{
|
||||
services = (ServiceRegistry)factory.getBean(ServiceRegistry.SERVICE_REGISTRY);
|
||||
dictionaryService = services.getDictionaryService();
|
||||
authorityDAO = (AuthorityDAO)factory.getBean("authorityDAO");
|
||||
workflowNotificationUtils = (WorkflowNotificationUtils)factory.getBean("workflowNotification");
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.jbpm.taskmgmt.def.AssignmentHandler#assign(org.jbpm.taskmgmt.exe.Assignable, org.jbpm.graph.exe.ExecutionContext)
|
||||
*/
|
||||
public void assign(Assignable assignable, ExecutionContext executionContext) throws Exception
|
||||
{
|
||||
if (actor == null && pooledactors == null)
|
||||
{
|
||||
throw new WorkflowException("no actor or pooled actors has been specified");
|
||||
}
|
||||
|
||||
//
|
||||
// extract actor
|
||||
//
|
||||
|
||||
String assignedActor = null;
|
||||
if (actor != null)
|
||||
{
|
||||
String actorValStr = actor.getTextTrim();
|
||||
if (actorValStr != null && actorValStr.length() > 0)
|
||||
{
|
||||
if (actorValStr.startsWith("#{"))
|
||||
{
|
||||
String expression = actorValStr.substring(2, actorValStr.length() -1);
|
||||
Object eval = AlfrescoJavaScript.executeScript(executionContext, services, expression, null, null);
|
||||
if (eval == null)
|
||||
{
|
||||
throw new WorkflowException("actor expression '" + actorValStr + "' evaluates to null");
|
||||
}
|
||||
|
||||
String theActor = null;
|
||||
if (eval instanceof String)
|
||||
{
|
||||
theActor = (String)eval;
|
||||
}
|
||||
else if (eval instanceof JBPMNode)
|
||||
{
|
||||
theActor = mapAuthorityToName((JBPMNode)eval, false);
|
||||
}
|
||||
if (theActor == null)
|
||||
{
|
||||
throw new WorkflowException("actor expression must evaluate to a person");
|
||||
}
|
||||
assignedActor = theActor;
|
||||
}
|
||||
else
|
||||
{
|
||||
assignedActor = actorValStr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// extract pooled actors
|
||||
//
|
||||
|
||||
String[] assignedPooledActors = null;
|
||||
if (pooledactors != null)
|
||||
{
|
||||
String pooledactorValStr = pooledactors.getTextTrim();
|
||||
if (pooledactorValStr != null && pooledactorValStr.length() > 0)
|
||||
{
|
||||
if (pooledactorValStr.startsWith("#{"))
|
||||
{
|
||||
String expression = pooledactorValStr.substring(2, pooledactorValStr.length() -1);
|
||||
Object eval = AlfrescoJavaScript.executeScript(executionContext, services, expression, null, null);
|
||||
if (eval == null)
|
||||
{
|
||||
throw new WorkflowException("pooledactors expression '" + pooledactorValStr + "' evaluates to null");
|
||||
}
|
||||
|
||||
if (eval instanceof ScriptNode[])
|
||||
{
|
||||
ScriptNode[] nodes = (ScriptNode[])eval;
|
||||
assignedPooledActors = new String[nodes.length];
|
||||
|
||||
int i = 0;
|
||||
for (ScriptNode node : nodes)
|
||||
{
|
||||
String theActor = mapAuthorityToName(node, true);
|
||||
if (theActor == null)
|
||||
{
|
||||
throw new WorkflowException("pooledactors expression does not evaluate to a collection of authorities");
|
||||
}
|
||||
assignedPooledActors[i++] = theActor;
|
||||
}
|
||||
}
|
||||
if (eval instanceof Collection)
|
||||
{
|
||||
List<String> actors = new ArrayList<String>();
|
||||
Collection<?> nodes = (Collection<?>)eval;
|
||||
for (Object node : nodes)
|
||||
{
|
||||
if (node instanceof ScriptNode)
|
||||
{
|
||||
String theActor = mapAuthorityToName((ScriptNode)node, true);
|
||||
if (theActor == null)
|
||||
{
|
||||
throw new WorkflowException("pooledactors expression does not evaluate to a collection of authorities");
|
||||
}
|
||||
actors.add(theActor);
|
||||
}
|
||||
}
|
||||
assignedPooledActors = new String[actors.size()];
|
||||
actors.toArray(assignedPooledActors);
|
||||
}
|
||||
else if (eval instanceof ScriptNode)
|
||||
{
|
||||
ScriptNode node = (ScriptNode)eval;
|
||||
String theActor = mapAuthorityToName(node, true);
|
||||
if (theActor == null)
|
||||
{
|
||||
throw new WorkflowException("pooledactors expression does not evaluate to a collection of authorities");
|
||||
}
|
||||
assignedPooledActors = new String[] {theActor};
|
||||
}
|
||||
else if (eval instanceof String)
|
||||
{
|
||||
assignedPooledActors = new String[] {(String)eval};
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new WorkflowException("pooledactors expression does not evaluate to a collection of authorities");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assignedPooledActors = new String[] {pooledactorValStr};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Determine whether we need to send email notifications of not
|
||||
Boolean sendEMailNotification = (Boolean)executionContext.getVariable(WorkflowNotificationUtils.PROP_SEND_EMAIL_NOTIFICATIONS);
|
||||
|
||||
//
|
||||
// make the assignment
|
||||
//
|
||||
if (assignedActor != null)
|
||||
{
|
||||
assignable.setActorId(assignedActor);
|
||||
|
||||
if (Boolean.TRUE.equals(sendEMailNotification) == true)
|
||||
{
|
||||
// Send the notification
|
||||
workflowNotificationUtils.sendWorkflowAssignedNotificationEMail(
|
||||
JBPMEngine.ENGINE_ID + "$" + executionContext.getTaskInstance().getId(), null,
|
||||
assignedActor,
|
||||
false);
|
||||
}
|
||||
|
||||
}
|
||||
if (assignedPooledActors != null)
|
||||
{
|
||||
assignable.setPooledActors(assignedPooledActors);
|
||||
|
||||
if (Boolean.TRUE.equals(sendEMailNotification) == true)
|
||||
{
|
||||
// Send the notification
|
||||
workflowNotificationUtils.sendWorkflowAssignedNotificationEMail(
|
||||
JBPMEngine.ENGINE_ID + "$" + executionContext.getTaskInstance().getId(), null,
|
||||
assignedPooledActors,
|
||||
true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert Alfresco authority to actor id
|
||||
*
|
||||
* @param authority ScriptNode
|
||||
* @param allowGroup boolean
|
||||
* @return actor id
|
||||
*/
|
||||
private String mapAuthorityToName(ScriptNode authority, boolean allowGroup)
|
||||
{
|
||||
String name = null;
|
||||
QName type = authority.getQNameType();
|
||||
|
||||
if (dictionaryService.isSubClass(type, ContentModel.TYPE_PERSON))
|
||||
{
|
||||
name = (String)authority.getProperties().get(ContentModel.PROP_USERNAME);
|
||||
}
|
||||
else if (allowGroup && dictionaryService.isSubClass(type, ContentModel.TYPE_AUTHORITY_CONTAINER))
|
||||
{
|
||||
name = authorityDAO.getAuthorityName(authority.getNodeRef());
|
||||
}
|
||||
else if (type.equals(ContentModel.TYPE_AUTHORITY))
|
||||
{
|
||||
name = authorityDAO.getAuthorityName(authority.getNodeRef());
|
||||
}
|
||||
return name;
|
||||
}
|
||||
}
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.jscript.ScriptNode;
|
||||
import org.alfresco.repo.security.authority.AuthorityDAO;
|
||||
import org.alfresco.repo.workflow.WorkflowNotificationUtils;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.workflow.WorkflowException;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.dom4j.Element;
|
||||
import org.jbpm.graph.exe.ExecutionContext;
|
||||
import org.jbpm.taskmgmt.exe.Assignable;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Assignment Handler for assigning Alfresco People and Groups to Tasks
|
||||
* and Swimlanes
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class AlfrescoAssignment extends JBPMSpringAssignmentHandler
|
||||
{
|
||||
private static final long serialVersionUID = 1025667849552265719L;
|
||||
private ServiceRegistry services;
|
||||
private DictionaryService dictionaryService;
|
||||
private AuthorityDAO authorityDAO;
|
||||
private WorkflowNotificationUtils workflowNotificationUtils;
|
||||
|
||||
private Element actor;
|
||||
private Element pooledactors;
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.workflow.jbpm.JBPMSpringActionHandler#initialiseHandler(org.springframework.beans.factory.BeanFactory)
|
||||
*/
|
||||
@Override
|
||||
protected void initialiseHandler(BeanFactory factory)
|
||||
{
|
||||
services = (ServiceRegistry)factory.getBean(ServiceRegistry.SERVICE_REGISTRY);
|
||||
dictionaryService = services.getDictionaryService();
|
||||
authorityDAO = (AuthorityDAO)factory.getBean("authorityDAO");
|
||||
workflowNotificationUtils = (WorkflowNotificationUtils)factory.getBean("workflowNotification");
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.jbpm.taskmgmt.def.AssignmentHandler#assign(org.jbpm.taskmgmt.exe.Assignable, org.jbpm.graph.exe.ExecutionContext)
|
||||
*/
|
||||
public void assign(Assignable assignable, ExecutionContext executionContext) throws Exception
|
||||
{
|
||||
if (actor == null && pooledactors == null)
|
||||
{
|
||||
throw new WorkflowException("no actor or pooled actors has been specified");
|
||||
}
|
||||
|
||||
//
|
||||
// extract actor
|
||||
//
|
||||
|
||||
String assignedActor = null;
|
||||
if (actor != null)
|
||||
{
|
||||
String actorValStr = actor.getTextTrim();
|
||||
if (actorValStr != null && actorValStr.length() > 0)
|
||||
{
|
||||
if (actorValStr.startsWith("#{"))
|
||||
{
|
||||
String expression = actorValStr.substring(2, actorValStr.length() -1);
|
||||
Object eval = AlfrescoJavaScript.executeScript(executionContext, services, expression, null, null);
|
||||
if (eval == null)
|
||||
{
|
||||
throw new WorkflowException("actor expression '" + actorValStr + "' evaluates to null");
|
||||
}
|
||||
|
||||
String theActor = null;
|
||||
if (eval instanceof String)
|
||||
{
|
||||
theActor = (String)eval;
|
||||
}
|
||||
else if (eval instanceof JBPMNode)
|
||||
{
|
||||
theActor = mapAuthorityToName((JBPMNode)eval, false);
|
||||
}
|
||||
if (theActor == null)
|
||||
{
|
||||
throw new WorkflowException("actor expression must evaluate to a person");
|
||||
}
|
||||
assignedActor = theActor;
|
||||
}
|
||||
else
|
||||
{
|
||||
assignedActor = actorValStr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// extract pooled actors
|
||||
//
|
||||
|
||||
String[] assignedPooledActors = null;
|
||||
if (pooledactors != null)
|
||||
{
|
||||
String pooledactorValStr = pooledactors.getTextTrim();
|
||||
if (pooledactorValStr != null && pooledactorValStr.length() > 0)
|
||||
{
|
||||
if (pooledactorValStr.startsWith("#{"))
|
||||
{
|
||||
String expression = pooledactorValStr.substring(2, pooledactorValStr.length() -1);
|
||||
Object eval = AlfrescoJavaScript.executeScript(executionContext, services, expression, null, null);
|
||||
if (eval == null)
|
||||
{
|
||||
throw new WorkflowException("pooledactors expression '" + pooledactorValStr + "' evaluates to null");
|
||||
}
|
||||
|
||||
if (eval instanceof ScriptNode[])
|
||||
{
|
||||
ScriptNode[] nodes = (ScriptNode[])eval;
|
||||
assignedPooledActors = new String[nodes.length];
|
||||
|
||||
int i = 0;
|
||||
for (ScriptNode node : nodes)
|
||||
{
|
||||
String theActor = mapAuthorityToName(node, true);
|
||||
if (theActor == null)
|
||||
{
|
||||
throw new WorkflowException("pooledactors expression does not evaluate to a collection of authorities");
|
||||
}
|
||||
assignedPooledActors[i++] = theActor;
|
||||
}
|
||||
}
|
||||
if (eval instanceof Collection)
|
||||
{
|
||||
List<String> actors = new ArrayList<String>();
|
||||
Collection<?> nodes = (Collection<?>)eval;
|
||||
for (Object node : nodes)
|
||||
{
|
||||
if (node instanceof ScriptNode)
|
||||
{
|
||||
String theActor = mapAuthorityToName((ScriptNode)node, true);
|
||||
if (theActor == null)
|
||||
{
|
||||
throw new WorkflowException("pooledactors expression does not evaluate to a collection of authorities");
|
||||
}
|
||||
actors.add(theActor);
|
||||
}
|
||||
}
|
||||
assignedPooledActors = new String[actors.size()];
|
||||
actors.toArray(assignedPooledActors);
|
||||
}
|
||||
else if (eval instanceof ScriptNode)
|
||||
{
|
||||
ScriptNode node = (ScriptNode)eval;
|
||||
String theActor = mapAuthorityToName(node, true);
|
||||
if (theActor == null)
|
||||
{
|
||||
throw new WorkflowException("pooledactors expression does not evaluate to a collection of authorities");
|
||||
}
|
||||
assignedPooledActors = new String[] {theActor};
|
||||
}
|
||||
else if (eval instanceof String)
|
||||
{
|
||||
assignedPooledActors = new String[] {(String)eval};
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new WorkflowException("pooledactors expression does not evaluate to a collection of authorities");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assignedPooledActors = new String[] {pooledactorValStr};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Determine whether we need to send email notifications of not
|
||||
Boolean sendEMailNotification = (Boolean)executionContext.getVariable(WorkflowNotificationUtils.PROP_SEND_EMAIL_NOTIFICATIONS);
|
||||
|
||||
//
|
||||
// make the assignment
|
||||
//
|
||||
if (assignedActor != null)
|
||||
{
|
||||
assignable.setActorId(assignedActor);
|
||||
|
||||
if (Boolean.TRUE.equals(sendEMailNotification) == true)
|
||||
{
|
||||
// Send the notification
|
||||
workflowNotificationUtils.sendWorkflowAssignedNotificationEMail(
|
||||
JBPMEngine.ENGINE_ID + "$" + executionContext.getTaskInstance().getId(), null,
|
||||
assignedActor,
|
||||
false);
|
||||
}
|
||||
|
||||
}
|
||||
if (assignedPooledActors != null)
|
||||
{
|
||||
assignable.setPooledActors(assignedPooledActors);
|
||||
|
||||
if (Boolean.TRUE.equals(sendEMailNotification) == true)
|
||||
{
|
||||
// Send the notification
|
||||
workflowNotificationUtils.sendWorkflowAssignedNotificationEMail(
|
||||
JBPMEngine.ENGINE_ID + "$" + executionContext.getTaskInstance().getId(), null,
|
||||
assignedPooledActors,
|
||||
true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert Alfresco authority to actor id
|
||||
*
|
||||
* @param authority ScriptNode
|
||||
* @param allowGroup boolean
|
||||
* @return actor id
|
||||
*/
|
||||
private String mapAuthorityToName(ScriptNode authority, boolean allowGroup)
|
||||
{
|
||||
String name = null;
|
||||
QName type = authority.getQNameType();
|
||||
|
||||
if (dictionaryService.isSubClass(type, ContentModel.TYPE_PERSON))
|
||||
{
|
||||
name = (String)authority.getProperties().get(ContentModel.PROP_USERNAME);
|
||||
}
|
||||
else if (allowGroup && dictionaryService.isSubClass(type, ContentModel.TYPE_AUTHORITY_CONTAINER))
|
||||
{
|
||||
name = authorityDAO.getAuthorityName(authority.getNodeRef());
|
||||
}
|
||||
else if (type.equals(ContentModel.TYPE_AUTHORITY))
|
||||
{
|
||||
name = authorityDAO.getAuthorityName(authority.getNodeRef());
|
||||
}
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
@@ -1,88 +1,88 @@
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
import org.alfresco.service.cmr.workflow.WorkflowException;
|
||||
import org.jbpm.calendar.BusinessCalendar;
|
||||
import org.jbpm.calendar.Duration;
|
||||
import org.jbpm.graph.def.GraphElement;
|
||||
import org.jbpm.graph.exe.ExecutionContext;
|
||||
import org.jbpm.job.Timer;
|
||||
import org.jbpm.jpdl.el.impl.JbpmExpressionEvaluator;
|
||||
import org.jbpm.scheduler.def.CreateTimerAction;
|
||||
|
||||
|
||||
/**
|
||||
* Extended Create Timer action for supporting Alfresco implemented timers.
|
||||
*
|
||||
* Alfresco timer supports ability to provide due date expression that can
|
||||
* evaluate to a date.
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class AlfrescoCreateTimerAction extends CreateTimerAction
|
||||
{
|
||||
private static final long serialVersionUID = -7427571820130058416L;
|
||||
protected static BusinessCalendar businessCalendar = new BusinessCalendar();
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.jbpm.scheduler.def.CreateTimerAction#createTimer(org.jbpm.graph.exe.ExecutionContext)
|
||||
*/
|
||||
@Override
|
||||
protected Timer createTimer(ExecutionContext executionContext)
|
||||
{
|
||||
Date dueDate = null;
|
||||
String dueDateExpression = getDueDate();
|
||||
if (dueDateExpression.startsWith("#{"))
|
||||
{
|
||||
Object result = JbpmExpressionEvaluator.evaluate(dueDateExpression, executionContext);
|
||||
if (result instanceof Date)
|
||||
{
|
||||
dueDate = (Date)result;
|
||||
}
|
||||
else if(result instanceof Calendar)
|
||||
{
|
||||
dueDate = ((Calendar)result).getTime();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new WorkflowException("duedate expression must evaluate to a date");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Duration duration = new Duration(getDueDate());
|
||||
dueDate = businessCalendar.add(new Date(), duration);
|
||||
}
|
||||
|
||||
Timer timer = new AlfrescoTimer(executionContext.getToken());
|
||||
timer.setName(getTimerName());
|
||||
timer.setRepeat(getRepeat());
|
||||
timer.setDueDate(dueDate);
|
||||
timer.setAction(getTimerAction());
|
||||
timer.setTransitionName(getTransitionName());
|
||||
timer.setGraphElement(executionContext.getEventSource());
|
||||
timer.setTaskInstance(executionContext.getTaskInstance());
|
||||
|
||||
// if this action was executed for a graph element
|
||||
if ((getEvent() != null) && (getEvent().getGraphElement() != null))
|
||||
{
|
||||
GraphElement graphElement = getEvent().getGraphElement();
|
||||
try
|
||||
{
|
||||
executionContext.setTimer(timer);
|
||||
// fire the create timer event on the same graph element
|
||||
graphElement.fireEvent("timer-create", executionContext);
|
||||
}
|
||||
finally
|
||||
{
|
||||
executionContext.setTimer(null);
|
||||
}
|
||||
}
|
||||
|
||||
return timer;
|
||||
}
|
||||
|
||||
}
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
import org.alfresco.service.cmr.workflow.WorkflowException;
|
||||
import org.jbpm.calendar.BusinessCalendar;
|
||||
import org.jbpm.calendar.Duration;
|
||||
import org.jbpm.graph.def.GraphElement;
|
||||
import org.jbpm.graph.exe.ExecutionContext;
|
||||
import org.jbpm.job.Timer;
|
||||
import org.jbpm.jpdl.el.impl.JbpmExpressionEvaluator;
|
||||
import org.jbpm.scheduler.def.CreateTimerAction;
|
||||
|
||||
|
||||
/**
|
||||
* Extended Create Timer action for supporting Alfresco implemented timers.
|
||||
*
|
||||
* Alfresco timer supports ability to provide due date expression that can
|
||||
* evaluate to a date.
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class AlfrescoCreateTimerAction extends CreateTimerAction
|
||||
{
|
||||
private static final long serialVersionUID = -7427571820130058416L;
|
||||
protected static BusinessCalendar businessCalendar = new BusinessCalendar();
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.jbpm.scheduler.def.CreateTimerAction#createTimer(org.jbpm.graph.exe.ExecutionContext)
|
||||
*/
|
||||
@Override
|
||||
protected Timer createTimer(ExecutionContext executionContext)
|
||||
{
|
||||
Date dueDate = null;
|
||||
String dueDateExpression = getDueDate();
|
||||
if (dueDateExpression.startsWith("#{"))
|
||||
{
|
||||
Object result = JbpmExpressionEvaluator.evaluate(dueDateExpression, executionContext);
|
||||
if (result instanceof Date)
|
||||
{
|
||||
dueDate = (Date)result;
|
||||
}
|
||||
else if(result instanceof Calendar)
|
||||
{
|
||||
dueDate = ((Calendar)result).getTime();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new WorkflowException("duedate expression must evaluate to a date");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Duration duration = new Duration(getDueDate());
|
||||
dueDate = businessCalendar.add(new Date(), duration);
|
||||
}
|
||||
|
||||
Timer timer = new AlfrescoTimer(executionContext.getToken());
|
||||
timer.setName(getTimerName());
|
||||
timer.setRepeat(getRepeat());
|
||||
timer.setDueDate(dueDate);
|
||||
timer.setAction(getTimerAction());
|
||||
timer.setTransitionName(getTransitionName());
|
||||
timer.setGraphElement(executionContext.getEventSource());
|
||||
timer.setTaskInstance(executionContext.getTaskInstance());
|
||||
|
||||
// if this action was executed for a graph element
|
||||
if ((getEvent() != null) && (getEvent().getGraphElement() != null))
|
||||
{
|
||||
GraphElement graphElement = getEvent().getGraphElement();
|
||||
try
|
||||
{
|
||||
executionContext.setTimer(timer);
|
||||
// fire the create timer event on the same graph element
|
||||
graphElement.fireEvent("timer-create", executionContext);
|
||||
}
|
||||
finally
|
||||
{
|
||||
executionContext.setTimer(null);
|
||||
}
|
||||
}
|
||||
|
||||
return timer;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,472 +1,472 @@
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.model.Repository;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.ScriptService;
|
||||
import org.alfresco.service.cmr.workflow.WorkflowException;
|
||||
import org.dom4j.Element;
|
||||
import org.jbpm.context.def.VariableAccess;
|
||||
import org.jbpm.context.exe.ContextInstance;
|
||||
import org.jbpm.graph.def.Node;
|
||||
import org.jbpm.graph.exe.ExecutionContext;
|
||||
import org.jbpm.graph.exe.Token;
|
||||
import org.jbpm.jpdl.xml.JpdlXmlReader;
|
||||
import org.jbpm.taskmgmt.def.Task;
|
||||
import org.jbpm.taskmgmt.exe.TaskInstance;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
/**
|
||||
* A jBPM Action Handler for executing Alfresco Script
|
||||
*
|
||||
* The configuration of this action is as follows:
|
||||
* <script>
|
||||
* <expression>
|
||||
* the script to execute
|
||||
* </expression>
|
||||
* <variable name="watcha" access="write"/>
|
||||
* </script>
|
||||
*
|
||||
* It's exactly the same as jBPM's own script configuration.
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class AlfrescoJavaScript extends JBPMSpringActionHandler
|
||||
{
|
||||
private static final long serialVersionUID = -2908748080671212745L;
|
||||
|
||||
private static JpdlXmlReader jpdlReader = new JpdlXmlReader((InputSource)null);
|
||||
private ServiceRegistry services;
|
||||
private NodeRef companyHome;
|
||||
private Element script;
|
||||
private String runas;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void initialiseHandler(BeanFactory factory)
|
||||
{
|
||||
this.services = (ServiceRegistry)factory.getBean(ServiceRegistry.SERVICE_REGISTRY);
|
||||
Repository repositoryHelper = (Repository)factory.getBean("repositoryHelper");
|
||||
this.companyHome = repositoryHelper.getCompanyHome();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.jbpm.graph.def.ActionHandler#execute(org.jbpm.graph.exe.ExecutionContext)
|
||||
*/
|
||||
public void execute(final ExecutionContext executionContext) throws Exception
|
||||
{
|
||||
// validate script
|
||||
if (script == null)
|
||||
{
|
||||
throw new WorkflowException("Script has not been provided");
|
||||
}
|
||||
boolean isTextOnly = isScriptOnlyText();
|
||||
|
||||
List<VariableAccess> variableAccesses = getVariableAccessors(isTextOnly);
|
||||
String expression = getExpression(isTextOnly);
|
||||
|
||||
// execute
|
||||
Object result = executeExpression(expression, executionContext, variableAccesses);
|
||||
|
||||
// map script return variable to process context
|
||||
VariableAccess returnVariable = getWritableVariable(variableAccesses);
|
||||
if (returnVariable != null)
|
||||
{
|
||||
ContextInstance contextInstance = executionContext.getContextInstance();
|
||||
Token token = executionContext.getToken();
|
||||
contextInstance.setVariable(returnVariable.getVariableName(), result, token);
|
||||
}
|
||||
}
|
||||
|
||||
private Object executeExpression(String expression, ExecutionContext executionContext, List<VariableAccess> variableAccesses)
|
||||
{
|
||||
boolean userChanged = checkFullyAuthenticatedUser(executionContext);
|
||||
Object result = executeScript(expression, executionContext, variableAccesses);
|
||||
if(userChanged)
|
||||
{
|
||||
AuthenticationUtil.clearCurrentSecurityContext();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Object executeScript(String expression,
|
||||
ExecutionContext executionContext,
|
||||
List<VariableAccess> variableAccesses)
|
||||
{
|
||||
String user = AuthenticationUtil.getFullyAuthenticatedUser();
|
||||
if (runas == null && user !=null)
|
||||
{
|
||||
return executeScript(executionContext, services, expression, variableAccesses, companyHome);
|
||||
}
|
||||
else
|
||||
{
|
||||
String runAsUser = runas;
|
||||
if(runAsUser == null)
|
||||
{
|
||||
runAsUser = AuthenticationUtil.getSystemUserName();
|
||||
}
|
||||
else
|
||||
{
|
||||
validateRunAsUser();
|
||||
}
|
||||
return executeScriptAs(runAsUser, expression, executionContext, variableAccesses);
|
||||
}
|
||||
}
|
||||
|
||||
private Object executeScriptAs(String runAsUser,
|
||||
final String expression,
|
||||
final ExecutionContext executionContext,
|
||||
final List<VariableAccess> variableAccesses)
|
||||
{
|
||||
// execute as specified runAsUser
|
||||
return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Object>()
|
||||
{
|
||||
public Object doWork() throws Exception
|
||||
{
|
||||
return executeScript(executionContext, services, expression, variableAccesses,companyHome);
|
||||
}
|
||||
}, runAsUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a valid Fully Authenticated User is set.
|
||||
* If none is set then attempts to set the task assignee as the Fully Authenticated User.
|
||||
* @param executionContext
|
||||
* @return <code>true</code> if the Fully Authenticated User was changes, otherwise <code>false</code>.
|
||||
*/
|
||||
private boolean checkFullyAuthenticatedUser(final ExecutionContext executionContext)
|
||||
{
|
||||
if(AuthenticationUtil.getFullyAuthenticatedUser()!= null)
|
||||
return false;
|
||||
|
||||
TaskInstance taskInstance = executionContext.getTaskInstance();
|
||||
if(taskInstance!=null)
|
||||
{
|
||||
String userName = taskInstance.getActorId();
|
||||
if (userName != null)
|
||||
{
|
||||
AuthenticationUtil.setFullyAuthenticatedUser(userName);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the specified 'runas' field
|
||||
* specifies a valid username.
|
||||
*/
|
||||
private void validateRunAsUser()
|
||||
{
|
||||
Boolean runAsExists = AuthenticationUtil.runAs(new RunAsWork<Boolean>()
|
||||
{
|
||||
// Validate using System user to ensure sufficient permissions available to access person node.
|
||||
|
||||
public Boolean doWork() throws Exception
|
||||
{
|
||||
return services.getPersonService().personExists(runas);
|
||||
}
|
||||
}, AuthenticationUtil.getSystemUserName());
|
||||
if (!runAsExists)
|
||||
{
|
||||
throw new WorkflowException("runas user '" + runas + "' does not exist.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expression {@link String} from the script.
|
||||
* @param isTextOnly Is the script text only or is it XML?
|
||||
* @return the expression {@link String}.
|
||||
*/
|
||||
private String getExpression(boolean isTextOnly)
|
||||
{
|
||||
if (isTextOnly)
|
||||
{
|
||||
return script.getText().trim();
|
||||
}
|
||||
else
|
||||
{
|
||||
Element expressionElement = script.element("expression");
|
||||
if (expressionElement == null)
|
||||
{
|
||||
throw new WorkflowException("Script expression has not been provided");
|
||||
}
|
||||
return expressionElement.getText().trim();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<VariableAccess> getVariableAccessors(boolean isTextOnly)
|
||||
{
|
||||
if (isTextOnly)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return jpdlReader.readVariableAccesses(script);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the script specified as text only, or as explicit expression, variable elements
|
||||
* @return boolean
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private boolean isScriptOnlyText()
|
||||
{
|
||||
Iterator<Element> iter = script.elementIterator();
|
||||
while (iter.hasNext())
|
||||
{
|
||||
Element element = iter.next();
|
||||
if (element.getNodeType() == org.dom4j.Node.ELEMENT_NODE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Execute a script
|
||||
*
|
||||
* @param context jBPM execution context
|
||||
* @param services Alfresco service registry
|
||||
* @param expression script to execute
|
||||
* @param variableAccesses (optional) list of jBPM variables to map into script (all, if not supplied)
|
||||
* @param companyHome TODO
|
||||
* @return script result
|
||||
*/
|
||||
public static Object executeScript(ExecutionContext context, ServiceRegistry services, String expression, List<VariableAccess> variableAccesses, NodeRef companyHome)
|
||||
{
|
||||
Map<String, Object> inputMap = createInputMap(services, companyHome, context, variableAccesses);
|
||||
ScriptService scriptService = services.getScriptService();
|
||||
scriptService.buildCoreModel(inputMap);
|
||||
Object result = scriptService.executeScriptString(expression, inputMap);
|
||||
result = convertForJBPM(result, services);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert values for JBPM Context
|
||||
*
|
||||
* @param value Object
|
||||
* @param services ServiceRegistry
|
||||
* @return Object
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Object convertForJBPM(Object value, ServiceRegistry services)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (value instanceof NodeRef)
|
||||
{
|
||||
value = new JBPMNode(((NodeRef)value), services);
|
||||
}
|
||||
else if (value instanceof Collection)
|
||||
{
|
||||
// recursively convert each value in the collection
|
||||
Collection<Object> collection = (Collection<Object>)value;
|
||||
|
||||
// Note: this needs to be cleaned up - we need to create appropriate collection type based
|
||||
// on collection contents
|
||||
boolean isNodeCollection = false;
|
||||
for (Object obj : collection)
|
||||
{
|
||||
if (obj instanceof NodeRef)
|
||||
{
|
||||
isNodeCollection = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isNodeCollection)
|
||||
{
|
||||
JBPMNodeList converted = new JBPMNodeList();
|
||||
for (Object obj : collection)
|
||||
{
|
||||
if (!(obj instanceof NodeRef))
|
||||
{
|
||||
throw new WorkflowException("Unable to convert script collection to JBPM value - mixed node/non-node collection");
|
||||
}
|
||||
converted.add((JBPMNode)convertForJBPM(obj, services));
|
||||
}
|
||||
value = converted;
|
||||
}
|
||||
else
|
||||
{
|
||||
Collection<Object> converted = new ArrayList<Object>();
|
||||
for (Object obj : collection)
|
||||
{
|
||||
converted.add(convertForJBPM(obj, services));
|
||||
}
|
||||
value = converted;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct map of arguments to pass to script
|
||||
*
|
||||
* Based on the <variable> elements of the action configuration.
|
||||
* @param companyHome TODO
|
||||
* @param executionContext the execution context
|
||||
* @param variableAccesses the variable configuration
|
||||
*
|
||||
* @return the map of script arguments
|
||||
*/
|
||||
private static Map<String, Object> createInputMap(ServiceRegistry services, NodeRef companyHome, ExecutionContext executionContext, List<VariableAccess> variableAccesses)
|
||||
{
|
||||
ScriptService scriptService = services.getScriptService();
|
||||
|
||||
// initialise global script variables
|
||||
NodeRef person = getPersonNode(services);
|
||||
NodeRef userHome = null;
|
||||
if (person != null)
|
||||
{
|
||||
NodeService nodeService = services.getNodeService();
|
||||
userHome = (NodeRef)nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER);
|
||||
}
|
||||
Map<String, Object> inputMap = scriptService.buildDefaultModel(person, companyHome, userHome, null, null, null);
|
||||
|
||||
// initialise process variables
|
||||
Token token = executionContext.getToken();
|
||||
inputMap.put("executionContext", executionContext);
|
||||
inputMap.put("token", token);
|
||||
Node node = executionContext.getNode();
|
||||
if (node != null)
|
||||
{
|
||||
inputMap.put("node", node);
|
||||
}
|
||||
Task task = executionContext.getTask();
|
||||
if (task != null)
|
||||
{
|
||||
inputMap.put("task", task);
|
||||
}
|
||||
TaskInstance taskInstance = executionContext.getTaskInstance();
|
||||
if (taskInstance != null)
|
||||
{
|
||||
inputMap.put("taskInstance", taskInstance);
|
||||
}
|
||||
|
||||
// if no readable variableInstances are specified,
|
||||
ContextInstance contextInstance = executionContext.getContextInstance();
|
||||
if (!hasReadableVariable(variableAccesses))
|
||||
{
|
||||
// copy all the variableInstances of the context into the interpreter
|
||||
Map<?, ?> variables = contextInstance.getVariables(token);
|
||||
if (variables != null)
|
||||
{
|
||||
for (Map.Entry<?, ?> entry : variables.entrySet())
|
||||
{
|
||||
String variableName = (String) entry.getKey();
|
||||
Object variableValue = entry.getValue();
|
||||
inputMap.put(variableName, variableValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// copy the specified variableInstances into the interpreterz
|
||||
for (VariableAccess variableAccess : variableAccesses)
|
||||
{
|
||||
if (variableAccess.isReadable())
|
||||
{
|
||||
String variableName = variableAccess.getVariableName();
|
||||
String mappedName = variableAccess.getMappedName();
|
||||
Object variableValue = contextInstance.getVariable(variableName, token);
|
||||
inputMap.put(mappedName, variableValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return inputMap;
|
||||
}
|
||||
|
||||
|
||||
private static NodeRef getPersonNode(ServiceRegistry services)
|
||||
{
|
||||
String userName = AuthenticationUtil.getFullyAuthenticatedUser();
|
||||
if (userName != null)
|
||||
{
|
||||
NodeRef person = services.getPersonService().getPerson(userName);
|
||||
return person;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if there are variables to read from the process context
|
||||
*
|
||||
* @param variableAccesses the variables configuration
|
||||
* @return true => there are variables to read
|
||||
*/
|
||||
private static boolean hasReadableVariable(List<VariableAccess> variableAccesses)
|
||||
{
|
||||
if (variableAccesses != null)
|
||||
{
|
||||
for (VariableAccess variableAccess : variableAccesses)
|
||||
{
|
||||
if (variableAccess.isReadable())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if there is a variable to write back to the process context
|
||||
*
|
||||
* @param variableAccesses the variables configuration
|
||||
* @return true => there is a variable to write
|
||||
*/
|
||||
private static VariableAccess getWritableVariable(List<VariableAccess> variableAccesses)
|
||||
{
|
||||
VariableAccess writable = null;
|
||||
if (variableAccesses != null)
|
||||
{
|
||||
for (VariableAccess variableAccess : variableAccesses)
|
||||
{
|
||||
if (variableAccess.isWritable())
|
||||
{
|
||||
if (writable != null)
|
||||
{
|
||||
throw new WorkflowException("AlfrescoJavaScript supports only one writable variable");
|
||||
}
|
||||
writable = variableAccess;
|
||||
}
|
||||
}
|
||||
}
|
||||
return writable;
|
||||
}
|
||||
|
||||
public void setScript(Element script)
|
||||
{
|
||||
this.script = script;
|
||||
}
|
||||
|
||||
public void setRunas(String runas)
|
||||
{
|
||||
this.runas = runas;
|
||||
}
|
||||
}
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.model.Repository;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.ScriptService;
|
||||
import org.alfresco.service.cmr.workflow.WorkflowException;
|
||||
import org.dom4j.Element;
|
||||
import org.jbpm.context.def.VariableAccess;
|
||||
import org.jbpm.context.exe.ContextInstance;
|
||||
import org.jbpm.graph.def.Node;
|
||||
import org.jbpm.graph.exe.ExecutionContext;
|
||||
import org.jbpm.graph.exe.Token;
|
||||
import org.jbpm.jpdl.xml.JpdlXmlReader;
|
||||
import org.jbpm.taskmgmt.def.Task;
|
||||
import org.jbpm.taskmgmt.exe.TaskInstance;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
/**
|
||||
* A jBPM Action Handler for executing Alfresco Script
|
||||
*
|
||||
* The configuration of this action is as follows:
|
||||
* <script>
|
||||
* <expression>
|
||||
* the script to execute
|
||||
* </expression>
|
||||
* <variable name="watcha" access="write"/>
|
||||
* </script>
|
||||
*
|
||||
* It's exactly the same as jBPM's own script configuration.
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class AlfrescoJavaScript extends JBPMSpringActionHandler
|
||||
{
|
||||
private static final long serialVersionUID = -2908748080671212745L;
|
||||
|
||||
private static JpdlXmlReader jpdlReader = new JpdlXmlReader((InputSource)null);
|
||||
private ServiceRegistry services;
|
||||
private NodeRef companyHome;
|
||||
private Element script;
|
||||
private String runas;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void initialiseHandler(BeanFactory factory)
|
||||
{
|
||||
this.services = (ServiceRegistry)factory.getBean(ServiceRegistry.SERVICE_REGISTRY);
|
||||
Repository repositoryHelper = (Repository)factory.getBean("repositoryHelper");
|
||||
this.companyHome = repositoryHelper.getCompanyHome();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.jbpm.graph.def.ActionHandler#execute(org.jbpm.graph.exe.ExecutionContext)
|
||||
*/
|
||||
public void execute(final ExecutionContext executionContext) throws Exception
|
||||
{
|
||||
// validate script
|
||||
if (script == null)
|
||||
{
|
||||
throw new WorkflowException("Script has not been provided");
|
||||
}
|
||||
boolean isTextOnly = isScriptOnlyText();
|
||||
|
||||
List<VariableAccess> variableAccesses = getVariableAccessors(isTextOnly);
|
||||
String expression = getExpression(isTextOnly);
|
||||
|
||||
// execute
|
||||
Object result = executeExpression(expression, executionContext, variableAccesses);
|
||||
|
||||
// map script return variable to process context
|
||||
VariableAccess returnVariable = getWritableVariable(variableAccesses);
|
||||
if (returnVariable != null)
|
||||
{
|
||||
ContextInstance contextInstance = executionContext.getContextInstance();
|
||||
Token token = executionContext.getToken();
|
||||
contextInstance.setVariable(returnVariable.getVariableName(), result, token);
|
||||
}
|
||||
}
|
||||
|
||||
private Object executeExpression(String expression, ExecutionContext executionContext, List<VariableAccess> variableAccesses)
|
||||
{
|
||||
boolean userChanged = checkFullyAuthenticatedUser(executionContext);
|
||||
Object result = executeScript(expression, executionContext, variableAccesses);
|
||||
if(userChanged)
|
||||
{
|
||||
AuthenticationUtil.clearCurrentSecurityContext();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Object executeScript(String expression,
|
||||
ExecutionContext executionContext,
|
||||
List<VariableAccess> variableAccesses)
|
||||
{
|
||||
String user = AuthenticationUtil.getFullyAuthenticatedUser();
|
||||
if (runas == null && user !=null)
|
||||
{
|
||||
return executeScript(executionContext, services, expression, variableAccesses, companyHome);
|
||||
}
|
||||
else
|
||||
{
|
||||
String runAsUser = runas;
|
||||
if(runAsUser == null)
|
||||
{
|
||||
runAsUser = AuthenticationUtil.getSystemUserName();
|
||||
}
|
||||
else
|
||||
{
|
||||
validateRunAsUser();
|
||||
}
|
||||
return executeScriptAs(runAsUser, expression, executionContext, variableAccesses);
|
||||
}
|
||||
}
|
||||
|
||||
private Object executeScriptAs(String runAsUser,
|
||||
final String expression,
|
||||
final ExecutionContext executionContext,
|
||||
final List<VariableAccess> variableAccesses)
|
||||
{
|
||||
// execute as specified runAsUser
|
||||
return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Object>()
|
||||
{
|
||||
public Object doWork() throws Exception
|
||||
{
|
||||
return executeScript(executionContext, services, expression, variableAccesses,companyHome);
|
||||
}
|
||||
}, runAsUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a valid Fully Authenticated User is set.
|
||||
* If none is set then attempts to set the task assignee as the Fully Authenticated User.
|
||||
* @param executionContext
|
||||
* @return <code>true</code> if the Fully Authenticated User was changes, otherwise <code>false</code>.
|
||||
*/
|
||||
private boolean checkFullyAuthenticatedUser(final ExecutionContext executionContext)
|
||||
{
|
||||
if(AuthenticationUtil.getFullyAuthenticatedUser()!= null)
|
||||
return false;
|
||||
|
||||
TaskInstance taskInstance = executionContext.getTaskInstance();
|
||||
if(taskInstance!=null)
|
||||
{
|
||||
String userName = taskInstance.getActorId();
|
||||
if (userName != null)
|
||||
{
|
||||
AuthenticationUtil.setFullyAuthenticatedUser(userName);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the specified 'runas' field
|
||||
* specifies a valid username.
|
||||
*/
|
||||
private void validateRunAsUser()
|
||||
{
|
||||
Boolean runAsExists = AuthenticationUtil.runAs(new RunAsWork<Boolean>()
|
||||
{
|
||||
// Validate using System user to ensure sufficient permissions available to access person node.
|
||||
|
||||
public Boolean doWork() throws Exception
|
||||
{
|
||||
return services.getPersonService().personExists(runas);
|
||||
}
|
||||
}, AuthenticationUtil.getSystemUserName());
|
||||
if (!runAsExists)
|
||||
{
|
||||
throw new WorkflowException("runas user '" + runas + "' does not exist.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expression {@link String} from the script.
|
||||
* @param isTextOnly Is the script text only or is it XML?
|
||||
* @return the expression {@link String}.
|
||||
*/
|
||||
private String getExpression(boolean isTextOnly)
|
||||
{
|
||||
if (isTextOnly)
|
||||
{
|
||||
return script.getText().trim();
|
||||
}
|
||||
else
|
||||
{
|
||||
Element expressionElement = script.element("expression");
|
||||
if (expressionElement == null)
|
||||
{
|
||||
throw new WorkflowException("Script expression has not been provided");
|
||||
}
|
||||
return expressionElement.getText().trim();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<VariableAccess> getVariableAccessors(boolean isTextOnly)
|
||||
{
|
||||
if (isTextOnly)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return jpdlReader.readVariableAccesses(script);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the script specified as text only, or as explicit expression, variable elements
|
||||
* @return boolean
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private boolean isScriptOnlyText()
|
||||
{
|
||||
Iterator<Element> iter = script.elementIterator();
|
||||
while (iter.hasNext())
|
||||
{
|
||||
Element element = iter.next();
|
||||
if (element.getNodeType() == org.dom4j.Node.ELEMENT_NODE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Execute a script
|
||||
*
|
||||
* @param context jBPM execution context
|
||||
* @param services Alfresco service registry
|
||||
* @param expression script to execute
|
||||
* @param variableAccesses (optional) list of jBPM variables to map into script (all, if not supplied)
|
||||
* @param companyHome TODO
|
||||
* @return script result
|
||||
*/
|
||||
public static Object executeScript(ExecutionContext context, ServiceRegistry services, String expression, List<VariableAccess> variableAccesses, NodeRef companyHome)
|
||||
{
|
||||
Map<String, Object> inputMap = createInputMap(services, companyHome, context, variableAccesses);
|
||||
ScriptService scriptService = services.getScriptService();
|
||||
scriptService.buildCoreModel(inputMap);
|
||||
Object result = scriptService.executeScriptString(expression, inputMap);
|
||||
result = convertForJBPM(result, services);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert values for JBPM Context
|
||||
*
|
||||
* @param value Object
|
||||
* @param services ServiceRegistry
|
||||
* @return Object
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Object convertForJBPM(Object value, ServiceRegistry services)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (value instanceof NodeRef)
|
||||
{
|
||||
value = new JBPMNode(((NodeRef)value), services);
|
||||
}
|
||||
else if (value instanceof Collection)
|
||||
{
|
||||
// recursively convert each value in the collection
|
||||
Collection<Object> collection = (Collection<Object>)value;
|
||||
|
||||
// Note: this needs to be cleaned up - we need to create appropriate collection type based
|
||||
// on collection contents
|
||||
boolean isNodeCollection = false;
|
||||
for (Object obj : collection)
|
||||
{
|
||||
if (obj instanceof NodeRef)
|
||||
{
|
||||
isNodeCollection = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isNodeCollection)
|
||||
{
|
||||
JBPMNodeList converted = new JBPMNodeList();
|
||||
for (Object obj : collection)
|
||||
{
|
||||
if (!(obj instanceof NodeRef))
|
||||
{
|
||||
throw new WorkflowException("Unable to convert script collection to JBPM value - mixed node/non-node collection");
|
||||
}
|
||||
converted.add((JBPMNode)convertForJBPM(obj, services));
|
||||
}
|
||||
value = converted;
|
||||
}
|
||||
else
|
||||
{
|
||||
Collection<Object> converted = new ArrayList<Object>();
|
||||
for (Object obj : collection)
|
||||
{
|
||||
converted.add(convertForJBPM(obj, services));
|
||||
}
|
||||
value = converted;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct map of arguments to pass to script
|
||||
*
|
||||
* Based on the <variable> elements of the action configuration.
|
||||
* @param companyHome TODO
|
||||
* @param executionContext the execution context
|
||||
* @param variableAccesses the variable configuration
|
||||
*
|
||||
* @return the map of script arguments
|
||||
*/
|
||||
private static Map<String, Object> createInputMap(ServiceRegistry services, NodeRef companyHome, ExecutionContext executionContext, List<VariableAccess> variableAccesses)
|
||||
{
|
||||
ScriptService scriptService = services.getScriptService();
|
||||
|
||||
// initialise global script variables
|
||||
NodeRef person = getPersonNode(services);
|
||||
NodeRef userHome = null;
|
||||
if (person != null)
|
||||
{
|
||||
NodeService nodeService = services.getNodeService();
|
||||
userHome = (NodeRef)nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER);
|
||||
}
|
||||
Map<String, Object> inputMap = scriptService.buildDefaultModel(person, companyHome, userHome, null, null, null);
|
||||
|
||||
// initialise process variables
|
||||
Token token = executionContext.getToken();
|
||||
inputMap.put("executionContext", executionContext);
|
||||
inputMap.put("token", token);
|
||||
Node node = executionContext.getNode();
|
||||
if (node != null)
|
||||
{
|
||||
inputMap.put("node", node);
|
||||
}
|
||||
Task task = executionContext.getTask();
|
||||
if (task != null)
|
||||
{
|
||||
inputMap.put("task", task);
|
||||
}
|
||||
TaskInstance taskInstance = executionContext.getTaskInstance();
|
||||
if (taskInstance != null)
|
||||
{
|
||||
inputMap.put("taskInstance", taskInstance);
|
||||
}
|
||||
|
||||
// if no readable variableInstances are specified,
|
||||
ContextInstance contextInstance = executionContext.getContextInstance();
|
||||
if (!hasReadableVariable(variableAccesses))
|
||||
{
|
||||
// copy all the variableInstances of the context into the interpreter
|
||||
Map<?, ?> variables = contextInstance.getVariables(token);
|
||||
if (variables != null)
|
||||
{
|
||||
for (Map.Entry<?, ?> entry : variables.entrySet())
|
||||
{
|
||||
String variableName = (String) entry.getKey();
|
||||
Object variableValue = entry.getValue();
|
||||
inputMap.put(variableName, variableValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// copy the specified variableInstances into the interpreterz
|
||||
for (VariableAccess variableAccess : variableAccesses)
|
||||
{
|
||||
if (variableAccess.isReadable())
|
||||
{
|
||||
String variableName = variableAccess.getVariableName();
|
||||
String mappedName = variableAccess.getMappedName();
|
||||
Object variableValue = contextInstance.getVariable(variableName, token);
|
||||
inputMap.put(mappedName, variableValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return inputMap;
|
||||
}
|
||||
|
||||
|
||||
private static NodeRef getPersonNode(ServiceRegistry services)
|
||||
{
|
||||
String userName = AuthenticationUtil.getFullyAuthenticatedUser();
|
||||
if (userName != null)
|
||||
{
|
||||
NodeRef person = services.getPersonService().getPerson(userName);
|
||||
return person;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if there are variables to read from the process context
|
||||
*
|
||||
* @param variableAccesses the variables configuration
|
||||
* @return true => there are variables to read
|
||||
*/
|
||||
private static boolean hasReadableVariable(List<VariableAccess> variableAccesses)
|
||||
{
|
||||
if (variableAccesses != null)
|
||||
{
|
||||
for (VariableAccess variableAccess : variableAccesses)
|
||||
{
|
||||
if (variableAccess.isReadable())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if there is a variable to write back to the process context
|
||||
*
|
||||
* @param variableAccesses the variables configuration
|
||||
* @return true => there is a variable to write
|
||||
*/
|
||||
private static VariableAccess getWritableVariable(List<VariableAccess> variableAccesses)
|
||||
{
|
||||
VariableAccess writable = null;
|
||||
if (variableAccesses != null)
|
||||
{
|
||||
for (VariableAccess variableAccess : variableAccesses)
|
||||
{
|
||||
if (variableAccess.isWritable())
|
||||
{
|
||||
if (writable != null)
|
||||
{
|
||||
throw new WorkflowException("AlfrescoJavaScript supports only one writable variable");
|
||||
}
|
||||
writable = variableAccess;
|
||||
}
|
||||
}
|
||||
}
|
||||
return writable;
|
||||
}
|
||||
|
||||
public void setScript(Element script)
|
||||
{
|
||||
this.script = script;
|
||||
}
|
||||
|
||||
public void setRunas(String runas)
|
||||
{
|
||||
this.runas = runas;
|
||||
}
|
||||
}
|
||||
|
@@ -1,203 +1,203 @@
|
||||
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.jbpm.JbpmConfiguration;
|
||||
import org.jbpm.JbpmContext;
|
||||
import org.jbpm.configuration.ObjectFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springmodules.workflow.jbpm31.JbpmFactoryLocator;
|
||||
|
||||
/**
|
||||
* Implementation of Spring Module's JbpmConfigurationFactoryBean for Jbpm 3.2.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author davidc
|
||||
*/
|
||||
public class AlfrescoJbpmConfigurationFactoryBean implements InitializingBean, FactoryBean<JbpmConfiguration>,
|
||||
BeanFactoryAware, BeanNameAware, DisposableBean
|
||||
{
|
||||
private JbpmConfiguration jbpmConfiguration;
|
||||
|
||||
private ObjectFactory objectFactory;
|
||||
|
||||
private Resource configuration;
|
||||
|
||||
private SessionFactory sessionFactory;
|
||||
|
||||
private String contextName = JbpmContext.DEFAULT_JBPM_CONTEXT_NAME;
|
||||
|
||||
/**
|
||||
* FactoryLocator
|
||||
*/
|
||||
private final AlfrescoJbpmFactoryLocator factoryLocator = new AlfrescoJbpmFactoryLocator();
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see
|
||||
* org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org
|
||||
* .springframework.beans.factory.BeanFactory)
|
||||
*/
|
||||
public void setBeanFactory(final BeanFactory beanFactory) throws BeansException
|
||||
{
|
||||
// TODO Added to get the build working. A better solution is needed
|
||||
// long-term.
|
||||
this.factoryLocator.destroy();
|
||||
|
||||
this.factoryLocator.setBeanFactory(beanFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void setBeanName(final String name)
|
||||
{
|
||||
this.factoryLocator.setBeanName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void afterPropertiesSet() throws Exception
|
||||
{
|
||||
if (this.configuration == null) { throw new IllegalArgumentException(
|
||||
"configuration or objectFactory property need to be not null"); }
|
||||
|
||||
// 1. Construct Jbpm Configuration
|
||||
// NOTE: Jbpm 3.2 adds a JbpmConfiguration value to its context
|
||||
final InputStream stream = this.configuration.getInputStream();
|
||||
this.jbpmConfiguration = JbpmConfiguration.parseInputStream(stream);
|
||||
|
||||
// 2. inject the HB session factory if it is the case
|
||||
if (this.sessionFactory != null)
|
||||
{
|
||||
final JbpmContext context = this.jbpmConfiguration.createJbpmContext(this.contextName);
|
||||
try
|
||||
{
|
||||
context.setSessionFactory(this.sessionFactory);
|
||||
}
|
||||
finally
|
||||
{
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public JbpmConfiguration getObject() throws Exception
|
||||
{
|
||||
return this.jbpmConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Class<JbpmConfiguration> getObjectType()
|
||||
{
|
||||
return JbpmConfiguration.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isSingleton()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the configuration.
|
||||
*/
|
||||
public Resource getConfiguration()
|
||||
{
|
||||
return this.configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param configuration The configuration to set
|
||||
*/
|
||||
public void setConfiguration(final Resource configuration)
|
||||
{
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the objectFactory.
|
||||
*/
|
||||
public ObjectFactory getObjectFactory()
|
||||
{
|
||||
return this.objectFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param objectFactory The objectFactory to set
|
||||
*/
|
||||
public void setObjectFactory(final ObjectFactory objectFactory)
|
||||
{
|
||||
this.objectFactory = objectFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the contextName.
|
||||
*/
|
||||
public String getContextName()
|
||||
{
|
||||
return this.contextName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param contextName The contextName to set
|
||||
*/
|
||||
public void setContextName(final String contextName)
|
||||
{
|
||||
this.contextName = contextName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the sessionFactory.
|
||||
*/
|
||||
public SessionFactory getSessionFactory()
|
||||
{
|
||||
return this.sessionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sessionFactory The sessionFactory to set
|
||||
*/
|
||||
public void setSessionFactory(final SessionFactory sessionFactory)
|
||||
{
|
||||
this.sessionFactory = sessionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void destroy() throws Exception
|
||||
{
|
||||
this.factoryLocator.destroy();
|
||||
}
|
||||
|
||||
private static class AlfrescoJbpmFactoryLocator extends JbpmFactoryLocator
|
||||
{
|
||||
public void destroy()
|
||||
{
|
||||
JbpmFactoryLocator.beanFactories.clear();
|
||||
JbpmFactoryLocator.beanFactoriesNames.clear();
|
||||
JbpmFactoryLocator.referenceCounter.clear();
|
||||
JbpmFactoryLocator.canUseDefaultBeanFactory = true;
|
||||
JbpmFactoryLocator.defaultFactory = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.jbpm.JbpmConfiguration;
|
||||
import org.jbpm.JbpmContext;
|
||||
import org.jbpm.configuration.ObjectFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springmodules.workflow.jbpm31.JbpmFactoryLocator;
|
||||
|
||||
/**
|
||||
* Implementation of Spring Module's JbpmConfigurationFactoryBean for Jbpm 3.2.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author davidc
|
||||
*/
|
||||
public class AlfrescoJbpmConfigurationFactoryBean implements InitializingBean, FactoryBean<JbpmConfiguration>,
|
||||
BeanFactoryAware, BeanNameAware, DisposableBean
|
||||
{
|
||||
private JbpmConfiguration jbpmConfiguration;
|
||||
|
||||
private ObjectFactory objectFactory;
|
||||
|
||||
private Resource configuration;
|
||||
|
||||
private SessionFactory sessionFactory;
|
||||
|
||||
private String contextName = JbpmContext.DEFAULT_JBPM_CONTEXT_NAME;
|
||||
|
||||
/**
|
||||
* FactoryLocator
|
||||
*/
|
||||
private final AlfrescoJbpmFactoryLocator factoryLocator = new AlfrescoJbpmFactoryLocator();
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see
|
||||
* org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org
|
||||
* .springframework.beans.factory.BeanFactory)
|
||||
*/
|
||||
public void setBeanFactory(final BeanFactory beanFactory) throws BeansException
|
||||
{
|
||||
// TODO Added to get the build working. A better solution is needed
|
||||
// long-term.
|
||||
this.factoryLocator.destroy();
|
||||
|
||||
this.factoryLocator.setBeanFactory(beanFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void setBeanName(final String name)
|
||||
{
|
||||
this.factoryLocator.setBeanName(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void afterPropertiesSet() throws Exception
|
||||
{
|
||||
if (this.configuration == null) { throw new IllegalArgumentException(
|
||||
"configuration or objectFactory property need to be not null"); }
|
||||
|
||||
// 1. Construct Jbpm Configuration
|
||||
// NOTE: Jbpm 3.2 adds a JbpmConfiguration value to its context
|
||||
final InputStream stream = this.configuration.getInputStream();
|
||||
this.jbpmConfiguration = JbpmConfiguration.parseInputStream(stream);
|
||||
|
||||
// 2. inject the HB session factory if it is the case
|
||||
if (this.sessionFactory != null)
|
||||
{
|
||||
final JbpmContext context = this.jbpmConfiguration.createJbpmContext(this.contextName);
|
||||
try
|
||||
{
|
||||
context.setSessionFactory(this.sessionFactory);
|
||||
}
|
||||
finally
|
||||
{
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public JbpmConfiguration getObject() throws Exception
|
||||
{
|
||||
return this.jbpmConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Class<JbpmConfiguration> getObjectType()
|
||||
{
|
||||
return JbpmConfiguration.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isSingleton()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the configuration.
|
||||
*/
|
||||
public Resource getConfiguration()
|
||||
{
|
||||
return this.configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param configuration The configuration to set
|
||||
*/
|
||||
public void setConfiguration(final Resource configuration)
|
||||
{
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the objectFactory.
|
||||
*/
|
||||
public ObjectFactory getObjectFactory()
|
||||
{
|
||||
return this.objectFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param objectFactory The objectFactory to set
|
||||
*/
|
||||
public void setObjectFactory(final ObjectFactory objectFactory)
|
||||
{
|
||||
this.objectFactory = objectFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the contextName.
|
||||
*/
|
||||
public String getContextName()
|
||||
{
|
||||
return this.contextName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param contextName The contextName to set
|
||||
*/
|
||||
public void setContextName(final String contextName)
|
||||
{
|
||||
this.contextName = contextName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the sessionFactory.
|
||||
*/
|
||||
public SessionFactory getSessionFactory()
|
||||
{
|
||||
return this.sessionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sessionFactory The sessionFactory to set
|
||||
*/
|
||||
public void setSessionFactory(final SessionFactory sessionFactory)
|
||||
{
|
||||
this.sessionFactory = sessionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void destroy() throws Exception
|
||||
{
|
||||
this.factoryLocator.destroy();
|
||||
}
|
||||
|
||||
private static class AlfrescoJbpmFactoryLocator extends JbpmFactoryLocator
|
||||
{
|
||||
public void destroy()
|
||||
{
|
||||
JbpmFactoryLocator.beanFactories.clear();
|
||||
JbpmFactoryLocator.beanFactoriesNames.clear();
|
||||
JbpmFactoryLocator.referenceCounter.clear();
|
||||
JbpmFactoryLocator.canUseDefaultBeanFactory = true;
|
||||
JbpmFactoryLocator.defaultFactory = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,101 +1,101 @@
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import org.alfresco.repo.lock.JobLockService;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.transaction.TransactionService;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jbpm.JbpmConfiguration;
|
||||
import org.jbpm.job.executor.JobExecutor;
|
||||
import org.springframework.beans.factory.access.BeanFactoryLocator;
|
||||
import org.springframework.beans.factory.access.BeanFactoryReference;
|
||||
import org.springmodules.workflow.jbpm31.JbpmFactoryLocator;
|
||||
|
||||
|
||||
/**
|
||||
* jBPM Job Executor
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class AlfrescoJobExecutor extends JobExecutor
|
||||
{
|
||||
private static final long serialVersionUID = -4576396495395482111L;
|
||||
|
||||
private static Log log = LogFactory.getLog(JobExecutor.class);
|
||||
private TransactionService transactionService;
|
||||
|
||||
private JbpmConfiguration jbpmConfiguration;
|
||||
|
||||
private JobLockService jobLockService;
|
||||
private boolean jobExecutorLockEnabled = true;
|
||||
|
||||
public void setJobExecutorLockEnabled(boolean jobExecutorLockEnabled)
|
||||
{
|
||||
this.jobExecutorLockEnabled = jobExecutorLockEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is Alfresco Job Executor Lock Enabled
|
||||
*
|
||||
* @return true if only one executor thread allowed (including across cluster)
|
||||
*
|
||||
* @since 3.2
|
||||
*/
|
||||
public boolean getJobExecutorLockEnabled()
|
||||
{
|
||||
return this.jobExecutorLockEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets Transaction Service
|
||||
*
|
||||
* @return transaction service
|
||||
*/
|
||||
public TransactionService getTransactionService()
|
||||
{
|
||||
return transactionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets Job Lock Service
|
||||
*
|
||||
* @return job lock service
|
||||
*
|
||||
* @since 3.2
|
||||
*/
|
||||
public JobLockService getJobLockService()
|
||||
{
|
||||
return jobLockService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public AlfrescoJobExecutor()
|
||||
{
|
||||
BeanFactoryLocator factoryLocator = new JbpmFactoryLocator();
|
||||
BeanFactoryReference factory = factoryLocator.useBeanFactory(null);
|
||||
|
||||
transactionService = (TransactionService)factory.getFactory().getBean(ServiceRegistry.TRANSACTION_SERVICE.getLocalName());
|
||||
jobLockService = (JobLockService)factory.getFactory().getBean(ServiceRegistry.JOB_LOCK_SERVICE.getLocalName());
|
||||
|
||||
jbpmConfiguration = (JbpmConfiguration)factory.getFactory().getBean("jbpm_configuration");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected synchronized void startThread()
|
||||
{
|
||||
String threadName = getNextThreadName();
|
||||
Thread thread = new AlfrescoJobExecutorThread(threadName, this, jbpmConfiguration, getIdleInterval(), getMaxIdleInterval(), getMaxLockTime(), getHistoryMaxSize());
|
||||
getThreads().put(threadName, thread);
|
||||
log.debug("starting new job executor thread '" + threadName + "'");
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
}
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import org.alfresco.repo.lock.JobLockService;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.transaction.TransactionService;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jbpm.JbpmConfiguration;
|
||||
import org.jbpm.job.executor.JobExecutor;
|
||||
import org.springframework.beans.factory.access.BeanFactoryLocator;
|
||||
import org.springframework.beans.factory.access.BeanFactoryReference;
|
||||
import org.springmodules.workflow.jbpm31.JbpmFactoryLocator;
|
||||
|
||||
|
||||
/**
|
||||
* jBPM Job Executor
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class AlfrescoJobExecutor extends JobExecutor
|
||||
{
|
||||
private static final long serialVersionUID = -4576396495395482111L;
|
||||
|
||||
private static Log log = LogFactory.getLog(JobExecutor.class);
|
||||
private TransactionService transactionService;
|
||||
|
||||
private JbpmConfiguration jbpmConfiguration;
|
||||
|
||||
private JobLockService jobLockService;
|
||||
private boolean jobExecutorLockEnabled = true;
|
||||
|
||||
public void setJobExecutorLockEnabled(boolean jobExecutorLockEnabled)
|
||||
{
|
||||
this.jobExecutorLockEnabled = jobExecutorLockEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is Alfresco Job Executor Lock Enabled
|
||||
*
|
||||
* @return true if only one executor thread allowed (including across cluster)
|
||||
*
|
||||
* @since 3.2
|
||||
*/
|
||||
public boolean getJobExecutorLockEnabled()
|
||||
{
|
||||
return this.jobExecutorLockEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets Transaction Service
|
||||
*
|
||||
* @return transaction service
|
||||
*/
|
||||
public TransactionService getTransactionService()
|
||||
{
|
||||
return transactionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets Job Lock Service
|
||||
*
|
||||
* @return job lock service
|
||||
*
|
||||
* @since 3.2
|
||||
*/
|
||||
public JobLockService getJobLockService()
|
||||
{
|
||||
return jobLockService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public AlfrescoJobExecutor()
|
||||
{
|
||||
BeanFactoryLocator factoryLocator = new JbpmFactoryLocator();
|
||||
BeanFactoryReference factory = factoryLocator.useBeanFactory(null);
|
||||
|
||||
transactionService = (TransactionService)factory.getFactory().getBean(ServiceRegistry.TRANSACTION_SERVICE.getLocalName());
|
||||
jobLockService = (JobLockService)factory.getFactory().getBean(ServiceRegistry.JOB_LOCK_SERVICE.getLocalName());
|
||||
|
||||
jbpmConfiguration = (JbpmConfiguration)factory.getFactory().getBean("jbpm_configuration");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected synchronized void startThread()
|
||||
{
|
||||
String threadName = getNextThreadName();
|
||||
Thread thread = new AlfrescoJobExecutorThread(threadName, this, jbpmConfiguration, getIdleInterval(), getMaxIdleInterval(), getMaxLockTime(), getHistoryMaxSize());
|
||||
getThreads().put(threadName, thread);
|
||||
log.debug("starting new job executor thread '" + threadName + "'");
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,448 +1,448 @@
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
|
||||
import org.alfresco.repo.lock.LockAcquisitionException;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
||||
import org.alfresco.repo.transaction.DoNotRetryException;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.jbpm.JbpmConfiguration;
|
||||
import org.jbpm.JbpmContext;
|
||||
import org.jbpm.db.JobSession;
|
||||
import org.jbpm.job.Job;
|
||||
import org.jbpm.job.executor.JobExecutorThread;
|
||||
import org.jbpm.persistence.JbpmPersistenceException;
|
||||
import org.jbpm.svc.Services;
|
||||
import org.jbpm.taskmgmt.exe.TaskInstance;
|
||||
|
||||
/**
|
||||
* Alfresco Job Executor Thread
|
||||
*
|
||||
* @author davidc, janv
|
||||
*/
|
||||
public class AlfrescoJobExecutorThread extends JobExecutorThread
|
||||
{
|
||||
/**
|
||||
* The name of the lock used to ensure that job executor does not run on
|
||||
* more than one node at the same time.
|
||||
*/
|
||||
private static final QName LOCK_QNAME = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI,
|
||||
"AlfrescoJbpmJobExecutor");
|
||||
|
||||
private static Log logger = LogFactory.getLog(AlfrescoJobExecutorThread.class);
|
||||
|
||||
private AlfrescoJobExecutor alfrescoJobExecutor;
|
||||
private boolean isActive = true;
|
||||
|
||||
private long jbpmMaxLockTime;
|
||||
|
||||
private long jobLockTTL = 0;
|
||||
private String jobLockToken = null;
|
||||
|
||||
private JbpmConfiguration jbpmConfiguration;
|
||||
|
||||
@Override
|
||||
public void setActive(boolean isActive)
|
||||
{
|
||||
this.isActive = isActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public AlfrescoJobExecutorThread(String name, AlfrescoJobExecutor jobExecutor, JbpmConfiguration jbpmConfiguration,
|
||||
int idleInterval, int maxIdleInterval, long maxLockTime, int maxHistory)
|
||||
{
|
||||
super(name, jobExecutor, jbpmConfiguration, idleInterval, maxIdleInterval, maxLockTime, maxHistory);
|
||||
this.alfrescoJobExecutor = jobExecutor;
|
||||
this.jbpmMaxLockTime = maxLockTime;
|
||||
|
||||
this.jobLockTTL = jbpmMaxLockTime + (1000 * 60 * 10);
|
||||
|
||||
this.jbpmConfiguration = jbpmConfiguration;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
protected Collection acquireJobs()
|
||||
{
|
||||
Collection jobs = Collections.EMPTY_LIST;
|
||||
|
||||
if ((isActive) && (!alfrescoJobExecutor.getTransactionService().isReadOnly()))
|
||||
{
|
||||
try
|
||||
{
|
||||
jobs = alfrescoJobExecutor.getTransactionService().getRetryingTransactionHelper()
|
||||
.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Collection>()
|
||||
{
|
||||
public Collection execute() throws Throwable
|
||||
{
|
||||
if (jobLockToken != null)
|
||||
{
|
||||
refreshExecutorLock(jobLockToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
jobLockToken = getExecutorLock();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return AlfrescoJobExecutorThread.super.acquireJobs();
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
logger.error("Failed to acquire jobs");
|
||||
releaseExecutorLock(jobLockToken);
|
||||
jobLockToken = null;
|
||||
throw t;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (jobs != null)
|
||||
{
|
||||
if (logger.isDebugEnabled() && (!logger.isTraceEnabled()) && (!jobs.isEmpty()))
|
||||
{
|
||||
logger.debug("acquired " + jobs.size() + " job" + ((jobs.size() != 1) ? "s" : ""));
|
||||
}
|
||||
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("acquired " + jobs.size() + " job" + ((jobs.size() != 1) ? "s" : "")
|
||||
+ ((jobs.size() > 0) ? ": " + jobs.toString() : ""));
|
||||
}
|
||||
|
||||
if (jobs.size() == 0)
|
||||
{
|
||||
releaseExecutorLock(jobLockToken);
|
||||
jobLockToken = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (LockAcquisitionException e)
|
||||
{
|
||||
// ignore
|
||||
jobLockToken = null;
|
||||
}
|
||||
}
|
||||
|
||||
return jobs;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Date getNextDueDate()
|
||||
{
|
||||
if (!isActive)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return alfrescoJobExecutor.getTransactionService().getRetryingTransactionHelper()
|
||||
.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Date>()
|
||||
{
|
||||
public Date execute() throws Throwable
|
||||
{
|
||||
return AlfrescoJobExecutorThread.super.getNextDueDate();
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void executeJob(final Job jobIn)
|
||||
{
|
||||
// execute the job as System (ALF-10776) so transaction commit level
|
||||
// operations have a security context.
|
||||
AuthenticationUtil.runAs(new RunAsWork<Void>()
|
||||
{
|
||||
public Void doWork() throws Exception
|
||||
{
|
||||
executeJobImpl(jobIn);
|
||||
return null;
|
||||
}
|
||||
}, getActorId(jobIn));
|
||||
|
||||
// clear authentication context for this thread
|
||||
AuthenticationUtil.clearCurrentSecurityContext();
|
||||
}
|
||||
|
||||
private String getActorId(final Job jobIn)
|
||||
{
|
||||
TaskInstance taskInstance = jobIn.getTaskInstance();
|
||||
|
||||
if (taskInstance != null)
|
||||
{
|
||||
String actorId = taskInstance.getActorId();
|
||||
if (actorId != null && actorId.length() > 0)
|
||||
{
|
||||
return actorId;
|
||||
}
|
||||
}
|
||||
return AuthenticationUtil.getSystemUserName();
|
||||
}
|
||||
|
||||
private void executeJobImpl(final Job jobIn)
|
||||
{
|
||||
if ((!isActive) || (alfrescoJobExecutor.getTransactionService().isReadOnly()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// based on JBPM 3.3.1 (JobExecutorThread.executeJob)
|
||||
// - wrap executeJob / deleteJob in Alfresco retries
|
||||
// - add setRollbackOnly warnings
|
||||
// - if Alfresco retries fail, attempt to set JBPM job exception/retries
|
||||
|
||||
try
|
||||
{
|
||||
RetryingTransactionHelper tranHelper = alfrescoJobExecutor.getTransactionService().getRetryingTransactionHelper();
|
||||
tranHelper.doInTransaction(new RetryingTransactionCallback<Object>()
|
||||
{
|
||||
public Object execute() throws Throwable
|
||||
{
|
||||
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
|
||||
try
|
||||
{
|
||||
JobSession jobSession = jbpmContext.getJobSession();
|
||||
Job job = jobSession.loadJob(jobIn.getId());
|
||||
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("executing " + job);
|
||||
}
|
||||
|
||||
if (job.execute(jbpmContext))
|
||||
{
|
||||
jobSession.deleteJob(job);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("executed and deleted: " + job);
|
||||
}
|
||||
}
|
||||
|
||||
// if this job is locked too long
|
||||
long totalLockTimeInMillis = System.currentTimeMillis() - job.getLockTime().getTime();
|
||||
if (totalLockTimeInMillis > jbpmMaxLockTime)
|
||||
{
|
||||
logger.warn("setRollbackOnly: exceeded maxLockTime (" + jbpmMaxLockTime + ") " + job);
|
||||
jbpmContext.setRollbackOnly();
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
if(isPersistenceException(e))
|
||||
{
|
||||
throw new AlfrescoJbpmPersistenceException(e);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
jbpmContext.getSession().flush();
|
||||
jbpmContext.close();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (LockAcquisitionException e)
|
||||
{
|
||||
// ignore
|
||||
jobLockToken = null;
|
||||
}
|
||||
catch(JbpmPersistenceException pe)
|
||||
{
|
||||
if(Services.isCausedByStaleState(pe))
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("optimistic locking failed, couldn't complete job "+ jobIn, pe);
|
||||
}
|
||||
}
|
||||
else handleException(jobIn, pe);
|
||||
}
|
||||
catch (final Exception e)
|
||||
{
|
||||
handleException(jobIn, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleException(final Job jobIn, final Exception e)
|
||||
{
|
||||
|
||||
if (logger.isErrorEnabled())
|
||||
{
|
||||
logger.error("failed to execute " + jobIn, e);
|
||||
}
|
||||
try
|
||||
{
|
||||
RetryingTransactionHelper tranHelper = alfrescoJobExecutor.getTransactionService().getRetryingTransactionHelper();
|
||||
tranHelper.doInTransaction(new RetryingTransactionCallback<Object>()
|
||||
{
|
||||
public Object execute() throws Throwable
|
||||
{
|
||||
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
|
||||
try
|
||||
{
|
||||
JobSession jobSession = jbpmContext.getJobSession();
|
||||
Job job = jobSession.loadJob(jobIn.getId());
|
||||
|
||||
StringWriter memoryWriter = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(memoryWriter));
|
||||
job.setException(memoryWriter.toString());
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("attempting to update exception/retries: " + job);
|
||||
}
|
||||
int retries = 0;
|
||||
if (isPersistenceException(e) ==false)
|
||||
{
|
||||
retries = job.getRetries() -1;
|
||||
}
|
||||
job.setRetries(retries);
|
||||
if (logger.isInfoEnabled())
|
||||
{
|
||||
String msg = "updated job exception and set to " + job.getRetries() + " retries: " + jobIn;
|
||||
logger.info(msg);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
jbpmContext.getSession().flush();
|
||||
jbpmContext.close();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception e2)
|
||||
{
|
||||
if (logger.isErrorEnabled())
|
||||
{
|
||||
logger.error("failed to update job exception/retries " + jobIn, e2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getExecutorLock()
|
||||
{
|
||||
String lockToken = null;
|
||||
|
||||
if (alfrescoJobExecutor.getJobExecutorLockEnabled())
|
||||
{
|
||||
try
|
||||
{
|
||||
lockToken = alfrescoJobExecutor.getJobLockService().getLock(LOCK_QNAME, jobLockTTL, 3000, 10);
|
||||
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace(Thread.currentThread().getName() + " got lock token: " + lockToken);
|
||||
}
|
||||
}
|
||||
catch (LockAcquisitionException e)
|
||||
{
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("Failed to get Alfresco Job Executor lock - may already running in another thread");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
return lockToken;
|
||||
}
|
||||
|
||||
private void refreshExecutorLock(String lockToken)
|
||||
{
|
||||
if (lockToken != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
alfrescoJobExecutor.getJobLockService().refreshLock(lockToken, LOCK_QNAME, jobLockTTL);
|
||||
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace(Thread.currentThread().getName() + " refreshed lock token: " + lockToken);
|
||||
}
|
||||
}
|
||||
catch (LockAcquisitionException e)
|
||||
{
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("Failed to refresh Alfresco Job Executor lock - may no longer exist (" + lockToken
|
||||
+ ")");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void releaseExecutorLock(String lockToken)
|
||||
{
|
||||
if (lockToken != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
alfrescoJobExecutor.getJobLockService().releaseLock(lockToken, LOCK_QNAME);
|
||||
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace(Thread.currentThread().getName() + " released lock token: " + lockToken);
|
||||
}
|
||||
}
|
||||
catch (LockAcquisitionException e)
|
||||
{
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("Failed to release Alfresco Job Executor lock - may no longer exist (" + lockToken
|
||||
+ ")");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isPersistenceException(Throwable throwable)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (throwable instanceof HibernateException)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
throwable = throwable.getCause();
|
||||
}
|
||||
while (throwable != null);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static class AlfrescoJbpmPersistenceException extends Exception implements DoNotRetryException
|
||||
{
|
||||
private static final long serialVersionUID = -2233119713831272158L;
|
||||
|
||||
public AlfrescoJbpmPersistenceException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
|
||||
import org.alfresco.repo.lock.LockAcquisitionException;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
||||
import org.alfresco.repo.transaction.DoNotRetryException;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.jbpm.JbpmConfiguration;
|
||||
import org.jbpm.JbpmContext;
|
||||
import org.jbpm.db.JobSession;
|
||||
import org.jbpm.job.Job;
|
||||
import org.jbpm.job.executor.JobExecutorThread;
|
||||
import org.jbpm.persistence.JbpmPersistenceException;
|
||||
import org.jbpm.svc.Services;
|
||||
import org.jbpm.taskmgmt.exe.TaskInstance;
|
||||
|
||||
/**
|
||||
* Alfresco Job Executor Thread
|
||||
*
|
||||
* @author davidc, janv
|
||||
*/
|
||||
public class AlfrescoJobExecutorThread extends JobExecutorThread
|
||||
{
|
||||
/**
|
||||
* The name of the lock used to ensure that job executor does not run on
|
||||
* more than one node at the same time.
|
||||
*/
|
||||
private static final QName LOCK_QNAME = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI,
|
||||
"AlfrescoJbpmJobExecutor");
|
||||
|
||||
private static Log logger = LogFactory.getLog(AlfrescoJobExecutorThread.class);
|
||||
|
||||
private AlfrescoJobExecutor alfrescoJobExecutor;
|
||||
private boolean isActive = true;
|
||||
|
||||
private long jbpmMaxLockTime;
|
||||
|
||||
private long jobLockTTL = 0;
|
||||
private String jobLockToken = null;
|
||||
|
||||
private JbpmConfiguration jbpmConfiguration;
|
||||
|
||||
@Override
|
||||
public void setActive(boolean isActive)
|
||||
{
|
||||
this.isActive = isActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public AlfrescoJobExecutorThread(String name, AlfrescoJobExecutor jobExecutor, JbpmConfiguration jbpmConfiguration,
|
||||
int idleInterval, int maxIdleInterval, long maxLockTime, int maxHistory)
|
||||
{
|
||||
super(name, jobExecutor, jbpmConfiguration, idleInterval, maxIdleInterval, maxLockTime, maxHistory);
|
||||
this.alfrescoJobExecutor = jobExecutor;
|
||||
this.jbpmMaxLockTime = maxLockTime;
|
||||
|
||||
this.jobLockTTL = jbpmMaxLockTime + (1000 * 60 * 10);
|
||||
|
||||
this.jbpmConfiguration = jbpmConfiguration;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
protected Collection acquireJobs()
|
||||
{
|
||||
Collection jobs = Collections.EMPTY_LIST;
|
||||
|
||||
if ((isActive) && (!alfrescoJobExecutor.getTransactionService().isReadOnly()))
|
||||
{
|
||||
try
|
||||
{
|
||||
jobs = alfrescoJobExecutor.getTransactionService().getRetryingTransactionHelper()
|
||||
.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Collection>()
|
||||
{
|
||||
public Collection execute() throws Throwable
|
||||
{
|
||||
if (jobLockToken != null)
|
||||
{
|
||||
refreshExecutorLock(jobLockToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
jobLockToken = getExecutorLock();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return AlfrescoJobExecutorThread.super.acquireJobs();
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
logger.error("Failed to acquire jobs");
|
||||
releaseExecutorLock(jobLockToken);
|
||||
jobLockToken = null;
|
||||
throw t;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (jobs != null)
|
||||
{
|
||||
if (logger.isDebugEnabled() && (!logger.isTraceEnabled()) && (!jobs.isEmpty()))
|
||||
{
|
||||
logger.debug("acquired " + jobs.size() + " job" + ((jobs.size() != 1) ? "s" : ""));
|
||||
}
|
||||
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("acquired " + jobs.size() + " job" + ((jobs.size() != 1) ? "s" : "")
|
||||
+ ((jobs.size() > 0) ? ": " + jobs.toString() : ""));
|
||||
}
|
||||
|
||||
if (jobs.size() == 0)
|
||||
{
|
||||
releaseExecutorLock(jobLockToken);
|
||||
jobLockToken = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (LockAcquisitionException e)
|
||||
{
|
||||
// ignore
|
||||
jobLockToken = null;
|
||||
}
|
||||
}
|
||||
|
||||
return jobs;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Date getNextDueDate()
|
||||
{
|
||||
if (!isActive)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return alfrescoJobExecutor.getTransactionService().getRetryingTransactionHelper()
|
||||
.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback<Date>()
|
||||
{
|
||||
public Date execute() throws Throwable
|
||||
{
|
||||
return AlfrescoJobExecutorThread.super.getNextDueDate();
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void executeJob(final Job jobIn)
|
||||
{
|
||||
// execute the job as System (ALF-10776) so transaction commit level
|
||||
// operations have a security context.
|
||||
AuthenticationUtil.runAs(new RunAsWork<Void>()
|
||||
{
|
||||
public Void doWork() throws Exception
|
||||
{
|
||||
executeJobImpl(jobIn);
|
||||
return null;
|
||||
}
|
||||
}, getActorId(jobIn));
|
||||
|
||||
// clear authentication context for this thread
|
||||
AuthenticationUtil.clearCurrentSecurityContext();
|
||||
}
|
||||
|
||||
private String getActorId(final Job jobIn)
|
||||
{
|
||||
TaskInstance taskInstance = jobIn.getTaskInstance();
|
||||
|
||||
if (taskInstance != null)
|
||||
{
|
||||
String actorId = taskInstance.getActorId();
|
||||
if (actorId != null && actorId.length() > 0)
|
||||
{
|
||||
return actorId;
|
||||
}
|
||||
}
|
||||
return AuthenticationUtil.getSystemUserName();
|
||||
}
|
||||
|
||||
private void executeJobImpl(final Job jobIn)
|
||||
{
|
||||
if ((!isActive) || (alfrescoJobExecutor.getTransactionService().isReadOnly()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// based on JBPM 3.3.1 (JobExecutorThread.executeJob)
|
||||
// - wrap executeJob / deleteJob in Alfresco retries
|
||||
// - add setRollbackOnly warnings
|
||||
// - if Alfresco retries fail, attempt to set JBPM job exception/retries
|
||||
|
||||
try
|
||||
{
|
||||
RetryingTransactionHelper tranHelper = alfrescoJobExecutor.getTransactionService().getRetryingTransactionHelper();
|
||||
tranHelper.doInTransaction(new RetryingTransactionCallback<Object>()
|
||||
{
|
||||
public Object execute() throws Throwable
|
||||
{
|
||||
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
|
||||
try
|
||||
{
|
||||
JobSession jobSession = jbpmContext.getJobSession();
|
||||
Job job = jobSession.loadJob(jobIn.getId());
|
||||
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("executing " + job);
|
||||
}
|
||||
|
||||
if (job.execute(jbpmContext))
|
||||
{
|
||||
jobSession.deleteJob(job);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("executed and deleted: " + job);
|
||||
}
|
||||
}
|
||||
|
||||
// if this job is locked too long
|
||||
long totalLockTimeInMillis = System.currentTimeMillis() - job.getLockTime().getTime();
|
||||
if (totalLockTimeInMillis > jbpmMaxLockTime)
|
||||
{
|
||||
logger.warn("setRollbackOnly: exceeded maxLockTime (" + jbpmMaxLockTime + ") " + job);
|
||||
jbpmContext.setRollbackOnly();
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
if(isPersistenceException(e))
|
||||
{
|
||||
throw new AlfrescoJbpmPersistenceException(e);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
jbpmContext.getSession().flush();
|
||||
jbpmContext.close();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (LockAcquisitionException e)
|
||||
{
|
||||
// ignore
|
||||
jobLockToken = null;
|
||||
}
|
||||
catch(JbpmPersistenceException pe)
|
||||
{
|
||||
if(Services.isCausedByStaleState(pe))
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("optimistic locking failed, couldn't complete job "+ jobIn, pe);
|
||||
}
|
||||
}
|
||||
else handleException(jobIn, pe);
|
||||
}
|
||||
catch (final Exception e)
|
||||
{
|
||||
handleException(jobIn, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleException(final Job jobIn, final Exception e)
|
||||
{
|
||||
|
||||
if (logger.isErrorEnabled())
|
||||
{
|
||||
logger.error("failed to execute " + jobIn, e);
|
||||
}
|
||||
try
|
||||
{
|
||||
RetryingTransactionHelper tranHelper = alfrescoJobExecutor.getTransactionService().getRetryingTransactionHelper();
|
||||
tranHelper.doInTransaction(new RetryingTransactionCallback<Object>()
|
||||
{
|
||||
public Object execute() throws Throwable
|
||||
{
|
||||
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
|
||||
try
|
||||
{
|
||||
JobSession jobSession = jbpmContext.getJobSession();
|
||||
Job job = jobSession.loadJob(jobIn.getId());
|
||||
|
||||
StringWriter memoryWriter = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(memoryWriter));
|
||||
job.setException(memoryWriter.toString());
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("attempting to update exception/retries: " + job);
|
||||
}
|
||||
int retries = 0;
|
||||
if (isPersistenceException(e) ==false)
|
||||
{
|
||||
retries = job.getRetries() -1;
|
||||
}
|
||||
job.setRetries(retries);
|
||||
if (logger.isInfoEnabled())
|
||||
{
|
||||
String msg = "updated job exception and set to " + job.getRetries() + " retries: " + jobIn;
|
||||
logger.info(msg);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
jbpmContext.getSession().flush();
|
||||
jbpmContext.close();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception e2)
|
||||
{
|
||||
if (logger.isErrorEnabled())
|
||||
{
|
||||
logger.error("failed to update job exception/retries " + jobIn, e2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getExecutorLock()
|
||||
{
|
||||
String lockToken = null;
|
||||
|
||||
if (alfrescoJobExecutor.getJobExecutorLockEnabled())
|
||||
{
|
||||
try
|
||||
{
|
||||
lockToken = alfrescoJobExecutor.getJobLockService().getLock(LOCK_QNAME, jobLockTTL, 3000, 10);
|
||||
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace(Thread.currentThread().getName() + " got lock token: " + lockToken);
|
||||
}
|
||||
}
|
||||
catch (LockAcquisitionException e)
|
||||
{
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("Failed to get Alfresco Job Executor lock - may already running in another thread");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
return lockToken;
|
||||
}
|
||||
|
||||
private void refreshExecutorLock(String lockToken)
|
||||
{
|
||||
if (lockToken != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
alfrescoJobExecutor.getJobLockService().refreshLock(lockToken, LOCK_QNAME, jobLockTTL);
|
||||
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace(Thread.currentThread().getName() + " refreshed lock token: " + lockToken);
|
||||
}
|
||||
}
|
||||
catch (LockAcquisitionException e)
|
||||
{
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("Failed to refresh Alfresco Job Executor lock - may no longer exist (" + lockToken
|
||||
+ ")");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void releaseExecutorLock(String lockToken)
|
||||
{
|
||||
if (lockToken != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
alfrescoJobExecutor.getJobLockService().releaseLock(lockToken, LOCK_QNAME);
|
||||
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace(Thread.currentThread().getName() + " released lock token: " + lockToken);
|
||||
}
|
||||
}
|
||||
catch (LockAcquisitionException e)
|
||||
{
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("Failed to release Alfresco Job Executor lock - may no longer exist (" + lockToken
|
||||
+ ")");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isPersistenceException(Throwable throwable)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (throwable instanceof HibernateException)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
throwable = throwable.getCause();
|
||||
}
|
||||
while (throwable != null);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static class AlfrescoJbpmPersistenceException extends Exception implements DoNotRetryException
|
||||
{
|
||||
private static final long serialVersionUID = -2233119713831272158L;
|
||||
|
||||
public AlfrescoJbpmPersistenceException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,74 +1,74 @@
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import org.jbpm.JbpmContext;
|
||||
import org.jbpm.graph.exe.Token;
|
||||
import org.jbpm.job.Timer;
|
||||
import org.jbpm.taskmgmt.exe.TaskInstance;
|
||||
|
||||
/**
|
||||
* Extended JBPM Timer that provides Alfresco context.
|
||||
*
|
||||
* NOTE: The action triggered by the timer is executed as the user assigned
|
||||
* to the task associated with the timer. If not associated with a
|
||||
* task, the timer is executed unauthenticated.
|
||||
*
|
||||
* @author davidc
|
||||
* @author Nick Smith
|
||||
*/
|
||||
public class AlfrescoTimer extends Timer
|
||||
{
|
||||
private static final long serialVersionUID = -6618486175822866286L;
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*/
|
||||
public AlfrescoTimer()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*
|
||||
* @param token Token
|
||||
*/
|
||||
public AlfrescoTimer(Token token)
|
||||
{
|
||||
super(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean execute(final JbpmContext jbpmContext)
|
||||
throws Exception
|
||||
{
|
||||
// establish authentication context
|
||||
final TaskInstance taskInstance = getTaskInstance();
|
||||
|
||||
// execute timer
|
||||
boolean deleteTimer = AlfrescoTimer.super.execute(jbpmContext);
|
||||
|
||||
// End the task if timer does not repeat.
|
||||
// Note the order is a little odd here as the task will be ended
|
||||
// after the token has been signalled to move to the next node.
|
||||
if (deleteTimer
|
||||
&& taskInstance != null
|
||||
&& taskInstance.isOpen())
|
||||
{
|
||||
taskInstance.setSignalling(false);
|
||||
String transitionName = getTransitionName();
|
||||
if (transitionName==null)
|
||||
{
|
||||
taskInstance.end();
|
||||
}
|
||||
else
|
||||
{
|
||||
taskInstance.end(transitionName);
|
||||
}
|
||||
}
|
||||
|
||||
return deleteTimer;
|
||||
}
|
||||
}
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import org.jbpm.JbpmContext;
|
||||
import org.jbpm.graph.exe.Token;
|
||||
import org.jbpm.job.Timer;
|
||||
import org.jbpm.taskmgmt.exe.TaskInstance;
|
||||
|
||||
/**
|
||||
* Extended JBPM Timer that provides Alfresco context.
|
||||
*
|
||||
* NOTE: The action triggered by the timer is executed as the user assigned
|
||||
* to the task associated with the timer. If not associated with a
|
||||
* task, the timer is executed unauthenticated.
|
||||
*
|
||||
* @author davidc
|
||||
* @author Nick Smith
|
||||
*/
|
||||
public class AlfrescoTimer extends Timer
|
||||
{
|
||||
private static final long serialVersionUID = -6618486175822866286L;
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*/
|
||||
public AlfrescoTimer()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*
|
||||
* @param token Token
|
||||
*/
|
||||
public AlfrescoTimer(Token token)
|
||||
{
|
||||
super(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean execute(final JbpmContext jbpmContext)
|
||||
throws Exception
|
||||
{
|
||||
// establish authentication context
|
||||
final TaskInstance taskInstance = getTaskInstance();
|
||||
|
||||
// execute timer
|
||||
boolean deleteTimer = AlfrescoTimer.super.execute(jbpmContext);
|
||||
|
||||
// End the task if timer does not repeat.
|
||||
// Note the order is a little odd here as the task will be ended
|
||||
// after the token has been signalled to move to the next node.
|
||||
if (deleteTimer
|
||||
&& taskInstance != null
|
||||
&& taskInstance.isOpen())
|
||||
{
|
||||
taskInstance.setSignalling(false);
|
||||
String transitionName = getTransitionName();
|
||||
if (transitionName==null)
|
||||
{
|
||||
taskInstance.end();
|
||||
}
|
||||
else
|
||||
{
|
||||
taskInstance.end(transitionName);
|
||||
}
|
||||
}
|
||||
|
||||
return deleteTimer;
|
||||
}
|
||||
}
|
||||
|
@@ -1,243 +1,243 @@
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.workflow.WorkflowException;
|
||||
import org.dom4j.Element;
|
||||
import org.jbpm.graph.def.Node;
|
||||
import org.jbpm.graph.def.Transition;
|
||||
import org.jbpm.graph.exe.ExecutionContext;
|
||||
import org.jbpm.graph.exe.Token;
|
||||
import org.jbpm.instantiation.FieldInstantiator;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
|
||||
|
||||
/**
|
||||
* For each "item in collection", create a fork.
|
||||
*/
|
||||
public class ForEachFork extends JBPMSpringActionHandler
|
||||
{
|
||||
private static final long serialVersionUID = 4643103713602441652L;
|
||||
|
||||
private ServiceRegistry services;
|
||||
|
||||
private Element foreach;
|
||||
private String var;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.workflow.jbpm.JBPMSpringActionHandler#initialiseHandler(org.springframework.beans.factory.BeanFactory)
|
||||
*/
|
||||
@Override
|
||||
protected void initialiseHandler(BeanFactory factory)
|
||||
{
|
||||
services = (ServiceRegistry)factory.getBean(ServiceRegistry.SERVICE_REGISTRY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new child token for each item in list.
|
||||
*
|
||||
* @param executionContext ExecutionContext
|
||||
* @throws Exception
|
||||
*/
|
||||
public void execute(final ExecutionContext executionContext)
|
||||
throws Exception
|
||||
{
|
||||
//
|
||||
// process action handler arguments
|
||||
//
|
||||
|
||||
if (foreach == null)
|
||||
{
|
||||
throw new WorkflowException("forEach has not been provided");
|
||||
}
|
||||
|
||||
// Collection<?> forEachColl = buildForEachCollection(executionContext);
|
||||
|
||||
if (var == null || var.length() == 0)
|
||||
{
|
||||
throw new WorkflowException("forEach variable name has not been provided");
|
||||
}
|
||||
|
||||
//
|
||||
// create forked paths
|
||||
//
|
||||
|
||||
Node node = executionContext.getNode();
|
||||
List<ForkedTransition> forkTransitions = new ArrayList<ForkedTransition>();
|
||||
|
||||
Collection<?> forEachColl = buildForEachCollection(executionContext);
|
||||
|
||||
// Create a new token and execution context for each node transition and item in list
|
||||
List<Transition> nodeTransitions = node.getLeavingTransitions();
|
||||
for (Transition noderansition : nodeTransitions)
|
||||
{
|
||||
int iVar = 0;
|
||||
for (Object item: forEachColl)
|
||||
{
|
||||
// create child token to represent new path
|
||||
Token loopToken = buildChildToken(executionContext, noderansition, iVar);
|
||||
iVar++;
|
||||
|
||||
// assign variable within path
|
||||
final ExecutionContext newExecutionContext = new ExecutionContext(loopToken);
|
||||
newExecutionContext.getContextInstance().createVariable(var, item, loopToken);
|
||||
|
||||
// record path & transition
|
||||
ForkedTransition forkTransition = new ForkedTransition();
|
||||
forkTransition.executionContext = newExecutionContext;
|
||||
forkTransition.transition = noderansition;
|
||||
forkTransitions.add(forkTransition);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// let each new token leave the node.
|
||||
//
|
||||
for (ForkedTransition forkTransition : forkTransitions)
|
||||
{
|
||||
node.leave(forkTransition.executionContext, forkTransition.transition);
|
||||
}
|
||||
}
|
||||
|
||||
private Token buildChildToken(final ExecutionContext executionContext, Transition noderansition,
|
||||
int iVar)
|
||||
{
|
||||
Token rootToken = executionContext.getToken();
|
||||
String tokenName = getTokenName(rootToken, noderansition.getName(), iVar);
|
||||
Token loopToken = new Token(rootToken, tokenName);
|
||||
loopToken.setTerminationImplicit(true);
|
||||
executionContext.getJbpmContext().getSession().save(loopToken);
|
||||
return loopToken;
|
||||
}
|
||||
|
||||
private Collection<?> buildForEachCollection(final ExecutionContext executionContext)
|
||||
{
|
||||
// build "for each" collection
|
||||
String text = foreach.getTextTrim();
|
||||
if (text != null && text.startsWith("#{"))
|
||||
{
|
||||
return evaluateForEachExpression(executionContext, text);
|
||||
}
|
||||
return (Collection<?>) FieldInstantiator.getValue(List.class, foreach);
|
||||
}
|
||||
|
||||
private Collection<?> evaluateForEachExpression(final ExecutionContext executionContext, String forEachText)
|
||||
{
|
||||
String expression = forEachText.substring(2, forEachText.length() -1);
|
||||
Object result = AlfrescoJavaScript.executeScript(executionContext, services, expression, null, null);
|
||||
if (result == null)
|
||||
{
|
||||
throw new WorkflowException("forEach expression '" + forEachText + "' evaluates to null");
|
||||
}
|
||||
// expression evaluates to string
|
||||
if (result instanceof String)
|
||||
{
|
||||
return buildStrings((String)result);
|
||||
}
|
||||
// expression evaluates to Node array
|
||||
else if (result instanceof Serializable[])
|
||||
{
|
||||
return buildJbpmNodes((Serializable[]) result);
|
||||
}
|
||||
// expression evaluates to collection
|
||||
else if (result instanceof Collection<?>)
|
||||
{
|
||||
return (Collection<?>)result;
|
||||
}
|
||||
else return null;
|
||||
}
|
||||
|
||||
private List<?> buildStrings(String result)
|
||||
{
|
||||
String[] results = result.trim().split(",");
|
||||
return Arrays.asList(results);
|
||||
}
|
||||
|
||||
private List<?> buildJbpmNodes(Serializable[] nodes)
|
||||
{
|
||||
List<JBPMNode> jbpmNodes = new ArrayList<JBPMNode>(nodes.length);
|
||||
for (Serializable node : nodes)
|
||||
{
|
||||
if (node instanceof NodeRef)
|
||||
{
|
||||
JBPMNode jbpmNode = new JBPMNode((NodeRef)node, services);
|
||||
jbpmNodes.add(jbpmNode);
|
||||
}
|
||||
}
|
||||
return jbpmNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a token name
|
||||
*
|
||||
* @param parent Token
|
||||
* @param transitionName String
|
||||
* @param loopIndex int
|
||||
* @return String
|
||||
*/
|
||||
protected String getTokenName(Token parent, String transitionName, int loopIndex)
|
||||
{
|
||||
String suffix = "." + loopIndex;
|
||||
if (transitionName == null || transitionName.isEmpty())
|
||||
{
|
||||
// No transition name
|
||||
int size = (parent.getChildren() != null) ? parent.getChildren().size() + 1 : 1;
|
||||
return buildTokenName("FOREACHFORK", suffix, size);
|
||||
}
|
||||
return findFirstAvailableTokenName(parent, transitionName, suffix);
|
||||
}
|
||||
|
||||
private String findFirstAvailableTokenName(Token parent, String transitionName, String suffix)
|
||||
{
|
||||
int i = 1;
|
||||
while (true)
|
||||
{
|
||||
String tokenName = buildTokenName(transitionName, suffix, i);
|
||||
if(!parent.hasChild(tokenName))
|
||||
{
|
||||
return tokenName;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
private String buildTokenName(String prefix, String suffix, int count)
|
||||
{
|
||||
String countStr = count<2 ? "": Integer.toString(count);
|
||||
return prefix + countStr + suffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of objects to be iterated over.
|
||||
* @param foreach the list of objects to set
|
||||
*/
|
||||
public void setForeach(Element foreach)
|
||||
{
|
||||
this.foreach = foreach;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the variable to which the eleements of <code>foreach</code> are assigned.
|
||||
* @param var the variable name to set
|
||||
*/
|
||||
public void setVar(String var)
|
||||
{
|
||||
this.var = var;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fork Transition
|
||||
*/
|
||||
private class ForkedTransition
|
||||
{
|
||||
private ExecutionContext executionContext;
|
||||
private Transition transition;
|
||||
}
|
||||
|
||||
}
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.workflow.WorkflowException;
|
||||
import org.dom4j.Element;
|
||||
import org.jbpm.graph.def.Node;
|
||||
import org.jbpm.graph.def.Transition;
|
||||
import org.jbpm.graph.exe.ExecutionContext;
|
||||
import org.jbpm.graph.exe.Token;
|
||||
import org.jbpm.instantiation.FieldInstantiator;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
|
||||
|
||||
/**
|
||||
* For each "item in collection", create a fork.
|
||||
*/
|
||||
public class ForEachFork extends JBPMSpringActionHandler
|
||||
{
|
||||
private static final long serialVersionUID = 4643103713602441652L;
|
||||
|
||||
private ServiceRegistry services;
|
||||
|
||||
private Element foreach;
|
||||
private String var;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.workflow.jbpm.JBPMSpringActionHandler#initialiseHandler(org.springframework.beans.factory.BeanFactory)
|
||||
*/
|
||||
@Override
|
||||
protected void initialiseHandler(BeanFactory factory)
|
||||
{
|
||||
services = (ServiceRegistry)factory.getBean(ServiceRegistry.SERVICE_REGISTRY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new child token for each item in list.
|
||||
*
|
||||
* @param executionContext ExecutionContext
|
||||
* @throws Exception
|
||||
*/
|
||||
public void execute(final ExecutionContext executionContext)
|
||||
throws Exception
|
||||
{
|
||||
//
|
||||
// process action handler arguments
|
||||
//
|
||||
|
||||
if (foreach == null)
|
||||
{
|
||||
throw new WorkflowException("forEach has not been provided");
|
||||
}
|
||||
|
||||
// Collection<?> forEachColl = buildForEachCollection(executionContext);
|
||||
|
||||
if (var == null || var.length() == 0)
|
||||
{
|
||||
throw new WorkflowException("forEach variable name has not been provided");
|
||||
}
|
||||
|
||||
//
|
||||
// create forked paths
|
||||
//
|
||||
|
||||
Node node = executionContext.getNode();
|
||||
List<ForkedTransition> forkTransitions = new ArrayList<ForkedTransition>();
|
||||
|
||||
Collection<?> forEachColl = buildForEachCollection(executionContext);
|
||||
|
||||
// Create a new token and execution context for each node transition and item in list
|
||||
List<Transition> nodeTransitions = node.getLeavingTransitions();
|
||||
for (Transition noderansition : nodeTransitions)
|
||||
{
|
||||
int iVar = 0;
|
||||
for (Object item: forEachColl)
|
||||
{
|
||||
// create child token to represent new path
|
||||
Token loopToken = buildChildToken(executionContext, noderansition, iVar);
|
||||
iVar++;
|
||||
|
||||
// assign variable within path
|
||||
final ExecutionContext newExecutionContext = new ExecutionContext(loopToken);
|
||||
newExecutionContext.getContextInstance().createVariable(var, item, loopToken);
|
||||
|
||||
// record path & transition
|
||||
ForkedTransition forkTransition = new ForkedTransition();
|
||||
forkTransition.executionContext = newExecutionContext;
|
||||
forkTransition.transition = noderansition;
|
||||
forkTransitions.add(forkTransition);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// let each new token leave the node.
|
||||
//
|
||||
for (ForkedTransition forkTransition : forkTransitions)
|
||||
{
|
||||
node.leave(forkTransition.executionContext, forkTransition.transition);
|
||||
}
|
||||
}
|
||||
|
||||
private Token buildChildToken(final ExecutionContext executionContext, Transition noderansition,
|
||||
int iVar)
|
||||
{
|
||||
Token rootToken = executionContext.getToken();
|
||||
String tokenName = getTokenName(rootToken, noderansition.getName(), iVar);
|
||||
Token loopToken = new Token(rootToken, tokenName);
|
||||
loopToken.setTerminationImplicit(true);
|
||||
executionContext.getJbpmContext().getSession().save(loopToken);
|
||||
return loopToken;
|
||||
}
|
||||
|
||||
private Collection<?> buildForEachCollection(final ExecutionContext executionContext)
|
||||
{
|
||||
// build "for each" collection
|
||||
String text = foreach.getTextTrim();
|
||||
if (text != null && text.startsWith("#{"))
|
||||
{
|
||||
return evaluateForEachExpression(executionContext, text);
|
||||
}
|
||||
return (Collection<?>) FieldInstantiator.getValue(List.class, foreach);
|
||||
}
|
||||
|
||||
private Collection<?> evaluateForEachExpression(final ExecutionContext executionContext, String forEachText)
|
||||
{
|
||||
String expression = forEachText.substring(2, forEachText.length() -1);
|
||||
Object result = AlfrescoJavaScript.executeScript(executionContext, services, expression, null, null);
|
||||
if (result == null)
|
||||
{
|
||||
throw new WorkflowException("forEach expression '" + forEachText + "' evaluates to null");
|
||||
}
|
||||
// expression evaluates to string
|
||||
if (result instanceof String)
|
||||
{
|
||||
return buildStrings((String)result);
|
||||
}
|
||||
// expression evaluates to Node array
|
||||
else if (result instanceof Serializable[])
|
||||
{
|
||||
return buildJbpmNodes((Serializable[]) result);
|
||||
}
|
||||
// expression evaluates to collection
|
||||
else if (result instanceof Collection<?>)
|
||||
{
|
||||
return (Collection<?>)result;
|
||||
}
|
||||
else return null;
|
||||
}
|
||||
|
||||
private List<?> buildStrings(String result)
|
||||
{
|
||||
String[] results = result.trim().split(",");
|
||||
return Arrays.asList(results);
|
||||
}
|
||||
|
||||
private List<?> buildJbpmNodes(Serializable[] nodes)
|
||||
{
|
||||
List<JBPMNode> jbpmNodes = new ArrayList<JBPMNode>(nodes.length);
|
||||
for (Serializable node : nodes)
|
||||
{
|
||||
if (node instanceof NodeRef)
|
||||
{
|
||||
JBPMNode jbpmNode = new JBPMNode((NodeRef)node, services);
|
||||
jbpmNodes.add(jbpmNode);
|
||||
}
|
||||
}
|
||||
return jbpmNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a token name
|
||||
*
|
||||
* @param parent Token
|
||||
* @param transitionName String
|
||||
* @param loopIndex int
|
||||
* @return String
|
||||
*/
|
||||
protected String getTokenName(Token parent, String transitionName, int loopIndex)
|
||||
{
|
||||
String suffix = "." + loopIndex;
|
||||
if (transitionName == null || transitionName.isEmpty())
|
||||
{
|
||||
// No transition name
|
||||
int size = (parent.getChildren() != null) ? parent.getChildren().size() + 1 : 1;
|
||||
return buildTokenName("FOREACHFORK", suffix, size);
|
||||
}
|
||||
return findFirstAvailableTokenName(parent, transitionName, suffix);
|
||||
}
|
||||
|
||||
private String findFirstAvailableTokenName(Token parent, String transitionName, String suffix)
|
||||
{
|
||||
int i = 1;
|
||||
while (true)
|
||||
{
|
||||
String tokenName = buildTokenName(transitionName, suffix, i);
|
||||
if(!parent.hasChild(tokenName))
|
||||
{
|
||||
return tokenName;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
private String buildTokenName(String prefix, String suffix, int count)
|
||||
{
|
||||
String countStr = count<2 ? "": Integer.toString(count);
|
||||
return prefix + countStr + suffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list of objects to be iterated over.
|
||||
* @param foreach the list of objects to set
|
||||
*/
|
||||
public void setForeach(Element foreach)
|
||||
{
|
||||
this.foreach = foreach;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name of the variable to which the eleements of <code>foreach</code> are assigned.
|
||||
* @param var the variable name to set
|
||||
*/
|
||||
public void setVar(String var)
|
||||
{
|
||||
this.var = var;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fork Transition
|
||||
*/
|
||||
private class ForkedTransition
|
||||
{
|
||||
private ExecutionContext executionContext;
|
||||
private Transition transition;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,61 +1,61 @@
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.jbpm.graph.def.ProcessDefinition;
|
||||
import org.jbpm.jpdl.JpdlException;
|
||||
import org.jbpm.jpdl.par.ProcessArchive;
|
||||
import org.jbpm.jpdl.par.ProcessArchiveParser;
|
||||
import org.jbpm.jpdl.xml.JpdlXmlReader;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
|
||||
/**
|
||||
* Alfresco specific process archive parser to allow for extensions
|
||||
* to jPDL.
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class JBPMJpdlArchiveParser implements ProcessArchiveParser
|
||||
{
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.jbpm.jpdl.par.ProcessArchiveParser#readFromArchive(org.jbpm.jpdl.par.ProcessArchive, org.jbpm.graph.def.ProcessDefinition)
|
||||
*/
|
||||
public ProcessDefinition readFromArchive(ProcessArchive processArchive, ProcessDefinition processDefinition)
|
||||
throws JpdlException
|
||||
{
|
||||
// NOTE: This method implementation is a copy from the JpdlXmlReader class
|
||||
// with the difference of constructing an AlfrescoCreateTimerAction.
|
||||
// It may need to be updated whenever a jbpm library upgrade is performed.
|
||||
|
||||
try
|
||||
{
|
||||
byte[] processBytes = processArchive.getEntry("processdefinition.xml");
|
||||
if (processBytes == null)
|
||||
{
|
||||
throw new JpdlException("no processdefinition.xml inside process archive");
|
||||
}
|
||||
|
||||
// creating the JpdlXmlReader
|
||||
InputStream processInputStream = new ByteArrayInputStream(processBytes);
|
||||
InputSource processInputSource = new InputSource(processInputStream);
|
||||
JpdlXmlReader jpdlXmlReader = new JBPMJpdlXmlReader(processInputSource, processArchive);
|
||||
processDefinition = jpdlXmlReader.readProcessDefinition();
|
||||
|
||||
// close all the streams
|
||||
jpdlXmlReader.close();
|
||||
processInputStream.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new JpdlException("io problem while reading processdefinition.xml: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
return processDefinition;
|
||||
}
|
||||
}
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.jbpm.graph.def.ProcessDefinition;
|
||||
import org.jbpm.jpdl.JpdlException;
|
||||
import org.jbpm.jpdl.par.ProcessArchive;
|
||||
import org.jbpm.jpdl.par.ProcessArchiveParser;
|
||||
import org.jbpm.jpdl.xml.JpdlXmlReader;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
|
||||
/**
|
||||
* Alfresco specific process archive parser to allow for extensions
|
||||
* to jPDL.
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class JBPMJpdlArchiveParser implements ProcessArchiveParser
|
||||
{
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.jbpm.jpdl.par.ProcessArchiveParser#readFromArchive(org.jbpm.jpdl.par.ProcessArchive, org.jbpm.graph.def.ProcessDefinition)
|
||||
*/
|
||||
public ProcessDefinition readFromArchive(ProcessArchive processArchive, ProcessDefinition processDefinition)
|
||||
throws JpdlException
|
||||
{
|
||||
// NOTE: This method implementation is a copy from the JpdlXmlReader class
|
||||
// with the difference of constructing an AlfrescoCreateTimerAction.
|
||||
// It may need to be updated whenever a jbpm library upgrade is performed.
|
||||
|
||||
try
|
||||
{
|
||||
byte[] processBytes = processArchive.getEntry("processdefinition.xml");
|
||||
if (processBytes == null)
|
||||
{
|
||||
throw new JpdlException("no processdefinition.xml inside process archive");
|
||||
}
|
||||
|
||||
// creating the JpdlXmlReader
|
||||
InputStream processInputStream = new ByteArrayInputStream(processBytes);
|
||||
InputSource processInputSource = new InputSource(processInputStream);
|
||||
JpdlXmlReader jpdlXmlReader = new JBPMJpdlXmlReader(processInputSource, processArchive);
|
||||
processDefinition = jpdlXmlReader.readProcessDefinition();
|
||||
|
||||
// close all the streams
|
||||
jpdlXmlReader.close();
|
||||
processInputStream.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new JpdlException("io problem while reading processdefinition.xml: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
return processDefinition;
|
||||
}
|
||||
}
|
||||
|
@@ -1,155 +1,155 @@
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.dom4j.Element;
|
||||
import org.jbpm.graph.def.Action;
|
||||
import org.jbpm.graph.def.Event;
|
||||
import org.jbpm.graph.def.Node;
|
||||
import org.jbpm.instantiation.Delegation;
|
||||
import org.jbpm.jpdl.xml.JpdlXmlReader;
|
||||
import org.jbpm.jpdl.xml.Problem;
|
||||
import org.jbpm.jpdl.xml.ProblemListener;
|
||||
import org.jbpm.scheduler.def.CancelTimerAction;
|
||||
import org.jbpm.scheduler.def.CreateTimerAction;
|
||||
import org.jbpm.taskmgmt.def.Task;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
/**
|
||||
* Extended JpdlXmlReader with access to problems encountered during compile.
|
||||
*
|
||||
* Provides extension to Timers (to allow for absolute date).
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class JBPMJpdlXmlReader extends JpdlXmlReader
|
||||
{
|
||||
private static final long serialVersionUID = -753730152120696221L;
|
||||
|
||||
/*
|
||||
* Construct
|
||||
*/
|
||||
public JBPMJpdlXmlReader(InputStream inputStream)
|
||||
{
|
||||
super(new InputSource(inputStream));
|
||||
}
|
||||
|
||||
/*
|
||||
* Construct
|
||||
*/
|
||||
public JBPMJpdlXmlReader(InputSource inputSource, ProblemListener problemListener)
|
||||
{
|
||||
super(inputSource, problemListener);
|
||||
}
|
||||
|
||||
/*
|
||||
* Construct
|
||||
*/
|
||||
public JBPMJpdlXmlReader(InputSource inputSource)
|
||||
{
|
||||
super(inputSource);
|
||||
}
|
||||
|
||||
/*
|
||||
* Construct
|
||||
*/
|
||||
public JBPMJpdlXmlReader(Reader reader)
|
||||
{
|
||||
super(reader);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void readNodeTimer(Element timerElement, Node node)
|
||||
{
|
||||
// NOTE: This method implementation is a copy from the JpdlXmlReader class
|
||||
// with the difference of constructing an AlfrescoCreateTimerAction.
|
||||
// It may need to be updated whenever a jbpm library upgrade is performed.
|
||||
|
||||
String name = timerElement.attributeValue("name", node.getName());
|
||||
|
||||
CreateTimerAction createTimerAction = new AlfrescoCreateTimerAction();
|
||||
createTimerAction.read(timerElement, this);
|
||||
createTimerAction.setTimerName(name);
|
||||
createTimerAction.setTimerAction(readSingleAction(timerElement));
|
||||
addAction(node, Event.EVENTTYPE_NODE_ENTER, createTimerAction);
|
||||
|
||||
CancelTimerAction cancelTimerAction = new CancelTimerAction();
|
||||
cancelTimerAction.setTimerName(name);
|
||||
addAction(node, Event.EVENTTYPE_NODE_LEAVE, cancelTimerAction);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void readTaskTimer(Element timerElement, Task task)
|
||||
{
|
||||
// NOTE: This method implementation is a copy from the JpdlXmlReader class
|
||||
// with the difference of constructing an AlfrescoCreateTimerAction.
|
||||
// It may need to be updated whenever a jbpm library upgrade is performed.
|
||||
|
||||
String name = timerElement.attributeValue("name", task.getName());
|
||||
if (name == null)
|
||||
name = "timer-for-task-" + task.getId();
|
||||
|
||||
CreateTimerAction createTimerAction = new AlfrescoCreateTimerAction();
|
||||
createTimerAction.read(timerElement, this);
|
||||
createTimerAction.setTimerName(name);
|
||||
Action action = null;
|
||||
if ("timer".equals(timerElement.getName()))
|
||||
{
|
||||
action = readSingleAction(timerElement);
|
||||
}
|
||||
else
|
||||
{
|
||||
Delegation delegation = createMailDelegation("task-reminder", null, null, null, null);
|
||||
action = new Action(delegation);
|
||||
}
|
||||
createTimerAction.setTimerAction(action);
|
||||
addAction(task, Event.EVENTTYPE_TASK_CREATE, createTimerAction);
|
||||
|
||||
// read the cancel-event types
|
||||
Collection<String> cancelEventTypes = new ArrayList<String>();
|
||||
|
||||
String cancelEventTypeText = timerElement.attributeValue("cancel-event");
|
||||
if (cancelEventTypeText != null)
|
||||
{
|
||||
// cancel-event is a comma separated list of events
|
||||
StringTokenizer tokenizer = new StringTokenizer(cancelEventTypeText, ",");
|
||||
while (tokenizer.hasMoreTokens())
|
||||
{
|
||||
cancelEventTypes.add(tokenizer.nextToken().trim());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// set the default
|
||||
cancelEventTypes.add(Event.EVENTTYPE_TASK_END);
|
||||
}
|
||||
for (String cancelEventType : cancelEventTypes)
|
||||
{
|
||||
CancelTimerAction cancelTimerAction = new CancelTimerAction();
|
||||
cancelTimerAction.setTimerName(name);
|
||||
addAction(task, cancelEventType, cancelTimerAction);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the problems
|
||||
*
|
||||
* @return problems
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Problem> getProblems()
|
||||
{
|
||||
return problems;
|
||||
}
|
||||
}
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.dom4j.Element;
|
||||
import org.jbpm.graph.def.Action;
|
||||
import org.jbpm.graph.def.Event;
|
||||
import org.jbpm.graph.def.Node;
|
||||
import org.jbpm.instantiation.Delegation;
|
||||
import org.jbpm.jpdl.xml.JpdlXmlReader;
|
||||
import org.jbpm.jpdl.xml.Problem;
|
||||
import org.jbpm.jpdl.xml.ProblemListener;
|
||||
import org.jbpm.scheduler.def.CancelTimerAction;
|
||||
import org.jbpm.scheduler.def.CreateTimerAction;
|
||||
import org.jbpm.taskmgmt.def.Task;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
/**
|
||||
* Extended JpdlXmlReader with access to problems encountered during compile.
|
||||
*
|
||||
* Provides extension to Timers (to allow for absolute date).
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class JBPMJpdlXmlReader extends JpdlXmlReader
|
||||
{
|
||||
private static final long serialVersionUID = -753730152120696221L;
|
||||
|
||||
/*
|
||||
* Construct
|
||||
*/
|
||||
public JBPMJpdlXmlReader(InputStream inputStream)
|
||||
{
|
||||
super(new InputSource(inputStream));
|
||||
}
|
||||
|
||||
/*
|
||||
* Construct
|
||||
*/
|
||||
public JBPMJpdlXmlReader(InputSource inputSource, ProblemListener problemListener)
|
||||
{
|
||||
super(inputSource, problemListener);
|
||||
}
|
||||
|
||||
/*
|
||||
* Construct
|
||||
*/
|
||||
public JBPMJpdlXmlReader(InputSource inputSource)
|
||||
{
|
||||
super(inputSource);
|
||||
}
|
||||
|
||||
/*
|
||||
* Construct
|
||||
*/
|
||||
public JBPMJpdlXmlReader(Reader reader)
|
||||
{
|
||||
super(reader);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void readNodeTimer(Element timerElement, Node node)
|
||||
{
|
||||
// NOTE: This method implementation is a copy from the JpdlXmlReader class
|
||||
// with the difference of constructing an AlfrescoCreateTimerAction.
|
||||
// It may need to be updated whenever a jbpm library upgrade is performed.
|
||||
|
||||
String name = timerElement.attributeValue("name", node.getName());
|
||||
|
||||
CreateTimerAction createTimerAction = new AlfrescoCreateTimerAction();
|
||||
createTimerAction.read(timerElement, this);
|
||||
createTimerAction.setTimerName(name);
|
||||
createTimerAction.setTimerAction(readSingleAction(timerElement));
|
||||
addAction(node, Event.EVENTTYPE_NODE_ENTER, createTimerAction);
|
||||
|
||||
CancelTimerAction cancelTimerAction = new CancelTimerAction();
|
||||
cancelTimerAction.setTimerName(name);
|
||||
addAction(node, Event.EVENTTYPE_NODE_LEAVE, cancelTimerAction);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void readTaskTimer(Element timerElement, Task task)
|
||||
{
|
||||
// NOTE: This method implementation is a copy from the JpdlXmlReader class
|
||||
// with the difference of constructing an AlfrescoCreateTimerAction.
|
||||
// It may need to be updated whenever a jbpm library upgrade is performed.
|
||||
|
||||
String name = timerElement.attributeValue("name", task.getName());
|
||||
if (name == null)
|
||||
name = "timer-for-task-" + task.getId();
|
||||
|
||||
CreateTimerAction createTimerAction = new AlfrescoCreateTimerAction();
|
||||
createTimerAction.read(timerElement, this);
|
||||
createTimerAction.setTimerName(name);
|
||||
Action action = null;
|
||||
if ("timer".equals(timerElement.getName()))
|
||||
{
|
||||
action = readSingleAction(timerElement);
|
||||
}
|
||||
else
|
||||
{
|
||||
Delegation delegation = createMailDelegation("task-reminder", null, null, null, null);
|
||||
action = new Action(delegation);
|
||||
}
|
||||
createTimerAction.setTimerAction(action);
|
||||
addAction(task, Event.EVENTTYPE_TASK_CREATE, createTimerAction);
|
||||
|
||||
// read the cancel-event types
|
||||
Collection<String> cancelEventTypes = new ArrayList<String>();
|
||||
|
||||
String cancelEventTypeText = timerElement.attributeValue("cancel-event");
|
||||
if (cancelEventTypeText != null)
|
||||
{
|
||||
// cancel-event is a comma separated list of events
|
||||
StringTokenizer tokenizer = new StringTokenizer(cancelEventTypeText, ",");
|
||||
while (tokenizer.hasMoreTokens())
|
||||
{
|
||||
cancelEventTypes.add(tokenizer.nextToken().trim());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// set the default
|
||||
cancelEventTypes.add(Event.EVENTTYPE_TASK_END);
|
||||
}
|
||||
for (String cancelEventType : cancelEventTypes)
|
||||
{
|
||||
CancelTimerAction cancelTimerAction = new CancelTimerAction();
|
||||
cancelTimerAction.setTimerName(name);
|
||||
addAction(task, cancelEventType, cancelTimerAction);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the problems
|
||||
*
|
||||
* @return problems
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Problem> getProblems()
|
||||
{
|
||||
return problems;
|
||||
}
|
||||
}
|
||||
|
@@ -1,82 +1,82 @@
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import org.alfresco.repo.jscript.ScriptNode;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.Scriptable;
|
||||
|
||||
|
||||
/**
|
||||
* Scriptable Node suitable for JBPM Beanshell access
|
||||
*
|
||||
* TODO: This implementation derives from the JavaScript Alfresco Node. At
|
||||
* some point we should look to having a script-independent node with various
|
||||
* script-specific sub-types (and value conversions).
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class JBPMNode extends ScriptNode
|
||||
{
|
||||
private static final long serialVersionUID = -826970280203254365L;
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*
|
||||
* @param nodeRef node reference
|
||||
* @param services services
|
||||
*/
|
||||
public JBPMNode(NodeRef nodeRef, ServiceRegistry services)
|
||||
{
|
||||
super(nodeRef, services, NullScope.instance());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected NodeValueConverter createValueConverter()
|
||||
{
|
||||
return new JBPMNodeConverter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Value converter for beanshell.
|
||||
*/
|
||||
private class JBPMNodeConverter extends NodeValueConverter
|
||||
{
|
||||
@Override
|
||||
public Serializable convertValueForRepo(Serializable value)
|
||||
{
|
||||
if (value instanceof Date)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return super.convertValueForRepo(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable convertValueForScript(ServiceRegistry serviceRegistry, Scriptable theScope, QName qname, Serializable value)
|
||||
{
|
||||
if (value instanceof NodeRef)
|
||||
{
|
||||
return new JBPMNode(((NodeRef)value), serviceRegistry);
|
||||
}
|
||||
else if (value instanceof Date)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return super.convertValueForScript(serviceRegistry, theScope, qname, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
import org.alfresco.repo.jscript.ScriptNode;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.Scriptable;
|
||||
|
||||
|
||||
/**
|
||||
* Scriptable Node suitable for JBPM Beanshell access
|
||||
*
|
||||
* TODO: This implementation derives from the JavaScript Alfresco Node. At
|
||||
* some point we should look to having a script-independent node with various
|
||||
* script-specific sub-types (and value conversions).
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class JBPMNode extends ScriptNode
|
||||
{
|
||||
private static final long serialVersionUID = -826970280203254365L;
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*
|
||||
* @param nodeRef node reference
|
||||
* @param services services
|
||||
*/
|
||||
public JBPMNode(NodeRef nodeRef, ServiceRegistry services)
|
||||
{
|
||||
super(nodeRef, services, NullScope.instance());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected NodeValueConverter createValueConverter()
|
||||
{
|
||||
return new JBPMNodeConverter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Value converter for beanshell.
|
||||
*/
|
||||
private class JBPMNodeConverter extends NodeValueConverter
|
||||
{
|
||||
@Override
|
||||
public Serializable convertValueForRepo(Serializable value)
|
||||
{
|
||||
if (value instanceof Date)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return super.convertValueForRepo(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable convertValueForScript(ServiceRegistry serviceRegistry, Scriptable theScope, QName qname, Serializable value)
|
||||
{
|
||||
if (value instanceof NodeRef)
|
||||
{
|
||||
return new JBPMNode(((NodeRef)value), serviceRegistry);
|
||||
}
|
||||
else if (value instanceof Date)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
return super.convertValueForScript(serviceRegistry, theScope, qname, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,15 +1,15 @@
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
||||
/**
|
||||
* List of jBPM Nodes
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class JBPMNodeList extends ArrayList<JBPMNode>
|
||||
{
|
||||
private static final long serialVersionUID = 1376915749912156471L;
|
||||
|
||||
}
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
||||
/**
|
||||
* List of jBPM Nodes
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class JBPMNodeList extends ArrayList<JBPMNode>
|
||||
{
|
||||
private static final long serialVersionUID = 1376915749912156471L;
|
||||
|
||||
}
|
||||
|
@@ -1,57 +1,57 @@
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import org.jbpm.job.executor.JobExecutor;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.extensions.surf.util.AbstractLifecycleBean;
|
||||
import org.springmodules.workflow.jbpm31.JbpmTemplate;
|
||||
|
||||
|
||||
/**
|
||||
* JBPM Scheduler
|
||||
*
|
||||
* Manages lifecycle of Jbpm Job Executor.
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class JBPMScheduler extends AbstractLifecycleBean
|
||||
{
|
||||
private JobExecutor executor = null;
|
||||
private JbpmTemplate jbpmTemplate;
|
||||
private boolean JbpmEngineEnabled = false;
|
||||
|
||||
/**
|
||||
* @param jbpmTemplate JbpmTemplate
|
||||
*/
|
||||
public void setJBPMTemplate(JbpmTemplate jbpmTemplate)
|
||||
{
|
||||
this.jbpmTemplate = jbpmTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param jbpmEngineEnabled whether or not the JBPM-Engine is enables. Please note that we are
|
||||
* not using the WorklfowAdminService since this is only initialized later in the sequence.
|
||||
*/
|
||||
public void setJBPMEngineEnabled(boolean jbpmEngineEnabled) {
|
||||
JbpmEngineEnabled = jbpmEngineEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBootstrap(ApplicationEvent event)
|
||||
{
|
||||
if(JbpmEngineEnabled)
|
||||
{
|
||||
executor = jbpmTemplate.getJbpmConfiguration().getJobExecutor();
|
||||
executor.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onShutdown(ApplicationEvent event)
|
||||
{
|
||||
if(JbpmEngineEnabled && executor.isStarted())
|
||||
{
|
||||
executor.stop();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import org.jbpm.job.executor.JobExecutor;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.extensions.surf.util.AbstractLifecycleBean;
|
||||
import org.springmodules.workflow.jbpm31.JbpmTemplate;
|
||||
|
||||
|
||||
/**
|
||||
* JBPM Scheduler
|
||||
*
|
||||
* Manages lifecycle of Jbpm Job Executor.
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class JBPMScheduler extends AbstractLifecycleBean
|
||||
{
|
||||
private JobExecutor executor = null;
|
||||
private JbpmTemplate jbpmTemplate;
|
||||
private boolean JbpmEngineEnabled = false;
|
||||
|
||||
/**
|
||||
* @param jbpmTemplate JbpmTemplate
|
||||
*/
|
||||
public void setJBPMTemplate(JbpmTemplate jbpmTemplate)
|
||||
{
|
||||
this.jbpmTemplate = jbpmTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param jbpmEngineEnabled whether or not the JBPM-Engine is enables. Please note that we are
|
||||
* not using the WorklfowAdminService since this is only initialized later in the sequence.
|
||||
*/
|
||||
public void setJBPMEngineEnabled(boolean jbpmEngineEnabled) {
|
||||
JbpmEngineEnabled = jbpmEngineEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBootstrap(ApplicationEvent event)
|
||||
{
|
||||
if(JbpmEngineEnabled)
|
||||
{
|
||||
executor = jbpmTemplate.getJbpmConfiguration().getJobExecutor();
|
||||
executor.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onShutdown(ApplicationEvent event)
|
||||
{
|
||||
if(JbpmEngineEnabled && executor.isStarted())
|
||||
{
|
||||
executor.stop();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,53 +1,53 @@
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import org.alfresco.repo.workflow.BPMEngineRegistry;
|
||||
import org.jbpm.graph.def.ActionHandler;
|
||||
import org.jbpm.graph.exe.ExecutionContext;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.access.BeanFactoryLocator;
|
||||
import org.springframework.beans.factory.access.BeanFactoryReference;
|
||||
import org.springmodules.workflow.jbpm31.JbpmFactoryLocator;
|
||||
|
||||
|
||||
/**
|
||||
* Abstract base implementation of a Jbpm Action Hander with access to
|
||||
* Alfresco Spring beans.
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public abstract class JBPMSpringActionHandler implements ActionHandler
|
||||
{
|
||||
private static final long serialVersionUID = 6848343645482681529L;
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*/
|
||||
protected JBPMSpringActionHandler()
|
||||
{
|
||||
// The following implementation is derived from Spring Modules v0.4
|
||||
BeanFactoryLocator factoryLocator = new JbpmFactoryLocator();
|
||||
BeanFactoryReference factory = factoryLocator.useBeanFactory(null);
|
||||
initialiseHandler(factory.getFactory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise Action Handler
|
||||
*
|
||||
* @param factory Spring bean factory for accessing Alfresco beans
|
||||
*/
|
||||
protected abstract void initialiseHandler(BeanFactory factory);
|
||||
|
||||
|
||||
/**
|
||||
* Gets the workflow instance id of the currently executing workflow
|
||||
*
|
||||
* @param context jBPM execution context
|
||||
* @return workflow instance id
|
||||
*/
|
||||
protected String getWorkflowInstanceId(ExecutionContext context)
|
||||
{
|
||||
String id = new Long(context.getProcessInstance().getId()).toString();
|
||||
return BPMEngineRegistry.createGlobalId(JBPMEngine.ENGINE_ID, id);
|
||||
}
|
||||
|
||||
}
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import org.alfresco.repo.workflow.BPMEngineRegistry;
|
||||
import org.jbpm.graph.def.ActionHandler;
|
||||
import org.jbpm.graph.exe.ExecutionContext;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.access.BeanFactoryLocator;
|
||||
import org.springframework.beans.factory.access.BeanFactoryReference;
|
||||
import org.springmodules.workflow.jbpm31.JbpmFactoryLocator;
|
||||
|
||||
|
||||
/**
|
||||
* Abstract base implementation of a Jbpm Action Hander with access to
|
||||
* Alfresco Spring beans.
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public abstract class JBPMSpringActionHandler implements ActionHandler
|
||||
{
|
||||
private static final long serialVersionUID = 6848343645482681529L;
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*/
|
||||
protected JBPMSpringActionHandler()
|
||||
{
|
||||
// The following implementation is derived from Spring Modules v0.4
|
||||
BeanFactoryLocator factoryLocator = new JbpmFactoryLocator();
|
||||
BeanFactoryReference factory = factoryLocator.useBeanFactory(null);
|
||||
initialiseHandler(factory.getFactory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise Action Handler
|
||||
*
|
||||
* @param factory Spring bean factory for accessing Alfresco beans
|
||||
*/
|
||||
protected abstract void initialiseHandler(BeanFactory factory);
|
||||
|
||||
|
||||
/**
|
||||
* Gets the workflow instance id of the currently executing workflow
|
||||
*
|
||||
* @param context jBPM execution context
|
||||
* @return workflow instance id
|
||||
*/
|
||||
protected String getWorkflowInstanceId(ExecutionContext context)
|
||||
{
|
||||
String id = new Long(context.getProcessInstance().getId()).toString();
|
||||
return BPMEngineRegistry.createGlobalId(JBPMEngine.ENGINE_ID, id);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,39 +1,39 @@
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import org.jbpm.taskmgmt.def.AssignmentHandler;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.access.BeanFactoryLocator;
|
||||
import org.springframework.beans.factory.access.BeanFactoryReference;
|
||||
import org.springmodules.workflow.jbpm31.JbpmFactoryLocator;
|
||||
|
||||
|
||||
/**
|
||||
* Abstract base implementation of a Jbpm Assignment Handler with access to
|
||||
* Alfresco Spring beans.
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public abstract class JBPMSpringAssignmentHandler implements AssignmentHandler
|
||||
{
|
||||
private static final long serialVersionUID = -2233750219905283562L;
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*/
|
||||
protected JBPMSpringAssignmentHandler()
|
||||
{
|
||||
// The following implementation is derived from Spring Modules v0.4
|
||||
BeanFactoryLocator factoryLocator = new JbpmFactoryLocator();
|
||||
BeanFactoryReference factory = factoryLocator.useBeanFactory(null);
|
||||
initialiseHandler(factory.getFactory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise Action Handler
|
||||
*
|
||||
* @param factory Spring bean factory for accessing Alfresco beans
|
||||
*/
|
||||
protected abstract void initialiseHandler(BeanFactory factory);
|
||||
|
||||
|
||||
}
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import org.jbpm.taskmgmt.def.AssignmentHandler;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.access.BeanFactoryLocator;
|
||||
import org.springframework.beans.factory.access.BeanFactoryReference;
|
||||
import org.springmodules.workflow.jbpm31.JbpmFactoryLocator;
|
||||
|
||||
|
||||
/**
|
||||
* Abstract base implementation of a Jbpm Assignment Handler with access to
|
||||
* Alfresco Spring beans.
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public abstract class JBPMSpringAssignmentHandler implements AssignmentHandler
|
||||
{
|
||||
private static final long serialVersionUID = -2233750219905283562L;
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*/
|
||||
protected JBPMSpringAssignmentHandler()
|
||||
{
|
||||
// The following implementation is derived from Spring Modules v0.4
|
||||
BeanFactoryLocator factoryLocator = new JbpmFactoryLocator();
|
||||
BeanFactoryReference factory = factoryLocator.useBeanFactory(null);
|
||||
initialiseHandler(factory.getFactory());
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise Action Handler
|
||||
*
|
||||
* @param factory Spring bean factory for accessing Alfresco beans
|
||||
*/
|
||||
protected abstract void initialiseHandler(BeanFactory factory);
|
||||
|
||||
|
||||
}
|
||||
|
@@ -1,47 +1,47 @@
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import org.alfresco.service.descriptor.DescriptorService;
|
||||
import org.jbpm.graph.exe.ExecutionContext;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Test Spring based Jbpm Action Handler
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class JBPMTestSpringActionHandler extends JBPMSpringActionHandler
|
||||
{
|
||||
private static final long serialVersionUID = -7659883022289711381L;
|
||||
|
||||
private DescriptorService descriptorService;
|
||||
private String value;
|
||||
|
||||
|
||||
/**
|
||||
* Setter accessible from jBPM jPDL
|
||||
* @param value String
|
||||
*/
|
||||
public void setValue(String value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.jbpm.graph.def.ActionHandler#execute(org.jbpm.graph.exe.ExecutionContext)
|
||||
*/
|
||||
public void execute(ExecutionContext arg0) throws Exception
|
||||
{
|
||||
String result = "Repo: " + descriptorService.getServerDescriptor().getVersion();
|
||||
result += ", Value: " + value + ", Node: " + arg0.getNode().getName() + ", Token: " + arg0.getToken().getFullName();
|
||||
arg0.getContextInstance().setVariable("jbpm.test.action.result", result);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialiseHandler(BeanFactory factory)
|
||||
{
|
||||
descriptorService = factory.getBean("DescriptorService", DescriptorService.class);
|
||||
}
|
||||
|
||||
}
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import org.alfresco.service.descriptor.DescriptorService;
|
||||
import org.jbpm.graph.exe.ExecutionContext;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Test Spring based Jbpm Action Handler
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class JBPMTestSpringActionHandler extends JBPMSpringActionHandler
|
||||
{
|
||||
private static final long serialVersionUID = -7659883022289711381L;
|
||||
|
||||
private DescriptorService descriptorService;
|
||||
private String value;
|
||||
|
||||
|
||||
/**
|
||||
* Setter accessible from jBPM jPDL
|
||||
* @param value String
|
||||
*/
|
||||
public void setValue(String value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.jbpm.graph.def.ActionHandler#execute(org.jbpm.graph.exe.ExecutionContext)
|
||||
*/
|
||||
public void execute(ExecutionContext arg0) throws Exception
|
||||
{
|
||||
String result = "Repo: " + descriptorService.getServerDescriptor().getVersion();
|
||||
result += ", Value: " + value + ", Node: " + arg0.getNode().getName() + ", Token: " + arg0.getToken().getFullName();
|
||||
arg0.getContextInstance().setVariable("jbpm.test.action.result", result);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialiseHandler(BeanFactory factory)
|
||||
{
|
||||
descriptorService = factory.getBean("DescriptorService", DescriptorService.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,211 +1,211 @@
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||
import org.alfresco.repo.transaction.TransactionListener;
|
||||
import org.alfresco.util.GUID;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jbpm.JbpmConfiguration;
|
||||
import org.jbpm.JbpmContext;
|
||||
import org.jbpm.graph.def.ProcessDefinition;
|
||||
import org.springmodules.workflow.jbpm31.JbpmTemplate;
|
||||
|
||||
|
||||
/**
|
||||
* JBPM Template that manages JBPM Context at the Alfresco Transaction level
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class JBPMTransactionTemplate extends JbpmTemplate
|
||||
implements TransactionListener
|
||||
{
|
||||
// Logging support
|
||||
private static Log logger = LogFactory.getLog("org.alfresco.repo.workflow");
|
||||
|
||||
/** Id used in equals and hash */
|
||||
private String id = GUID.generate();
|
||||
|
||||
// JBPM Template Keys
|
||||
private static final String JBPM_CONTEXT_KEY = JBPMTransactionTemplate.class.getName() + ".context";
|
||||
|
||||
/** Use local or transaction bound JBPM Context */
|
||||
private boolean localContext = true;
|
||||
|
||||
|
||||
/*
|
||||
* Constructor
|
||||
*/
|
||||
public JBPMTransactionTemplate()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructor
|
||||
*/
|
||||
public JBPMTransactionTemplate(JbpmConfiguration jbpmConfiguration, ProcessDefinition processDefinition)
|
||||
{
|
||||
super(jbpmConfiguration, processDefinition);
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructor
|
||||
*/
|
||||
public JBPMTransactionTemplate(JbpmConfiguration jbpmConfiguration)
|
||||
{
|
||||
super(jbpmConfiguration);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@SuppressWarnings("synthetic-access")
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
JBPMTransactionTemplate.super.afterPropertiesSet();
|
||||
}
|
||||
finally
|
||||
{
|
||||
localContext = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected JbpmContext getContext()
|
||||
{
|
||||
if (localContext)
|
||||
{
|
||||
return super.getContext();
|
||||
}
|
||||
else
|
||||
{
|
||||
JbpmContext context = (JbpmContext)AlfrescoTransactionSupport.getResource(JBPM_CONTEXT_KEY);
|
||||
if (context == null)
|
||||
{
|
||||
context = super.getContext();
|
||||
AlfrescoTransactionSupport.bindResource(JBPM_CONTEXT_KEY, context);
|
||||
AlfrescoTransactionSupport.bindListener(this);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Attached JBPM Context to transaction " + AlfrescoTransactionSupport.getTransactionId());
|
||||
}
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void releaseContext(JbpmContext jbpmContext)
|
||||
{
|
||||
if (localContext)
|
||||
{
|
||||
jbpmContext.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: Defer release to end of transaction
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void flush()
|
||||
{
|
||||
//NOOP
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void beforeCommit(boolean readOnly)
|
||||
{
|
||||
//NOOP
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void beforeCompletion()
|
||||
{
|
||||
//NOOP
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void afterCommit()
|
||||
{
|
||||
JbpmContext context = (JbpmContext)AlfrescoTransactionSupport.getResource(JBPM_CONTEXT_KEY);
|
||||
if (context != null)
|
||||
{
|
||||
super.releaseContext(context);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Detached (commit) JBPM Context from transaction " + AlfrescoTransactionSupport.getTransactionId());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void afterRollback()
|
||||
{
|
||||
JbpmContext context = (JbpmContext)AlfrescoTransactionSupport.getResource(JBPM_CONTEXT_KEY);
|
||||
if (context != null)
|
||||
{
|
||||
super.releaseContext(context);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Detached (rollback) JBPM Context from transaction " + AlfrescoTransactionSupport.getTransactionId());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return this.id.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (obj instanceof JBPMTransactionTemplate)
|
||||
{
|
||||
JBPMTransactionTemplate that = (JBPMTransactionTemplate) obj;
|
||||
return (this.id.equals(that.id));
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||
import org.alfresco.repo.transaction.TransactionListener;
|
||||
import org.alfresco.util.GUID;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.jbpm.JbpmConfiguration;
|
||||
import org.jbpm.JbpmContext;
|
||||
import org.jbpm.graph.def.ProcessDefinition;
|
||||
import org.springmodules.workflow.jbpm31.JbpmTemplate;
|
||||
|
||||
|
||||
/**
|
||||
* JBPM Template that manages JBPM Context at the Alfresco Transaction level
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class JBPMTransactionTemplate extends JbpmTemplate
|
||||
implements TransactionListener
|
||||
{
|
||||
// Logging support
|
||||
private static Log logger = LogFactory.getLog("org.alfresco.repo.workflow");
|
||||
|
||||
/** Id used in equals and hash */
|
||||
private String id = GUID.generate();
|
||||
|
||||
// JBPM Template Keys
|
||||
private static final String JBPM_CONTEXT_KEY = JBPMTransactionTemplate.class.getName() + ".context";
|
||||
|
||||
/** Use local or transaction bound JBPM Context */
|
||||
private boolean localContext = true;
|
||||
|
||||
|
||||
/*
|
||||
* Constructor
|
||||
*/
|
||||
public JBPMTransactionTemplate()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructor
|
||||
*/
|
||||
public JBPMTransactionTemplate(JbpmConfiguration jbpmConfiguration, ProcessDefinition processDefinition)
|
||||
{
|
||||
super(jbpmConfiguration, processDefinition);
|
||||
}
|
||||
|
||||
/*
|
||||
* Constructor
|
||||
*/
|
||||
public JBPMTransactionTemplate(JbpmConfiguration jbpmConfiguration)
|
||||
{
|
||||
super(jbpmConfiguration);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@SuppressWarnings("synthetic-access")
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
JBPMTransactionTemplate.super.afterPropertiesSet();
|
||||
}
|
||||
finally
|
||||
{
|
||||
localContext = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected JbpmContext getContext()
|
||||
{
|
||||
if (localContext)
|
||||
{
|
||||
return super.getContext();
|
||||
}
|
||||
else
|
||||
{
|
||||
JbpmContext context = (JbpmContext)AlfrescoTransactionSupport.getResource(JBPM_CONTEXT_KEY);
|
||||
if (context == null)
|
||||
{
|
||||
context = super.getContext();
|
||||
AlfrescoTransactionSupport.bindResource(JBPM_CONTEXT_KEY, context);
|
||||
AlfrescoTransactionSupport.bindListener(this);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Attached JBPM Context to transaction " + AlfrescoTransactionSupport.getTransactionId());
|
||||
}
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected void releaseContext(JbpmContext jbpmContext)
|
||||
{
|
||||
if (localContext)
|
||||
{
|
||||
jbpmContext.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: Defer release to end of transaction
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void flush()
|
||||
{
|
||||
//NOOP
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void beforeCommit(boolean readOnly)
|
||||
{
|
||||
//NOOP
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void beforeCompletion()
|
||||
{
|
||||
//NOOP
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void afterCommit()
|
||||
{
|
||||
JbpmContext context = (JbpmContext)AlfrescoTransactionSupport.getResource(JBPM_CONTEXT_KEY);
|
||||
if (context != null)
|
||||
{
|
||||
super.releaseContext(context);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Detached (commit) JBPM Context from transaction " + AlfrescoTransactionSupport.getTransactionId());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void afterRollback()
|
||||
{
|
||||
JbpmContext context = (JbpmContext)AlfrescoTransactionSupport.getResource(JBPM_CONTEXT_KEY);
|
||||
if (context != null)
|
||||
{
|
||||
super.releaseContext(context);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Detached (rollback) JBPM Context from transaction " + AlfrescoTransactionSupport.getTransactionId());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return this.id.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (obj instanceof JBPMTransactionTemplate)
|
||||
{
|
||||
JBPMTransactionTemplate that = (JBPMTransactionTemplate) obj;
|
||||
return (this.id.equals(that.id));
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,50 +1,50 @@
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import org.dom4j.Element;
|
||||
import org.jbpm.graph.def.Action;
|
||||
import org.jbpm.graph.def.Event;
|
||||
import org.jbpm.instantiation.Delegation;
|
||||
import org.jbpm.jpdl.xml.JpdlXmlReader;
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of Join which ends child tokens / tasks for nOutM cases.
|
||||
*
|
||||
* @author davidc
|
||||
*
|
||||
*/
|
||||
public class Join extends org.jbpm.graph.node.Join
|
||||
{
|
||||
private static final long serialVersionUID = 6417483503439714897L;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public Join()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public Join(String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void read(Element element, JpdlXmlReader jpdlReader)
|
||||
{
|
||||
// Add "on node leave" event handler which ends child tokens / tasks
|
||||
Delegation delegation = new Delegation(JoinEndForkedTokens.class.getName());
|
||||
Action theAction = new Action(delegation);
|
||||
Event event = new Event(Event.EVENTTYPE_NODE_LEAVE);
|
||||
event.addAction(theAction);
|
||||
addEvent(event);
|
||||
}
|
||||
|
||||
}
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import org.dom4j.Element;
|
||||
import org.jbpm.graph.def.Action;
|
||||
import org.jbpm.graph.def.Event;
|
||||
import org.jbpm.instantiation.Delegation;
|
||||
import org.jbpm.jpdl.xml.JpdlXmlReader;
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of Join which ends child tokens / tasks for nOutM cases.
|
||||
*
|
||||
* @author davidc
|
||||
*
|
||||
*/
|
||||
public class Join extends org.jbpm.graph.node.Join
|
||||
{
|
||||
private static final long serialVersionUID = 6417483503439714897L;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public Join()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public Join(String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void read(Element element, JpdlXmlReader jpdlReader)
|
||||
{
|
||||
// Add "on node leave" event handler which ends child tokens / tasks
|
||||
Delegation delegation = new Delegation(JoinEndForkedTokens.class.getName());
|
||||
Action theAction = new Action(delegation);
|
||||
Event event = new Event(Event.EVENTTYPE_NODE_LEAVE);
|
||||
event.addAction(theAction);
|
||||
addEvent(event);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,94 +1,94 @@
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jbpm.graph.def.ActionHandler;
|
||||
import org.jbpm.graph.exe.ExecutionContext;
|
||||
import org.jbpm.graph.exe.Token;
|
||||
import org.jbpm.taskmgmt.exe.TaskInstance;
|
||||
import org.jbpm.taskmgmt.exe.TaskMgmtInstance;
|
||||
|
||||
/**
|
||||
* Action Handler for ending child tokens / tasks
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class JoinEndForkedTokens implements ActionHandler
|
||||
{
|
||||
private static final long serialVersionUID = 8679390550752208189L;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public JoinEndForkedTokens()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void execute(ExecutionContext executionContext)
|
||||
{
|
||||
Token token = executionContext.getToken();
|
||||
Map<?, ?> childTokens = token.getActiveChildren();
|
||||
for (Object childToken : childTokens.values())
|
||||
{
|
||||
cancelToken(executionContext, (Token)childToken);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel token
|
||||
*
|
||||
* @param executionContext ExecutionContext
|
||||
* @param token Token
|
||||
*/
|
||||
protected void cancelToken(ExecutionContext executionContext, Token token)
|
||||
{
|
||||
// visit child tokens
|
||||
Map<?, ?> childTokens = token.getActiveChildren();
|
||||
for (Object childToken : childTokens.values())
|
||||
{
|
||||
cancelToken(executionContext, (Token)childToken);
|
||||
}
|
||||
|
||||
// end token
|
||||
if (!token.hasEnded())
|
||||
{
|
||||
token.end(false);
|
||||
}
|
||||
|
||||
// end any associated tasks
|
||||
cancelTokenTasks(executionContext, token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel tasks associated with a token
|
||||
*
|
||||
* @param executionContext ExecutionContext
|
||||
* @param token Token
|
||||
*/
|
||||
protected void cancelTokenTasks(ExecutionContext executionContext, Token token)
|
||||
{
|
||||
TaskMgmtInstance tms = executionContext.getTaskMgmtInstance();
|
||||
Collection<TaskInstance> tasks = tms.getUnfinishedTasks(token);
|
||||
for (Object task : tasks)
|
||||
{
|
||||
TaskInstance taskInstance = (TaskInstance)task;
|
||||
if (taskInstance.isBlocking())
|
||||
{
|
||||
taskInstance.setBlocking(false);
|
||||
}
|
||||
if (taskInstance.isSignalling())
|
||||
{
|
||||
taskInstance.setSignalling(false);
|
||||
}
|
||||
if (!taskInstance.hasEnded())
|
||||
{
|
||||
taskInstance.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jbpm.graph.def.ActionHandler;
|
||||
import org.jbpm.graph.exe.ExecutionContext;
|
||||
import org.jbpm.graph.exe.Token;
|
||||
import org.jbpm.taskmgmt.exe.TaskInstance;
|
||||
import org.jbpm.taskmgmt.exe.TaskMgmtInstance;
|
||||
|
||||
/**
|
||||
* Action Handler for ending child tokens / tasks
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class JoinEndForkedTokens implements ActionHandler
|
||||
{
|
||||
private static final long serialVersionUID = 8679390550752208189L;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public JoinEndForkedTokens()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void execute(ExecutionContext executionContext)
|
||||
{
|
||||
Token token = executionContext.getToken();
|
||||
Map<?, ?> childTokens = token.getActiveChildren();
|
||||
for (Object childToken : childTokens.values())
|
||||
{
|
||||
cancelToken(executionContext, (Token)childToken);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel token
|
||||
*
|
||||
* @param executionContext ExecutionContext
|
||||
* @param token Token
|
||||
*/
|
||||
protected void cancelToken(ExecutionContext executionContext, Token token)
|
||||
{
|
||||
// visit child tokens
|
||||
Map<?, ?> childTokens = token.getActiveChildren();
|
||||
for (Object childToken : childTokens.values())
|
||||
{
|
||||
cancelToken(executionContext, (Token)childToken);
|
||||
}
|
||||
|
||||
// end token
|
||||
if (!token.hasEnded())
|
||||
{
|
||||
token.end(false);
|
||||
}
|
||||
|
||||
// end any associated tasks
|
||||
cancelTokenTasks(executionContext, token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel tasks associated with a token
|
||||
*
|
||||
* @param executionContext ExecutionContext
|
||||
* @param token Token
|
||||
*/
|
||||
protected void cancelTokenTasks(ExecutionContext executionContext, Token token)
|
||||
{
|
||||
TaskMgmtInstance tms = executionContext.getTaskMgmtInstance();
|
||||
Collection<TaskInstance> tasks = tms.getUnfinishedTasks(token);
|
||||
for (Object task : tasks)
|
||||
{
|
||||
TaskInstance taskInstance = (TaskInstance)task;
|
||||
if (taskInstance.isBlocking())
|
||||
{
|
||||
taskInstance.setBlocking(false);
|
||||
}
|
||||
if (taskInstance.isSignalling())
|
||||
{
|
||||
taskInstance.setSignalling(false);
|
||||
}
|
||||
if (!taskInstance.hasEnded())
|
||||
{
|
||||
taskInstance.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,63 +1,63 @@
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.jbpm.context.exe.Converter;
|
||||
import org.springframework.beans.factory.access.BeanFactoryLocator;
|
||||
import org.springframework.beans.factory.access.BeanFactoryReference;
|
||||
import org.springmodules.workflow.jbpm31.JbpmFactoryLocator;
|
||||
|
||||
|
||||
/**
|
||||
* jBPM Converter for transforming Alfresco Node to string and back
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class NodeConverter implements Converter
|
||||
{
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static BeanFactoryLocator jbpmFactoryLocator = new JbpmFactoryLocator();
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.jbpm.context.exe.Converter#supports(java.lang.Object)
|
||||
*/
|
||||
public boolean supports(Object value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return (value.getClass() == JBPMNode.class);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.jbpm.context.exe.Converter#convert(java.lang.Object)
|
||||
*/
|
||||
public Object convert(Object o)
|
||||
{
|
||||
Object converted = null;
|
||||
if (o != null)
|
||||
{
|
||||
converted = ((JBPMNode)o).getNodeRef().toString();
|
||||
}
|
||||
return converted;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.jbpm.context.exe.Converter#revert(java.lang.Object)
|
||||
*/
|
||||
public Object revert(Object o)
|
||||
{
|
||||
Object reverted = null;
|
||||
if (o != null)
|
||||
{
|
||||
BeanFactoryReference factory = jbpmFactoryLocator.useBeanFactory(null);
|
||||
ServiceRegistry serviceRegistry = (ServiceRegistry)factory.getFactory().getBean(ServiceRegistry.SERVICE_REGISTRY);
|
||||
reverted = new JBPMNode(new NodeRef((String)o), serviceRegistry);
|
||||
}
|
||||
return reverted;
|
||||
}
|
||||
|
||||
}
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.jbpm.context.exe.Converter;
|
||||
import org.springframework.beans.factory.access.BeanFactoryLocator;
|
||||
import org.springframework.beans.factory.access.BeanFactoryReference;
|
||||
import org.springmodules.workflow.jbpm31.JbpmFactoryLocator;
|
||||
|
||||
|
||||
/**
|
||||
* jBPM Converter for transforming Alfresco Node to string and back
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class NodeConverter implements Converter
|
||||
{
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static BeanFactoryLocator jbpmFactoryLocator = new JbpmFactoryLocator();
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.jbpm.context.exe.Converter#supports(java.lang.Object)
|
||||
*/
|
||||
public boolean supports(Object value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return (value.getClass() == JBPMNode.class);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.jbpm.context.exe.Converter#convert(java.lang.Object)
|
||||
*/
|
||||
public Object convert(Object o)
|
||||
{
|
||||
Object converted = null;
|
||||
if (o != null)
|
||||
{
|
||||
converted = ((JBPMNode)o).getNodeRef().toString();
|
||||
}
|
||||
return converted;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.jbpm.context.exe.Converter#revert(java.lang.Object)
|
||||
*/
|
||||
public Object revert(Object o)
|
||||
{
|
||||
Object reverted = null;
|
||||
if (o != null)
|
||||
{
|
||||
BeanFactoryReference factory = jbpmFactoryLocator.useBeanFactory(null);
|
||||
ServiceRegistry serviceRegistry = (ServiceRegistry)factory.getFactory().getBean(ServiceRegistry.SERVICE_REGISTRY);
|
||||
reverted = new JBPMNode(new NodeRef((String)o), serviceRegistry);
|
||||
}
|
||||
return reverted;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,110 +1,110 @@
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.jbpm.context.exe.converter.SerializableToByteArrayConverter;
|
||||
import org.jbpm.graph.def.ProcessDefinition;
|
||||
import org.springframework.beans.factory.access.BeanFactoryLocator;
|
||||
import org.springframework.beans.factory.access.BeanFactoryReference;
|
||||
import org.springmodules.workflow.jbpm31.JbpmFactoryLocator;
|
||||
|
||||
|
||||
/**
|
||||
* jBPM Converter for transforming Alfresco Node to string and back
|
||||
*
|
||||
* @author davidc
|
||||
* @author Nick Smith
|
||||
*/
|
||||
public class NodeListConverter extends SerializableToByteArrayConverter
|
||||
{
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static BeanFactoryLocator jbpmFactoryLocator = new JbpmFactoryLocator();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Object value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return (value.getClass() == JBPMNodeList.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Object convert(Object o)
|
||||
{
|
||||
Object converted = null;
|
||||
if (o != null)
|
||||
{
|
||||
JBPMNodeList nodes = (JBPMNodeList)o;
|
||||
List<NodeRef> values = new ArrayList<NodeRef>(nodes.size());
|
||||
for (JBPMNode node : nodes)
|
||||
{
|
||||
values.add(node.getNodeRef());
|
||||
}
|
||||
converted = super.convert(values);
|
||||
}
|
||||
return converted;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Object revert(Object o)
|
||||
{
|
||||
Object reverted = null;
|
||||
if (o != null)
|
||||
{
|
||||
Object nodeRefs = super.revert(o);
|
||||
reverted = revertNodes(nodeRefs);
|
||||
}
|
||||
return reverted;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Object revert(Object o, ProcessDefinition processDefinition)
|
||||
{
|
||||
Object reverted = null;
|
||||
if (o != null)
|
||||
{
|
||||
Object nodeRefs = super.revert(o, processDefinition);
|
||||
reverted = revertNodes(nodeRefs);
|
||||
}
|
||||
return reverted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value Object
|
||||
* @return JBPMNodeList
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private JBPMNodeList revertNodes(Object value)
|
||||
{
|
||||
BeanFactoryReference factory = jbpmFactoryLocator.useBeanFactory(null);
|
||||
ServiceRegistry serviceRegistry = (ServiceRegistry)factory.getFactory().getBean(ServiceRegistry.SERVICE_REGISTRY);
|
||||
|
||||
JBPMNodeList nodes = new JBPMNodeList();
|
||||
Collection<NodeRef> nodeRefs = (Collection<NodeRef>) value;
|
||||
for (NodeRef nodeRef : nodeRefs)
|
||||
{
|
||||
nodes.add(new JBPMNode(nodeRef, serviceRegistry));
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
}
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.jbpm.context.exe.converter.SerializableToByteArrayConverter;
|
||||
import org.jbpm.graph.def.ProcessDefinition;
|
||||
import org.springframework.beans.factory.access.BeanFactoryLocator;
|
||||
import org.springframework.beans.factory.access.BeanFactoryReference;
|
||||
import org.springmodules.workflow.jbpm31.JbpmFactoryLocator;
|
||||
|
||||
|
||||
/**
|
||||
* jBPM Converter for transforming Alfresco Node to string and back
|
||||
*
|
||||
* @author davidc
|
||||
* @author Nick Smith
|
||||
*/
|
||||
public class NodeListConverter extends SerializableToByteArrayConverter
|
||||
{
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static BeanFactoryLocator jbpmFactoryLocator = new JbpmFactoryLocator();
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Object value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return (value.getClass() == JBPMNodeList.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Object convert(Object o)
|
||||
{
|
||||
Object converted = null;
|
||||
if (o != null)
|
||||
{
|
||||
JBPMNodeList nodes = (JBPMNodeList)o;
|
||||
List<NodeRef> values = new ArrayList<NodeRef>(nodes.size());
|
||||
for (JBPMNode node : nodes)
|
||||
{
|
||||
values.add(node.getNodeRef());
|
||||
}
|
||||
converted = super.convert(values);
|
||||
}
|
||||
return converted;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Object revert(Object o)
|
||||
{
|
||||
Object reverted = null;
|
||||
if (o != null)
|
||||
{
|
||||
Object nodeRefs = super.revert(o);
|
||||
reverted = revertNodes(nodeRefs);
|
||||
}
|
||||
return reverted;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public Object revert(Object o, ProcessDefinition processDefinition)
|
||||
{
|
||||
Object reverted = null;
|
||||
if (o != null)
|
||||
{
|
||||
Object nodeRefs = super.revert(o, processDefinition);
|
||||
reverted = revertNodes(nodeRefs);
|
||||
}
|
||||
return reverted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value Object
|
||||
* @return JBPMNodeList
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private JBPMNodeList revertNodes(Object value)
|
||||
{
|
||||
BeanFactoryReference factory = jbpmFactoryLocator.useBeanFactory(null);
|
||||
ServiceRegistry serviceRegistry = (ServiceRegistry)factory.getFactory().getBean(ServiceRegistry.SERVICE_REGISTRY);
|
||||
|
||||
JBPMNodeList nodes = new JBPMNodeList();
|
||||
Collection<NodeRef> nodeRefs = (Collection<NodeRef>) value;
|
||||
for (NodeRef nodeRef : nodeRefs)
|
||||
{
|
||||
nodes.add(new JBPMNode(nodeRef, serviceRegistry));
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,131 +1,131 @@
|
||||
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.repo.workflow.WorkflowModel;
|
||||
import org.alfresco.service.cmr.workflow.WorkflowException;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.jbpm.graph.def.Transition;
|
||||
import org.jbpm.graph.exe.ExecutionContext;
|
||||
import org.jbpm.taskmgmt.exe.TaskInstance;
|
||||
import org.springframework.beans.factory.access.BeanFactoryLocator;
|
||||
import org.springframework.beans.factory.access.BeanFactoryReference;
|
||||
import org.springmodules.workflow.jbpm31.JbpmFactoryLocator;
|
||||
|
||||
/**
|
||||
* Alfresco specific implementation of a jBPM task instance
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class WorkflowTaskInstance extends TaskInstance
|
||||
{
|
||||
private static final long serialVersionUID = 6824116036569411964L;
|
||||
|
||||
/**
|
||||
* Used to look up the Alfresco JBPM Engine.
|
||||
*/
|
||||
private String jbpmEngineName = null;
|
||||
|
||||
/** Alfresco JBPM Engine */
|
||||
private static JBPMEngine jbpmEngine = null;
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*/
|
||||
public WorkflowTaskInstance()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*
|
||||
* @param taskName String
|
||||
* @param actorId String
|
||||
*/
|
||||
public WorkflowTaskInstance(String taskName, String actorId)
|
||||
{
|
||||
super(taskName, actorId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets jbpmEngineName which is used to get the JBPMEngine instance from a
|
||||
* BeanFactory
|
||||
*
|
||||
* @param jbpmEngineName the jbpmEngineName to set
|
||||
*/
|
||||
public void setJbpmEngineName(String jbpmEngineName)
|
||||
{
|
||||
this.jbpmEngineName = jbpmEngineName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the JBPM Engine instance
|
||||
*
|
||||
* @return JBPM Engine
|
||||
*/
|
||||
private JBPMEngine getJBPMEngine()
|
||||
{
|
||||
if (jbpmEngine == null)
|
||||
{
|
||||
BeanFactoryLocator factoryLocator = new JbpmFactoryLocator();
|
||||
BeanFactoryReference factory = factoryLocator.useBeanFactory(null);
|
||||
if (jbpmEngineName == null) jbpmEngineName = "jbpm_engine";
|
||||
jbpmEngine = (JBPMEngine) factory.getFactory().getBean(jbpmEngineName);
|
||||
if (jbpmEngine == null) { throw new WorkflowException(
|
||||
"Failed to retrieve JBPMEngine component"); }
|
||||
}
|
||||
return jbpmEngine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create(ExecutionContext executionContext)
|
||||
{
|
||||
super.create(executionContext);
|
||||
getJBPMEngine().setDefaultTaskProperties(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end(Transition transition)
|
||||
{
|
||||
// Force assignment of task if transition is taken, but no owner has yet
|
||||
// been assigned
|
||||
if (actorId == null)
|
||||
{
|
||||
actorId = AuthenticationUtil.getFullyAuthenticatedUser();
|
||||
}
|
||||
|
||||
// Set task properties on completion of task
|
||||
// NOTE: Set properties first, so they're available during the
|
||||
// submission of
|
||||
// task variables to the process context
|
||||
Map<QName, Serializable> taskProperties = new HashMap<QName, Serializable>();
|
||||
Transition outcome = (transition == null) ? token.getNode().getDefaultLeavingTransition()
|
||||
: transition;
|
||||
if (outcome != null)
|
||||
{
|
||||
taskProperties.put(WorkflowModel.PROP_OUTCOME, outcome.getName());
|
||||
}
|
||||
taskProperties.put(WorkflowModel.PROP_STATUS, "Completed");
|
||||
getJBPMEngine().setTaskProperties(this, taskProperties);
|
||||
|
||||
// perform transition
|
||||
super.end(transition);
|
||||
|
||||
if (getTask().getStartState() != null)
|
||||
{
|
||||
// if ending a start task, push start task properties to process
|
||||
// context, if not
|
||||
// already done
|
||||
getJBPMEngine().setDefaultWorkflowProperties(this);
|
||||
|
||||
// set task description
|
||||
getJBPMEngine().setDefaultStartTaskDescription(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||
import org.alfresco.repo.workflow.WorkflowModel;
|
||||
import org.alfresco.service.cmr.workflow.WorkflowException;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.jbpm.graph.def.Transition;
|
||||
import org.jbpm.graph.exe.ExecutionContext;
|
||||
import org.jbpm.taskmgmt.exe.TaskInstance;
|
||||
import org.springframework.beans.factory.access.BeanFactoryLocator;
|
||||
import org.springframework.beans.factory.access.BeanFactoryReference;
|
||||
import org.springmodules.workflow.jbpm31.JbpmFactoryLocator;
|
||||
|
||||
/**
|
||||
* Alfresco specific implementation of a jBPM task instance
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class WorkflowTaskInstance extends TaskInstance
|
||||
{
|
||||
private static final long serialVersionUID = 6824116036569411964L;
|
||||
|
||||
/**
|
||||
* Used to look up the Alfresco JBPM Engine.
|
||||
*/
|
||||
private String jbpmEngineName = null;
|
||||
|
||||
/** Alfresco JBPM Engine */
|
||||
private static JBPMEngine jbpmEngine = null;
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*/
|
||||
public WorkflowTaskInstance()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct
|
||||
*
|
||||
* @param taskName String
|
||||
* @param actorId String
|
||||
*/
|
||||
public WorkflowTaskInstance(String taskName, String actorId)
|
||||
{
|
||||
super(taskName, actorId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets jbpmEngineName which is used to get the JBPMEngine instance from a
|
||||
* BeanFactory
|
||||
*
|
||||
* @param jbpmEngineName the jbpmEngineName to set
|
||||
*/
|
||||
public void setJbpmEngineName(String jbpmEngineName)
|
||||
{
|
||||
this.jbpmEngineName = jbpmEngineName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the JBPM Engine instance
|
||||
*
|
||||
* @return JBPM Engine
|
||||
*/
|
||||
private JBPMEngine getJBPMEngine()
|
||||
{
|
||||
if (jbpmEngine == null)
|
||||
{
|
||||
BeanFactoryLocator factoryLocator = new JbpmFactoryLocator();
|
||||
BeanFactoryReference factory = factoryLocator.useBeanFactory(null);
|
||||
if (jbpmEngineName == null) jbpmEngineName = "jbpm_engine";
|
||||
jbpmEngine = (JBPMEngine) factory.getFactory().getBean(jbpmEngineName);
|
||||
if (jbpmEngine == null) { throw new WorkflowException(
|
||||
"Failed to retrieve JBPMEngine component"); }
|
||||
}
|
||||
return jbpmEngine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create(ExecutionContext executionContext)
|
||||
{
|
||||
super.create(executionContext);
|
||||
getJBPMEngine().setDefaultTaskProperties(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end(Transition transition)
|
||||
{
|
||||
// Force assignment of task if transition is taken, but no owner has yet
|
||||
// been assigned
|
||||
if (actorId == null)
|
||||
{
|
||||
actorId = AuthenticationUtil.getFullyAuthenticatedUser();
|
||||
}
|
||||
|
||||
// Set task properties on completion of task
|
||||
// NOTE: Set properties first, so they're available during the
|
||||
// submission of
|
||||
// task variables to the process context
|
||||
Map<QName, Serializable> taskProperties = new HashMap<QName, Serializable>();
|
||||
Transition outcome = (transition == null) ? token.getNode().getDefaultLeavingTransition()
|
||||
: transition;
|
||||
if (outcome != null)
|
||||
{
|
||||
taskProperties.put(WorkflowModel.PROP_OUTCOME, outcome.getName());
|
||||
}
|
||||
taskProperties.put(WorkflowModel.PROP_STATUS, "Completed");
|
||||
getJBPMEngine().setTaskProperties(this, taskProperties);
|
||||
|
||||
// perform transition
|
||||
super.end(transition);
|
||||
|
||||
if (getTask().getStartState() != null)
|
||||
{
|
||||
// if ending a start task, push start task properties to process
|
||||
// context, if not
|
||||
// already done
|
||||
getJBPMEngine().setDefaultWorkflowProperties(this);
|
||||
|
||||
// set task description
|
||||
getJBPMEngine().setDefaultStartTaskDescription(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,39 +1,39 @@
|
||||
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import org.jbpm.graph.exe.ExecutionContext;
|
||||
import org.jbpm.taskmgmt.TaskInstanceFactory;
|
||||
import org.jbpm.taskmgmt.exe.TaskInstance;
|
||||
|
||||
/**
|
||||
* jBPM factory for creating Alfresco derived Task Instances
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class WorkflowTaskInstanceFactory implements TaskInstanceFactory
|
||||
{
|
||||
private static final long serialVersionUID = -8097108150047415711L;
|
||||
|
||||
private String jbpmEngineName;
|
||||
|
||||
/**
|
||||
* @param jbpmEngine the jbpmEngine to set
|
||||
*/
|
||||
public void setJbpmEngine(String jbpmEngine)
|
||||
{
|
||||
this.jbpmEngineName = jbpmEngine;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see
|
||||
* org.jbpm.taskmgmt.TaskInstanceFactory#createTaskInstance(org.jbpm.graph
|
||||
* .exe.ExecutionContext)
|
||||
*/
|
||||
public TaskInstance createTaskInstance(ExecutionContext executionContext)
|
||||
{
|
||||
WorkflowTaskInstance taskInstance = new WorkflowTaskInstance();
|
||||
taskInstance.setJbpmEngineName(jbpmEngineName);
|
||||
return taskInstance;
|
||||
}
|
||||
}
|
||||
|
||||
package org.alfresco.repo.workflow.jbpm;
|
||||
|
||||
import org.jbpm.graph.exe.ExecutionContext;
|
||||
import org.jbpm.taskmgmt.TaskInstanceFactory;
|
||||
import org.jbpm.taskmgmt.exe.TaskInstance;
|
||||
|
||||
/**
|
||||
* jBPM factory for creating Alfresco derived Task Instances
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class WorkflowTaskInstanceFactory implements TaskInstanceFactory
|
||||
{
|
||||
private static final long serialVersionUID = -8097108150047415711L;
|
||||
|
||||
private String jbpmEngineName;
|
||||
|
||||
/**
|
||||
* @param jbpmEngine the jbpmEngine to set
|
||||
*/
|
||||
public void setJbpmEngine(String jbpmEngine)
|
||||
{
|
||||
this.jbpmEngineName = jbpmEngine;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see
|
||||
* org.jbpm.taskmgmt.TaskInstanceFactory#createTaskInstance(org.jbpm.graph
|
||||
* .exe.ExecutionContext)
|
||||
*/
|
||||
public TaskInstance createTaskInstance(ExecutionContext executionContext)
|
||||
{
|
||||
WorkflowTaskInstance taskInstance = new WorkflowTaskInstance();
|
||||
taskInstance.setJbpmEngineName(jbpmEngineName);
|
||||
return taskInstance;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user