This concept is meant to illustrate what is needed in technical terms in order to implement the new UX Pulse concept with particular reference to the workflow.
At the moment, work items or tasks as we will call them from now on (work item is very jBPM specific) are treated like any informational Pulse message (i.e. errors, warnings) and there's no way to know what the status of a given item is, unless one tries to perform an action on it. What's worse, one can delete a work item without handling it, which might basically break the workflow, leaving a process pending forever, unless some other user in the same group the task has been assigned to, takes it over.
The UX concept puts special emphasis on the handling of workflow tasks, how to display their current state (i.e. who's in charge, whether it has been already handled or not, what the outcome was, etc.), how to perform the necessary actions so that each workflow process can be successfully carried through. It also worth mentioning that, as specified in the UX concept "The user does not see the entire work flow and its current state in Pulse - there's actually a separate app for that".
Workflow and Pulse interaction
Here we briefly sketch how Workflow works and how it currently interacts with Pulse
- workflow process defined as bpmn 2.0 (Business Process Model and Notation) file
- each process is registered by name at startup and made available to the workflow engine, see /modules/workflow/workflows
- each step in the workflow is a workitem defined in a wid (work item definition, a jBPM specific file), e.g. MgnlDefinitions.wid
- each work item is mapped to a work item handler. Handlers are under workItemHandlers nodes, eg. /modules/workflow-jbpm/workItemHandlers
- handlers implement WorkItemHandler, they are a Java class executed at runtime when the workflow reaches the corresponding work item or step
- workflows are launched via commands, e.g. /modules/workflow/workflows/activate which has a workflow property referring to the name of the workflow to launch
Interaction example with Pulse
activation workflow is defined as SimpleActivation.bpmn
- its first workitem is called publishNotification which maps to a NotificationWorkItemHandler class
- when activation workflow is started, publishNotification is executed
- publishNotification populates a map of arbitrary data with
- the work item id
- the process id this work item belongs to
- who started the process
- who's been assigned to
- the path to activate
- if activation is recursive
- publication date in case of scheduled activation
- the message view to handle the item in Pulse
- publishNotification creates a Message with the above data and sends it to Pulse
- user opens the message in Pulse and decides to approve or reject
- in either case a CompleteTaskAction is triggered and the decision is passed back to the workflow engine in a results map
Of the many new things introduced by the Pulse UX concept, it was agreed that the first thing to tackle, which would solve most of the problems currently affecting the workflow, is the new Tasks inbox. As illustrated by the mockup below, the main novelty is the sub tabs (all items, not started, ongoing, done) introducing filters displaying the status of the each task assigned to the current user or the group he or she is part of. An additional status column has been introduced too.
- we want to be able to display tasks independent of the presence of an underlying workflow engine
- i.e a one-shot task (e.g. a short translation assignment given to a particular user) for which setting up a whole workflow process would be overkill.
- we want to be able to retrieve the status of a given task
- ideally we would like the list to be updated automatically as new tasks arrive, are taken, updated or completed.
jBPM User Tasks
What is a User Task?
From the official jBPM documentation:
Processes can also involve tasks that need to be executed by human actors. A User Task represents an atomic task to be executed by a human actor. It should have one incoming connection and one outgoing connection. User Tasks can be used in combination with Swimlanes to assign multiple human tasks to similar actors. Refer to the chapter on human tasks for more details. A User Task is actually nothing more than a specific type of service node (of type "Human Task"). A User Task contains the following properties:
Instead of using human tasks for interacting with human actors Magnolia decided to leave this concept out and use Service Tasks for sending messages to The Pulse.
But what exactly is the difference between a Service Tasks and Human Tasks?
From a technical point of view: None.
Both Tasks are handled by a WorkItemHandler which implement the same interface.
When modelling your process it does matter. A User Task gives you much more possibilities to set parameters tailored for user interactions:
Further more User Tasks allow defining Swim lanes inside your process. A swim lane allows you to define multiple steps or user tasks inside your process to be automatically assigned to the first user who picks up the first task of the swim lane.
Note: Human Tasks allow setting a
Task Name, which could be used to define the further visual representation or the actions available for a certain task in Magnolia. This is not the case for other Service Tasks, which is why you end up creating a handler per service task. What the
Implementation field can be used for has to be clarified.
Human Task WorkItemHandler
The handler is simply registered in the
Human Task as identifier. Magnolia's 5.3 version of workflow already provides a custom class called
What the Handler does internally is:
- create a Task based on the parameters provided by the workItem
- create the ContentData based on parameters from the workItem (? This data is then altered by the user executing the task and later on appended as
resultmap to the process)
- add the task to the TaskService
- claim the task is the task is part of a swim lane, otherwise an actor would have to manually claim it
At this point the WorkitemHandler is finished. It does not complete the WorkItem and for now the process is paused at this stage. Even more the whole execution of the task is decoupled from the process execution and has a completely separated life cycle. This lifecycle is taken care of by the
Proceed execution of process after User tasks completes
Because the User Tasks lifecycle is decoupled, the process needs to be informed when a task has been completed and allow the process to proceed its execution. This is is done exactly the same way other tasks are completed inside the process by notifying the
WorkItemManager (manager.completeWorkItem()), but as part of the
HTWorkItemHandler it is triggered by an event fired by
LifeCycleManager and handled by e.g.
org.jbpm.services.task.wih.ExternalTaskEventListener#processTaskState. The Listener is registered when registering the
HTWorkItemHandler inside the
The TaskService is registered by the
RuntimeManager to the
RuntimeEngine. Magnolia provides it's own RuntimeManager implementation for easy registration of a custom TaskService Implementation. It's purpose is to handle the lifecycle of User Tasks, which is pretty obvious when looking at its interface:
UML showing the default jBPM implementation
This diagram shows how User Tasks are persisted in JPA. Please compare this with the current implementation to see where Magnolia's implementation can easily be extended for a custom TaskService implementation.
Introduce own implementation of
TaskService which delegates to Magnolia's
By choosing this approach we would sacrifice the idea of having a complete integration of jBPM (in case that idea ever was there..) in Magnolia. But, as part of workflow 5.3 we have laid the ground to hook into jBPM's engine at the right spots and provide custom, yet slimmed down implementations targeted towards our needs and most probably towards 97,6% of our customers needs. Still if somebody would need to have the full persistence of User Tasks as specified by jBPM registering the default CommandBasedTaskService based on a custom TaskPersistenceContext is possible, in theory.
During implementation we realised that this will be more complicated than necessary. While registering the TaskService to the RuntimeEngine would be nice, the gain is not much more than being a bit more consistent with jBPM. And when looking at the TaskService interface we would somehow have to wrap a lot of objects to work with our own Task's implementation, which should be part of the UI.
So instead of implementing this the TaskService interface and registering it to the RuntimeEngine we are going to create a custom TaskManager interface (naming due to consistency with MessageManager/MessageStore).
Compared to the first diagram, the
TaskManager is not registered to the Runtime, but it is injected into the
LifeCycleManager which takes care of notifying the process about completed tasks is replaced by
TasksEventManager, which is part of the UI project and allows registering EventHandlers. This could e.g. be used to register an handler which sends mail every time a task is created, or in our case we register an handler, which takes care of notifying the process about completed user tasks.
This approach allows a very slim integration with Magnolia's
TaskManager. The whole Task Management is a standalone, reusable part of the UI project and for jBPM we communicate with the standard TaskManager interfaces using the
HTWorkItemHandler and event handlers registered to the
Input from architecture meeting
3.4.2014: Task related classes should go to a separate module in main
Possible Risks and Problems
Accessing the TaskManager
Accessing the TaskManager is different than in jBPM. In jBPM the TaskService is bound to a RuntimeEngine and can be obtained like this:
long taskId = ((InternalTaskService) runtime.getTaskService()).addTask(task, content);
This will not be doable with our approach.
Extending the Task object
How to extend the Task object by workflow specific parameters, how to store it?
Other Possible approaches
By implementing a JCR implementation of the TaskPersistenceContext we would be able to create a complete storage for User Tasks. This would be the recommended approach in case we wanted to provide a complete integration of jBPM in Magnolia where customers would be able to take full advantage of jBPM and possibly also integrate 3rd party clients to connect to Magnolia's engine.
Having the persistence in place still leaves questions open on how to actually interact with the tasks. We would have to access the stored tasks, and provide a limited set of possibilities to manipulate them.
The JPATaskPersistenceContext alone is around 550 LOC and all it does is delegate the to the EntityManager using calls like em.merge(object) em.remove( object )
Continue down the road we chose and ignore User Tasks:
<Insert magic here>