JBoss.orgCommunity Documentation

Chapter 13. Human Tasks

13.1. Human tasks inside processes
13.1.1. User and group assignment
13.1.2. Task escalation and notification
13.1.3. Data mapping
13.1.4. Swimlanes
13.1.5. Examples
13.2. Human task service
13.2.1. Task life cycle
13.2.2. Linking the human task service to the jBPM engine
13.2.3. Interacting with the human task service
13.2.4. User and group assignment
13.2.5. Starting the human task service
13.2.6. Starting the human task service as web application
13.3. Human task clients
13.3.1. Eclipse demo task client
13.3.2. Web-based task client in jBPM Console
13.4. Human task persistence
13.4.1. Task related entities
13.4.2. Deadline, Escalation and Notification related entities

An important aspect of business processes is human task management. While some of the work performed in a process can be executed automatically, some tasks need to be executed by human actors.

jBPM supports a special human task node inside processes for modeling this interaction with human users. This human task node allows process designers to define the properties related to the task that the human actor needs to execute, like for example the type of task, the actor(s), or the data associated with the task.

jBPM also includes a so-called human task service, a back-end service that manages the life cycle of these tasks at runtime. The jBPM implementation is based on the WS-HumanTask specification. Note however that this implementation is fully pluggable, meaning that users can integrate their own human task solution if necessary.

In order to have human actors participate in your processes, you first need to (1) include human task nodes inside your process to model the interaction with human actors, (2) integrate a task management component (like for example the WS-HumanTask based implementation provided by jBPM) and (3) have end users interact with a human task client to request their task list and claim and complete the tasks assigned to them. Each of these three elements will be discussed in more detail in the next sections.

jBPM supports the use of human tasks inside processes using a special user task node (as shown in the figure above). A user task node represents an atomic task that needs to be executed by a human actor.

[Although jBPM has a special user task node for including human tasks inside a process, human tasks are considered the same as any other kind of external service that needs to be invoked and are therefore simply implemented as a domain-specific service. See the chapter on domain-specific processes to learn more about this.]

A user task node contains the following properties:

  • Id: The id of the node (which is unique within one node container).

  • Name: The display name of the node.

  • TaskName: The name of the human task.

  • Priority: An integer indicating the priority of the human task.

  • Comment: A comment associated with the human task.

  • ActorId: The actor id that is responsible for executing the human task. A list of actor id's can be specified using a comma (',') as separator.

  • GroupId: The group id that is responsible for executing the human task. A list of group id's can be specified using a comma (',') as separator.

  • Skippable: Specifies whether the human task can be skipped, i.e., whether the actor may decide not to execute the task.

  • Content: The data associated with this task.

  • Swimlane: The swimlane this human task node is part of. Swimlanes make it easy to assign multiple human tasks to the same actor. See the human tasks chapter for more detail on how to use swimlanes.

  • On entry and on exit actions: Action scripts that are executed upon entry and exit of this node, respectively.

  • Parameter mapping: Allows copying the value of process variables to parameters of the human task. Upon creation of the human tasks, the values will be copied.

  • Result mapping: Allows copying the value of result parameters of the human task to a process variable. Upon completion of the human task, the values will be copied. A human task has a result variable "Result" that contains the data returned by the human actor. The variable "ActorId" contains the id of the actor that actually executed the task.

You can edit these variables in the properties view (see below) when selecting the user task node, or the most important properties can also be edited by double-clicking the user task node, after which a custom user task node editor is opened, as shown below as well.

In many cases, the parameters of a user task (like for example the task name, actorId, or priority) can be defined when creating the process. You simply fill in the value of these properties in the property editor. It is however likely that some of the properties of the human task are dependent on some data related to the process instance this task is being requested in. For example, if a business process is used to model how to handle incoming sales requests, tasks that are assigned to a sales representative could include information related to that specific sales request, like its unique id, the name of the customer that requested it, etc. You can make your human task properties dynamic in two ways:

  • #{expression}: Task parameters of type String can use #{expression} to embed the value of the given expression in the String. For example, the comment related to a task might be "Please review this request from user #{user}", where user is a variable in the process. At runtime, #{user} will be replaced by the actual user name for that specific process instance. The value of #{expression} will be resolved when creating human task and the #{...} will be replaced by the toString() value of the value it resolves to. The expression could simply be the name of a variable (in which case it will be resolved to the value of the variable), but more advanced MVEL expressions are possible as well, like for example #{person.name.firstname}. Note that this approach can only be used for String parameters. Other parameters should use parameter mapping to map a value to that parameter.
  • Parameter mapping: You can map the value of a process variable (or a value derived from a variable) to a task parameter. For example, if you need to assign a task to a user whose id is a variable in your process, you can do so by mapping that variable to the parameter ActorId, as shown in the following screenshot. [Note that, for parameters of type String, this would be identical to specifying the ActorId using #{userVariable}, so it would probably be easier to use #{expression} in this case, but parameter mapping also allow you to assign a value to properties that are not of type String.]

There are number of situations that can raise a need for escalation of a task, for instance - user assigned to a task can be on vacation or too busy with other work. In such cases task should be automatically reassigned to another actor or group. Escalation can be defined for tasks that are in following statuses:

Whenever an escalation is reached users/groups defined in it will be assigned to the task as potential owners, replacing those that were previously set. If actual owner was already assigned it will be reset and task will be put in READY state.

Following is a list of attributes that can be specified:

In addition to escalation, email notifications can be sent out as well. It is very similar to escalation in terms of definition, allows notification to be sent for tasks that are in following statuses:

Email notification has following properties:

Notification can reference process variables by #{processVariable} and task variables ${taskVariable}. Main difference between those two is that process variables will be resolved at task creation time and task variables will be resolved at notification time. There are several task variables (besides regular ones) that can be used while working with notifications:

An example that illustrates a simple notification message (its body) that shows how different variables can be accessed:

<html>
	<body>
		<b>${owners[0].id} you have been assigned to a task (task-id ${taskId})</b><br>
		You can access it in your task 
		<a href="http://localhost:8080/jbpm-console/app.html#errai_ToolSet_Tasks;Group_Tasks.3">inbox</a><br/>
		Important technical information that can be of use when working on it<br/>
		- process instance id - ${processInstanceId}<br/>
		- work item id - ${workItemId}<br/>
		
		<hr/>
		
		Here are some task variables available
		<ul>
			<li>ActorId = ${doc['ActorId']}</li>
			<li>GroupId = ${doc['GroupId']}</li>
			<li>Comment = ${doc['Comment']}</li>
		</ul>
		<hr/>
		Here are all potential owners for this task
		<ul>
		$foreach{orgEntity : owners}
			<li>Potential owner = ${orgEntity.id}</li>
		$end{}
		</ul>
		
		<i>Regards from jBPM team</i>
	</body>
</html>
	 

Human tasks typically present some data related to the task that needs to be performed to the actor that is executing the task and usually also request the actor to provide some result data related to the execution of the task. Task forms are typically used to present this data to the actor and request results.

User tasks can be used in combination with swimlanes to assign multiple human tasks to the same actor. Whenever the first task in a swimlane is created, and that task has an actorId specified, that actorId will be assigned to (all other tasks of) that swimlane as well. Note that this would override the actorId of subsequent tasks in that swimlane (if specified), so only the actorId of the first human task in a swimlane will be taken into account, all others will then take the actorId as assigned in the first one.

Whenever a human task that is part of a swimlane is completed, the actorId of that swimlane is set to the actorId that executed that human task. This allows for example to assign a human task to a group of users, and to assign future tasks of that swimlame to the user that claimed the first task. This will also automatically change the assignment of tasks if at some point one of the tasks is reassigned to another user.

To add a human task to a swimlane, simply specify the name of the swimlane as the value of the "Swimlane" parameter of the user task node. A process must also define all the swimlanes that it contains. To do so, open the process properties by clicking on the background of the process and click on the "Swimlanes" property. You can add new swimlanes there.

The new BPMN2 Eclipse editor will support a visual representation of swimlanes (as horizontal lanes), so that it will be possible to define a human task as part of a swimlane simply by dropping the task in that lane on the process model.

As far as the jBPM engine is concerned, human tasks are similar to any other external service that needs to be invoked and are implemented as a domain-specific service. (For more on domain-specific services, see the chapter on them here.) Because a human task is an example of such a domain-specific service, the process itself only contains a high-level, abstract description of the human task to be executed and a work item handler that is responsible for binding this (abstract) task to a specific implementation.

Users can plug in any human task service implementation, such as the one that's provided by jBPM, or they may register their own implementation. In the next paragraphs, we will describe the human task servcie implementation provided by jBPM.

The jBPM project provides a default implementation of a human task service based on the WS-HumanTask specification. If you do not need to integrate jBPM with another existing implementation of a human task service, you can use this service. The jBPM implementation manages the life cycle of the tasks (creation, claiming, completion, etc.) and stores the state of all the tasks, task lists, and other associated information. It also supports features like internationalization, calendar integration, different types of assignments, delegation, escalation and deadlines. The code for the implementation itself can be found in the jbpm-human-task module.

The jBPM task service implementation is based on the WS-HumanTask (WS-HT) specification. This specification defines (in detail) the model of the tasks, the life cycle, and many other features. It is very comprehensive and the first version can be found here.

From the perspective of a process, when a user task node is encountered during the execution, a human task is created. The process will then only leave the user task node when the associated human task has been completed or aborted.

The human task itself usually has a complete life cycle itself as well. For details beyond what is described below, please check out the WS-HumanTask specification. The following diagram is from the WS-HumanTask specification and describes the human task life cycle.

A newly created task starts in the "Created" stage. Usually, it will then automatically become "Ready", after which the task will show up on the task list of all the actors that are allowed to execute the task. The task will stay "Ready" until one of these actors claims the task, indicating that he or she will be executing it.

When a user then eventually claims the task, the status will change to "Reserved". Note that a task that only has one potential (specific) actor will automatically be assigned to that actor upon creation of the task. When the user who has claimed the task starts executing it, the task status will change from "Reserved" to "InProgress".

Lastly, once the user has performed and completed the task, the task status will change to "Completed". In this step, the user can optionally specify the result data related to the task. If the task could not be completed, the user could also indicate this by using a fault response, possibly including fault data, in which case the status would change to "Failed".

While the life cycle explained above is the normal life cycle, the specification also describes a number of other life cycle methods, including:

Just like any other external service, the human task service can be integrated with the jBPM engine by registering a work item handler that translates the abstract work item (in this case a human task) to a specific invocation of a service (in this case, the jBPM implementation of the human task service). This work item handler has been implemented in the org.jbpm.process.workitem.wsht.WSHumanTaskHandler class in the jbpm-human-task module, so that you can register this work item handler like this:

StatefulKnowledgeSession ksession = ...;

ksession.getWorkItemManager().registerWorkItemHandler("Human Task", new WSHumanTaskHandler());

If you are using persistence, you should use the CommandBasedWSHumanTaskHandler instead (see the org.jbpm.process.workitem.wsht.CommandBasedWSHumanTaskHandler class in the jbpm-human-task module), like this:

StatefulKnowledgeSession ksession = ...;

ksession.getWorkItemManager().registerWorkItemHandler("Human Task", new CommandBasedWSHumanTaskHandler());

By default, this handler will connect to the human task service on the local machine on port 9123. You can easily change the address and port of the human task service that should be used by by invoking the setConnection(ipAddress, port) method on the WSHumanTaskHandler.

The communication between the human task service and the process engine, or any task client, is message based. While the client/server transport mechanism is pluggable (allowing different implementations), the default is Mina (http://mina.apache.org/). An alternative implementation using HornetQ is also available.

The human task service exposes a Java API for managing the life cycle of tasks. This allows clients to integrate (at a low level) with the human task service. Note that end users should probably not interact with this low-level API directly, but use one of the more user-friendly task clients (see below) instead. These clients offer a graphical user interface to request task lists, claim and complete tasks, and manage tasks in general. The task clients listed below use the Java API to internally interact with the human task service. Of course, the low-level API is also available so that developers can use it in their code to interact with the human task service directly.

A task client (class org.jbpm.task.service.TaskClient) offers the following methods (among others) for managing the life cycle of human tasks:

public void start( long taskId, String userId, TaskOperationResponseHandler responseHandler )

public void stop( long taskId, String userId, TaskOperationResponseHandler responseHandler )
public void release( long taskId, String userId, TaskOperationResponseHandler responseHandler )
public void suspend( long taskId, String userId, TaskOperationResponseHandler responseHandler )
public void resume( long taskId, String userId, TaskOperationResponseHandler responseHandler )
public void skip( long taskId, String userId, TaskOperationResponseHandler responseHandler )
public void delegate( long taskId, String userId, String targetUserId,
                      TaskOperationResponseHandler responseHandler )
public void complete( long taskId, String userId, ContentData outputData,
                      TaskOperationResponseHandler responseHandler )

If you take a look at the method signatures you will notice that almost all of these methods take the following arguments:

When you invoke a message on the TaskClient, a message is created that will be sent to the server. The server then executes the operation requested in the message.

The following code sample shows how to create a task client and interact with the task service to create, start and complete a task.

TaskClient client = new TaskClient(new MinaTaskClientConnector("client 1",

    new MinaTaskClientHandler(SystemEventListenerFactory.getSystemEventListener())));
client.connect("127.0.0.1", 9123);
// adding a task
BlockingAddTaskResponseHandler addTaskResponseHandler = new BlockingAddTaskResponseHandler();
Task task = ...; 
client.addTask( task, null, addTaskResponseHandler );
long taskId = addTaskResponseHandler.getTaskId();
        
// getting tasks for user "bobba"
BlockingTaskSummaryResponseHandler taskSummaryResponseHandler =
    new BlockingTaskSummaryResponseHandler();
client.getTasksAssignedAsPotentialOwner("bobba", "en-UK", taskSummaryResponseHandler);
List<TaskSummary> tasks = taskSummaryResponseHandler.getResults();
// starting a task
BlockingTaskOperationResponseHandler responseHandler =
    new BlockingTaskOperationResponseHandler();
client.start( taskId, "bobba", responseHandler );
responseHandler.waitTillDone(1000); 
// completing a task
responseHandler = new BlockingTaskOperationResponseHandler();
client.complete( taskId, "bobba".getId(), null, responseHandler );
responseHandler.waitTillDone(1000);

Tasks can be assigned to one specific user. In that case, the task will show up on the task list of that specific user only. If a task is assigned to more than one user, any of those users can claim and execute this task. Tasks can also be assigned to one or more groups. This means that any user that is part of the group can claim and execute the task.

The human task service needs to know about valid user and group ids (to make sure tasks are assigned to existing users and/or groups to avoid errors and tasks that end up assigned to non-existing users). User and group registration has to be done before tasks can be assigned to them. One possible registration method is to dynamically adding users and groups to the task service session:

EntityManagerFactory emf = Persistence.createEntityManagerFactory("org.jbpm.task");

TaskService taskService = new TaskService(emf, SystemEventListenerFactory.getSystemEventListener());
TaskServiceSession taskSession = taskService.createSession();
// now register new users and groups
taskSession.addUser(new User("krisv"));
taskSession.addGroup(new Group("developers"));

The human task service itself does not maintain the relationship between users and groups. This is considered outside the scope of the human task service: in general, businesses already have existing services that manage this information (i.e. an LDAP service). The human task service does allow you to specify the list of groups that a user is part of, so that this information can also be taken into account when managing tasks.

For example, if a task is assigned to the group "sales" and the user "sales-rep-1", who is a member of "sales", wants to claim that task, then that user needs to pass the fact that he is a member of "sales" when requesting the list of tasks that he is assigned to as potential owner:

List<String> groups = new ArrayList<String>();

groups.add("sales");
taskClient.getTasksAssignedAsPotentialOwner("sales-rep", groups, "en-UK", taskSummaryHandler);

The WS-HumanTask specification also introduces the role of an administrator. An administrator can manipulate the life cycle of the task, even though he might not be assigned as a potential owner of that task. By default, jBPM registers a special user with userId "Administrator" as the administrator of each task. You should therefor make sure that you always define at least a user "Adminstrator" when registering the list of valid users at the task service.

It is often necessary to hook into existing systems and/or services (such as LDAP) where users and groups are maintained in order to perform validation without having to manually register all users and group with the task service. jBPM provides the UserGroupCallback interface which allows you to create your own implementation for user and group management:



public interface UserGroupCallback {
    /**
     * Resolves existence of user id.
     * @param userId    the user id assigned to the task
     * @return true if userId exists, false otherwise.
     */
    boolean existsUser(String userId);
    
    /**
     * Resolves existence of group id.
     * @param groupId   the group id assigned to the task
     * @return true if groupId exists, false otherwise.
     */
    boolean existsGroup(String groupId);
    
    /**
     * Returns list of group ids for specified user id.
     * @param userId    the user id assigned to the task
     * @param groupIds  list of group ids assigned to the task
     * @param allExistingGroupIds   list of all currently known group ids
     * @return List of group ids.
     */
    List<String> getGroupsForUser(String userId, List<String> groupIds, List<String> allExistingGroupIds);
}

If you register your own implementation of the UserGroupCallback interface, the human task service will call it whenever it needs to perform user and group validation. Here is a very simple example implementation which treats all users and groups as being valid:



public class DefaultUserGroupCallbackImpl implements UserGroupCallback {
    public boolean existsUser(String userId) {
        // accept all by default
        return true;
    }
    public boolean existsGroup(String groupId) {
        // accept all by default
        return true;
    }
    
    public List<String> getGroupsForUser(String userId, List<String> groupIds,
            List<String> allExistingGroupIds) {
        if(groupIds != null) {
            List<String> retList = new ArrayList<String>(groupIds);
            // merge all groups
            if(allExistingGroupIds != null) {
                for(String grp : allExistingGroupIds) {
                    if(!retList.contains(grp)) {
                        retList.add(grp);
                    }
                }
            } 
            return retList;
        } else {
            // return empty list by default
            return new ArrayList<String>();
        }
    }
}

You can register your own implementation of the UserGroupCallback interface in a properties file called jbpm.usergroup.callback.properties which should be available on the classpath, for example:



jbpm.usergroup.callback=org.jbpm.task.service.DefaultUserGroupCallbackImpl

or via a system property, for example -Djbpm.usergroup.callback=org.jbpm.task.service.DefaultUserGroupCallbackImpl. If you are using the jBPM installer, you can also modify $jbpm-installer-dir$/task-service/resources/org/jbpm/jbpm.usergroup.callback.properties directly to register your own callback implementation.

jBPM comes with a dedicated UserGroupCallback implementation for LDAP servers that allows task server to retrieve user and group/role information directly from LDAP. To be able to use this callback it must be configured according to specifics of LDAP server and its structure to collect proper information.

LDAP UserGroupCallback properties
  • ldap.bind.user : username used to connect to the LDAP server (optional if LDAP server accepts anonymous access)
  • ldap.bind.pwd : password used to connect to the LDAP server(optional if LDAP server accepts anonymous access)
  • ldap.user.ctx : context in LDAP that will be used when searching for user information (mandatory)
  • ldap.role.ctx : context in LDAP that will be used when searching for group/role information (mandatory)
  • ldap.user.roles.ctx : context in LDAP that will be used when searching for user group/role membership information (optional, if not given ldap.role.ctx will be used)
  • ldap.user.filter : filter that will be used to search for user information, usually will contain substitution keys {0} to be replaced with parameters (mandatory)
  • ldap.role.filter : filter that will be used to search for group/role information, usually will contain substitution keys {0} to be replaced with parameters (mandatory)
  • ldap.user.roles.filter : filter that will be used to search for user group/role membership information, usually will contain substitution keys {0} to be replaced with parameters (mandatory)
  • ldap.user.attr.id : attribute name of the user id in LDAP (optional, if not given 'uid' will be used)
  • ldap.roles.attr.id : attribute name of the group/role id in LDAP (optional, if not given 'cn' will be used)
  • ldap.user.id.dn : is user id a DN, instructs the callback to query for user DN before searching for roles (optional, default false)
  • java.naming.factory.initial : initial conntext factory class name (default com.sun.jndi.ldap.LdapCtxFactory)
  • java.naming.security.authentication : authentication type (none, simple, strong where simple is default one)
  • java.naming.security.protocol : specifies security protocol to be used, for instance ssl
  • java.naming.provider.url : LDAP url to be used default is ldap://localhost:389, or if protocol is set to ssl ldap://localhost:636
Depending on how human task server is started LDAP callback can be configured in two ways:
  • programatically - build property object with all required attributes and register new callback
    
    
    Properties properties = new Properties();
    properties.setProperty(LDAPUserGroupCallbackImpl.USER_CTX, "ou=People,dc=my-domain,dc=com");
    properties.setProperty(LDAPUserGroupCallbackImpl.ROLE_CTX, "ou=Roles,dc=my-domain,dc=com");
    properties.setProperty(LDAPUserGroupCallbackImpl.USER_ROLES_CTX, "ou=Roles,dc=my-domain,dc=com");
    properties.setProperty(LDAPUserGroupCallbackImpl.USER_FILTER, "(uid={0})");
    properties.setProperty(LDAPUserGroupCallbackImpl.ROLE_FILTER, "(cn={0})");
    properties.setProperty(LDAPUserGroupCallbackImpl.USER_ROLES_FILTER, "(member={0})");
    UserGroupCallback ldapUserGroupCallback = new LDAPUserGroupCallbackImpl(properties);
    UserGroupCallbackManager.getInstance().setCallback(ldapUserGroupCallback);
          
  • declaratively - create property file (jbpm.usergroup.callback.properties) with all required attributes, place it on the root of the classpath and declare LDAP callback to be registered (see section Starting the human task server for deatils). Alternatively, location of jbpm.usergroup.callback.properties can be specified via system property -Djbpm.usergroup.callback.properties=FILE_LOCATION_ON_CLASSPATH
    
    
    #ldap.bind.user=
    #ldap.bind.pwd=
    ldap.user.ctx=ou\=People,dc\=my-domain,dc\=com
    ldap.role.ctx=ou\=Roles,dc\=my-domain,dc\=com
    ldap.user.roles.ctx=ou\=Roles,dc\=my-domain,dc\=com
    ldap.user.filter=(uid\={0})
    ldap.role.filter=(cn\={0})
    ldap.user.roles.filter=(member\={0})
    #ldap.user.attr.id=
    #ldap.roles.attr.id=
          

The human task service is a completely independent service that the process engine communicates with. We therefore recommend that you start it as a separate service as well. The jBPM installer contains a command to start the task server (in this case using Mina as transport protocol), or you can use the following code fragment:

EntityManagerFactory emf = Persistence.createEntityManagerFactory("org.jbpm.task");

TaskService taskService = new TaskService(emf, SystemEventListenerFactory.getSystemEventListener());
MinaTaskServer server = new MinaTaskServer( taskService );
Thread thread = new Thread( server );
thread.start();

The task management component uses the Java Persistence API (JPA) to store all task information in a persistent manner. To configure the persistence, you need to modify the persistence.xml configuration file accordingly. We refer to the JPA documentation on how to do that. The following fragment shows for example how to use the task management component with hibernate and an in-memory H2 database:


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persistence
    version="1.0"
    xsi:schemaLocation=
      "http://java.sun.com/xml/ns/persistence
       http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd
       http://java.sun.com/xml/ns/persistence/orm
       http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"
    xmlns:orm="http://java.sun.com/xml/ns/persistence/orm"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/persistence">

  <persistence-unit name="org.jbpm.task">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <class>org.jbpm.task.Attachment</class>
    <class>org.jbpm.task.Content</class>
    <class>org.jbpm.task.BooleanExpression</class>
    <class>org.jbpm.task.Comment</class>
    <class>org.jbpm.task.Deadline</class>
    <class>org.jbpm.task.Comment</class>
    <class>org.jbpm.task.Deadline</class>
    <class>org.jbpm.task.Delegation</class>
    <class>org.jbpm.task.Escalation</class>
    <class>org.jbpm.task.Group</class>
    <class>org.jbpm.task.I18NText</class>
    <class>org.jbpm.task.Notification</class>
    <class>org.jbpm.task.EmailNotification</class>
    <class>org.jbpm.task.EmailNotificationHeader</class>
    <class>org.jbpm.task.PeopleAssignments</class>
    <class>org.jbpm.task.Reassignment</class>
    <class>org.jbpm.task.Status</class>
    <class>org.jbpm.task.Task</class>
    <class>org.jbpm.task.TaskData</class>
    <class>org.jbpm.task.SubTasksStrategy</class>
    <class>org.jbpm.task.OnParentAbortAllSubTasksEndStrategy</class>
    <class>org.jbpm.task.OnAllSubTasksEndParentEndStrategy</class>
    <class>org.jbpm.task.User</class>

    <properties>
      <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
      <property name="hibernate.connection.driver_class" value="org.h2.Driver"/>
      <property name="hibernate.connection.url" value="jdbc:h2:mem:mydb" />
      <property name="hibernate.connection.username" value="sa"/>
      <property name="hibernate.connection.password" value="sasa"/>
      <property name="hibernate.connection.autocommit" value="false" />
      <property name="hibernate.max_fetch_depth" value="3"/>
      <property name="hibernate.hbm2ddl.auto" value="create" />
      <property name="hibernate.show_sql" value="true" />
    </properties>
  </persistence-unit>
</persistence>

The first time you start the task management component, you need to make sure that all the necessary users and groups are added to the database. Our implementation requires all users and groups to be predefined before trying to assign a task to that user or group. So you need to make sure you add the necessary users and group to the database using the taskSession.addUser(user) and taskSession.addGroup(group) methods. Note that you at least need an "Administrator" user as all tasks are automatically assigned to this user as the administrator role.

The jbpm-human-task module contains a org.jbpm.task.RunTaskService class in the src/test/java source folder that can be used to start a task server. It automatically adds users and groups as defined in LoadUsers.mvel and LoadGroups.mvel configuration files.

The jBPM installer automatically starts a human task service (using an in-memory H2 database) as a separate Java application. This task service is defined in the task-service directory in the jbpm-installer folder. You can register new users and task by modifying the LoadUsers.mvel and LoadGroups.mvel scripts in the resources directory.

To allow Task Server to perform escalations and notification a bit of configuration is required. Most of the configuration is for notification support as it relies on external system (mail server) but as they are handled by EscalatedDeadlineHandler implementation so configuration apply to both.



// configure email service
Properties emailProperties = new Properties();
emailProperties.setProperty("from", "jbpm@domain.com");
emailProperties.setProperty("replyTo", "jbpm@domain.com");
emailProperties.setProperty("mail.smtp.host", "localhost");
emailProperties.setProperty("mail.smtp.port", "2345");
// configure default UserInfo
Properties userInfoProperties = new Properties();
// : separated values for each org entity email:locale:display-name
userInfoProperties.setProperty("john", "john@domain.com:en-UK:John");
userInfoProperties.setProperty("mike", "mike@domain.com:en-UK:Mike");
userInfoProperties.setProperty("Administrator", "admin@domain.com:en-UK:Admin");
// build escalation handler    
DefaultEscalatedDeadlineHandler handler = new DefaultEscalatedDeadlineHandler(emailProperties);
// set user info on the escalation handler
handler.setUserInfo(new DefaultUserInfo(userInfoProperties));
EntityManagerFactory emf = Persistence.createEntityManagerFactory("org.jbpm.task");
// when building TaskService provide escalation handler as argument
TaskService taskService = new TaskService(emf, SystemEventListenerFactory.getSystemEventListener(), handler);
MinaTaskServer server = new MinaTaskServer( taskService );
Thread thread = new Thread( server );
thread.start();

Note that default implementation of UserInfo is just for demo purposes to have a fully operational task server. Custom user info classes can be provided that implement following interface:



public interface UserInfo {
    String getDisplayName(OrganizationalEntity entity);
    
    Iterator<OrganizationalEntity> getMembersForGroup(Group group);
    
    boolean hasEmail(Group group);
    
    String getEmailForEntity(OrganizationalEntity entity);
    
    String getLanguageForEntity(OrganizationalEntity entity);
}
      

If you are using the jBPM installer, just drop your property files into $jbpm-installer-dir$/task-service/resources/org/jbpm/, make sure that they are named email.properties and userinfo.properties.

More production alike configuration would be to use LDAP server as user information repository and to achieve that a dedicated UserInfo implementation is shipped with jBPM - LDAPUserInfoImpl. This is especially useful when configuring task server to use LDAP based user group callback, with this complete user/group information are externalized to LDAP server.

LDAP UserGroupCallback properties
  • ldap.bind.user : username used to connect to the LDAP server (optional if LDAP server accepts anonymous access)
  • ldap.bind.pwd : password used to connect to the LDAP server(optional if LDAP server accepts anonymous access)
  • ldap.user.ctx : context in LDAP that will be used when searching for user information (mandatory)
  • ldap.role.ctx : context in LDAP that will be used when searching for group/role information (mandatory)
  • ldap.user.filter : filter that will be used to search for user information, usually will contain substitution keys {0} to be replaced with parameters (mandatory)
  • ldap.role.filter : filter that will be used to search for group/role information, usually will contain substitution keys {0} to be replaced with parameters (mandatory)
  • ldap.role.members.filter : filter that will be used to search for user group/role membership information, usually will contain substitution keys {0} to be replaced with parameters (optional default same as ldap.role.filter)
  • ldap.email.attr.id : attribute id that contains email address in LDAP (default mail)
  • ldap.name.attr.id : attribute id that contians display name in LDAP (default displayName)
  • ldap.lang.attr.id : attribute id that contians language information (default locale)
  • ldap.member.attr.id : attribute id on group/role object in LDAP that contains members (default member)
  • ldap.user.attr.id : attribute id that contains user id in LDAP server (default uid)
  • ldap.role.attr.id : attribute id that contains group/role id in LDAP server (default cn)
  • ldap.entity.id.dn : instructs if the organizational entity is (or can be) DN, especially important when members of a group will be returned as DN instead of user ids (default false)
  • java.naming.factory.initial : initial conntext factory class name (default com.sun.jndi.ldap.LdapCtxFactory)
  • java.naming.security.authentication : authentication type (none, simple, strong where simple is default one)
  • java.naming.security.protocol : specifies security protocol to be used, for instance ssl
  • java.naming.provider.url : LDAP url to be used default is ldap://localhost:389, or if protocol is set to ssl ldap://localhost:636
Depending on how human task server is started LDAP user info can be configured in two ways:
  • programatically - build property object with all required attributes and register new user info on escalation handler
    
    
    Properties properties = new Properties();
    properties.setProperty(LDAPUserInfoImpl.USER_CTX, "ou=People,dc=jbpm,dc=org");
    properties.setProperty(LDAPUserInfoImpl.ROLE_CTX, "ou=Roles,dc=jbpm,dc=org");
    properties.setProperty(LDAPUserInfoImpl.USER_FILTER, "(uid={0})");
    properties.setProperty(LDAPUserInfoImpl.ROLE_FILTER, "(cn={0})");
    properties.setProperty(LDAPUserInfoImpl.IS_ENTITY_ID_DN, "true");
    UserInfo ldapUserInfo = new LDAPUserInfoImpl(properties);
    DefaultEscalatedDeadlineHandler handler = new DefaultEscalatedDeadlineHandler(emailProperties);
    handler.setUserInfo(ldapUserInfo);
          
  • declaratively - create property file (jbpm.user.info.properties) with all required attributes, place it on the root of the classpath and declare LDAP user info implementation to be registered (see section Starting the human task server for deatils). Alternatively, location of jbpm.user.info.properties can be specified via system property -Djbpm.user.info.properties=FILE_LOCATION_ON_CLASSPATH
    
    
    #ldap.bind.user=
    #ldap.bind.pwd=
    ldap.user.ctx=ou\=People,dc\=my-domain,dc\=com
    ldap.role.ctx=ou\=Roles,dc\=my-domain,dc\=com
    ldap.user.filter=(uid\={0})
    ldap.role.filter=(cn\={0})
    #ldap.role.members.filter=
    #ldap.email.attr.id
    #ldap.name.attr.id
    #ldap.lang.attr.id
    #ldap.member.attr.id
    #ldap.user.attr.id
    #ldap.role.attr.id
    ldap.entity.id.dn=true
          

Human task service can be started as web application to simplify deployment. As part of application configuration user can select number of settings to be applied on startup. Configuration is done via web.xml of jbpm-human-task-war application by setting init parameters of the HumanTaskServiceServlet.Following is a complete list of supported parameters and their meaning:

General settings

Transport settings

Apache Mina

HornetQ

JMS

The folowing entity relationship diagram (ERD) shows the persitent entities used by the Human Task service. (Clicking on the image below will take you to an enlarged view of the image.)

Human Task service data model

Figure 13.1. Human Task service data model


The data model above is organized around 2 groups of entities:

  • The task entity which represents the main information for a task. (See the righthand side of the ERD above.)
  • The deadline, escalation and notification entities which represent deadlines and escalations for a task as well as any notifications associated with those deadlines. (See the lefthand side of the ERD above.)

Two other main entities in the data model are the i18ntext and organizationalentity.

  • The i18ntext entity is used to store text which may be language related, such as names or descriptions entered by users.
  • The organizationalentity entity represents a user in some way.

The following paragraphs and tables describe the group of entities including and associated with the task entity. These entities are shown on the right hand side of the ERD. (See below for information about the deadline, escalation and notification group of entities).

The column “FK” in the tables below, indicates whether or not a column in a database table has a foreign key constraint on it. If the “Nullable” column is empty, then the described database table column is nullable.

While a number of foreign key columns of different tables are specified as non-nullable, many of these columns will simply contain the value -1 or 0 if there is no associated entity.

The task entity contains much of the essential information for describing a task. Although a number of columns are not nullable, many of them are simply set to "-1" if the value used in the column hasn't been set by the task service.

Table 13.1. Task

FieldDescriptionNullableFK
idThe primary key of the task identityNOT 
priorityThe priority of the taskNOT 
allowedtodelegateThe group to whom this task may be delegated  
statusThe status of the task  
previousstatusThe previous status of the task  
actualowner_idThe id of the organizational entity who owns the taskNOTFK
createdby_idThe id of the organizational entity who created the taskNOTFK
createdonThe timestamp describing when this task was created  
activationtimeThe timestamp describing when this task was activated  
expirationtimeThe timestamp describing when this task will expire  
skipableWhether or not this task may be skippedNOT 
workitemidThe id of the work item associated with this task (see jBPM core schema)NOT 
processinstanceidThe id of the process instance associated with this task (see jBPM core schema)NOT 
documentaccesstypeHow a document associated with the task can be accessed  
documenttypeThe type of data in the document  
documentcontentidThe id of the content entity containing the document dataNOT 
outputaccesstypeHow the output document associated with the task can be accessed  
outputtypeThe type of data in the output document  
outputcontentidThe id of the content entity containing the output document dataNOT 
faultnameThe name of the fault generated, if a fault occurs  
faultaccesstypeHow the document associated with the fault can be accessed  
faulttypeThe type of data in the fault document  
faultcontentidThe id of the content entity containing the fault document dataNOT 
parentidThis is the id of the parent taskNOT 
processidThe name (id) of the associated process  
processsessionidThe id of the associated (knowledge) sessionNOT 
taskinitiator_idThe id of the organizational entity who created the taskNOTFK

The subtasksstrategy entity is used to save the strategy that describes how parent and sub-tasks should react when either parent or sub-tasks are ended.


The organizationalentity entity is extended to represent the different people assignments that are part of the task.


The attachment entity describes attachments that have been added to the task.


The task_comment entity describes comments added to tasks.


The delegation_delegates table is a join table for relationships between the task entity and the organizationalentity.


The peopleassignments_stakeholders table is a join table that describes which organizationalentity entities are task stakeholders of a particular task.


The peopleassignments_potowners table is a join table that describes which organizationalentity entities are potential owners of a particular task.


The peopleassignments_exclowners table is a join table that describes which organizationalentity entities are the excluded owners of a particular task.


The peopleassignments_bas table is a join table that describes which organizationalentity entities are business administrators of a particular task.


The peopleassignments_recipients table is a join table that describes which organizationalentity entities are notification recipients for a particular task.


The following paragraphs and tables describe the group of entities having to do with deadline, escalation, and notification information. These entities are shown on the left hand side of the ERD diagram above.

The deadline entity represents a deadline for a task.


The escalation entity descibes an escalation action that should be taken for a particular deadline.


The booleanexpression entity represents an expression that evaluates to a boolean. These expressions are used in order to determine whether or not a constraint should be applied.


The notification entity describes a notification generated by an escalation action.


The email_header entity describes an e-mail that will be sent as part of a notification.


The notification_email_header table is a join table that describes and qualifies which email_header entities are part of a notification.


The reassignment entity describes reassignments associated with escalations.


The reassignments_potentialowners table is a join table that describes which organizationalentity entities are potential owners if a reassignment happens as part of an escalation.


The notification_bas table is a join table that describes which business administrators will be notified by a notification.


The notification_recipients table is a join table that describes which recipients entities will be received a notification.


The content entity represents the content of a document, output document, fault or other object.


The i18ntext entity is used by a number of different other entities to store text fields. The deadline, notification, reassignment and task entities use this entity to store descriptions, subjects, names and other documentation.

Although all foreign keys are not nullable, they will be set to 0 if they are not being used.