概述
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 Human Task 代码
- 部署jBPM Human Task 服务到JBoss
- jBPM Human Task 数据模型
- 通过TaskClient与Human Task 服务端交互
- Human Task的生命周期
下载编译jBPM Human Task 代码
本部分我们下载编译jBPM 5.2.x代码(https://github.com/droolsjbpm/jbpm/tree/5.2.x),具体使用如下git命令克隆:
git clone --branch=5.2.x git@github.com:droolsjbpm/jbpm.git
克隆完成使用如下maven命令编译jBPM 5.2.x代码:
mvn clean install -Dmaven.test.skip=true
如以上步骤没有出现错误则该部分完成,我们需要如下编译生成的包或Maven本地仓库中的包来供我们使用:
- jbpm-human-task-war-5.2.6-SNAPSHOT-EE6.war - 位于jbpm-human-task-war/target目录下
部署jBPM Human Task 服务到JBoss
本部分我们部署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
2. 创建数据库
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>
4. 修改persistence.xml
修改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 !
使用netstat命令,输出所有端口使用情况:
[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)
注意,5153为 Human Task Service在服务器端监听的端口。
jBPM Human Task 数据模型
上面一部分中,我们创建了空白的数据库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所需要的表共23个。
我们在jbpm human task 源代码分析 - I的org.jbpm.task.Task部分解释了数据模型类,以上这些表是数据实体模型对应数据库中表的映射。接下来的一个部分我们演示如何使用jBPM Human Task Service提供的接口对数据库中的数据进行操作。
通过TaskClient与Human Task 服务端交互
在之前步骤的基础上,本处我们通过TaskClient与Human Task 服务端交互,创建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);
以Java Application的方式运行org.jbpm.conductor.humantask.TaskAdd输出
Add Task to human task service, taskId: 1
在Mysql数据库端通过SQL语句查询得到如下结果:
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 |
+----+----------+----------------+--------------+---------------------+
获取Task
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());
以Java Application的方式运行org.jbpm.conductor.humantask.TaskGetting输出
Getting tasks for human task service via user kylin, tasks size: 1
开始Task
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 ");
以Java Application的方式运行org.jbpm.conductor.humantask.TaskStart输出
kylin starting Task
在Mysql数据库端通过SQL语句查询得到如下结果:
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 |
+----+------------+----------------+--------------+---------------------+
完成Task
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 ");
以Java Application的方式运行org.jbpm.conductor.humantask.TaskComplete输出
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 节点在流程执行过程中被激活,一个人为参与的工作产生,流程只有在该人为参与的工作完成或取消后才离开该节点。
Human Task 的生命周期如下:- Task被创建,一开始状态为'created'
- Task状态通常会自动转变为'Ready',该状态的Task会在Task列表中,可以被用户请求
- Task被用户请求,状态变为'Reserved'
- Task被用户开始,状态变为'InProgress'
- Task被用户结束,状态变成'Completed',如果用户不能够结束Task,会返回一个错误的结果,这样Task状态变成'Failed'
Next