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

in 编程
关注公众号【好便宜】( ID:haopianyi222 ),领红包啦~
阿里云,国内最大的云服务商,注册就送数千元优惠券:https://t.cn/AiQe5A0g
腾讯云,良心云,价格优惠: https://t.cn/AieHwwKl
搬瓦工,CN2 GIA 优质线路,搭梯子、海外建站推荐: https://t.cn/AieHwfX9

本文节选自《疯狂工作流讲义(第2版)》

京东购买地址:https://item.jd.com/12246565.html

疯狂Activiti电子书:https://my.oschina.net/JavaLaw/blog/1570397

工作流Activiti教学视频:https://my.oschina.net/JavaLaw/blog/1577577

 

补偿中间事件

        在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   name="ctProcess">
		<startEvent   name="Start"></startEvent>
		<serviceTask   name="正常工作"								①
			activiti:class="org.crazyit.activiti.RegularWork">
			<multiInstanceLoopCharacteristics
				isSequential="true">
				<loopCardinality>3</loopCardinality>
			</multiInstanceLoopCharacteristics>
		</serviceTask>
		<boundaryEvent   cancelActivity="true"
			attachedToRef="servicetask1">
			<compensateEventDefinition></compensateEventDefinition>
		</boundaryEvent>
		<serviceTask   name="抛出错误"								②
	activiti:class="org.crazyit.activiti.ThrowError"></serviceTask>
		<boundaryEvent   cancelActivity="true"
			attachedToRef="servicetask2">
			<errorEventDefinition></errorEventDefinition>
		</boundaryEvent>
		<intermediateThrowEvent  						③
			name="SignalThrowEvent">
			<compensateEventDefinition></compensateEventDefinition>
		</intermediateThrowEvent>
		<endEvent   name="End"></endEvent>
		<endEvent   name="End"></endEvent>
		<sequenceFlow   name="" sourceRef="startevent1"
			targetRef="servicetask1"></sequenceFlow>
		<sequenceFlow   name="" sourceRef="servicetask1"
			targetRef="servicetask2"></sequenceFlow>
		<sequenceFlow   name="" sourceRef="servicetask2"
			targetRef="endevent1"></sequenceFlow>
		<sequenceFlow   name="" sourceRef="boundaryerror1"
			targetRef="signalintermediatethrowevent1"></sequenceFlow>
		<sequenceFlow   name=""
			sourceRef="signalintermediatethrowevent1" targetRef="endevent2"></sequenceFlow>
		<serviceTask   name="补偿工作"	isForCompensation="true"		④		activiti:class="org.crazyit.activiti.CompensationWork"></serviceTask>
		<association   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   name="csProcess">
		<startEvent   name="Start"></startEvent>
		<serviceTask   name="HandlerA"			activiti:class="org.crazyit.activiti.HandlerA"></serviceTask>
		<boundaryEvent   cancelActivity="true"
			attachedToRef="servicetask1">
			<compensateEventDefinition></compensateEventDefinition>
		</boundaryEvent>
		<serviceTask   name="HandlerB"			activiti:class="org.crazyit.activiti.HandlerB"></serviceTask>
		<boundaryEvent   cancelActivity="true"
			attachedToRef="servicetask2">
			<compensateEventDefinition></compensateEventDefinition>
		</boundaryEvent>
		<intermediateThrowEvent  
			name="SignalThrowEvent">
			<compensateEventDefinition></compensateEventDefinition>
		</intermediateThrowEvent>
		<endEvent   name="End"></endEvent>
		<serviceTask   name="CompansationA"
			activiti: 
			isForCompensation="true"></serviceTask>
		<serviceTask   name="CompansationB"
			activiti: 
			isForCompensation="true"></serviceTask>
		<association   sourceRef="boundarysignal2"
			targetRef="servicetask4" associationDirection="None"></association>
		<association   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   name="myProcess">
		<startEvent   name="Start"></startEvent>
		<serviceTask   name="HandlerB"
		activiti:class="org.crazyit.activiti.HandlerB"></serviceTask>
		<boundaryEvent   cancelActivity="false"
			attachedToRef="servicetask2">
			<!-- <signalEventDefinition></signalEventDefinition> -->
			<compensateEventDefinition></compensateEventDefinition>
		</boundaryEvent>
		<serviceTask   name="HandlerA"
		activiti:class="org.crazyit.activiti. HandlerA"></serviceTask>
		<boundaryEvent   cancelActivity="false"
			attachedToRef="servicetask1">
			<compensateEventDefinition></compensateEventDefinition>
		</boundaryEvent>
		<serviceTask   name="CompensationA"
			activiti: 
			isForCompensation="true"></serviceTask>
		<serviceTask   name="CompensationB"
			activiti: 
			isForCompensation="true"></serviceTask>
		<parallelGateway   name="Parallel Gateway"></parallelGateway>
		<parallelGateway   name="Parallel Gateway"></parallelGateway>
		<endEvent   name="End"></endEvent>
		<intermediateThrowEvent  
			name="SignalThrowEvent">
			<compensateEventDefinition></compensateEventDefinition>
		</intermediateThrowEvent>
		<association   sourceRef="boundarysignal2"
			targetRef="servicetask4" associationDirection="None"></association>
		<association   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方法设置的参数。

本文节选自《疯狂工作流讲义(第2版)》

京东购买地址:https://item.jd.com/12246565.html

疯狂Activiti电子书:https://my.oschina.net/JavaLaw/blog/1570397

工作流Activiti教学视频:https://my.oschina.net/JavaLaw/blog/1577577

本书代码目录:https://gitee.com/yangenxiong/CrazyActiviti

关注公众号【好便宜】( ID:haopianyi222 ),领红包啦~
阿里云,国内最大的云服务商,注册就送数千元优惠券:https://t.cn/AiQe5A0g
腾讯云,良心云,价格优惠: https://t.cn/AieHwwKl
搬瓦工,CN2 GIA 优质线路,搭梯子、海外建站推荐: https://t.cn/AieHwfX9
扫一扫关注公众号添加购物返利助手,领红包
Comments are closed.

推荐使用阿里云服务器

超多优惠券

服务器最低一折,一年不到100!

朕已阅去看看