- 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

@@ -43,9 +43,11 @@ ok> redeploy
Redeploy the last workflow definition.
ok> show definitions
ok> show definitions [all]
List all deployed workflow definitions.
List latest deployed workflow definitions. Or, display all workflows
definitions (including previous versions) with the additional keyword
'all'.
ok> use definition [<workflowDefId>]
@@ -60,7 +62,14 @@ ok> undeploy definition <workflowDefId>
workflows associated with the definition.
If multiple versions of the definition exist, you will have to undeploy
each in turn to remove the definition completely.
each in turn to remove the definition completely or issue the 'undeploy
definition name' command.
ok> undeploy definition name <workflowName>
Undeploy all versions of a workflow definition. As with 'undeploy
definition', all "in-flight" workflows associated with each version
are terminated.
##
## Variable Commands
@@ -140,18 +149,27 @@ ok> start [<varName[=varValue>]]*
ok> show workflows [all]
Display the list of active workflows for the currently selected workflow
definition. Or, display the list of all workflows when used with additional
keyword 'all'.
definition. Or, display the list of all workflows when used with the
additional keyword 'all'.
ok> use workflow <workflowId>
Use the specified <workflowId>.
ok> desc workflow <workflowId>
Describe the specified <workflowId>.
ok> show paths [<workflowId>]
Display the workflow paths for the specified <workflowId>. If <workflowId>
is omitted, the paths for the currently started workflow are shown.
ok> desc path <pathId>
Describe the specified <pathId>. Includes the list of properties associated
with the path.
ok> show transitions [<workflowId>]
Display all available transitions for the specified <workflowId>. If
@@ -163,9 +181,9 @@ ok> signal <pathId> [<transitionName>]
Signal transition on specified <pathId>. If <transitionName> is omitted, the
default transition is taken.
ok> desc workflow <workflowId>
ok> event <pathId> <eventtype>
Describe the specified <workflowId>.
Fire an event of custom 'eventtype' against the specified path.
ok> end workflow <workflowId>
@@ -179,6 +197,16 @@ ok> delete all workflows
Force deletion of all "in-flight" workflows. Use with care!
##
## Timer Commands
##
ok> show timers [all]
Display the list of active timers for the currently selected workflow
definition. Or, display the list of all timers when used with the
additional keyword 'all'.
##
## Task Commands
##
@@ -222,6 +250,44 @@ ok> end task <taskId> [<transitionName>]
End the task identified by <taskId>. If <transitionName> is omitted, the
default transition is taken.
ok> query task [predicate]*
Query for tasks. If no predicates are provided, all "in-progress" tasks
are returned (across all "active" workflows).
Predicates are:
taskId=<taskId>
taskName=<taskName> e.g. taskName=wf:reviewTask
taskState=IN_PROGRESS|COMPLETED
taskActor=<actorId> e.g. taskActor=admin
task.<propqname>=<propvalue> e.g. task.bpm:outcome=approve
processId=<processId>
processName=<processName> e.g. processName=wf:review
processActive=true|false e.g. processActive=true
process.<propqname>=<propvalue> e.g. process.initiator=admin
orderBy=<orderBy>* e.g. orderBy=TaskDue_Desc,TaskActor_Asc
Where <orderBy> is one of:
TaskId_Asc,
TaskId_Desc,
TaskCreated_Asc,
TaskCreated_Desc,
TaskDue_Asc,
TaskDue_Desc,
TaskName_Asc,
TaskName_Desc,
TaskActor_Asc,
TaskActor_Desc,
TaskState_Asc,
TaskState_Desc
e.g. query all in-progress pending submissions for web project X, ordered by pending due date, submitter
query task taskName=wcmwf:submitpending taskState=IN_PROGRESS \
process.wcwmf:webproject=workspace://SpacesStore/projectx orderBy=TaskDue_Desc,TaskActor_Asc
##
## end

View File

@@ -35,6 +35,7 @@ import org.alfresco.service.cmr.workflow.WorkflowDeployment;
import org.alfresco.service.cmr.workflow.WorkflowInstance;
import org.alfresco.service.cmr.workflow.WorkflowPath;
import org.alfresco.service.cmr.workflow.WorkflowTask;
import org.alfresco.service.cmr.workflow.WorkflowTimer;
import org.alfresco.service.namespace.QName;
@@ -88,6 +89,14 @@ public interface WorkflowComponent
*/
public List<WorkflowDefinition> getDefinitions();
/**
* Gets all deployed Workflow Definitions (with all previous versions)
*
* @return the deployed (and previous) workflow definitions
*/
@Auditable
public List<WorkflowDefinition> getAllDefinitions();
/**
* Gets a Workflow Definition by unique Id
*
@@ -104,6 +113,15 @@ public interface WorkflowComponent
*/
public WorkflowDefinition getDefinitionByName(String workflowName);
/**
* Gets all (including previous) Workflow Definitions for the given unique name
*
* @param workflowName workflow name e.g. jbpm://review
* @return the deployed workflow definition (or null if not found)
*/
@Auditable(parameters = {"workflowName"})
public List<WorkflowDefinition> getAllDefinitionsByName(String workflowName);
/**
* Gets a graphical view of the Workflow Definition
*
@@ -152,6 +170,14 @@ public interface WorkflowComponent
*/
public List<WorkflowPath> getWorkflowPaths(String workflowId);
/**
* Gets the properties associated with the specified path (and parent paths)
*
* @param pathId workflow path id
* @return map of path properties
*/
public Map<QName, Serializable> getPathProperties(String pathId);
/**
* Cancel an "in-fligth" Workflow instance
*
@@ -178,6 +204,15 @@ public interface WorkflowComponent
*/
public WorkflowPath signal(String pathId, String transitionId);
/**
* Fire custom event against specified path
*
* @param pathId the workflow path to fire event on
* @param event name of event
* @return workflow path (it may have been updated as a result of firing the event
*/
public WorkflowPath fireEvent(String pathId, String event);
/**
* Gets all Tasks associated with the specified path
*
@@ -185,6 +220,13 @@ public interface WorkflowComponent
* @return the list of associated tasks
*/
public List<WorkflowTask> getTasksForWorkflowPath(String pathId);
/**
* Gets all active timers for the specified workflow
*
* @return the list of active timers
*/
public List<WorkflowTimer> getTimers(String workflowId);
}

View File

@@ -37,17 +37,17 @@ import java.util.List;
import java.util.Map;
import org.alfresco.i18n.I18NUtil;
import org.alfresco.repo.avm.AVMNodeConverter;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.avm.AVMNodeConverter;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.security.authority.AuthorityDAO;
import org.alfresco.repo.transaction.TransactionUtil;
import org.alfresco.service.cmr.avm.AVMNodeDescriptor;
import org.alfresco.service.cmr.avm.AVMService;
import org.alfresco.service.cmr.avmsync.AVMDifference;
import org.alfresco.service.cmr.avmsync.AVMSyncService;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.security.authority.AuthorityDAO;
import org.alfresco.repo.transaction.TransactionUtil;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.ContentWriter;
@@ -61,7 +61,9 @@ import org.alfresco.service.cmr.workflow.WorkflowInstance;
import org.alfresco.service.cmr.workflow.WorkflowPath;
import org.alfresco.service.cmr.workflow.WorkflowService;
import org.alfresco.service.cmr.workflow.WorkflowTask;
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.NamespaceService;
import org.alfresco.service.namespace.QName;
@@ -351,7 +353,22 @@ public class WorkflowInterpreter
else if (command[1].equals("definitions"))
{
List<WorkflowDefinition> defs = workflowService.getDefinitions();
List<WorkflowDefinition> defs = null;
if (command.length == 3)
{
if (command[2].equals("all"))
{
defs = workflowService.getAllDefinitions();
}
else
{
return "Syntax Error.\n";
}
}
else
{
defs = workflowService.getDefinitions();
}
for (WorkflowDefinition def : defs)
{
out.println("id: " + def.id + " , name: " + def.name + " , title: " + def.title + " , version: " + def.version);
@@ -379,12 +396,12 @@ public class WorkflowInterpreter
if (id.equals("all"))
{
for (WorkflowDefinition def : workflowService.getDefinitions())
for (WorkflowDefinition def : workflowService.getAllDefinitions())
{
List<WorkflowInstance> workflows = workflowService.getActiveWorkflows(def.id);
for (WorkflowInstance workflow : workflows)
{
out.println("id: " + workflow.id + " , desc: " + workflow.description + " , start date: " + workflow.startDate + " , def: " + workflow.definition.title);
out.println("id: " + workflow.id + " , desc: " + workflow.description + " , start date: " + workflow.startDate + " , def: " + workflow.definition.name + " v" + workflow.definition.version);
}
}
}
@@ -393,7 +410,7 @@ public class WorkflowInterpreter
List<WorkflowInstance> workflows = workflowService.getActiveWorkflows(id);
for (WorkflowInstance workflow : workflows)
{
out.println("id: " + workflow.id + " , desc: " + workflow.description + " , start date: " + workflow.startDate + " , def: " + workflow.definition.title);
out.println("id: " + workflow.id + " , desc: " + workflow.description + " , start date: " + workflow.startDate + " , def: " + workflow.definition.name);
}
}
}
@@ -453,6 +470,54 @@ public class WorkflowInterpreter
}
}
else if (command[1].equals("timers"))
{
String id = (currentWorkflowDef != null) ? currentWorkflowDef.id : null;
if (id == null && command.length == 2)
{
return "workflow definition not in use. Enter command 'show timers all' or 'use <workflowDefId>'.\n";
}
if (command.length == 3)
{
if (command[2].equals("all"))
{
id = "all";
}
else
{
return "Syntax Error.\n";
}
}
if (id.equals("all"))
{
for (WorkflowDefinition def : workflowService.getAllDefinitions())
{
List<WorkflowInstance> workflows = workflowService.getActiveWorkflows(def.id);
for (WorkflowInstance workflow : workflows)
{
List<WorkflowTimer> timers = workflowService.getTimers(workflow.id);
for (WorkflowTimer timer : timers)
{
out.println("id: " + timer.id + " , name: " + timer.name + " , due date: " + timer.dueDate + " , path: " + timer.path.id + " , node: " + timer.path.node.name + " , process: " + timer.path.instance.id + " , task: " + timer.task.name);
}
}
}
}
else
{
List<WorkflowInstance> workflows = workflowService.getActiveWorkflows(id);
for (WorkflowInstance workflow : workflows)
{
List<WorkflowTimer> timers = workflowService.getTimers(workflow.id);
for (WorkflowTimer timer : timers)
{
out.println("id: " + timer.id + " , name: " + timer.name + " , due date: " + timer.dueDate + " , path: " + timer.path.id + " , node: " + timer.path.node.name + " , process: " + timer.path.instance.id + " , task: " + timer.task.name);
}
}
}
}
else if (command[1].equals("my"))
{
if (command.length != 3)
@@ -550,6 +615,144 @@ public class WorkflowInterpreter
out.println("context: " + workflow.context);
out.println("package: " + workflow.workflowPackage);
}
else if (command[1].equals("path"))
{
if (command.length != 3)
{
return "Syntax Error.\n";
}
Map<QName, Serializable> properties = workflowService.getPathProperties(command[2]);
out.println("path: " + command[1]);
out.println("properties: " + properties.size());
for (Map.Entry<QName, Serializable> prop : properties.entrySet())
{
out.println(" " + prop.getKey() + " = " + prop.getValue());
}
}
else
{
return "Syntax Error.\n";
}
}
else if (command[0].equals("query"))
{
if (command.length < 2)
{
return "Syntax Error.\n";
}
if (command[1].equals("task"))
{
// build query
WorkflowTaskQuery query = new WorkflowTaskQuery();
Map<QName, Object> taskProps = new HashMap<QName, Object>();
Map<QName, Object> procProps = new HashMap<QName, Object>();
for (int i = 2; i < command.length; i++)
{
String[] predicate = command[i].split("=");
if (predicate.length == 1)
{
return "Syntax Error.\n";
}
String[] predicateName = predicate[0].split("\\.");
if (predicateName.length == 1)
{
if (predicate[0].equals("taskId"))
{
query.setTaskId(predicate[1]);
}
else if (predicate[0].equals("taskState"))
{
WorkflowTaskState state = WorkflowTaskState.valueOf(predicate[1]);
if (state == null)
{
return "Syntax Error. Unknown task state\n";
}
query.setTaskState(state);
}
else if (predicate[0].equals("taskName"))
{
query.setTaskName(QName.createQName(predicate[1], namespaceService));
}
else if (predicate[0].equals("taskActor"))
{
query.setActorId(predicate[1]);
}
else if (predicate[0].equals("processId"))
{
query.setProcessId(predicate[1]);
}
else if (predicate[0].equals("processName"))
{
query.setProcessName(QName.createQName(predicate[1], namespaceService));
}
else if (predicate[0].equals("processActive"))
{
Boolean active = Boolean.valueOf(predicate[1]);
query.setActive(active);
}
else if (predicate[0].equals("orderBy"))
{
String[] orderBy = predicate[1].split(",");
WorkflowTaskQuery.OrderBy[] queryOrderBy = new WorkflowTaskQuery.OrderBy[orderBy.length];
for (int iOrderBy = 0; iOrderBy < orderBy.length; iOrderBy++)
{
queryOrderBy[iOrderBy] = WorkflowTaskQuery.OrderBy.valueOf(orderBy[iOrderBy]);
if (queryOrderBy[iOrderBy] == null)
{
return "Syntax Error. Unknown orderBy.\n";
}
}
query.setOrderBy(queryOrderBy);
}
else
{
return "Syntax Error. Unknown query predicate.\n";
}
}
else if (predicateName.length == 2)
{
if (predicateName[0].equals("task"))
{
taskProps.put(QName.createQName(predicateName[1], namespaceService), predicate[1]);
}
else if (predicateName[0].equals("process"))
{
procProps.put(QName.createQName(predicateName[1], namespaceService), predicate[1]);
}
else
{
return "Syntax Error. Unknown query predicate.\n";
}
}
else
{
return "Syntax Error.\n";
}
}
if (taskProps.size() > 0)
{
query.setTaskCustomProps(taskProps);
}
if (procProps.size() > 0)
{
query.setProcessCustomProps(procProps);
}
// execute query
List<WorkflowTask> tasks = workflowService.queryTasks(query);
out.println("found " + tasks.size() + " tasks.");
for (WorkflowTask task : tasks)
{
out.println("task id: " + task.id + " , name: " + task.name + " , properties: " + task.properties.size() + ", process id: " + task.path.instance);
}
}
else
{
return "Syntax Error.\n";
@@ -591,14 +794,38 @@ public class WorkflowInterpreter
}
if (command[1].equals("definition"))
{
if (command.length != 3)
if (command.length == 3)
{
workflowService.undeployDefinition(command[2]);
currentWorkflowDef = null;
currentPath = null;
out.print(executeCommand("show definitions"));
}
else if (command.length == 4)
{
if (command[2].equals("name"))
{
out.print("undeploying...");
List<WorkflowDefinition> defs = workflowService.getAllDefinitionsByName(command[3]);
for (WorkflowDefinition def: defs)
{
workflowService.undeployDefinition(def.id);
out.print(" v" + def.version);
}
out.println("");
currentWorkflowDef = null;
currentPath = null;
out.print(executeCommand("show definitions all"));
}
else
{
return "Syntax Error.\n";
}
}
else
{
return "Syntax Error.\n";
}
workflowService.undeployDefinition(command[2]);
currentWorkflowDef = null;
currentPath = null;
out.print(executeCommand("show definitions"));
}
else
{
@@ -610,7 +837,7 @@ public class WorkflowInterpreter
{
if (command.length == 1)
{
out.println("definition: " + ((currentWorkflowDef == null) ? "None" : currentWorkflowDef.id + " , name: " + currentWorkflowDef.title));
out.println("definition: " + ((currentWorkflowDef == null) ? "None" : currentWorkflowDef.id + " , name: " + currentWorkflowDef.title + " , version: " + currentWorkflowDef.version));
out.println("workflow: " + ((currentPath == null) ? "None" : currentPath.instance.id + " , active: " + currentPath.instance.active));
out.println("path: " + ((currentPath == null) ? "None" : currentPath.id + " , node: " + currentPath.node.title));
}
@@ -748,6 +975,17 @@ public class WorkflowInterpreter
out.print(interpretCommand("show transitions"));
}
else if (command[0].equals("event"))
{
if (command.length < 3)
{
return "Syntax Error.\n";
}
WorkflowPath path = workflowService.fireEvent(command[1], command[2]);
out.println("event " + command[2] + " fired - path id: " + path.id);
out.print(interpretCommand("show transitions"));
}
else if (command[0].equals("end"))
{
if (command.length < 3)
@@ -769,7 +1007,6 @@ public class WorkflowInterpreter
}
workflowService.cancelWorkflow(workflowId);
out.println("workflow " + workflowId + " cancelled.");
out.print(interpretCommand("show transitions"));
}
else
{
@@ -807,7 +1044,7 @@ public class WorkflowInterpreter
}
if (command[3].equals("imeanit"))
{
for (WorkflowDefinition def : workflowService.getDefinitions())
for (WorkflowDefinition def : workflowService.getAllDefinitions())
{
List<WorkflowInstance> workflows = workflowService.getActiveWorkflows(def.id);
for (WorkflowInstance workflow : workflows)

View File

@@ -44,6 +44,7 @@ import org.alfresco.service.cmr.workflow.WorkflowService;
import org.alfresco.service.cmr.workflow.WorkflowTask;
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.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -158,7 +159,22 @@ public class WorkflowServiceImpl implements WorkflowService
}
return Collections.unmodifiableList(definitions);
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.workflow.WorkflowService#getAllDefinitions()
*/
public List<WorkflowDefinition> getAllDefinitions()
{
List<WorkflowDefinition> definitions = new ArrayList<WorkflowDefinition>(10);
String[] ids = registry.getWorkflowComponents();
for (String id: ids)
{
WorkflowComponent component = registry.getWorkflowComponent(id);
definitions.addAll(component.getAllDefinitions());
}
return Collections.unmodifiableList(definitions);
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.workflow.WorkflowService#getDefinitionById(java.lang.String)
*/
@@ -179,6 +195,16 @@ public class WorkflowServiceImpl implements WorkflowService
return component.getDefinitionByName(workflowName);
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.workflow.WorkflowService#getAllDefinitionsByName(java.lang.String)
*/
public List<WorkflowDefinition> getAllDefinitionsByName(String workflowName)
{
String engineId = BPMEngineRegistry.getEngineId(workflowName);
WorkflowComponent component = getWorkflowComponent(engineId);
return component.getAllDefinitionsByName(workflowName);
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.workflow.WorkflowService#getDefinitionImage(java.lang.String)
*/
@@ -243,6 +269,16 @@ public class WorkflowServiceImpl implements WorkflowService
return component.getWorkflowPaths(workflowId);
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.workflow.WorkflowService#getPathProperties(java.lang.String)
*/
public Map<QName, Serializable> getPathProperties(String pathId)
{
String engineId = BPMEngineRegistry.getEngineId(pathId);
WorkflowComponent component = getWorkflowComponent(engineId);
return component.getPathProperties(pathId);
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.workflow.WorkflowService#cancelWorkflow(java.lang.String)
*/
@@ -281,6 +317,26 @@ public class WorkflowServiceImpl implements WorkflowService
return component.signal(pathId, transition);
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.workflow.WorkflowService#fireEvent(java.lang.String, java.lang.String)
*/
public WorkflowPath fireEvent(String pathId, String event)
{
String engineId = BPMEngineRegistry.getEngineId(pathId);
WorkflowComponent component = getWorkflowComponent(engineId);
return component.fireEvent(pathId, event);
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.workflow.WorkflowService#getTimers(java.lang.String)
*/
public List<WorkflowTimer> getTimers(String workflowId)
{
String engineId = BPMEngineRegistry.getEngineId(workflowId);
WorkflowComponent component = getWorkflowComponent(engineId);
return component.getTimers(workflowId);
}
/* (non-Javadoc)
* @see org.alfresco.service.cmr.workflow.WorkflowService#getTasksForWorkflowPath(java.lang.String)
*/

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;
}
}
}

View File

@@ -97,13 +97,21 @@ public interface WorkflowService
public void undeployDefinition(String workflowDefinitionId);
/**
* Gets all deployed Workflow Definitions
* Gets latest deployed Workflow Definitions
*
* @return the deployed workflow definitions
* @return the latest deployed workflow definitions
*/
@Auditable
public List<WorkflowDefinition> getDefinitions();
/**
* Gets all deployed Workflow Definitions (with all previous versions)
*
* @return the deployed (and previous) workflow definitions
*/
@Auditable
public List<WorkflowDefinition> getAllDefinitions();
/**
* Gets a Workflow Definition by unique Id
*
@@ -114,7 +122,7 @@ public interface WorkflowService
public WorkflowDefinition getDefinitionById(String workflowDefinitionId);
/**
* Gets a Workflow Definition by unique name
* Gets the latest Workflow Definition by unique name
*
* @param workflowName workflow name e.g. jbpm://review
* @return the deployed workflow definition (or null if not found)
@@ -122,6 +130,15 @@ public interface WorkflowService
@Auditable(parameters = {"workflowName"})
public WorkflowDefinition getDefinitionByName(String workflowName);
/**
* Gets all (including previous) Workflow Definitions for the given unique name
*
* @param workflowName workflow name e.g. jbpm://review
* @return the deployed workflow definition (or null if not found)
*/
@Auditable(parameters = {"workflowName"})
public List<WorkflowDefinition> getAllDefinitionsByName(String workflowName);
/**
* Gets a graphical view of the Workflow Definition
*
@@ -184,6 +201,15 @@ public interface WorkflowService
@Auditable(parameters = {"workflowId"})
public List<WorkflowPath> getWorkflowPaths(String workflowId);
/**
* Gets the properties associated with the specified path (and parent paths)
*
* @param pathId workflow path id
* @return map of path properties
*/
@Auditable(parameters = {"pathId"})
public Map<QName, Serializable> getPathProperties(String pathId);
/**
* Cancel an "in-fligth" Workflow instance
*
@@ -215,6 +241,16 @@ public interface WorkflowService
@Auditable(parameters = {"pathId", "transitionId"})
public WorkflowPath signal(String pathId, String transitionId);
/**
* Fire custom event against specified path
*
* @param pathId the workflow path to fire event on
* @param event name of event
* @return workflow path (it may have been updated as a result of firing the event
*/
@Auditable(parameters = {"pathId", "event"})
public WorkflowPath fireEvent(String pathId, String event);
/**
* Gets all Tasks associated with the specified path
*
@@ -223,8 +259,21 @@ public interface WorkflowService
*/
@Auditable(parameters = {"pathId"})
public List<WorkflowTask> getTasksForWorkflowPath(String pathId);
//
// Workflow Timer Management
//
/**
* Gets all active timers for the specified workflow
*
* @return the list of active timers
*/
@Auditable(parameters = {"workflowId"})
public List<WorkflowTimer> getTimers(String workflowId);
//
// Task Management
//
@@ -263,7 +312,7 @@ public interface WorkflowService
* @param query the filter by which tasks are queried
* @return the list of tasks matching the specified query
*/
@Auditable(parameters = {"filter"})
@Auditable(parameters = {"query"})
public List<WorkflowTask> queryTasks(WorkflowTaskQuery query);
/**

View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2005-2007 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* As a special exception to the terms and conditions of version 2.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* and Open Source Software ("FLOSS") applications as described in Alfresco's
* FLOSS exception. You should have recieved a copy of the text describing
* the FLOSS exception, and it is also available here:
* http://www.alfresco.com/legal/licensing"
*/
package org.alfresco.service.cmr.workflow;
import java.util.Date;
public class WorkflowTimer
{
/** Timer Id */
public String id;
/** Transition Name */
public String name;
/** Associated Workflow Path */
public WorkflowPath path;
/** Associated Workflow Task (if any) */
public WorkflowTask task;
/** Due Date */
public Date dueDate;
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
public String toString()
{
return "WorkflowTimer[id=" + id + ",name=" + name + ",dueDate=" + dueDate + ",path=" + path + ",task=" + task + "]";
}
}

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<process-definition xmlns="urn:jbpm.org:jpdl-3.1" name="wf:testcustomevent">
<swimlane name="initiator" />
<start-state name="start">
<task name="bpm:startTask" swimlane="initiator" >
<event type="where">
<script>
System.out.println(taskInstance.name);
</script>
</event>
</task>
<event type="where">
<script>
System.out.println(node.name);
</script>
</event>
<transition name="" to="customevent" />
</start-state>
<task-node name="customevent">
<task swimlane="initiator">
<event type="where">
<script>
System.out.println(taskInstance.name);
</script>
</event>
</task>
<transition name="" to="end" />
</task-node>
<end-state name="end" />
</process-definition>