JBoss.orgCommunity Documentation
The jBPM's core role is to persist the execution of a process. This feature is extremely useful when one is seeking to manage tasks and task-lists for people. The jBPM allows one to specify a piece of software that describes an overall process. Such a piece of software can have wait states for human tasks.
Tasks are part of the process definition. They define how task instances will be created and assigned during process executions.
Define tasks in task-nodes and in the
process-definition. The most common
way is to define one or more tasks in
a task-node. In that case the
task-node represents a task to be
undertaken by the user and the process execution should wait
until the actor completes the task. When the actor completes
the task, process execution continues. When more tasks are
specified in a task-node, the default
behaviour is to wait until all the tasks have ended.
One can also specify tasks on the
process-definition. Tasks specified in
this way can be found by searching for their names. One can
also reference them from within
task-nodes or use them from within
actions. In fact, every task (or
task-node) that is given a name can be
found in the process-definition.
Ensure that each task name is unique. Also, give the task a
priority. This will be used as the
initial priority for each task instance created for this task.
(This initial priority can be changed by the task instance
afterwards.)
It is possible to assign a task instance to an
actorId
(java.lang.String). Every task instance
is stored in one table (JBPM_TASKINSTANCE.)
Query this table for every task instances for a given actorId,
in order to obtain the task list for that particular user.
Use the jBPM task list mechanism to combine jBPM tasks with other tasks, even when those other tasks are unrelated to a process execution. In this way, one can easily combine jBPM-process-tasks with other application's tasks in one centralised repository.
The task instance life-cycle is straightforward: after creation, one can start the instances. They can then be ended, which means that they will be marked as completed.
For the sake of flexibility, assignment is not part of the life-cycle.
Task instances are normally created when the process execution
enters a task-node (via the
TaskMgmtInstance.createTaskInstance(...)
method.)
A user interface component then queries the database
for the task lists. It does so by using the
TaskMgmtSession.findTaskInstancesByActorId(...) method.
Then, after collecting input from the user, the UI component
calls TaskInstance.assign(String),
TaskInstance.start() or
TaskInstance.end(...).
A task instance maintains its state by means of three date-properties:
create
start
end
Access these properties via their respective "getters", which
can be found on the TaskInstance.
Completed task instances are marked with an end date so that
they are not fetched when subsequent queries search for tasks
lists. The completed tasks do, however, remain in the
JBPM_TASKINSTANCE table.
Task instances are the items in an
actor's task list. A signalling task instance is a task
instance that, when completed, sends a signal to its token to
continue the process execution. Blocking task instances are
those that the related token (the path of execution) is not
allowed to leave the task-node before
the task instance is completed. By default, task instances are
configured to be signalling and non-blocking.
If more than one task instance is associated with a
task-node, the process developer can
specify the way in which completion of the task instances
affects continuation of the process. Give any of these values to
the task-node's
signal-property:
This is the default. It proceeds execution when the last task instance has been completed. When no tasks are created on entrance of this node, execution is continued.
This proceeds execution when the last task instance has been completed. When no tasks are created on entrance of this node, execution waits in the task node until tasks are created.
This proceeds execution when the first task instance has been completed. When no tasks are created upon the entry of this node, execution is continued.
This proceeds execution when the first task instance has been completed. When no tasks are created on entrance of this node, execution waits in the task node until tasks are created.
In this case, execution always continues, regardless of whether tasks are created or still unfinished.
In this case, execution never continues, regardless whether tasks are created or still unfinished.
Task instance creation can be based upon a run-time
calculation. In these cases, add an
ActionHandler to the
task-node's
node-enter event and set
create-tasks="false". Here is an example:
public class CreateTasks implements ActionHandler {
public void execute(ExecutionContext executionContext) throws Exception {
Token token = executionContext.getToken();
TaskMgmtInstance tmi = executionContext.getTaskMgmtInstance();
TaskNode taskNode = (TaskNode) executionContext.getNode();
Task changeNappy = taskNode.getTask("change nappy");
// now, 2 task instances are created for the same task.
tmi.createTaskInstance(changeNappy, token);
tmi.createTaskInstance(changeNappy, token);
}
}
Here, the tasks to be created are specified in the
task-node. They could also be
specified in the process-definition
and fetched from the
TaskMgmtDefinition.
(TaskMgmtDefinition extends the
process definition by adding task management information.)
The TaskInstance.end() method is
used to mark task instances as completed. One can optionally
specify a transition in the end method. In case the completion
of this task instance triggers continuation of the execution,
the task-node is left over the
specified transition.
A process definition contains task nodes. A
task-node contains zero or more tasks.
Tasks are static descriptions of part of the process
definition. At run-time, executing tasks result in the creation
of task instances. A task instance corresponds to one entry in
a person's task list.
With the jBPM, one can apply the push (personal task list) and pull (group task list) models of task assignment in combination. The process determines those responsible for a task and push it to their task lists. A task can also be assigned to a pool of actors, in which case each of the actors pull the task and put it in their personal task lists. (Refer to Section 11.3.3, “ The Personal Task List ” and Section 11.3.4, “ The Group Task List ” for more information.)
Assign task instances via the
AssignmentHandler interface:
public interface AssignmentHandler extends Serializable {
void assign( Assignable assignable, ExecutionContext executionContext );
}
An assignment handler implementation is called when a task
instance is created. At that time, the task instance is
assigned to one or more actors. The
AssignmentHandler implementation calls
the assignable methods (setActorId or
setPooledActors) to assign a task. The
assignable item is either a
TaskInstance or a
SwimlaneInstance (that is, a process
role).
public interface Assignable {
public void setActorId(String actorId);
public void setPooledActors(String[] pooledActors);
}
Both TaskInstances and
SwimlaneInstances can be assigned to a
specific user or to a pool of actors. To assign a
TaskInstance to a user, call
Assignable.setActorId(String actorId).
To assign a TaskInstance to a pool of
candidate actors, call
Assignable.setPooledActors(String[]
actorIds).
One can associate each task in the process definition with an handler implementation to perform the assignment at run-time.
When more than one task in a process should be assigned to the same person or group of actors, consider the usage of a swimlane, see Section 11.6, “ Swimlanes ”.
To create reusable AssignmentHandlers,
configure each one via the
processdefinition.xml file. (See Section 17.2, “Delegation” for more information on how to add
configuration to assignment handlers.)
The data model for managing assignments of task instances and
swimlane instances to actors is the following. Each
TaskInstance has an
actorId and a set of pooled actors.
The actorId is the responsible for the
task, while the set of pooled actors represents a collection of
candidates one of whom will become responsible if they take the
task. Both actorId and
pooledActors are optional and can also
be combined.
The personal task list denotes all the task instances that are
assigned to a specific individual. This is indicated by the
presence of the actorId property on a
TaskInstance. Put a
TaskInstance in someone's task list in
one of these ways:
specify an expression in the task element's actor-id attribute
use the TaskInstance.setActorId(String) method from anywhere in the code
use the assignable.setActorId(String) in an AssignmentHandler
To fetch the personal task list for a given user, use
TaskMgmtSession.findTaskInstances(String
actorId).
The pooled actors are the group of candidates to whom the task is offered. One candidate has to accept it. Users can not start working on tasks immediately as that would, potentially, result in a conflict if many people commenced work on the same task. To prevent this, users can only take task instances from the group task list and move these into their personal task lists. It is only when a task is placed on the user's personal task list that her or she can commence working on it.
To put a taskInstance in someone's
group task list, add the user's actorId
or one of the user's groupIds to the
pooledActorIds. To specify the pooled
actors, use one of the following methods:
specify an expression in the attribute
pooled-actor-ids of the task
element in the process
use
TaskInstance.setPooledActorIds(String[])
from anywhere in your code
use
assignable.setPooledActorIds(String[])
in an AssignmentHandler
To fetch the group task list for a given user, make a
collection that includes the user's
actorId and those of all the groups to
which the user belongs. Use
TaskMgmtSession.findPooledTaskInstances(String
actorId) or
TaskMgmtSession.findPooledTaskInstances(List
actorIds) to search for task instances that are
not in a personal task list (actorId==null) and
for which there is a match amongst the pooled
actorId.
The software was designed this way in order to separate the identity component from jBPM task assignment. The jBPM only stores strings as actorIds. It does not understand the relationships between the users and groups or any other identity information.
The actorId always overrides pooled
actors. Hence, a taskInstance that has an
actorId and a list of
pooledActorIds will only show up in
the actor's personal task list. Retain the
pooledActorIds in order to put a task
instance back into the group by simply setting the
taskInstance's
actorId property to null.
A task instance can have its own set of variables and can also "see" the process variables. Task instances are usually created in an execution path (a token). This creates a parent-child relation between the token and the task instance, which is similar to the parent-child relation between the tokens themselves. Note that the normal scoping rules apply.
Use the controller to create, populate and submit variables between the task instance scope and the process scoped variables.
This means that a task instance can 'see' its own variables plus all the variables of its related token.
The controller can be used to create populate and submit variables between the task instance scope and the process scoped variables.
When task instances are created, one can use task controllers populate the task instance variables. When the task instances terminate, one can use task controllers to submit the data belonging to them to the process variables.
Use of task controllers is optional. Task instances also are able to "see" the process variables related to its token. Use task controllers to undertake these tasks:
create copies of task instance variabless so that intermediate updates to them do not affect the process variables until the process is finished. At this time, the copies are submitted back into the process variables.
the task instance variables do not have a one-to-one
relationship with the process variables. For
instance, if the process has variables named
sales in January sales in
February and sales in
March, then the task instance form might
need to show the average sales for those three
months.
Tasks collect input from users. But there are many user interfaces which could be used to present the tasks to the users. E.g. a web application, a swing application, an instant messenger, an email form,... So the task controllers make the bridge between the process variables (=process context) and the user interface application. The task controllers provide a view of process variables to the user interface application.
When a task instance is created, the task controller translates process variables, if there are any, into task variables. The task variables serve as the input for the user interface form. The user input itself is stored in the task variables. When the user ends the task, the task controller updates the process variables based on the task instance data.
In a simple scenario, there is a one-on-one mapping between
process variables and the form parameters. Specify task
controllers in a task element. In this case, the default JBPM
task controller can be used. It takes a list of
variable elementswhich express how the
process variables are copied in the task variables.
The next example demonstrates how to create separate copies of task instance variable, based on the process variables:
<task name="clean ceiling">
<controller>
<variable name="a" access="read" mapped-name="x" />
<variable name="b" access="read,write,required" mapped-name="y" />
<variable name="c" access="read,write" />
</controller>
</task>
The name attribute refers to the name of the process variable. The mapped-name is optional and refers to the name of the task instance variable. If the mapped-name attribute is omitted, mapped-name defaults to the name. Note that the mapped-name is also used as the label for the fields in the web application's task instance form.
Use the access attribute to specify as to
whether or not the variable copied at task instance creation, will
be written back to the process variables at task instance
conclusion. (This information can be used by the user interface to
generate the proper form controls.) The access
attribute is optional and the default access is
read,write.
A task-node can have many tasks whilst a
start-state has one task.
If the simple one-to-one mapping between process variables and
form parameters is too limiting, create a custom
TaskControllerHandler implementation.
Here is the interface for it:.
public interface TaskControllerHandler extends Serializable {
void initializeTaskVariables(TaskInstance taskInstance, ContextInstance contextInstance, Token token);
void submitTaskVariables(TaskInstance taskInstance, ContextInstance contextInstance, Token token);
}
This code sample demonstrates how to configure it:
<task name="clean ceiling">
<controller class="com.yourcom.CleanCeilingTaskControllerHandler">
-- here goes your task controller handler configuration --
</controller>
</task>
A swimlane is a process role. Use this
mechanism to specify that multiple tasks in the process are to
be be undertaken by the same actor. After the first task
instance for a given swimlane is created, the actor is
"remembered" for every subsequent task in the same swimlane. A
swimlane therefore has one assignment.
Study Section 11.3, “
Assignment
” to learn more.
When the first task in a given swimlane is created, the
AssignmentHandler is called. The
Assignable item that is passed to the
AssignmentHandler is
SwimlaneInstance. Every assignment
undertaken on the task instances in a given swimlane will
propagate to the swimlane instance. This is the default
behaviour because the person that takes a task will have a
knowledge of that particular process. Hence, ever subsequent
task instances in that swimlane is automatically assigned to
that user.
It is possible to associate a swimlane with the start task. One does this to capture the process initiator.
A task can be specified in a start-state, which will associate
it with a swimlane. When a new task instance is created, the
current authenticated actor is captured via the
Authentication.getAuthenticatedActorId()
method. The actor is stored in the start task's swimlane.
<process-definition>
<swimlane name='initiator' />
<start-state>
<task swimlane='initiator' />
<transition to='...' />
</start-state>
...
</process-definition>
Add variables to the start task using the normal method. Do so to define the form associated with the task. See Section 11.5, “ Task Controllers ” for more information.
One can associate actions with tasks. There are four standard event types:
task-create, which is fired when a
task instance is created.
task-assign, which is fired when a
task instance is being assigned. Note that in actions that are
executed on this event, one can access the previous actor with
the
executionContext.getTaskInstance().getPreviousActorId();
method.
task-start, which is fired when the
TaskInstance.start() method is called.
Use this optional feature to indicate that the user is actually
starting work on the task instance.
task-end, which is fired when
TaskInstance.end(...) is called. This
marks the completion of the task. If the task is related to a
process execution, this call might trigger the resumption of
the process execution.
Exception handlers can be associated with tasks, For more information about this, read Section 9.7, “ Exception Handling ”.
One can specify timers on tasks. See Section 12.1, “ Timers ”.
It is possible to customise cancel-event for task timers. By default, a task timer cancels when the task is ended but with the cancel-event attribute on the timer, one can customise that to task-assign or task-start. The cancel-event supports multiple events. To combine cancel-event types, specify them in a comma-separated list in the attribute.
To customise a task instance, follow these steps:
create a sub-class of TaskInstance
create a
org.jbpm.taskmgmt.TaskInstanceFactory
implementation
configure the implementation by setting the
jbpm.task.instance.factory configuration
property to the fully qualified class name in the
jbpm.cfg.xml file.
if using a sub-class of TaskInstance,
create a Hibernate mapping file for
the sub-class (using
extends="org.jbpm.taskmgmt.exe.TaskInstance"
add that mapping file to the list in
hibernate.cfg.xml.
Management of users, groups and permissions is termed identity management. The jBPM includes an optional identity component. One can easily replace it with one's company's own data store.
The jBPM identity management component holds knowledge of the organisational model and uses this to assign tasks. This model describes the users, groups, systems and the relationships between these. Optionally, permissions and roles can also be included.
The jBPM handles this by defining an actor as an actual
participant in a process. An actor is identified by its ID
called an actorId. The jBPM has only knowledge about actorIds and
they are represented as
java.lang.Strings for maximum
flexibility. So any knowledge about the organizational model
and the structure of that data is outside the scope of the jBPM's
core engine.
As an extension to jBPM we will provide (in the future) a component to manage that simple user-roles model. This many to many relation between users and roles is the same model as is defined in the J2EE and the servlet specs and it could serve as a starting point in new developments.
Note that the user-roles model as it is used in the servlet, ejb and portlet specifications, is not sufficiently powerful for handling task assignments. That model is a many-to-many relation between users and roles. This doesn't include information about the teams and the organizational structure of users involved in a process.
The classes in yellow are those which pertain to the expression assignment handler discussed next.
A User represents a user or a service. A
Group is any kind of group of users.
Groups can be nested to model the relation
between a team, a business unit and the whole company. Groups have
a type to differentiate between the hierarchical groups and e.g.
hair color groups. Memberships represent the
many-to-many relation between users and groups. A membership can be
used to represent a position in a company. The name of the membership
can be used to indicate the role that the user fulfills in the
group.
The identity component comes with one implementation that evaluates an expression for the calculation of actors during assignment of tasks. Here's an example of using the assignment expression in a process definition:
<process-definition>
<task-node name='a'>
<task name='laundry'>
<assignment expression='previous --> group(hierarchy) --> member(boss)' />
</task>
<transition to='b' />
</task-node>
<para>Syntax of the assignment expression is like this:</para>
first-term --> next-term --> next-term --> ... --> next-term
where
first-term ::= previous |
swimlane(swimlane-name) |
variable(variable-name) |
user(user-name) |
group(group-name)
and
next-term ::= group(group-type) |
member(role-name)
</programlisting>
An expression is resolved from left to right. The first-term specifies
a User or Group in the identity model.
Subsequent terms calculate the next term from the intermediate user or
group.
previous means the task is assigned to the current
authenticated actor. This means the actor that performed the previous step in
the process.
swimlane(swimlane-name) means the user or group is taken
from the specified swimlane instance.
variable(variable-name) means the user or group is taken
from the specified variable instance. The variable instance can contain a
java.lang.String, in which case that user or group is fetched from
the identity component. Or the variable instance contains a User
or Group object.
user(user-name) means the given user is taken from the
identity component.
group(group-name) means the given group is taken from the
identity component.
group(group-type) gets the group for a user. Meaning that
previous terms must have resulted in a User. It searches for the
the group with the given group-type in all the memberships for the user.
member(role-name) gets the user that performs a given role
for a group. The previous terms must have resulted in a Group.
This term searches for the user with a membership to the group for which the name
of the membership matches the given role-name.
When you want to use your own datasource for organizational
information such as your company's user database or LDAP system,
you can remove the jBPM identity component. The only thing
you need to do is make sure that you delete the following line
from the hibernate.cfg.xml.
<mapping resource="org/jbpm/identity/User.hbm.xml"/>
<mapping resource="org/jbpm/identity/Group.hbm.xml"/>
<mapping resource="org/jbpm/identity/Membership.hbm.xml"/>
The ExpressionAssignmentHandler is dependent on the identity
component so you will not be able to use it as is. In case you want
to reuse the ExpressionAssignmentHandler and bind it to your user data
store, you can extend from the ExpressionAssignmentHandler and override
the method getExpressionSession.
protected ExpressionSession getExpressionSession(AssignmentContext assignmentContext);