Language and Page Formatting Options
10.3. Human Task Service
10.3.1. Human Task Service
Human tasks are similar to any other external service and are implemented as a domain-specific service. Refer to Section 9.1, “Domain-Specific Service Nodes” for details about including domain-specific services in a process. Because a human task is a domain-specific service, the process itself contains a high-level, abstract description of the human tasks that need to be executed, and a work item handler is responsible for binding this abstract tasks to a specific implementation. With this pluggable work item handler approach, users can plug in the human task service that is provided, as described below, or they can register their own implementation.
The default implementation of a human task service is based on the WS-HumanTask specification. It manages the life cycle of the tasks (creation, claiming, completion, etc.) and stores the state of all the tasks, task lists, etc. It also supports features like internationalization, calendar integration, different types of assignments, delegation, deadlines, etc. It is implemented as part of the jbpm-human-task module. The WS-HumanTask (WS-HT) specification can be downloaded from the following location http://download.boulder.ibm.com/ibmdl/pub/software/dw/specs/ws-bpel4people/WS-HumanTask_v1.pdf.
10.3.2. Task Life Cycle
Whenever a user task node is triggered during the execution of a process instance, a human task is created, and the process only leaves that node when the human task has been completed or aborted.
The human task life cycle is as follows:
- The task is created and starts at the 'created' stage.
- The task is usually transferred to the 'Ready' stage automatically, and it is displayed on the task lists of users who can claim the task.
- A user claims the task and the status is set to 'Reserved'.
- The user starts the task (executes the task) and the status is set to 'InProgress'
- The user completes the task and specifies the result data of the task, and the status is set to 'Completed'. If the user was unable to complete the task, they indicate this with a fault response (including the associated fault data) and the status is set to 'Failed.
The human task life cycle can also include the following steps:
- Delegating or forwarding a task to be assigned to another user.
- Revoking a task. After claiming a task, a user can revoke the task and it will become available again to all the users who can claim it.
- Temporarily suspending and resuming a task.
- Stopping a task in progress.
- Skipping a task (if the task has been marked as skippable), in which case the task will not be executed.
10.3.3. Integrate a Human Task Service
To integrate an alternative human task service, a custom work item handler must be registered. The custom work item handler can be registered as follows:
StatefulKnowledgeSession ksession = ...; ksession.getWorkItemManager().registerWorkItemHandler("Human Task", new CommandBasedHornetQWSHumanTaskHandler());
By default, this handler will connect to the human task service on the local machine on port 5446. To change the address and port of the human task service, invoke the setConnection(ipAddress, port) method on the CommandBasedHornetQWSHumanTaskHandler.
The communication between the human task service and the process engine, or any task client, is done by sending messages between the client and the server. HornetQ is the default transport mechanism for client server communication.
10.3.4. Interacting with the Human Task Service
The Business Central Console offers a graphical interface for users to interact with the human task service, see Section 8.1, “Business Central Console” for more details. The human task service exposes various methods to manage the life cycle of the tasks through a Java API.
A task client (class org.jbpm.task.service.TaskClient) offers the following methods 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 ) ...
All of the above methods take the following arguments:
- taskId: The ID of the task. This is usually extracted from the currently selected task in the user task list in the user interface.
- userId: The ID of the user that is executing the action. This is usually the ID of the user that is logged in to the application.
- responseHandler: Communication with the task service is asynchronous, so you should use a response handler that will be notified when the results are available.
When a message is invoked on the TaskClient, a message is created that will be sent to the server, and the server will execute the logic that implements the correct action.
The following code sample shows how to create a task client and interact with the task service to create, start and complete a task:
//Use UUID.randomUUID() to ensure the HornetQ Connector has a unique name TaskClient client = new TaskClient(new HornetQTaskClientConnector("HornetQConnector" + UUID.randomUUID(), new HornetQTaskClientHandler(SystemEventListenerFactory.getSystemEventListener()))); client.connect("127.0.0.1", 5446); CommandBasedHornetQWSHumanTaskHandler handler = new CommandBasedHornetQWSHumanTaskHandler(ksession); handler.setClient(client); handler.connect(); ksession.getWorkItemManager().registerWorkItemHandler("Human Task", handler); // 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);
10.3.5. User and Group Assignment
Tasks can be assigned to one or more users. If a task is assigned to one user, it will show up in that user's task list. If the task is assigned to more than one user, any one of those users can claim and execute the task. Tasks can also be assigned to groups, and any user who is a member of one of the groups the task is assigned to can claim the task.
Users and groups need to be registered before tasks can be assigned to them. This can be done dynamically.
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("userA")); taskSession.addGroup(new Group("groupA"));
The human tasks service does not maintain the relationships between users and groups. A user group callback class must be created and listed in the
jbpm-human-task.war/WEB-INF/web.xmlfile. The default implementation,
org.jbpm.task.service.DefaultUserGroupCallbackImpl, assigns all users to all groups and is provided for testing purposes only.
To add the user group callback class open
jbpm-human-task.war/WEB-INF/web.xmland add the class as in the following example:
<!-- use org.jbpm.task.service.DefaultUserGroupCallbackImpl to configure sample user group callback for demo purpose--> <init-param> <param-name>user.group.callback.class</param-name> <param-value>org.jbpm.task.service.DefaultUserGroupCallbackImpl</param-value> </init-param>
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, which are located in the
10.3.6. Starting the Human Task Service
When using an independent human task service that the process engine communicates with, it is necessary to start the service:
org.jbpm.task.service.TaskService taskService = new org.jbpm.task.service.TaskService( emf, SystemEventListenerFactory.getSystemEventListener()); TaskServiceSession taskServiceSession = taskService.createSession(); //adding users to TaskServiceSession taskServiceSession.addUser(new User("Administrator")); taskServiceSession.addUser(new User("jsmith")); LocalTaskService localTaskService = new LocalTaskService( taskService ); humanTaskHandler = new SyncWSHumanTaskHandler( localTaskService, ksession ); humanTaskHandler.setLocal( true ); humanTaskHandler.connect(); ksession.getWorkItemManager().registerWorkItemHandler( "Human Task", humanTaskHandler ); //using HT API ... List<TaskSummary> tasks = localTaskService.getTasksAssignedAsPotentialOwner("jsmith", "en-US");
The task management component uses the Java Persistence API (JPA) to store all task information in a persistent manner. To configure persistence, edit the persistence.xml configuration file.
The following example shows how to use the task management component with hibernate and an in-memory H2 database. Please note that H2 databases are not supported in a production environment. The following is provided as an example only, and providing
createas the value for the
hibernate.hbm2ddl.autoproperty will result in the jBPM schemas being recreated every time the server is restarted. The indexes should only be created once and then this functionality should be disabled. This can be achieved by placing comment tags around the hibernate.hbm2ddl.auto property after the schema has been created:
<!-- <property name="hibernate.hbm2ddl.auto" value="create" /> -->
<?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>
10.3.7. Starting the Human Task Service as a Web Application
The human task service can be started as a web application to simplify deployment. As part of the application configuration, users can select a number of settings to be applied on startup. Configuration is done via
web.xmlwhich is located in the
jbpm-human-task-war/WEB-INF/directory by setting init parameters of the HumanTaskServiceServlet.
The following is a list of the supported parameters and their meaning:
- task.persistence.unit: The name of persistence unit that will be used to build EntityManagerFactory (default org.jbpm.task).
- user.group.callback.class: The implementation of UserGroupCallback interface to be used to resolve users and groups (default DefaultUserGroupCallbackImpl which is provided for testing purposes).
- escalated.deadline.handler.class: The implementation of EscalatedDeadlineHandler interface to be used to handle escalations and notifications (default DefaultEscalatedDeadlineHandler).
- user.info.class: The implementation of UserInfo interface to be used to resolve user/group information such as email address and preferred language.
- load.users: This specifies the location of a file that will be used to initially populate task server db with users. It accepts two types of files: MVEL and properties; It must be suffixed with .mvel or .properties. Location of the file can be either on classpath (with prefix classpath:) or valid URL. NOTE: That with custom user files, Administrator user must always be present.
- load.groups: This specifies the location of a file that will be used to initially populate task server db with groups. It accepts two types of files: MVEL and properties; the file must be suffixed with .mvel or .properties. Location of the file can be either on classpath (with prefix classpath:) or valid URL.
- active.config: The main parameter that controls what transport is configured for Task Server. By default this is set to HornetQ, but it also accepts Mina and JMS.
- mina.host: The host/ip address used to bind Apache Mina server (localhost).
- mina.port: The port used to bind Apache Mina server (default 9123).
- hornetq.port: The port used to bind HornetQ server (default 5446).
- JMSTaskServer.connectionFactory: JNDI name of QueueConnectionFactory to look up (no default).
- JMSTaskServer.transacted : A boolean flag that indicates if JMS session will be transacted or not (no default).
- JMSTaskServer.acknowledgeMode: Acknowledgment mode (default DUPS_OK_ACKNOWLEDGE).
- JMSTaskServer.queueName: The name of JMS queue (no default).
- JMSTaskServer.responseQueueName: The name of JMS response queue (no default).