JBPM-jPDL学习笔记——流程设计与控制(转载)

背景
本片文章,我们将从业务流程的设计开始,通过带领大家完成一个完整工作流的程序设计,来学习jPDL的使用。

业务流程设计
这里我们实现一个相对简化的公司借款申请流程。流程图如下:


在jPDL中,与流程设计相关的文件有三个:processdefinition.xml、gdp.xml、processimage.jpg。其中processdefinition.xml是流程定义的描述文件;gpd.xml是对图形界面呈现的XML描述;而processimage.jpg则是对图形界面的快照。下面我们将展示本样例的流程定义文件。

流程定义描述

processdefinition.xml文件


引用

<?xml version="1.0" encoding="UTF-8"?>

<process-definition xmlns ="urn:jbpm.org:jpdl-3.2" name="simple">
<start-state name="开始">
<transition name="借款发起" to="填写借款申请">
<action name="Action_StartProcess" class="com.firstflow.action.StartProcessActionHander"></action>
</transition>
</start-state>
<task-node name="填写借款申请">
<task name="Task_AssignToInitiator">
<assignment class="com.firstflow.task.NewApplicationAssignmentHandler"></assignment>
</task>
<transition to="部门经理审批" name="提交申请">
<action name="Action_SubmitApply" class="com.firstflow.action.SubmitApplyActionHandler"></action>
</transition>
</task-node>

<task-node name="部门经理审批">
<task name="Task_ManagerApprove">
<assignment class="com.firstflow.task.DepartmentManagerApproveAssignmentHandler"></assignment>
</task>
<transition to="金额判定" name="部门经理审批通过">
<action name="Task_ManagerApproved" class="com.firstflow.action.ManagerApprovedActionHandler"></action>
</transition>
<transition to="结束" name="部门经理驳回">
<action name="Action_ManagerDisapprove" class="com.firstflow.action.ManagerDisapproveActionHandler"></action>
</transition>
</task-node>

<node name="财务拨款">
<action name="Action_AccountantProcess" class="com.firstflow.action.AccountantProcessActoinHandler"></action>
<transition to="结束" name="邮件通知">
<action name="Action_Mail" class="com.firstflow.action.SendMailActionHandler"></action>
</transition>
</node>

<decision name="金额判定">
<handler class="com.firstflow.decision.MoneyCheckDecisionHandler"></handler>
<transition to="总经理审批" name=">5000元总经理审批"></transition>
<transition to="财务拨款" name="<5000元 财务拨款"></transition>
</decision>

<task-node name="总经理审批">
<task name="Task_PresidentApprove">
<assignment class="com.firstflow.task.PresidentApproveAssignmentHandler"></assignment>
</task>
<transition to="财务拨款" name="总经理审批通过">
<action name="Action_PresidentApproved" class="com.firstflow.action.PresidentApprovedActionHandler"></action>
</transition>
<transition to="结束" name="总经理驳回">
<action name="Action_PresidentDisapproved" class="com.firstflow.action.PresidentDisapprovedActionHandler"></action>
</transition>
</task-node>
<end-state name="结束"></end-state>
</process-definition>


在样例流程中,除了开始和结束结点外,我们定义了三种类型的结点:

任务结点<task-node>
任务结点是一个需要人工参与的结点类型。当流程进入结点时,会生成相应的任务实例(TaskInstatnce),并通过委派接口AssignmentHandler或jBPM表达式将任务委派给一个或多个特定的角色或参与者。结点自身进入等待状态,直到任务被参与者完成或者跳过,流程继续。

判定结点<decision>
判定结点的设计目标是根据上下文环境和程序逻辑,判定流程转向。通过指定一个实现DecisionHandlder接口的Java委派类或jBPM表达式,来返回转向(transition)的字符窜类型的名称(可以是中文哦),来达到决定流程方向的功能。

普通结点<node>
普通结点也可以定义相应的处理任务,通过定义相应的ActioinHandler类。同任务结点不同的是,普通结点定义的任务是由流程自动执行的,无须人工干预。

三种结点都可定义结点事件(event):
node-enter,该事件在流程进入结点时触发
node-leave,该事件在流程离开节点是触发
可以在事件上挂接ActioinHandler接口的实现类来完成一些特定的功能。

三种节点都可以定义异步处理方式(async属性):
异步处理意味着每个结点的事务处理是通过消息机制分离的,不再同一线程中统一调用执行。而是由消息监听线程从消息队列中取得消息体来运行相应得程序。
此外我们定义了结点间的转向(transition),用来记录和处理状态的变迁。每个转向中,可以委派一个或多个的ActioinHandler接口实现类,负责处理节点变迁时的上下文状态变更及回调用户定义的处理程序。

流程的程序接口说明

动作处理接口(ActioinHandler)
接口方法:void execute( ExecutionContext executionContext ) throws Exception
该接口是jPDL中最常用的一个回调接口。从它的接口方法可以发现,它仅仅暴露了流程执行上下文变量ExecutionContext。用户程序通过ExecutionContext来了解流程的执行状态,并通过改变ExecutionContext中的属性值来影响流程的执行。
ActioinHandler接口可以在所有能包含事件(event)、动作(action)元素的地方被回调。

判定处理接口(DecisionHandlder)
接口方法:String decide(ExecutionContext executionContext) throws Exception
判定接口仅适用于判定节点(decision)中。从它的接口方法可以看出,方法要返回一个字符串型的结果,这个结果必须和判定节点拥有的转向(transition)集合中的一条转向名称相匹配。
在DecisionHandlder的接口方法中一样能访问到ExecutionContext变量,这为判定提供了执行上下文的根据。当然,如果有必要,用户也可以在该接口中改变ExecutionContext中的变量值。

委派处理接口(AssignmentHandler)
接口方法:void assign(Assignable assignable, ExecutionContext executionContext) throws Exception;
委派处理接口被用户任务元素(task)的委派(assignment)子元素中,它的职责很明确,就是将任务分配给指定的人员或角色。
在AssignmentHandler接口的方法中,Assignable变量通常指任务实例(TaskInstance)。通过将ExecutionContext和TaskInstance两个变量都暴露给接口方法,用户就可以根据流程上下文情况,来决定要将指定的任务分配个谁。

流程的部署

用户使用jPDL的流程设计器定义业务流程,当然,你也可以直接用文档编辑器直接编辑processdefinition.xml定义文件。定义文档是可以直接被ProcessDefinition类载入使用的,但在正式运行的系统中,流程定义信息更多是使用关系型数据库来存储。从流程定义文件将数据导入流程数据库的过程,我们称之为流程部署。
jPDL的流程部署文件包含processdefinition.xml的定义部分和Java处理器的代码部分,这些文件可以被一起打包成.jpdl的zip格式包而后上传服务器端。这个过程可以在流程设计器界面的“deployment”标签页中操作:

这里我们着重要讲述的是接受部署文件上载的服务器端配置。在jBPM3.2的包中带着一个jPDL的管理控制台web应用,默认名字为jbpm-console。该应用带有接受流程定义包部署的程序,但不是最小化的。实际上完成流程部署功能的,只是jbpm-jpdl.jar核心包中的一个servlet类:org.jbpm.web.ProcessUploadServlet . 完成这个Servlet的成功部署,需要以下工作:

1. 配置web.xml,将servlet配置成启动时加载,如下:

引用

<web-app>
<servlet>
<servlet-name>GDP Deployer Servlet</servlet-name>
<servlet-class>org.jbpm.web.ProcessUploadServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>GDP Deployer Servlet</servlet-name>
<url-pattern>/upload/*</url-pattern>
</servlet-mapping>

</web-app>


2. 建立流程定义存储数据库表:
Demo中,我们使用的数据库是MySQL的,在E:\Java\tools\jbpm-jpdl-3.2.2\db\目录下有个jbpm.jpdl.mysql.sql数据库脚本文件。但我们不能直接导入该文件, 会提示有错误, 应为该文件的SQL语句末尾少了分号,在批量执行时,MySQL报错。需要在每一行SQL的末尾添加一个分号,这样就可以用source命令导入了。

3. 配置Hibernate.cfg.xml
由于jBPM的数据库持久化是依靠Hibernate进行的,因此需要配置Hibernate.cfg.xml使其适应我们的MySQL环境

引用

<!-- hibernate dialect -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/linly</property>
<property name="hibernate.connection.username">linly</property>
<property name="hibernate.connection.password">coffee</property>
<property name="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property>


4. Import需要的jar包
这里的jar包包括三部分:jbpm的核心包;Hibernate及其支撑包;MySQL的JDBC驱动包。


到此,我们的配置工作完成,这是实现jBPM流程部署服务端的最小化应用配置。

流程控制及API使用

样例程序中的Handler接口实现

下面,根据上述的接口分类,列出样例程序中的类名及相应的功能说明,具体可参考源代码。
动作处理接口(ActioinHandler)

这里要提到一个很重要的区别,就是作用于Node上的ActoinHandler和作用于Transition上的ActoinHandler是有不同的。区别在于,Node上的ActoinHandler在结束业务逻辑处理后,必须调用executionContext.leaveNode();或executionContext.leaveNode(transition)来保证流程向下执行;而作用于Transition上的则不需要。

判定处理接口(DecisionHandlder)


委派处理接口(AssignmentHandler)


流程测试剖析

本章节,我们将给大家剖析两个流程测试类。一个是简单的基于内存模型的流程测试FirstFlowProcessTest;一个是更贴近实用的,基于MySQL数据库操作的标准测试案例。通过对这两个测试例程的分析,来直观的学习如何通过Java API操作jPDL。

简单流程测试案例
测试案例类:FirstFlowProcessTest.java

Java代码
1.public class FirstFlowProcessTest extends TestCase {
2. ProcessDefinition pdf ;
3. ProcessInstance pi;
4.
5. public void test4000YuanApplication() throws Exception {
6. deployProcessDefinition();
7. createProcessInstance("linly");
8. submitApplication(4000);
9. approveByManager(true);
10. checkTasks();
11. }
12.
13. public void test6000YuanApplication() throws Exception {
14. deployProcessDefinition();
15. createProcessInstance("linly");
16. submitApplication(6000);
17. approveByManager(true);
18. approveByPresident(true);
19. checkTasks();
20. }
21.
22. public void test7000YuanApplication() throws Exception {
23. deployProcessDefinition();
24. createProcessInstance("linly");
25. submitApplication(7000);
26. approveByManager(true);
27. approveByPresident(false);
28. checkTasks();
29. }
30.
31. /**
32. * 部署流程定义
33. * @throws Exception
34. */
35. protected void deployProcessDefinition() throws Exception{
36. System.out.println("==FirstFlowProcessTest.deployProcessDefinition()==");
37. pdf = ProcessDefinition.parseXmlResource("firstflow/processdefinition.xml");
38. assertNotNull("Definition should not be null", pdf);
39. }
40. /**
41. * 生成流程实例
42. */
43. protected void createProcessInstance(String user){
44. System.out.println("==FirstFlowProcessTest.createProcessInstance()==");
45. assertNotNull("Definition should not be null", pdf);
46. //生成实例
47. pi = pdf.createProcessInstance();
48. assertNotNull("processInstance should not be null", pi);
49. //设置流程发起人
50. pi.getContextInstance().createVariable("initiator", user);
51. //触发流程转向
52. pi.signal();
53. }
54. /**
55. * 填写提交申请单
56. * @param money
57. */
58. protected void submitApplication(int money){
59. System.out.println("==FirstFlowProcessTest.submitApplication()==");
60. TaskInstance ti = (TaskInstance)pi.getTaskMgmtInstance().getTaskInstances().iterator() .next() ;
61. System.out.println("ti.actor = " + ti.getActorId());
62. ContextInstance ci = ti.getContextInstance();
63. ci.setVariable("money",new Integer(money));
64. ti.end();
65. }
66. /**
67. * 部门经理审批
68. * @param pass
69. */
70. @SuppressWarnings("unchecked")
71. protected void approveByManager(boolean pass){
72. System.out.println("==FirstFlowProcessTest.approveByManager()==");
73. Iterator<TaskInstance> it = pi.getTaskMgmtInstance().getTaskInstances().iterator();
74. for( ;it.hasNext(); ){
75. TaskInstance ti = it.next();
76. if(ti.getActorId().equals("DepartmentManager")){
77. List<Transition> transitions = ti.getToken().getNode().getLeavingTransitions();
78. for(Transition t : transitions){
79. System.out.println("----Transition" + t.getName());
80. }
81. assertEquals("DepartmentManager",ti.getActorId());
82. if(pass){
83. ti.end("部门经理审批通过");
84. }else{
85. ti.end("部门经理驳回");
86. }
87. return;
88. }
89. }
90. }
91.
92. /**
93. * 总经理审批
94. * @param pass
95. */
96. @SuppressWarnings("unchecked")
97. protected void approveByPresident(boolean pass){
98. System.out.println("==FirstFlowProcessTest.approveByPresident()==");
99.
100. Iterator<TaskInstance> it = pi.getTaskMgmtInstance().getTaskInstances().iterator();
101.
102. for( ;it.hasNext(); ){
103.
104. TaskInstance ti = it.next();
105.
106. if(ti.getActorId().equals("President")){
107. List<Transition> transitions = ti.getToken().getNode().getLeavingTransitions();
108. for(Transition t : transitions){
109. System.out.println("----Transition" + t.getName());
110. }
111. assertEquals("President",ti.getActorId());
112. if(pass){
113. ti.end("总经理审批通过");
114. }else{
115. ti.end("总经理驳回");
116. }
117. return;
118. }
119. }
120. }
121. /**
122. * 打印任务记录
123. */
124. @SuppressWarnings("unchecked")
125. protected void checkTasks(){
126. System.out.println("==FirstFlowProcessTest.checkTasks()==");
127. Collection<TaskInstance> coll = pi.getTaskMgmtInstance().getTaskInstances();
128. System.out.println("====Process has task:====");
129. for(TaskInstance ti : coll){
130. System.out.println("=="+ti.getName()+"==");
131. System.out.println("=="+ti.getActorId()+"==");
132. System.out.println("=="+ti.getVariables().toString() +"==");
133. }
134. System.out.println("====end====");
135. }
public class FirstFlowProcessTest extends TestCase {
ProcessDefinition pdf ;
ProcessInstance pi;

public void test4000YuanApplication() throws Exception {
deployProcessDefinition();
createProcessInstance("linly");
submitApplication(4000);
approveByManager(true);
checkTasks();
}

public void test6000YuanApplication() throws Exception {
deployProcessDefinition();
createProcessInstance("linly");
submitApplication(6000);
approveByManager(true);
approveByPresident(true);
checkTasks();
}

public void test7000YuanApplication() throws Exception {
deployProcessDefinition();
createProcessInstance("linly");
submitApplication(7000);
approveByManager(true);
approveByPresident(false);
checkTasks();
}

/**
* 部署流程定义
* @throws Exception
*/
protected void deployProcessDefinition() throws Exception{
System.out.println("==FirstFlowProcessTest.deployProcessDefinition()==");
pdf = ProcessDefinition.parseXmlResource("firstflow/processdefinition.xml");
assertNotNull("Definition should not be null", pdf);
}
/**
* 生成流程实例
*/
protected void createProcessInstance(String user){
System.out.println("==FirstFlowProcessTest.createProcessInstance()==");
assertNotNull("Definition should not be null", pdf);
//生成实例
pi = pdf.createProcessInstance();
assertNotNull("processInstance should not be null", pi);
//设置流程发起人
pi.getContextInstance().createVariable("initiator", user);
//触发流程转向
pi.signal();
}
/**
* 填写提交申请单
* @param money
*/
protected void submitApplication(int money){
System.out.println("==FirstFlowProcessTest.submitApplication()==");
TaskInstance ti = (TaskInstance)pi.getTaskMgmtInstance().getTaskInstances().iterator() .next() ;
System.out.println("ti.actor = " + ti.getActorId());
ContextInstance ci = ti.getContextInstance();
ci.setVariable("money",new Integer(money));
ti.end();
}
/**
* 部门经理审批
* @param pass
*/
@SuppressWarnings("unchecked")
protected void approveByManager(boolean pass){
System.out.println("==FirstFlowProcessTest.approveByManager()==");
Iterator<TaskInstance> it = pi.getTaskMgmtInstance().getTaskInstances().iterator();
for( ;it.hasNext(); ){
TaskInstance ti = it.next();
if(ti.getActorId().equals("DepartmentManager")){
List<Transition> transitions = ti.getToken().getNode().getLeavingTransitions();
for(Transition t : transitions){
System.out.println("----Transition" + t.getName());
}
assertEquals("DepartmentManager",ti.getActorId());
if(pass){
ti.end("部门经理审批通过");
}else{
ti.end("部门经理驳回");
}
return;
}
}
}

/**
* 总经理审批
* @param pass
*/
@SuppressWarnings("unchecked")
protected void approveByPresident(boolean pass){
System.out.println("==FirstFlowProcessTest.approveByPresident()==");

Iterator<TaskInstance> it = pi.getTaskMgmtInstance().getTaskInstances().iterator();

for( ;it.hasNext(); ){

TaskInstance ti = it.next();

if(ti.getActorId().equals("President")){
List<Transition> transitions = ti.getToken().getNode().getLeavingTransitions();
for(Transition t : transitions){
System.out.println("----Transition" + t.getName());
}
assertEquals("President",ti.getActorId());
if(pass){
ti.end("总经理审批通过");
}else{
ti.end("总经理驳回");
}
return;
}
}
}
/**
* 打印任务记录
*/
@SuppressWarnings("unchecked")
protected void checkTasks(){
System.out.println("==FirstFlowProcessTest.checkTasks()==");
Collection<TaskInstance> coll = pi.getTaskMgmtInstance().getTaskInstances();
System.out.println("====Process has task:====");
for(TaskInstance ti : coll){
System.out.println("=="+ti.getName()+"==");
System.out.println("=="+ti.getActorId()+"==");
System.out.println("=="+ti.getVariables().toString() +"==");
}
System.out.println("====end====");
}


该案例是在没有数据库支持的情况下,对报销流程进行运行测试,测试逻辑如下:

1. 加载流程定义

Java代码
1.ProcessDefinition.parseXmlResource("firstflow/processdefinition.xml")
ProcessDefinition.parseXmlResource("firstflow/processdefinition.xml")代码说明:
在没有数据库存储的情况下,流程定义通过ProcessDefinition类直接从processdefinition.xml文件中解析加载。

2. 实例化流程对象

Java代码
1.//生成实例
2. pi = pdf.createProcessInstance();
3.assertNotNull("processInstance should not be null", pi);
4.//设置流程发起人
5. pi.getContextInstance().createVariable("initiator", user);
6.//触发流程转向
7. pi.signal();
//生成实例
pi = pdf.createProcessInstance();
assertNotNull("processInstance should not be null", pi);
//设置流程发起人
pi.getContextInstance().createVariable("initiator", user);
//触发流程转向
pi.signal();代码说明:
在获得流程定义的实例后,可以用它生成流程实例,使用如下的语句:
pi = pdf.createProcessInstance();
流程实例拥有自己的ContextInstance环境变量对象。它实际上是一个HashMap,以key-value方式记录了流程的上下文变量值,代码中的
pi.getContextInstance().createVariable("initiator", user);就是向环境变量中添加一个key为initiator的对象。
每个流程实例都拥有自己Token令牌对象,主流程有自己的RootToken,子流程也拥有自己的子Token。父流程的Token和子流程的Token相互关联,形成Token树。
Token对象表示流程运行的当前位置(运行到哪个节点了)。通过对Token对象的signal()方法调用,可以使流程向下运行。代码中的pi.signal();实际上是间接调用了pi.getRootToken().signal();它使得新建的流程继续向下个节点(即借款申请单填写)进发。

3. 员工发起借款申请

Java代码
1./**
2. * 填写提交申请单
3. * @param money
4. */
5.protected void submitApplication(int money){
6. System.out.println("==FirstFlowProcessTest.submitApplication()==");
7.
8. TaskInstance ti = (TaskInstance)pi.getTaskMgmtInstance().getTaskInstances().iterator() .next()
9. System.out.println("ti.actor = " + ti.getActorId());
10. ContextInstance ci = ti.getContextInstance();
11. ci.setVariable("money",new Integer(money));
12. ti.end();
/**
* 填写提交申请单
* @param money
*/
protected void submitApplication(int money){
System.out.println("==FirstFlowProcessTest.submitApplication()==");

TaskInstance ti = (TaskInstance)pi.getTaskMgmtInstance().getTaskInstances().iterator() .next()
System.out.println("ti.actor = " + ti.getActorId());
ContextInstance ci = ti.getContextInstance();
ci.setVariable("money",new Integer(money));
ti.end();
}代码说明:
在借款流程发起后,流程进入了申请单填写阶段。这个阶段是个人工的任务,需要用户的介入。因此,对于要借款的用户而言,首先是获取填写申请单的任务实例:
TaskInstance ti = (TaskInstance)pi.getTaskMgmtInstance().getTaskInstances().iterator() .next()
在这个测试类中,由于没有数据库。对流程实例的引用是依靠了类的全局标量pi。这里通过pi获取全部的任务列表。实际上有且仅有一个任务,就是我们刚刚发起的申请单填写任务。
接下来,我们获取流程的上下文变量,将申请借款的数额记录在上下文变量中ContextInstance ci = ti.getContextInstance();
ci.setVariable("money",new Integer(money));
最后,我们要结束当前任务,告诉流程继续下行,调用ti.end();这个方法的本质依然是调用了token.signal(),它选择了一个默认的transition进行转向。这里要说明的是signal方法有多态的实现signal(Transition transition),是可以指定具体转向参数的。

4. 部门领导审批申请

Java代码
1. /**
2. * 部门经理审批
3. * @param pass
4. */
5. @SuppressWarnings("unchecked")
6. protected void approveByManager(boolean pass){
7. System.out.println("==FirstFlowProcessTest.approveByManager()==");
8. Iterator<TaskInstance> it = pi.getTaskMgmtInstance().getTaskInstances().iterator();
9. for( ;it.hasNext(); ){
10. TaskInstance ti = it.next();
11. if(ti.getActorId().equals("DepartmentManager")){
12. List<Transition> transitions = ti.getToken().getNode().getLeavingTransitions();
13. for(Transition t : transitions){
14. System.out.println("----Transition" + t.getName());
15. }
16. assertEquals("DepartmentManager",ti.getActorId());
17. if(pass){
18. ti.end("部门经理审批通过");
19. }else{
20. ti.end("部门经理驳回");
21. }
22. return;
23. }
24. }
25.}
/**
* 部门经理审批
* @param pass
*/
@SuppressWarnings("unchecked")
protected void approveByManager(boolean pass){
System.out.println("==FirstFlowProcessTest.approveByManager()==");
Iterator<TaskInstance> it = pi.getTaskMgmtInstance().getTaskInstances().iterator();
for( ;it.hasNext(); ){
TaskInstance ti = it.next();
if(ti.getActorId().equals("DepartmentManager")){
List<Transition> transitions = ti.getToken().getNode().getLeavingTransitions();
for(Transition t : transitions){
System.out.println("----Transition" + t.getName());
}
assertEquals("DepartmentManager",ti.getActorId());
if(pass){
ti.end("部门经理审批通过");
}else{
ti.end("部门经理驳回");
}
return;
}
}
}代码说明:
这里,流程进入了部门经理审批阶段。由于没有数据库支持,我们只能采取遍历任务列表,并比对委派者ID的方式来确定委派给部门经理的任务实例。(在后面的基于数据库的标准案例中,我们会看到如果根据用户的ID来获取分配给指定用户的任务)
ti.getActorId().equals("DepartmentManager") // 比对任务的委派人。
ti.getToken().getNode().getLeavingTransitions();//获取任务在当前节点上的所有转向。
这里我们要特别指出的是ti.end("部门经理审批通过")和ti.end("部门经理驳回")这实际上调用token.signal(transition);来完成任务的转向,从而使流程继续。

5. 总经理审批申请

Java代码
1. /**
2. * 总经理审批
3. * @param pass
4. */
5. @SuppressWarnings("unchecked")
6. protected void approveByPresident(boolean pass){
7. System.out.println("==FirstFlowProcessTest.approveByPresident()==");
8. Iterator<TaskInstance> it = pi.getTaskMgmtInstance().getTaskInstances().iterator();
9.
10. for( ;it.hasNext(); ){
11. TaskInstance ti = it.next();
12. if(ti.getActorId().equals("President")){
13. List<Transition> transitions = ti.getToken().getNode().getLeavingTransitions();
14. for(Transition t : transitions){
15. System.out.println("----Transition" + t.getName());
16. }
17. assertEquals("President",ti.getActorId());
18. if(pass){
19. ti.end("总经理审批通过");
20. }else{
21. ti.end("总经理驳回");
22. }
23. return;
24. }
25. }
26.}
/**
* 总经理审批
* @param pass
*/
@SuppressWarnings("unchecked")
protected void approveByPresident(boolean pass){
System.out.println("==FirstFlowProcessTest.approveByPresident()==");
Iterator<TaskInstance> it = pi.getTaskMgmtInstance().getTaskInstances().iterator();

for( ;it.hasNext(); ){
TaskInstance ti = it.next();
if(ti.getActorId().equals("President")){
List<Transition> transitions = ti.getToken().getNode().getLeavingTransitions();
for(Transition t : transitions){
System.out.println("----Transition" + t.getName());
}
assertEquals("President",ti.getActorId());
if(pass){
ti.end("总经理审批通过");
}else{
ti.end("总经理驳回");
}
return;
}
}
}代码说明:
此步代码同“部门经理审批”代码相似,不作更多说明。

标准流程测试案例

该案例模拟了标准运行环境中,基于关系型数据库的jBPM系统是如何执行流程的。
测试案例类:FirstFlowProcessDBTest.java

Java代码
1.public class FirstFlowProcessDBTest {
2. /*
3. * 初始化jBPM配置
4. * 包含对Hibernate的数据库初始化
5. */
6. static JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();
7. public static void main(String[] args){
8. FirstFlowProcessDBTest test = new FirstFlowProcessDBTest();
9. test.test4000YuanApplication();
10. test.test6000YuanApplication();
11. test.test7000YuanApplication();
12. }
13.
14. public void test4000YuanApplication(){
15. ProcessInstance pi = createProcessInstance("linly");
16. submitApplication("linly" , 4000);
17. approveByManager(true);
18. checkTasks(pi);
19. }
20. public void test6000YuanApplication() {
21. ProcessInstance pi = createProcessInstance("linly");
22. submitApplication("linly" , 6000);
23. approveByManager(true);
24. approveByPresident(true);
25. checkTasks(pi);
26. }
27. public void test7000YuanApplication() {
28. ProcessInstance pi = createProcessInstance("linly");
29. submitApplication("linly" , 7000);
30. approveByManager(true);
31. approveByPresident(false);
32. checkTasks(pi);
33. }
34. /**
35. * 生成流程实例
36. */
37. protected ProcessInstance createProcessInstance(String user){
38. System.out.println("==FirstFlowProcessTest.createProcessInstance()==");
39. JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
40. try {
41. GraphSession graphSession = jbpmContext.getGraphSession();
42. /*
43. * 从数据库获取指定的流程定义
44. */
45. ProcessDefinition pdf = graphSession.findLatestProcessDefinition("simple");
46. //生成流程实例
47. ProcessInstance pi = pdf.createProcessInstance();
48. //设置流程发起人
49. pi.getContextInstance().createVariable("initiator", user);
50. //触发流程转向
51. pi.signal();
52. /*
53. * 保存流程实例
54. */
55. jbpmContext.save(pi);
56. return pi;
57. }finally{
58. jbpmContext.close();
59. }
60. }
61.
62. /**
63. * 填写提交申请单
64. * @param money
65. */
66. @SuppressWarnings("unchecked")
67. protected void submitApplication(String actorId , int money){
68. System.out.println("==FirstFlowProcessTest.submitApplication()==");
69. JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
70. try {
71. /*
72. *根据操作者ID,获取属于该操作者的任务集
73. */
74. List<TaskInstance> taskInstances = jbpmContext.getTaskList(actorId);
75. for(TaskInstance ti : taskInstances){
76. System.out.println("ti.name = " + ti.getName());
77. System.out.println("ti.actor = " + ti.getActorId());
78. ContextInstance ci = ti.getContextInstance();
79. ci.setVariable("money",new Integer(money));
80. ti.end();
81. }
82. }finally{
83. jbpmContext.close();
84. }
85. }
86.
87. /**
88. * 部门经理审批
89. * @param pass
90. */
91. @SuppressWarnings("unchecked")
92. protected void approveByManager(boolean pass){
93. System.out.println("==FirstFlowProcessTest.approveByManager()==");
94. JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
95. try{
96. List<TaskInstance> taskInstances = jbpmContext.getTaskList("DepartmentManager");
97. for(TaskInstance ti : taskInstances){
98. System.out.println("ti.name = " + ti.getName());
99. System.out.println("ti.actor = " + ti.getActorId());
100. if(pass){
101. ti.end("部门经理审批通过");
102. }else{
103. ti.end("部门经理驳回");
104. }
105. }
106. }finally{
107. jbpmContext.close();
108. }
109. }
110.
111. /**
112. * 总经理审批
113. * @param pass
114. */
115. @SuppressWarnings("unchecked")
116. protected void approveByPresident(boolean pass){
117. System.out.println("==FirstFlowProcessTest.approveByPresident()==");
118. JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
119. try{
120. List<TaskInstance> taskInstances = jbpmContext.getTaskList("President");
121. for(TaskInstance ti : taskInstances){
122. System.out.println("ti.name = " + ti.getName());
123. System.out.println("ti.actor = " + ti.getActorId());
124. if(pass){
125. ti.end("总经理审批通过");
126. }else{
127. ti.end("总经理驳回");
128. }
129. }
130. }finally{
131. jbpmContext.close();
132. }
133. }
134.
135. /**
136. * 打印任务记录
137. */
138. @SuppressWarnings("unchecked")
139. protected void checkTasks(ProcessInstance pi){
140. System.out.println("==FirstFlowProcessTest.checkTasks()==");
141. JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
142. try{
143. List<TaskInstance> coll = jbpmContext.getTaskMgmtSession().findTaskInstancesByProcessInstance(pi);
144. System.out.println("====Process has task:====");
145. for(TaskInstance ti : coll){
146. System.out.println("=="+ti.getName()+"==");
147. System.out.println("=="+ti.getActorId()+"==");
148. System.out.println("=="+ti.getVariables().toString() +"==");
149. }
150. System.out.println("====end====");
151. }finally{
152. jbpmContext.close();
153. }
154. }
155.}
public class FirstFlowProcessDBTest {
/*
* 初始化jBPM配置
* 包含对Hibernate的数据库初始化
*/
static JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();
public static void main(String[] args){
FirstFlowProcessDBTest test = new FirstFlowProcessDBTest();
test.test4000YuanApplication();
test.test6000YuanApplication();
test.test7000YuanApplication();
}

public void test4000YuanApplication(){
ProcessInstance pi = createProcessInstance("linly");
submitApplication("linly" , 4000);
approveByManager(true);
checkTasks(pi);
}
public void test6000YuanApplication() {
ProcessInstance pi = createProcessInstance("linly");
submitApplication("linly" , 6000);
approveByManager(true);
approveByPresident(true);
checkTasks(pi);
}
public void test7000YuanApplication() {
ProcessInstance pi = createProcessInstance("linly");
submitApplication("linly" , 7000);
approveByManager(true);
approveByPresident(false);
checkTasks(pi);
}
/**
* 生成流程实例
*/
protected ProcessInstance createProcessInstance(String user){
System.out.println("==FirstFlowProcessTest.createProcessInstance()==");
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
GraphSession graphSession = jbpmContext.getGraphSession();
/*
* 从数据库获取指定的流程定义
*/
ProcessDefinition pdf = graphSession.findLatestProcessDefinition("simple");
//生成流程实例
ProcessInstance pi = pdf.createProcessInstance();
//设置流程发起人
pi.getContextInstance().createVariable("initiator", user);
//触发流程转向
pi.signal();
/*
* 保存流程实例
*/
jbpmContext.save(pi);
return pi;
}finally{
jbpmContext.close();
}
}

/**
* 填写提交申请单
* @param money
*/
@SuppressWarnings("unchecked")
protected void submitApplication(String actorId , int money){
System.out.println("==FirstFlowProcessTest.submitApplication()==");
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
/*
*根据操作者ID,获取属于该操作者的任务集
*/
List<TaskInstance> taskInstances = jbpmContext.getTaskList(actorId);
for(TaskInstance ti : taskInstances){
System.out.println("ti.name = " + ti.getName());
System.out.println("ti.actor = " + ti.getActorId());
ContextInstance ci = ti.getContextInstance();
ci.setVariable("money",new Integer(money));
ti.end();
}
}finally{
jbpmContext.close();
}
}

/**
* 部门经理审批
* @param pass
*/
@SuppressWarnings("unchecked")
protected void approveByManager(boolean pass){
System.out.println("==FirstFlowProcessTest.approveByManager()==");
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try{
List<TaskInstance> taskInstances = jbpmContext.getTaskList("DepartmentManager");
for(TaskInstance ti : taskInstances){
System.out.println("ti.name = " + ti.getName());
System.out.println("ti.actor = " + ti.getActorId());
if(pass){
ti.end("部门经理审批通过");
}else{
ti.end("部门经理驳回");
}
}
}finally{
jbpmContext.close();
}
}

/**
* 总经理审批
* @param pass
*/
@SuppressWarnings("unchecked")
protected void approveByPresident(boolean pass){
System.out.println("==FirstFlowProcessTest.approveByPresident()==");
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try{
List<TaskInstance> taskInstances = jbpmContext.getTaskList("President");
for(TaskInstance ti : taskInstances){
System.out.println("ti.name = " + ti.getName());
System.out.println("ti.actor = " + ti.getActorId());
if(pass){
ti.end("总经理审批通过");
}else{
ti.end("总经理驳回");
}
}
}finally{
jbpmContext.close();
}
}

/**
* 打印任务记录
*/
@SuppressWarnings("unchecked")
protected void checkTasks(ProcessInstance pi){
System.out.println("==FirstFlowProcessTest.checkTasks()==");
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try{
List<TaskInstance> coll = jbpmContext.getTaskMgmtSession().findTaskInstancesByProcessInstance(pi);
System.out.println("====Process has task:====");
for(TaskInstance ti : coll){
System.out.println("=="+ti.getName()+"==");
System.out.println("=="+ti.getActorId()+"==");
System.out.println("=="+ti.getVariables().toString() +"==");
}
System.out.println("====end====");
}finally{
jbpmContext.close();
}
}
}
相对于简单流程测试案例,标准流程的业务是相同的。它们的不同点在于:简单流程通过XML载入流程定义,并使用类的全局变量来保存流程实例的引用;而标准流程则是通过数据库载入流程定义,并使用数据库的会话来维护流程的运行。

1. 从数据库载入流程定义

Java代码
1. JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
 JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();从jBPM配置环境中获取jBPM上下文实例,jbpmContext是对jbpm持久层操作API及上下文环境的封装,它根据jBPM的配置文件生成。

Java代码
1.GraphSession graphSession = jbpmContext.getGraphSession();
GraphSession graphSession = jbpmContext.getGraphSession();生成对流程图操作的持久层会话

Java代码
1.ProcessDefinition pdf = graphSession.findLatestProcessDefinition("simple");
ProcessDefinition pdf = graphSession.findLatestProcessDefinition("simple");从数据库获取命名为“simple”的流程定义。

2. 新建流程实例,并存入数据库持久化

Java代码
1.ProcessInstance pi = pdf.createProcessInstance();
ProcessInstance pi = pdf.createProcessInstance();根据流程定义生成实例。

Java代码
1.pi.getContextInstance().createVariable("initiator", user);
2.pi.signal();
pi.getContextInstance().createVariable("initiator", user);
pi.signal();
设置实例的上下文变量,激活实例执行进程。

Java代码
1.jbpmContext.save(pi);
jbpmContext.save(pi);
保存实例到数据库,持久化实例。

3. 从数据库获取属于指定操作者的任务集

Java代码
1.JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
2.try{
3. List<TaskInstance> taskInstances = jbpmContext.getTaskList("DepartmentManager");
4. for(TaskInstance ti : taskInstances){
5. System.out.println("ti.name = " + ti.getName());
6. System.out.println("ti.actor = " + ti.getActorId());
7. if(pass){
8. ti.end("部门经理审批通过");
9. }else{
10. ti.end("部门经理驳回");
11. }
12. }
13.}finally{
14. jbpmContext.close();
15.}
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try{
List<TaskInstance> taskInstances = jbpmContext.getTaskList("DepartmentManager");
for(TaskInstance ti : taskInstances){
System.out.println("ti.name = " + ti.getName());
System.out.println("ti.actor = " + ti.getActorId());
if(pass){
ti.end("部门经理审批通过");
}else{
ti.end("部门经理驳回");
}
}
}finally{
jbpmContext.close();
}通过JbpmContext对象,从数据库获取指定操作者的任务集合:

Java代码
1.List<TaskInstance> taskInstances = jbpmContext.getTaskList("DepartmentManager");
List<TaskInstance> taskInstances = jbpmContext.getTaskList("DepartmentManager");
注意,在每次的JbpmContext对象使用完毕后,一定要执行jbpmContext.close(),其本质是要释放数据库的操作连接。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值