jBPM5的 User Task是指节点必须有人的参与后才能够完成,是BPM重要特新的体现,User Task必须使用数据库存储流程运行时数据,如下为我们本文中使用到的流程示意:
为了方便数据库方面查看,我们使用Mysql存数流程运行时状态,我们使用如下SQL语句创建数据库用户:
CREATE DATABASE jbpm;
create user 'jbpm_user'@'localhost' identified by 'jbpm_pass';
grant all on jbpm.* to jbpm_user@'localhost';
FLUSH PRIVILEGES;
我们本文的内容包括:
基本运行User Task的完全代码见github,我们抽取如下部分:
setupDataSource();
KnowledgeBase kbase = readKnowledgeBase(JBPM5_BASIC_NAME);
StatefulKnowledgeSession ksession = newStatefulKnowledgeSession(kbase);
LocalTaskService localTaskService = getTaskServiceAndRegisterHumanTaskHandler(ksession);
Map params = new HashMap();
ProcessInstance processInstance = ksession.startProcess("com.sample.bpmn.hello", params);
System.out.println("Start Process... : " + processInstance.getId());
List list = localTaskService.getTasksAssignedAsPotentialOwner("john", "en-UK");
TaskSummary taskSummary = list.get(0);
localTaskService.start(taskSummary.getId(), "john");
localTaskService.complete(taskSummary.getId(), "john", null);
自定义自己的SyncWSHumanTaskHandler执行User Task的代码位于github目录下,运行JBPM5HumanTaskCustomized将运行自定义的SyncWSHumanTaskHandler执行User Task,CustomizedSyncWSHumanTaskHandler为我们自定义的Handler,与SyncWSHumanTaskHandler相比,我们只是在executeWorkItem方法中添加了三行,在方法一开始执行输出workItem内容代码:
System.out.println("Before execute WorkItem, workItem.getId() = " + workItem.getId() + ", workItem.getName() = " + workItem.getName() + ", workItem.getProcessInstanceId() = " + workItem.getProcessInstanceId() + ", workItem.getState() = " + workItem.getState() + ", workItem.getParameters() = " + workItem.getParameters() + ", workItem.getResults() = " + workItem.getResults());
try {
System.out.println("Task id(auto-generated by JPA): " + task.getId());
System.out.println("After execute WorkItem, workItem.getId() = " + workItem.getId() + ", workItem.getName() = " + workItem.getName() + ", workItem.getProcessInstanceId() = " + workItem.getProcessInstanceId() + ", workItem.getState() = " + workItem.getState() + ", workItem.getParameters() = " + workItem.getParameters() + ", workItem.getResults() = " + workItem.getResults());
} catch (Exception e) {
throw new RuntimeException(e);
}
Before execute WorkItem, workItem.getId() = 1, workItem.getName() = Human Task, workItem.getProcessInstanceId() = 1, workItem.getState() = 0, workItem.getParameters() = {ActorId=john, Comment=, Skippable=false, Locale=en-UK, TaskName=Task1, GroupId=, Priority=}, workItem.getResults() = {}
Task id(auto-generated by JPA): 1
After execute WorkItem, workItem.getId() = 1, workItem.getName() = Human Task, workItem.getProcessInstanceId() = 1, workItem.getState() = 0, workItem.getParameters() = {ActorId=john, Comment=, Skippable=false, Locale=en-UK, TaskName=Task1, GroupId=, Priority=}, workItem.getResults() = {}
运行JBPM5PersistenceMysql类将或运行User Task,且使用Mysql数据库为流程执行过程中的数据库。注意,我们设定数据源的代码如下所示:
private static void setupDataSource() {
PoolingDataSource pds = new PoolingDataSource();
pds.setUniqueName("jdbc/jbpm-ds");
pds.setClassName("bitronix.tm.resource.jdbc.lrc.LrcXADataSource");
pds.setMaxPoolSize(5);
pds.setAllowLocalTransactions(true);
pds.getDriverProperties().put("user", "jbpm_user");
pds.getDriverProperties().put("password", "jbpm_pass");
pds.getDriverProperties().put("url", "jdbc:mysql://localhost:3306/jbpm");
pds.getDriverProperties().put("driverClassName", "com.mysql.jdbc.Driver");
pds.init();
UserGroupCallbackManager.getInstance().setCallback(new DefaultUserGroupCallbackImpl());
Map map = new HashMap();
emf = Persistence.createEntityManagerFactory("org.jbpm.persistence.jpa", map);
}
mysql> select id, actualOwner_id, createdBy_id from Task;
+----+----------------+--------------+
| id | actualOwner_id | createdBy_id |
+----+----------------+--------------+
| 1 | john | john |
+----+----------------+--------------+
mysql> select * from OrganizationalEntity;
+-------+---------------+
| DTYPE | id |
+-------+---------------+
| User | Administrator |
| User | john |
+-------+---------------+
JBPM5ContentMapping演示User Task中变量Mapping,WorkflowPOJO as a parameter, mapped by Parameter Mapping and Result Mapping, in the demo,
The following is code snippets:
TaskSummary taskSummary = list.get(0);
Map map = getContentData(localTaskService, taskSummary);
WorkflowPOJO workflowPOJO = (WorkflowPOJO) map.get("inWorkflowTask");
System.out.println("Before Mapping: " + workflowPOJO);
workflowPOJO.setId("101010");
workflowPOJO.setName("JBPM5ContentMapping");
localTaskService.start(taskSummary.getId(), "Administrator");
ContentData contentData = createContentData(workflowPOJO);
localTaskService.complete(taskSummary.getId(), "Administrator", contentData);
Before Mapping: WorkflowPOJO [id=null, name=null]
After Mapping : WorkflowPOJO [id=101010, name=JBPM5ContentMapping]
1. the 'workflowTask' variable should be defined;
2. Task Parameter Mapping define inWorkflowTask map to workflowTask, so above code map.get("inWorkflowTask") can get WorkflowPOJO;
3. Task Result Mapping define outWorkflowTask map to workflowTask, so createContentData method like below
private static ContentData createContentData(WorkflowPOJO workflowPOJO) {
...
Map results = new HashMap();
results.put("outWorkflowTask", workflowPOJO);
...
}
jBPM使用Hibernate操作数据库,如果我们配置persistence.xml中hibernate.show_sql属性为true,如下:
流程执行完成会产生如下SQL操作:
Hibernate: insert into SessionInfo (lastModificationDate, rulesByteArray, startDate, OPTLOCK) values (?, ?, ?, ?)
Hibernate: select task0_.id as col_0_0_, deadline1_.id as col_1_0_, deadline1_.deadline_date as col_2_0_ from Task task0_, Deadline deadline1_ where task0_.archived=0 and (deadline1_.id in (select startdeadl2_.id from Deadline startdeadl2_ where task0_.id=startdeadl2_.Deadlines_StartDeadLine_Id) or deadline1_.id in (select enddeadlin3_.id from Deadline enddeadlin3_ where task0_.id=enddeadlin3_.Deadlines_EndDeadLine_Id)) and deadline1_.escalated=0 order by deadline1_.deadline_date
Hibernate: insert into ProcessInstanceInfo (OPTLOCK, processId, startDate, lastReadDate, lastModificationDate, state, processInstanceByteArray) values (?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into WorkItemInfo (creationDate, name, processInstanceId, state, OPTLOCK, workItemByteArray) values (?, ?, ?, ?, ?, ?)
Hibernate: select user0_.id as id13_0_ from OrganizationalEntity user0_ where user0_.id=? and user0_.DTYPE='User'
Hibernate: insert into OrganizationalEntity (DTYPE, id) values ('User', ?)
Hibernate: select user_.id from OrganizationalEntity user_ where user_.id=?
Hibernate: insert into Task (archived, allowedToDelegate, taskInitiator_id, priority, activationTime, actualOwner_id, completedOn, createdBy_id, createdOn, documentAccessType, documentContentId, documentType, expirationTime, faultAccessType, faultContentId, faultName, faultType, outputAccessType, outputContentId, outputType, parentId, previousStatus, processId, processInstanceId, processSessionId, skipable, status, workItemId, OPTLOCK) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into I18NText (language, shortText, text) values (?, ?, ?)
Hibernate: insert into I18NText (language, shortText, text) values (?, ?, ?)
Hibernate: insert into I18NText (language, shortText, text) values (?, ?, ?)
Hibernate: insert into Content (content) values (?)
Hibernate: update SessionInfo set lastModificationDate=?, rulesByteArray=?, startDate=?, OPTLOCK=? where id=? and OPTLOCK=?
Hibernate: update ProcessInstanceInfo set OPTLOCK=?, processId=?, startDate=?, lastReadDate=?, lastModificationDate=?, state=?, processInstanceByteArray=? where InstanceId=? and OPTLOCK=?
Hibernate: update WorkItemInfo set creationDate=?, name=?, processInstanceId=?, state=?, OPTLOCK=?, workItemByteArray=? where workItemId=? and OPTLOCK=?
Hibernate: update Task set archived=?, allowedToDelegate=?, taskInitiator_id=?, priority=?, activationTime=?, actualOwner_id=?, completedOn=?, createdBy_id=?, createdOn=?, documentAccessType=?, documentContentId=?, documentType=?, expirationTime=?, faultAccessType=?, faultContentId=?, faultName=?, faultType=?, outputAccessType=?, outputContentId=?, outputType=?, parentId=?, previousStatus=?, processId=?, processInstanceId=?, processSessionId=?, skipable=?, status=?, workItemId=?, OPTLOCK=? where id=? and OPTLOCK=?
Hibernate: update I18NText set Task_Descriptions_Id=? where id=?
Hibernate: update I18NText set Task_Names_Id=? where id=?
Hibernate: insert into PeopleAssignments_BAs (task_id, entity_id) values (?, ?)
Hibernate: insert into PeopleAssignments_PotOwners (task_id, entity_id) values (?, ?)
Hibernate: update I18NText set Task_Subjects_Id=? where id=?
Hibernate: select task0_.id as col_0_0_, task0_.processInstanceId as col_1_0_, names5_.text as col_2_0_, subjects3_.text as col_3_0_, descriptio4_.text as col_4_0_, task0_.status as col_5_0_, task0_.priority as col_6_0_, task0_.skipable as col_7_0_, user2_.id as col_8_0_, user1_.id as col_9_0_, task0_.createdOn as col_10_0_, task0_.activationTime as col_11_0_, task0_.expirationTime as col_12_0_, task0_.processId as col_13_0_, task0_.processSessionId as col_14_0_ from Task task0_ left outer join OrganizationalEntity user1_ on task0_.createdBy_id=user1_.id left outer join OrganizationalEntity user2_ on task0_.actualOwner_id=user2_.id left outer join I18NText subjects3_ on task0_.id=subjects3_.Task_Subjects_Id left outer join I18NText descriptio4_ on task0_.id=descriptio4_.Task_Descriptions_Id left outer join I18NText names5_ on task0_.id=names5_.Task_Names_Id, OrganizationalEntity organizati6_ where task0_.archived=0 and organizati6_.id=? and (organizati6_.id in (select potentialo7_.entity_id from PeopleAssignments_PotOwners potentialo7_ where task0_.id=potentialo7_.task_id)) and (names5_.language=? or (select count(names8_.Task_Names_Id) from I18NText names8_ where task0_.id=names8_.Task_Names_Id)=0) and (subjects3_.language=? or (select count(subjects9_.Task_Subjects_Id) from I18NText subjects9_ where task0_.id=subjects9_.Task_Subjects_Id)=0) and (descriptio4_.language=? or (select count(descriptio10_.Task_Descriptions_Id) from I18NText descriptio10_ where task0_.id=descriptio10_.Task_Descriptions_Id)=0) and (task0_.status in ('Created' , 'Ready' , 'Reserved' , 'InProgress' , 'Suspended')) and (task0_.expirationTime is null)
Hibernate: select group0_.id as col_0_0_ from OrganizationalEntity group0_ where group0_.DTYPE='Group'
Hibernate: update Task set archived=?, allowedToDelegate=?, taskInitiator_id=?, priority=?, activationTime=?, actualOwner_id=?, completedOn=?, createdBy_id=?, createdOn=?, documentAccessType=?, documentContentId=?, documentType=?, expirationTime=?, faultAccessType=?, faultContentId=?, faultName=?, faultType=?, outputAccessType=?, outputContentId=?, outputType=?, parentId=?, previousStatus=?, processId=?, processInstanceId=?, processSessionId=?, skipable=?, status=?, workItemId=?, OPTLOCK=? where id=? and OPTLOCK=?
Hibernate: select group0_.id as col_0_0_ from OrganizationalEntity group0_ where group0_.DTYPE='Group'
Hibernate: update Task set archived=?, allowedToDelegate=?, taskInitiator_id=?, priority=?, activationTime=?, actualOwner_id=?, completedOn=?, createdBy_id=?, createdOn=?, documentAccessType=?, documentContentId=?, documentType=?, expirationTime=?, faultAccessType=?, faultContentId=?, faultName=?, faultType=?, outputAccessType=?, outputContentId=?, outputType=?, parentId=?, previousStatus=?, processId=?, processInstanceId=?, processSessionId=?, skipable=?, status=?, workItemId=?, OPTLOCK=? where id=? and OPTLOCK=?
Hibernate: select workitemin0_.workItemId as workItemId3_0_, workitemin0_.creationDate as creation2_3_0_, workitemin0_.name as name3_0_, workitemin0_.processInstanceId as processI4_3_0_, workitemin0_.state as state3_0_, workitemin0_.OPTLOCK as OPTLOCK3_0_, workitemin0_.workItemByteArray as workItem7_3_0_ from WorkItemInfo workitemin0_ where workitemin0_.workItemId=?
Hibernate: select processins0_.InstanceId as InstanceId0_0_, processins0_.OPTLOCK as OPTLOCK0_0_, processins0_.processId as processId0_0_, processins0_.startDate as startDate0_0_, processins0_.lastReadDate as lastRead5_0_0_, processins0_.lastModificationDate as lastModi6_0_0_, processins0_.state as state0_0_, processins0_.processInstanceByteArray as processI8_0_0_ from ProcessInstanceInfo processins0_ where processins0_.InstanceId=?
Hibernate: select processins0_.InstanceId as col_0_0_ from ProcessInstanceInfo processins0_ inner join EventTypes eventtypes1_ on processins0_.InstanceId=eventtypes1_.InstanceId where eventtypes1_.element=?
Hibernate: update SessionInfo set lastModificationDate=?, rulesByteArray=?, startDate=?, OPTLOCK=? where id=? and OPTLOCK=?
Hibernate: delete from EventTypes where InstanceId=?
Hibernate: delete from ProcessInstanceInfo where InstanceId=? and OPTLOCK=?
Hibernate: delete from WorkItemInfo where workItemId=? and OPTLOCK=?