Human Task 是BPM流程中的节点必需通过人为的手动操作才能够执行。jBPM 5 通过 User Task(jBPM5示例之 User Task) 节点来支持Human Task。Human Task通常要求流程设计者在设计流程时指定流程运行相关的属性,流程类型,流程的执行者,流程运行相关的数据。流程运行时我们根据这些属性运行流程。
Human Task 是BPM核心,为什么可以这样说呢?一位BPM专家曾在某次BPM技术峰会上这样定义BPM:“BPM is a blending of process management/workflow with application integration technology...to support rich human interaction and deep application connectivity”。注意他所表达BPM的目的或价值就是为复杂的企业应用提供广泛的人为操作的可能性。
本文主要目的是通过实验理解jBPM Human Task ,我们所需要的工具包括git,Maven,JBoss等,这些安装可参照软件安装及资料下载。本文主要内容包括:
本部分我们下载编译jBPM 5.2.x代码(https://github.com/droolsjbpm/jbpm/tree/5.2.x),具体使用如下git命令克隆:
git clone --branch=5.2.x [email protected]:droolsjbpm/jbpm.git
mvn clean install -Dmaven.test.skip=true
本部分我们部署jbpm-human-task-war-5.2.6-SNAPSHOT-EE6.war到JBoss 7。具体参照使用4种方式部署应用到JBoss7/WildFly。
我们需要基于jbpm-human-task-war-5.2.6-SNAPSHOT-EE6.war做如下操作:
1. 重命名
unzip jbpm-human-task-war-5.2.6-SNAPSHOT-EE6.war jbpm-human-task.war
jBPM Human Task执行过程需要存储数据在数据库中,我们本实验使用mysql数据库,我们使用如下SQL语句创建数据库jbpm,jbpm_user用来操作数据库jbpm,jbpm_user对应密码jbpm_pass:
CREATE DATABASE jbpm; create user 'jbpm_user'@'localhost' identified by 'jbpm_pass'; grant all on jbpm.* to jbpm_user@'localhost'; FLUSH PRIVILEGES;
3. 创建jbpmDS数据源
使用JBoss 7/WildFly中配置使用Mysql数据库中描述的方法创建数据源jbpmDS指向上面步骤2创建的数据库和用户,数据源如下:
<datasource jndi-name="java:jboss/datasources/jbpmDS" pool-name="jbpmPool"> <connection-url>jdbc:mysql://localhost:3306/jbpm</connection-url> <driver>mysql</driver> <security> <user-name>jbpm_user</user-name> <password>jbpm_pass</password> </security> </datasource>
修改jbpm-human-task.war/WEB-INF/classes/META-INF/persistence.xml文件,配置hibernate.dialect属性为org.hibernate.dialect.MySQL5Dialect,hibernate.show_sql属性为true。
5. 更新hornetq-core-2.2.10.Final.jar
这一步是可选择的,hornetq-core-2.2.10.Final.jar包存在Socket leak等不稳定问题,我建议升级此包。
6. 启动JBoss
使用JBoss启动脚本./standalone.sh启动JBoss,启动完成后可以看到jbpm-human-task.war部署成功提示。我们会发现如下日志信息:
17:19:07,950 INFO [stdout] (ServerService Thread Pool -- 52) Task service startup completed successfully !
[kylin@localhost lib]$ netstat -antulop | grep 13179 (Not all processes could be identified, non-owned process info will not be shown, you would have to be root to see it all.) tcp 0 0 127.0.0.1:4447 0.0.0.0:* LISTEN 13179/java off (0.00/0/0) tcp 0 0 127.0.0.1:5153 0.0.0.0:* LISTEN 13179/java off (0.00/0/0) tcp 0 0 127.0.0.1:9990 0.0.0.0:* LISTEN 13179/java off (0.00/0/0) tcp 0 0 127.0.0.1:9999 0.0.0.0:* LISTEN 13179/java off (0.00/0/0) tcp 0 0 127.0.0.1:8080 0.0.0.0:* LISTEN 13179/java off (0.00/0/0) tcp 0 0 127.0.0.1:44186 127.0.0.1:5153 ESTABLISHED 13179/java keepalive (5695.77/0/0) tcp 0 0 127.0.0.1:34368 127.0.0.1:3306 ESTABLISHED 13179/java keepalive (5695.77/0/0) tcp 0 0 127.0.0.1:5153 127.0.0.1:44186 ESTABLISHED 13179/java keepalive (5695.77/0/0)
上面一部分中,我们创建了空白的数据库jbpm,jBPM使用JPA/Hobernate作为数据持久工具,当JBoss启动成功后Hibernate会向数据库jbpm创建表,我们在mysql管理端使用jbpm_user用户登录后查看表创建情况。
show tables输出:
mysql> show tables; +--------------------------------+ | Tables_in_jbpm | +--------------------------------+ | Attachment | | BooleanExpression | | Content | | Deadline | | Delegation_delegates | | Escalation | | I18NText | | Notification | | Notification_BAs | | Notification_Recipients | | Notification_email_header | | OrganizationalEntity | | PeopleAssignments_BAs | | PeopleAssignments_ExclOwners | | PeopleAssignments_PotOwners | | PeopleAssignments_Recipients | | PeopleAssignments_Stakeholders | | Reassignment | | Reassignment_potentialOwners | | SubTasksStrategy | | Task | | email_header | | task_comment | +--------------------------------+ 23 rows in set (0.00 sec)
我们在jbpm human task 源代码分析 - I的org.jbpm.task.Task部分解释了数据模型类,以上这些表是数据实体模型对应数据库中表的映射。接下来的一个部分我们演示如何使用jBPM Human Task Service提供的接口对数据库中的数据进行操作。
在之前步骤的基础上,本处我们通过TaskClient与Human Task 服务端交互,创建Task,开始Task,结束Task。
org.jbpm.conductor.humantask.TaskAdd演示如何使用TaskClient在客户端执行创建Task的操作,如下为部分代码片段:
TaskClient client = getTaskClientInstance(); client.connect(); Task task = newTask(); ContentData content = new ContentData(); BlockingAddTaskResponseHandler addTaskResponseHandler = new BlockingAddTaskResponseHandler(); client.addTask(task, content, addTaskResponseHandler ); long taskId = addTaskResponseHandler.getTaskId(); System.out.println("Add Task to human task service, taskId: " + taskId);
Add Task to human task service, taskId: 1
mysql> select id, status, actualOwner_id, createdBy_id, activationTime from Task; +----+----------+----------------+--------------+---------------------+ | id | status | actualOwner_id | createdBy_id | activationTime | +----+----------+----------------+--------------+---------------------+ | 1 | Reserved | kylin | kylin | 2013-12-02 16:51:24 | +----+----------+----------------+--------------+---------------------+
org.jbpm.conductor.humantask.Getting演示如何使用TaskClient在客户端执行获取Task的操作,如下为部分代码片段:
TaskClient client = getTaskClientInstance(); client.connect(); BlockingTaskSummaryResponseHandler taskSummaryResponseHandler = new BlockingTaskSummaryResponseHandler(); client.getTasksAssignedAsPotentialOwner("kylin", "en-UK", taskSummaryResponseHandler); List<TaskSummary> tasks = taskSummaryResponseHandler.getResults(); System.out.println("Getting tasks for human task service via user kylin, tasks size: " + tasks.size());
Getting tasks for human task service via user kylin, tasks size: 1
org.jbpm.conductor.humantask.TaskStart演示如何使用TaskClient在客户端执行开始Task的操作,如下为部分代码片段:
TaskClient client = getTaskClientInstance(); client.connect(); BlockingTaskOperationResponseHandler responseHandler = new BlockingTaskOperationResponseHandler(); client.start(1, "kylin", responseHandler); responseHandler.waitTillDone(1000); System.out.println("kylin starting Task ");
kylin starting Task
mysql> select id, status, actualOwner_id, createdBy_id, activationTime from Task; +----+------------+----------------+--------------+---------------------+ | id | status | actualOwner_id | createdBy_id | activationTime | +----+------------+----------------+--------------+---------------------+ | 1 | InProgress | kylin | kylin | 2013-12-02 16:51:24 | +----+------------+----------------+--------------+---------------------+
org.jbpm.conductor.humantask.TaskComplete演示如何使用TaskClient在客户端执行完成Task的操作,如下为部分代码片段:
TaskClient client = getTaskClientInstance(); client.connect(); ContentData content = new ContentData(); BlockingTaskOperationResponseHandler responseHandler = new BlockingTaskOperationResponseHandler(); client.complete(2, "kylin", content, responseHandler); responseHandler.waitTillDone(1000); System.out.println("kylin completing Task ");
kylin completing Task
在Mysql数据库端通过SQL语句查询得到如下结果:
mysql> select id, status, actualOwner_id, createdBy_id, activationTime from Task; +----+-----------+----------------+--------------+---------------------+ | id | status | actualOwner_id | createdBy_id | activationTime | +----+-----------+----------------+--------------+---------------------+ | 1 | Completed | kylin | kylin | 2013-12-02 16:51:24 | +----+-----------+----------------+--------------+---------------------+
另外我们还可以使用如下SQL在Mysql管理端进行管理查询:
select DTYPE, id from OrganizationalEntity; select id, language, text from I18NText;
当一个 Human Task 节点在流程执行过程中被激活,一个人为参与的工作产生,流程只有在该人为参与的工作完成或取消后才离开该节点。
Human Task 的生命周期如下:Next