- Workflow Service

-- addition of get process definition history
-- addition of get active timers (for a process definition)
-- addition of fire custom workflow events
-- addition of get process variables

- Workflow Console
-- addition of undeploy all versions of a process definition
-- addition of list all versions of a process definition
-- addition of list workflows for previous version of a process definition
-- addition of query tasks
-- addition of firing custom workflow event
-- addition of list timers
-- addition of show process variables

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@5754 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
David Caruana
2007-05-22 17:48:11 +00:00
parent 28996e9a03
commit 3682ff9474
8 changed files with 882 additions and 64 deletions

View File

@@ -31,8 +31,8 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.List;
import java.util.Set;
import java.util.Map.Entry;
import java.util.zip.ZipInputStream;
@@ -70,6 +70,7 @@ import org.alfresco.service.cmr.workflow.WorkflowTask;
import org.alfresco.service.cmr.workflow.WorkflowTaskDefinition;
import org.alfresco.service.cmr.workflow.WorkflowTaskQuery;
import org.alfresco.service.cmr.workflow.WorkflowTaskState;
import org.alfresco.service.cmr.workflow.WorkflowTimer;
import org.alfresco.service.cmr.workflow.WorkflowTransition;
import org.alfresco.service.namespace.NamespaceException;
import org.alfresco.service.namespace.NamespaceService;
@@ -91,11 +92,14 @@ import org.jbpm.context.exe.VariableInstance;
import org.jbpm.db.GraphSession;
import org.jbpm.db.TaskMgmtSession;
import org.jbpm.file.def.FileDefinition;
import org.jbpm.graph.def.Event;
import org.jbpm.graph.def.Node;
import org.jbpm.graph.def.ProcessDefinition;
import org.jbpm.graph.def.Transition;
import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.graph.exe.ProcessInstance;
import org.jbpm.graph.exe.Token;
import org.jbpm.job.Timer;
import org.jbpm.jpdl.par.ProcessArchive;
import org.jbpm.jpdl.xml.Problem;
import org.jbpm.taskmgmt.def.Task;
@@ -139,6 +143,14 @@ public class JBPMEngine extends BPMEngine
"where ti.actorId = :actorId " +
"and ti.isOpen = false " +
"and ti.end is not null";
// Note: jBPM query which is not provided out-of-the-box
// TODO: Check jBPMg future and get this implemented in jBPM
private final static String PROCESS_TIMERS_QUERY =
"select timer " +
"from org.jbpm.job.Timer timer " +
"where timer.processInstance = :process ";
// Workflow Path Seperators
private final static String WORKFLOW_PATH_SEPERATOR = "-";
@@ -363,6 +375,36 @@ public class JBPMEngine extends BPMEngine
}
}
/* (non-Javadoc)
* @see org.alfresco.repo.workflow.WorkflowDefinitionComponent#getDefinitions()
*/
@SuppressWarnings("unchecked")
public List<WorkflowDefinition> getAllDefinitions()
{
try
{
return (List<WorkflowDefinition>)jbpmTemplate.execute(new JbpmCallback()
{
public Object doInJbpm(JbpmContext context)
{
GraphSession graphSession = context.getGraphSession();
List<ProcessDefinition> processDefs = (List<ProcessDefinition>)graphSession.findAllProcessDefinitions();
List<WorkflowDefinition> workflowDefs = new ArrayList<WorkflowDefinition>(processDefs.size());
for (ProcessDefinition processDef : processDefs)
{
WorkflowDefinition workflowDef = createWorkflowDefinition(processDef);
workflowDefs.add(workflowDef);
}
return workflowDefs;
}
});
}
catch(JbpmException e)
{
throw new WorkflowException("Failed to retrieve workflow definitions", e);
}
}
/* (non-Javadoc)
* @see org.alfresco.repo.workflow.WorkflowDefinitionComponent#getDefinitionById(java.lang.String)
*/
@@ -410,7 +452,38 @@ public class JBPMEngine extends BPMEngine
throw new WorkflowException("Failed to retrieve workflow definition '" + workflowName + "'", e);
}
}
/* (non-Javadoc)
* @see org.alfresco.repo.workflow.WorkflowComponent#getAllDefinitionsByName(java.lang.String)
*/
@SuppressWarnings("unchecked")
public List<WorkflowDefinition> getAllDefinitionsByName(final String workflowName)
{
try
{
return (List<WorkflowDefinition>)jbpmTemplate.execute(new JbpmCallback()
{
@SuppressWarnings("synthetic-access")
public Object doInJbpm(JbpmContext context)
{
GraphSession graphSession = context.getGraphSession();
List<ProcessDefinition> processDefs = (List<ProcessDefinition>)graphSession.findAllProcessDefinitionVersions(createLocalId(workflowName));
List<WorkflowDefinition> workflowDefs = new ArrayList<WorkflowDefinition>(processDefs.size());
for (ProcessDefinition processDef : processDefs)
{
WorkflowDefinition workflowDef = createWorkflowDefinition(processDef);
workflowDefs.add(workflowDef);
}
return workflowDefs;
}
});
}
catch(JbpmException e)
{
throw new WorkflowException("Failed to retrieve all definitions for workflow '" + workflowName + "'", e);
}
}
/* (non-Javadoc)
* @see org.alfresco.repo.workflow.WorkflowComponent#getDefinitionImage(java.lang.String)
*/
@@ -630,6 +703,55 @@ public class JBPMEngine extends BPMEngine
}
}
/* (non-Javadoc)
* @see org.alfresco.repo.workflow.WorkflowComponent#getPathProperties(java.lang.String)
*/
@SuppressWarnings("unchecked")
public Map<QName, Serializable> getPathProperties(final String pathId)
{
try
{
return (Map<QName, Serializable>) jbpmTemplate.execute(new JbpmCallback()
{
public Map<QName, Serializable> doInJbpm(JbpmContext context)
{
// retrieve jBPM token for workflow position
GraphSession graphSession = context.getGraphSession();
Token token = getWorkflowToken(graphSession, pathId);
ContextInstance instanceContext = token.getProcessInstance().getContextInstance();
Map<QName, Serializable> properties = new HashMap<QName, Serializable>(10);
while (token != null)
{
TokenVariableMap varMap = instanceContext.getTokenVariableMap(token);
if (varMap != null)
{
Map<String, Object> tokenVars = varMap.getVariablesLocally();
for (Map.Entry<String, Object> entry : tokenVars.entrySet())
{
String key = entry.getKey();
QName qname = mapNameToQName(key);
if (!properties.containsKey(key))
{
Serializable value = convertValue(entry.getValue());
properties.put(qname, value);
}
}
}
token = token.getParent();
}
return properties;
}
});
}
catch(JbpmException e)
{
throw new WorkflowException("Failed to retrieve properties of path '" + pathId + "'", e);
}
}
/* (non-Javadoc)
* @see org.alfresco.repo.workflow.WorkflowComponent#cancelWorkflow(java.lang.String)
*/
@@ -739,6 +861,78 @@ public class JBPMEngine extends BPMEngine
}
}
/* (non-Javadoc)
* @see org.alfresco.repo.workflow.WorkflowComponent#fireEvent(java.lang.String, java.lang.String)
*/
public WorkflowPath fireEvent(final String pathId, final String event)
{
try
{
return (WorkflowPath) jbpmTemplate.execute(new JbpmCallback()
{
@SuppressWarnings("unchecked")
public Object doInJbpm(JbpmContext context)
{
// NOTE: Do not allow jBPM built-in events to be fired
if (event.equals(Event.EVENTTYPE_AFTER_SIGNAL) ||
event.equals(Event.EVENTTYPE_BEFORE_SIGNAL) ||
event.equals(Event.EVENTTYPE_NODE_ENTER) ||
event.equals(Event.EVENTTYPE_NODE_LEAVE) ||
event.equals(Event.EVENTTYPE_PROCESS_END) ||
event.equals(Event.EVENTTYPE_PROCESS_START) ||
event.equals(Event.EVENTTYPE_SUBPROCESS_CREATED) ||
event.equals(Event.EVENTTYPE_SUBPROCESS_END) ||
event.equals(Event.EVENTTYPE_SUPERSTATE_ENTER) ||
event.equals(Event.EVENTTYPE_SUPERSTATE_LEAVE) ||
event.equals(Event.EVENTTYPE_TASK_ASSIGN) ||
event.equals(Event.EVENTTYPE_TASK_CREATE) ||
event.equals(Event.EVENTTYPE_TASK_END) ||
event.equals(Event.EVENTTYPE_TASK_START) ||
event.equals(Event.EVENTTYPE_TIMER) ||
event.equals(Event.EVENTTYPE_TRANSITION))
{
throw new WorkflowException("Event " + event + " is not a valid event");
}
// retrieve jBPM token for workflow position
GraphSession graphSession = context.getGraphSession();
Token token = getWorkflowToken(graphSession, pathId);
ExecutionContext executionContext = new ExecutionContext(token);
TaskMgmtSession taskSession = context.getTaskMgmtSession();
List<TaskInstance> tasks = taskSession.findTaskInstancesByToken(token.getId());
if (tasks.size() == 0)
{
// fire the event against current node for the token
Node node = token.getNode();
node.fireEvent(event, executionContext);
}
else
{
// fire the event against tasks associated with the node
// NOTE: this will also propagate the event to the node
for (TaskInstance task : tasks)
{
executionContext.setTaskInstance(task);
task.getTask().fireEvent(event, executionContext);
}
}
// save
ProcessInstance processInstance = token.getProcessInstance();
context.save(processInstance);
// return new workflow path
return createWorkflowPath(token);
}
});
}
catch(JbpmException e)
{
throw new WorkflowException("Failed to fire event '" + event + "' on workflow path '" + pathId + "'", e);
}
}
/* (non-Javadoc)
* @see org.alfresco.repo.workflow.WorkflowComponent#getTasksForWorkflowPath(java.lang.String)
*/
@@ -772,6 +966,44 @@ public class JBPMEngine extends BPMEngine
}
}
/* (non-Javadoc)
* @see org.alfresco.repo.workflow.WorkflowComponent#getTimers(java.lang.String)
*/
@SuppressWarnings("unchecked")
public List<WorkflowTimer> getTimers(final String workflowId)
{
try
{
return (List<WorkflowTimer>) jbpmTemplate.execute(new JbpmCallback()
{
public List<WorkflowTimer> doInJbpm(JbpmContext context)
{
// retrieve process
GraphSession graphSession = context.getGraphSession();
ProcessInstance process = getProcessInstance(graphSession, workflowId);
// retrieve timers for process
Session session = context.getSession();
Query query = session.createQuery(PROCESS_TIMERS_QUERY);
query.setEntity("process", process);
List<Timer> timers = query.list();
// convert timers to appropriate service response format
List<WorkflowTimer> workflowTimers = new ArrayList<WorkflowTimer>(timers.size());
for (Timer timer : timers)
{
WorkflowTimer workflowTimer = createWorkflowTimer(timer);
workflowTimers.add(workflowTimer);
}
return workflowTimers;
}
});
}
catch(JbpmException e)
{
throw new JbpmException("Couldn't get timers for process '" + workflowId + "'", e);
}
}
//
// Task Management ...
@@ -1562,30 +1794,8 @@ public class JBPMEngine extends BPMEngine
// add variable, only if part of task definition or locally defined on task
if (taskProperties.containsKey(qname) || taskAssocs.containsKey(qname) || instance.hasVariableLocally(key))
{
Object value = entry.getValue();
//
// perform data conversions
//
// Convert Nodes to NodeRefs
if (value instanceof JBPMNode)
{
value = ((JBPMNode)value).getNodeRef();
}
else if (value instanceof JBPMNodeList)
{
JBPMNodeList nodes = (JBPMNodeList)value;
List<NodeRef> nodeRefs = new ArrayList<NodeRef>(nodes.size());
for (JBPMNode node : nodes)
{
nodeRefs.add(node.getNodeRef());
}
value = (Serializable)nodeRefs;
}
// place task variable in map to return
properties.put(qname, (Serializable)value);
Serializable value = convertValue(entry.getValue());
properties.put(qname, value);
}
}
@@ -1982,6 +2192,45 @@ public class JBPMEngine extends BPMEngine
return (missingProps == null) ? null : missingProps.toArray(new QName[missingProps.size()]);
}
/**
* Convert a jBPM Value to an Alfresco value
*
* @param value jBPM value
* @return alfresco value
*/
private Serializable convertValue(Object value)
{
Serializable alfValue = null;
if (value == null)
{
// NOOP
}
else if (value instanceof JBPMNode)
{
alfValue = ((JBPMNode)value).getNodeRef();
}
else if (value instanceof JBPMNodeList)
{
JBPMNodeList nodes = (JBPMNodeList)value;
List<NodeRef> nodeRefs = new ArrayList<NodeRef>(nodes.size());
for (JBPMNode node : nodes)
{
nodeRefs.add(node.getNodeRef());
}
alfValue = (Serializable)nodeRefs;
}
else if (value instanceof Serializable)
{
alfValue = (Serializable)value;
}
else
{
throw new WorkflowException("Unable to convert jBPM value " + value + " to Alfresco Value - not serializable");
}
return alfValue;
}
/**
* Convert a Repository association to JBPMNodeList or JBPMNode
*
@@ -2187,15 +2436,7 @@ public class JBPMEngine extends BPMEngine
workflowNode.name = node.getName();
workflowNode.title = getLabel(processName + ".node." + workflowNode.name, TITLE_LABEL, workflowNode.name);
workflowNode.description = getLabel(processName + ".node." + workflowNode.name, DESC_LABEL, workflowNode.title);
if (node instanceof HibernateProxy)
{
Node realNode = (Node)((HibernateProxy)node).getHibernateLazyInitializer().getImplementation();
workflowNode.type = realNode.getClass().getSimpleName();
}
else
{
workflowNode.type = node.getClass().getSimpleName();
}
workflowNode.type = getRealNode(node).getClass().getSimpleName();
// TODO: Is there a formal way of determing if task node?
workflowNode.isTaskNode = workflowNode.type.equals("TaskNode");
List transitions = node.getLeavingTransitions();
@@ -2358,6 +2599,22 @@ public class JBPMEngine extends BPMEngine
return deployment;
}
/**
* Creates a Workflow Timer
* @param timer jBPM Timer
* @return workflow timer
*/
protected WorkflowTimer createWorkflowTimer(Timer timer)
{
WorkflowTimer workflowTimer = new WorkflowTimer();
workflowTimer.id = createGlobalId(new Long(timer.getId()).toString());
workflowTimer.name = timer.getName();
workflowTimer.dueDate = timer.getDueDate();
workflowTimer.path = createWorkflowPath(timer.getToken());
workflowTimer.task = createWorkflowTask(timer.getTaskInstance());
return workflowTimer;
}
/**
* Get the Workflow Task State for the specified JBoss JBPM Task
*
@@ -2376,4 +2633,23 @@ public class JBPMEngine extends BPMEngine
}
}
/**
* Helper to retrieve the real jBPM Node
*
* @param node Node
* @return real Node (i.e. the one that's not a Hibernate proxy)
*/
private Node getRealNode(Node node)
{
if (node instanceof HibernateProxy)
{
Node realNode = (Node)((HibernateProxy)node).getHibernateLazyInitializer().getImplementation();
return realNode;
}
else
{
return node;
}
}
}