今天继续学习jbpm活动元素之 fork-join(分支/聚合活动)
当我们需要流程并发(concurrency)执行的时候,就需要使用到fork-join活动组合,fork活动可以使流程在一条主干上出现并行的分支,join活动则可以使流程的并行分支聚合成一条主干。
Fork活动仅具有jbpm活动的最基本特征,即具有1个name属性和n个流出转移元素。
相关jpdl如下:
<?xml version="1.0" encoding="UTF-8"?>
<process name="forkAndJoin" xmlns="http://jbpm.org/4.4/jpdl">
<start>
<transition to="fork" />
</start>
<!-- 流程在此产生 3个并行的分支 -->
<fork name="fork">
<transition to="pingpang" />
<transition to="football" />
<transition to="basketball" />
</fork>
<state name="pingpang">
<transition to="to playball" /><!-- 就是打一般的球 -->
</state>
<!-- 足球和篮球属于大球 会join到to play bigball活动上去-->
<state name="football">
<transition to="to play bigball" />
</state>
<state name="basketball">
<transition to="to play bigball" />
</state>
<!-- 在此定义to play bigball的join活动 -->
<join name="to play bigball">
<transition to="play bigball" /> <!-- 开始打大球 -->
</join>
<state name="play bigball">
<transition to="to playball" /><!-- 其实是一起去打普通的球 -->
</state>
<join name="to playball">
<transition to="play over" /><!-- 打球马上结束了 -->
</join>
<end name="play over"/>
</process>
编写单元测试代码执行上面的流程定义
package org.test;
import java.util.HashSet;
import java.util.Set;
import org.jbpm.api.ProcessInstance;
import org.jbpm.test.JbpmTestCase;
public class ForkAndJoinTest extends JbpmTestCase {
String deploymentId;
@Override
public void setUp() throws Exception {
super.setUp();
deploymentId = repositoryService.createDeployment()
.addResourceFromClasspath("org/test/forkAndJoin.jpdl.xml")
.deploy();
}
@Override
public void tearDown() throws Exception {
repositoryService.deleteDeploymentCascade(deploymentId);
super.tearDown();
}
public void testForkAndJoin() {
// 发起forkAndJoin流程实例
ProcessInstance pi = executionService
.startProcessInstanceByKey("forkAndJoin");
String pid = pi.getId();
// 构造一个活动名称集合以验证分支。设置3个分支活动的名称
Set<String> ballsName = new HashSet<String>();
ballsName.add("pingpang");
ballsName.add("football");
ballsName.add("basketball");
// 断言当前活动即为产生的3个分支
assertEquals(ballsName, pi.findActiveActivityNames());
// 发出执行信号通过 "pingpang" 活动,这时候,流程会在最后的聚合活动"play"上等待其他分支的到来
String pingpangExecuId = pi.findActiveExecutionIn("pingpang").getId();
pi = executionService.signalExecutionById(pingpangExecuId);
// 在活动名称集合中排除 "pingpang"活动
ballsName.remove("pingpang");
// 此时,仍然可以断言另外2个分支还在等待
assertNotNull(pi.findActiveExecutionIn("football"));
assertNotNull(pi.findActiveExecutionIn("basketball"));
// 发出执行信号通过剩下的其中一个分支--football活动
String footballExecuId = pi.findActiveExecutionIn("football").getId();
pi = executionService.signalExecutionById(footballExecuId);
// 在活动名称集合中排除 "football"活动
ballsName.remove("football");
// 发出执行信号通过剩下的第二个分支--basketball活动
String basketballExecuId = pi.findActiveExecutionIn("basketball")
.getId();
pi = executionService.signalExecutionById(basketballExecuId);
// 在活动名称集合中排除"basketball"活动
ballsName.remove("basketball");
// size应该是0
System.out.println("ballsName.size(): " + ballsName.size());
// 断言通过了第一个聚合活动 "to play bigball",到达了"play bigball"活动
assertNotNull(pi.findActiveExecutionIn("play bigball"));
//发出执行信号通过"play bigball"活动
String playBigBallExecuId=pi.findActiveExecutionIn("play bigball").getId();
pi=executionService.signalExecutionById(playBigBallExecuId);
//最终的聚合活动"play over"等到了它的最后一个流入转移后,流向了end活动,所以流程实例结束
//因此可以断言此流程已经不存在了.
assertNull("execution"+pid+"应该不存在了",executionService.findExecutionById(pid));
System.out.println("结束了");
}
}
Fork-join活动看似其实不难,但是当流程变多的时候,我感觉在写代码的时候容易弄混淆,比如,流程在某个阶段执行到哪儿了,该怎样启动下一个流程,甚至有时候感觉有的state根本不需要,不知道其他人是否这样的感觉,但是我要说的是,假如真的能够体会你自己业务的每一步骤的话,就不会出现这种混乱的思想,举个简单的例子:假如有这样一个逻辑,某一个订单在发货后,1,客户要接收,2厂家要收款。这两个活动都完成了,那么才能结束流程,首先,你应该这样去考虑它们的步骤:发订单——》客户接收——客户打钱——厂家收款——销账(完成)。假如把这个过程再细化的话,首先发完订单,客户处于待接收状态,货到后客户可以接收,就变成待打钱状态,此时厂家就处于待收款状态,厂家收款后,销账完成。而这个细化的过程即基于每个状态点(如待接收状态),然后激起流程转移的动作(如接收),使我们真正在处理流程的过程。由于篇幅原因,下一张再介绍end等活动。