您的位置:首页 > 产品设计 > 产品经理

疯狂Activiti6.0连载(28)BPMN补偿中间事件

2017-12-20 22:50 507 查看
 本文节选自《疯狂工作流讲义(第2版)》
京东购买地址:https://item.jd.com/12246565.html
工作流Activiti6电子书:http://blog.csdn.net/boxiong86/article/details/78488562
工作流Activiti6教学视频:http://blog.csdn.net/boxiong86/article/details/78608585

 


补偿中间事件

在11.5.4章节中,讲解了补偿边界事件的使用,在该小节的案例中,使用了补偿中间事件触发补偿边界事件,补偿中间事件主要用于触发当前所在执行流的全部补偿Catching事件(补偿边界事件),补偿中间事件是Throwing事件,每个补偿事件都需要有关联的处理者,当补偿事件触发时,会转给这些处理者处理补偿,本小节将讲解在使用补偿中间事件的各种特性。


补偿执行次数

当补偿中间事件被触发,会触发当前执行流的补偿Catching事件,如果一个附有补偿Catching事件的流程活动会执行多次(具有多实例的特性),那么在进行补偿时,补偿次数与流程活动的执行次数一样。BPMN2.0中允许多实例的流程活动,在一个流程中,一个流程活动(某些部分或者全部)可以被执行多次,现定义一个测试流程,如图11-16所示。



图11-16
测试补偿次数流程

图11-16表示一个简单的流程,流程首先会到达“正常工作”的ServiceTask,然后会到达“抛出错误”的ServiceTask,这个“抛出错误”的ServiceTask总会抛出错误,然后触发错误边界事件,然后流程会到达补偿中间事件,此时补偿中间事件会触发“正常工作”的补偿边界事件,补偿将会由“补偿工作”的ServiceTask执行,该流程对应的流程文件内容如代码清单11-37所示。

代码清单11-37:codes\11\11.7\compensation-times\resource\bpmn\CompensationTimes.bpmn

<process id="ctProcess" name="ctProcess">

<startEvent id="startevent1" name="Start"></startEvent>

<serviceTask id="servicetask1" name="正常工作"

activiti:class="org.crazyit.activiti.RegularWork">

<multiInstanceLoopCharacteristics

isSequential="true">

<loopCardinality>3</loopCardinality>

</multiInstanceLoopCharacteristics>

</serviceTask>

<boundaryEvent id="boundarysignal1" cancelActivity="true"

attachedToRef="servicetask1">

<compensateEventDefinition></compensateEventDefinition>

</boundaryEvent>

<serviceTask id="servicetask2" name="抛出错误"

activiti:class="org.crazyit.activiti.ThrowError"></serviceTask>

<boundaryEvent id="boundaryerror1" cancelActivity="true"

attachedToRef="servicetask2">

<errorEventDefinition></errorEventDefinition>

</boundaryEvent>

<intermediateThrowEvent id="signalintermediatethrowevent1"

name="SignalThrowEvent">

<compensateEventDefinition></compensateEventDefinition>

</intermediateThrowEvent>

<endEvent id="endevent1" name="End"></endEvent>

<endEvent id="endevent2" name="End"></endEvent>

<sequenceFlow id="flow1" name="" sourceRef="startevent1"

targetRef="servicetask1"></sequenceFlow>

<sequenceFlow id="flow2" name="" sourceRef="servicetask1"

targetRef="servicetask2"></sequenceFlow>

<sequenceFlow id="flow3" name="" sourceRef="servicetask2"

targetRef="endevent1"></sequenceFlow>

<sequenceFlow id="flow4" name="" sourceRef="boundaryerror1"

targetRef="signalintermediatethrowevent1"></sequenceFlow>

<sequenceFlow id="flow5" name=""

sourceRef="signalintermediatethrowevent1"
targetRef="endevent2"></sequenceFlow>

<serviceTask id="servicetask3" name="补偿工作"isForCompensation="true"activiti:class="org.crazyit.activiti.CompensationWork"></serviceTask>

<association id="a2" sourceRef="boundarysignal1"

targetRef="servicetask3"></association>

</process>

代码清单11-37中的①定义了一个ServiceTask,这是一个多实例的ServiceTask,多实例活动是为了让流程活动能重复执行,声明一个流程活动需要执行多次,可以为serviceTask加入multiInstanceLoopCharacteristics子元素,在本例中,定义了“正常工作”的ServiceTask会执行3次,这个ServiceTask对应的类为RegularWork,该类实现如代码清单11-38所示。

代码清单11-38:codes\11\11.7\compensation-times\src\org\crazyit\activiti\RegularWork.java

public class RegularWork implements JavaDelegate {

int i = 0;

public void execute(DelegateExecution execution) throws Exception {

i++;

System.out.println("处理第
" + i + " 次正常工作...");

}

}

RegularWork类会打印执行次数,由于本例定义了“正常工作”的ServiceTask会执行3次,因此可知道此处会输出3次。代码清单11-37中的④定义了一个“补偿工作”的ServiceTask,对应的是CompensationWork类,该类的实现如代码清单11-39所示。

代码清单11-39:

codes\11\11.7\compensation-times\src\org\crazyit\activiti\CompensationWork.java

public class CompensationWork implements JavaDelegate {

int i = 0;

public void execute(DelegateExecution execution) throws Exception {

i++;

System.out.println("处理第
" + i + " 次补偿...");

}

}

CompensationWork类同样会打印执行次数,代码清单11-37中的②定义了一个“抛出错误”的ServiceTask,对应的类为ThrowError,该类无论怎样,均会抛出BpmnError,依附在这个ServiceTask的错误边界事件会被触发,然后流程会转向补偿边界事件(代码清单11-37中的③)并触发补偿。编写代码加载流程文件,启动流程,详细代码请见codes\11\11.7\compensation-times\src\org\crazyit\activiti\CompensationTimes.java,仅仅加载流程和启动流程,并没有其他特殊操作,运行CompensationTimes.java后,可以看到输出结果如下:

处理第 1
次正常工作...

处理第 2
次正常工作...

处理第 3
次正常工作...

抛出错误,触发补偿

处理第 1
次补偿...

处理第 2
次补偿...

处理第 3
次补偿...

根据结果可知,“正常工作”对应的JavaDelegate执行了3次,当补偿中间事件被触发时,“补偿工作”对应的JavaDelegate同样执行了3次,因此可以得出结论:补偿的执行次数与对应的流程活动的执行次数相等。


补偿的执行顺序

当流程的补偿触发时,各个补偿处理者会按照倒序进行补偿,最先完成的流程活动,其补偿处理者会最后执行,即使在并行的流程中,先完成的流程活动,其补偿处理者也会最后执行。图11-23为没有流程分支且需要补偿的流程。



图11-17
无流程分支的补偿

如图11-17所示,该流程执行完前面的两个ServiceTask后,会触发补偿中间事事件,第一个ServiceTask对应的补偿处理者为“CompansationA”,第二个ServiceTask对应的补偿处理者为“CompansationB”,当补偿中间事件触发时,会先执行“CompansationB”,再执行“CompansationA”。图11-17对应的流程文件内容如代码清单11-40所示。

代码清单11-40:

codes\11\11.7\compensation-sequence\resource\bpmn\CompensationSequence.bpmn

<process id="csProcess" name="csProcess">

<startEvent id="startevent1" name="Start"></startEvent>

<serviceTask id="servicetask1"
name="HandlerA"activiti:class="org.crazyit.activiti.HandlerA"></serviceTask>

<boundaryEvent id="boundarysignal1" cancelActivity="true"

attachedToRef="servicetask1">

<compensateEventDefinition></compensateEventDefinition>

</boundaryEvent>

<serviceTask id="servicetask2" name="HandlerB"activiti:class="org.crazyit.activiti.HandlerB"></serviceTask>

<boundaryEvent id="boundarysignal2" cancelActivity="true"

attachedToRef="servicetask2">

<compensateEventDefinition></compensateEventDefinition>

</boundaryEvent>

<intermediateThrowEvent id="signalintermediatethrowevent1"

name="SignalThrowEvent">

<compensateEventDefinition></compensateEventDefinition>

</intermediateThrowEvent>

<endEvent id="endevent1" name="End"></endEvent>

<serviceTask id="servicetask3" name="CompansationA"

activiti:class="org.crazyit.activiti.CompensationA"

isForCompensation="true"></serviceTask>

<serviceTask id="servicetask4" name="CompansationB"

activiti:class="org.crazyit.activiti.CompensationB"

isForCompensation="true"></serviceTask>

<association id="a1" sourceRef="boundarysignal2"

targetRef="servicetask4"
associationDirection="None"></association>

<association id="a2" sourceRef="boundarysignal1"

targetRef="servicetask3"
associationDirection="None"></association>

...省略顺序流配置

</process>

代码清单11-40中各个ServiceTask对应的JavaDelegate类,均只打印一句话,表示运行到该类,编写代码加载该流程,本例中对应的运行类为codes\11\11.7\compensation-sequence\src\org\crazyit\activiti\CompensationSequence.java,运行该类,可以看到输出结果如下:

A处理类处理任务...

B处理类处理任务...

B补偿类处理任务...

A补偿类处理任务...

对于一些并行的流程,并且在每一个流程分支中均有需要补偿的流程活动,那么相应的补偿处理者的执行顺序与正常流程一致,先完成的活动,补偿会最后执行,即使这些并行的活动是异步的。图11-18为含有流程分支并且需要进行补偿的流程,代码清单11-41为对应的流程文件内容。



图11-18
并行且发生补偿的流程

代码清单11-41:

codes\11\11.7\compensation-sequence\resource\bpmn\CompensationSequence2.bpmn

<process id="myProcess" name="myProcess">

<startEvent id="startevent1" name="Start"></startEvent>

<serviceTask id="servicetask2" name="HandlerB"

activiti:class="org.crazyit.activiti.HandlerB"></serviceTask>

<boundaryEvent id="boundarysignal2" cancelActivity="false"

attachedToRef="servicetask2">

<!--
<signalEventDefinition></signalEventDefinition> -->

<compensateEventDefinition></compensateEventDefinition>

</boundaryEvent>

<serviceTask id="servicetask1" name="HandlerA"

activiti:class="org.crazyit.activiti.
HandlerA"></serviceTask>

<boundaryEvent id="boundarysignal1" cancelActivity="false"

attachedToRef="servicetask1">

<compensateEventDefinition></compensateEventDefinition>

</boundaryEvent>

<serviceTask id="servicetask3" name="CompensationA"

activiti:class="org.crazyit.activiti.CompensationA"

isForCompensation="true"></serviceTask>

<serviceTask id="servicetask4" name="CompensationB"

activiti:class="org.crazyit.activiti.
CompensationB"

isForCompensation="true"></serviceTask>

<parallelGateway id="parallelgateway1" name="Parallel Gateway"></parallelGateway>

<parallelGateway id="parallelgateway2" name="Parallel Gateway"></parallelGateway>

<endEvent id="endevent1" name="End"></endEvent>

<intermediateThrowEvent id="signalintermediatethrowevent2"

name="SignalThrowEvent">

<compensateEventDefinition></compensateEventDefinition>

</intermediateThrowEvent>

<association id="a1" sourceRef="boundarysignal2"

targetRef="servicetask4"
associationDirection="None"></association>

<association id="a2" sourceRef="boundarysignal1"

targetRef="servicetask3"
associationDirection="None"></association>

...省略顺序流配置

</process>

编写运行代码加载该流程文件并启动流程,本例对应的Java类如下:codes\11\11.7\compensation-sequence\src\org\crazyit\activiti\CompensationSequence2.java。运行效果如下:

A处理类处理任务...

B处理类处理任务...

B补偿类处理任务...

A补偿类处理任务...


补偿的参数设置

在流程中设置参数,可以分为两类,一类是设置了只对当前执行流有效的参数(setVariableLocal),另外一类是设置了对整个流程有效的参数(setVariable)。那么在补偿触发时,如果在执行的流程活动中设置了参数,需要在补偿处理者中获取参数,其中就涉及这些参数的作用域问题。如果在流程活动中设置了全局的参数,那么在补偿处理者中肯定可以获取,如果在流程活动中只设置了对当前执行流有效的参数(setVariableLocal),那么在补偿处理者同样可以获取该参数。同样地,如果对一个子流程进行补偿,那么在补偿处理者中,可以获取setVariableLocal设置的参数,根据前面章节可以得知,有补偿边界事件的子执行流完成后,其相关数据仍然会保存在数据库中,这些数据包括执行流数据、流程参数和事件描述等。图11-19为一个含有补偿中间事件的普通流程。



图11-19
含有补偿中间事件的普通流程

在本例中,可以在流程活动(ServiceA的TaskService)中设置参数,然后在补偿处理者中获取参数。ServiceA对应的JavaDelegate和补偿处理者(CompensationA)对应的JavaDelegate如代码清单11-42所示。

代码清单11-42:

codes\11\11.7\compensastion-vars\src\org\crazyit\activiti\CompensationA.java

codes\11\11.7\compensastion-vars\src\org\crazyit\activiti\ServiceA.java

public class ServiceA implements JavaDelegate {

public void execute(DelegateExecution execution) throws Exception {

execution.setVariableLocal("user1", "angus");

System.out.println("处理类A执行...");

}

}

public class CompensationA implements JavaDelegate {

public void execute(DelegateExecution execution) throws Exception {

System.out.println("补偿A获取参数
" + execution.getVariable("user1"));

System.out.println("补偿A处理...");

}

}

代码清单11-42中的ServiceA是流程活动的JavaDelegate,而CompensationA则是补偿处理者,图11-19对应的流程文件为codes\11\11.7\compensastion-vars\resource\bpmn\Variable1.bpmn,在此不贴出该文件内容,编写代码加载流程文件并启动流程,可以看到输出结果如下:

处理类A执行...

补偿A获取参数
angus

补偿A处理...

根据结果可以看出,在流程活动中使用执行流对象的setVariableLocal方法设置的参数,在补偿处理类中,可以使用执行流对象的getVariable方法获取。同样地,在子流程中,补偿处理者也可以使用同样的方式获取流程活动中使用setVaribleLocal方法设置的参数。

京东购买地址:https://item.jd.com/12246565.html
工作流Activiti6电子书:http://blog.csdn.net/boxiong86/article/details/78488562
工作流Activiti6教学视频:http://blog.csdn.net/boxiong86/article/details/78608585
本书代码共享地址:https://gitee.com/yangenxiong/CrazyActiviti



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息