疯狂Activiti6.0连载(25)BPMN结束事件

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/boxiong86/article/details/78767144

 本文节选自《疯狂工作流讲义(第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

 

BPMN结束事件

结束事件表示流程的结束,因此结束事件并不允许有输出的顺序流,BPMN2.0规定没有顺序流可以从结束事件中输出。根据前面的章节所述,结束事件总是抛出事件,这些事件会自动执行并反馈结果,并不需要触发。BPMN2.0中定义了多种结束事件,包括:无指定None结束事件、消息(Message)结束事件、升级(Escalation)结束事件、错误(Error)取消事件、取消Cancel结束事件、补偿Compensation结束事件、信号Signal结束事件、终止Terminate结束事件和组合Multiple结束事件,目前Activiti支持无指定结束事件、错误结束事件取消结束事件和终止结束事件

无指定结束事件

与无指定开始事件一样,无指定结束事件,是指流程在结束时,不会进行任何的额外操作,结束事件中不使用任何事件的定义。11-5为结束事件的图形。


11-5 结束事件的图形

在流程描述文件中,使用endEvent元素定义一件结束事件,代码清单11-14为一个简单的流程配置内容,该段配置的粗体字代码,定义了一个无指定结束事件。

代码清单11-14codes\11\11.4\end-event\resource\bpmn\NoneEndEvent.bpmn

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

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

        <userTask id="usertask1" name="User Task"></userTask>

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

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

            targetRef="usertask1"></sequenceFlow>

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

            targetRef="endevent1"></sequenceFlow>

    </process>

错误结束事件

当执行流到达错误结束事件时,会结束该执行流并且抛出错误,该错误可以被错误边界事件捕获,如果没有定义任何的错误边界事件,那么将会被当作无指定错误事件执行,因此,错误结束事件一般使用在子流程当中错误事件结束,就会触发依附在该子流程上的错误边界事件。11-6定义了一个含有错误结束事件和错误边界事件的流程。


11-6 含有错误结束事件和错误边界事件的流程

如图11-6所示,该流程一启动,就会进入一个嵌套子流程,当完成了“Sub Task”的用户任务后,会遇到单向网关,此时会产生两个分支,一个分支会正常结束该子流程,另外一个分支会触发错误结束事件,如果正常结束该子流程,流程会到达“End Task”任务,而触发错误结束事件后,流程会到达“Error Task”任务。该流程图对应的流程文件如代码清单11-15所示。

代码清单11-15codes\11\11.4\end-event\resource\bpmn\ErrorEndEvent.bpmn

    <error id="myError" errorCode="myError"></error>   

    <process id="errorEndProcess" name="errorEndProcess">

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

        <subProcess id="subprocess1" name="Sub Process">

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

            <userTask id="usertask1" name="Sub Task"></userTask>

            <endEvent id="endevent1" name="ErrorEnd">

                <errorEventDefinition errorRef="myError"></errorEventDefinition>

            </endEvent>

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

                targetRef="usertask1"></sequenceFlow>

            <exclusiveGateway id="exclusivegateway1" name="Exclusive Gateway"></exclusiveGateway>

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

            <sequenceFlow id="flow7" name="" sourceRef="usertask1"

                targetRef="exclusivegateway1"></sequenceFlow>

            <sequenceFlow id="flow8" name="" sourceRef="exclusivegateway1"

                targetRef="endevent3">

                <conditionExpression xsi:type="tFormalExpression">${success == true}

                </conditionExpression>

            </sequenceFlow>

            <sequenceFlow id="flow9" name="" sourceRef="exclusivegateway1"

                targetRef="endevent1">

                <conditionExpression xsi:type="tFormalExpression">${success ==

                    false}</conditionExpression>

            </sequenceFlow>

        </subProcess>

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

            attachedToRef="subprocess1">

            <errorEventDefinition errorRef="myError"></errorEventDefinition>

        </boundaryEvent>

        <userTask id="usertask3" name="End Task"></userTask>

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

        <userTask id="usertask4" name="Error Task"></userTask>

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

            targetRef="subprocess1"></sequenceFlow>

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

            targetRef="usertask3"></sequenceFlow>

        <sequenceFlow id="flow5" name="" sourceRef="usertask3"

            targetRef="endevent2"></sequenceFlow>

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

            targetRef="usertask4"></sequenceFlow>

    </process>

代码清单11-15中的粗体字代码endEventboundaryEvent,分别定义了一个错误结束事件和错误边界事件,这两个事件中的错误事件定义,均引用了“myError”的错误,需要注意的是,在代码清单11-15中,需要使用单向网关,当流程参数“success”的值为true时,将会正常完成子流程,如果流程参数“success”的值为false(代码清单的粗体部分的表达式),将会触发错误结束事件。代码清单11-16加载该流程文件并执行相应的流程控制。

代码清单11-16codes\11\11.4\end-event\src\org\crazyit\activiti\ErrorEndEvent.java

        // 创建流程引擎

        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();

        // 得到流程存储服务组件

        RepositoryService repositoryService = engine.getRepositoryService();

        // 得到运行时服务组件

        RuntimeService runtimeService = engine.getRuntimeService();

        //获取流程任务组件

        TaskService taskService = engine.getTaskService();

        // 部署流程文件

        repositoryService.createDeployment()

            .addClasspathResource("bpmn/ErrorEndEvent.bpmn").deploy();

        // 启动流程

        runtimeService.startProcessInstanceByKey("errorEndProcess");       

        // 结束子流程中的任务,并设置结束参数

        Task subTask = taskService.createTaskQuery().singleResult();

        Map<String, Object> vars = new HashMap<String, Object>();

        //设置success参数为true

        vars.put("success", "true");

        taskService.complete(subTask.getId(), vars);

        // 查看到达的任务

        List<Task> tasks = taskService.createTaskQuery().list();

        for (Task task : tasks) {

            System.out.println(task.getName());

        }

注意代码清单11-16粗体字代码,该句代码主要用于设置“success”流程参数值,此处设置为“true”,即运行以上代码,将会正常完成子流程,流程会到达“End Task”的任务(不会触发错误结束事件),最终输出结果End Task;如果将“success”的参数值设置为false,那么将会触发错误结束事件,此时依附在子流程中的错误边界事件将会捕获到错误,流程会到达“Error Task”任务,最终输出结果Error Task

Activiti中,如果触发了边界事件,那么将会产生新的执行流,如果不希望取消原来的执行流,那么可以设置边界事件的cancelActivity属性为false(本例为true),即使新产生的执行流结束,原来的执行流不会中断原来执行流当前活动仍然为边界事件,如果cancelActivity设置为true,原来的执行流将会被取消。

取消结束事件和取消边界事件

取消结束事件只能使用在事务子流程(Transaction Sub-Process)中,该事件表示事务将会取消,并且会触发依附在事务子流程上的取消边界事件,与错误结束事件类似,取消结束事件会抛出,而取消边界事件则会捕获事件除此之外,事务子流程的取消事件的触发,还会导致补偿的触发。

BPMN2.0中,对于已经完成的活动,可以使用补偿机制,而对于一些正在进行的(活跃的)活动,不能使用补偿机制,而可以使用取消机制。当取消边界事件被触发,则会将当前的执行流中断,然后会同步地进行补偿机制,取消边界事件在离开事件子流程前,会一直等待补偿的结束,当补偿结束后,执行流会从取消边界事件离开事务子流程。假设现在有一个汇款的流程,该汇款流程作为一个事务子流程使用在主流程中,当汇款完成后,需要用户进行最终确认,如果用户确认,则结束流程,否则将触发取消结束事件并进行补偿操作,图11-7为该业务流程的流程图。


11-7 汇款业务的流程图

11-7的流程启动后,会马上进入事务子流,进入子流程后,会直接进行汇款操作,在此是一个ServiceTask,完成了汇款操作后,会进行用户的确认,用户确认完后会到达一个单向网关,判断用户确认的结果,如果用户确认的结果为false,则会进入取消结束事件,最后会触发依附在事务子流程中的取消边界事件(在此之前会触发事务子流程中的补偿事件)。11-7对应的流程描述文件内容如代码清单11-17所示。

代码清单11-17codes\11\11.4\end-event\resource\bpmn\CancelEndEvent.bpmn

    <process id="cancelProcess" name="cancelProcess">

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

        <transaction id="subprocess1" name="Sub Process">

            <serviceTask id="servicetask1" name="汇款操作"

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

            <boundaryEvent id="boundarysignal1" attachedToRef="servicetask1">           

                <compensateEventDefinition></compensateEventDefinition>

            </boundaryEvent>

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

            <serviceTask id="servicetask2" name="取消汇款"                           

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

                isForCompensation="true"></serviceTask>

            <userTask id="usertask1" name="确认汇款"></userTask>

            <exclusiveGateway id="exclusivegateway1" name="Exclusive Gateway"></exclusiveGateway>

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

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

                <cancelEventDefinition></cancelEventDefinition>

            </endEvent>

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

                targetRef="servicetask1"></sequenceFlow>

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

                targetRef="usertask1"></sequenceFlow>

            <sequenceFlow id="flow5" name="" sourceRef="usertask1"

                targetRef="exclusivegateway1"></sequenceFlow>

            <sequenceFlow id="flow6" name="" sourceRef="exclusivegateway1"

                targetRef="endevent1">

                <conditionExpression xsi:type="tFormalExpression">${confirm == true}

                </conditionExpression>

            </sequenceFlow>

            <sequenceFlow id="flow7" name="" sourceRef="exclusivegateway1"

                targetRef="endevent2">

                <conditionExpression xsi:type="tFormalExpression">${confirm ==

                    false}

                </conditionExpression>

            </sequenceFlow>

            <association associationDirection="One" id="a1"

                sourceRef="boundarysignal1" targetRef="servicetask2" />

        </transaction>

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

            attachedToRef="subprocess1">

            <cancelEventDefinition></cancelEventDefinition>

        </boundaryEvent>

        <serviceTask id="servicetask3" name="接收取消操作"                               

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

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

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

            targetRef="subprocess1"></sequenceFlow>

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

            targetRef="endevent3"></sequenceFlow>

        <sequenceFlow id="flow8" name="" sourceRef="boundarysignal2"

            targetRef="servicetask3"></sequenceFlow>

    </process>

注意代码清单11-17粗体字代码,其中定义了三个ServiceTask,对应的是流程中的汇款操作、取消汇款操作和接收取消操作的ServiceTask对应的是RemitDelegate类(codes\11\11.4\end-event\src\org\crazyit\activiti\RemitDelegate.java),该类用于处理汇款操作,本例只输出“处理汇款业务”。

代码清单11-17中的定义的ServiceTask对应的是RollbackRemitDelegatecodes\11\11.4\end-event\src\org\crazyit\activiti\RollbackRemitDelegate.javaServiceTask会在补偿边界事件被触发后执行,RollbackRemitDelegate在业务上表示用于处理汇款的回滚(取消汇款)操作,此处只会输出“处理回滚汇款业务

需要注意的是,在定义取消汇款的ServiceTask的时候,需要为serviceTask加入isForCompensation属性并将值设置为true,该属性表示这个ServiceTask是一个补偿处理者的角色代码清单中的为接收取消通知的ServiceTask,对应的类为ReceiveCancelDelegatecodes\11\11.4\end-event\src\org\crazyit\activiti\ReceiveCancelDelegate.java,如果事务子流程被触发并且处理完全部的补偿事件后,则会执行该ServiceTask,本例中该类输出“处理子流程取消后的业务”。

代码清单11-17中的定义了一个补偿边界事件,如果该补偿边界事件所处的事务子流程被取消,则该补偿边界事件就会被触发,在本例中,如果的补偿边界事件被触发,就会执行“取消汇款”的ServiceTask

代码清单11-17中的定义了一个取消结束事件,当执行流到达取消结束事件时,就会抛出取消事件,而在代码清11-17中的则会捕获该事件,定义了一个取消边界事件,取消边界事件用于捕获取消事件,当捕获到事件后,则会中断当前的执行流,然后触发事务子流程中的补偿事件,最后流程离开取消边界事件,最后执行所定义的ServiceTask,代码清单11-18加载该流程文件。

代码清单11-18codes\11\11.4\end-event\src\org\crazyit\activiti\CancelEndEvent.java

        // 创建流程引擎

        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();

        // 得到流程存储服务组件

        RepositoryService repositoryService = engine.getRepositoryService();

        // 得到运行时服务组件

        RuntimeService runtimeService = engine.getRuntimeService();

        // 获取流程任务组件

        TaskService taskService = engine.getTaskService();

        // 部署流程文件

        repositoryService.createDeployment()

                .addClasspathResource("bpmn/CancelEndEvent.bpmn").deploy();

        // 启动流程

        runtimeService.startProcessInstanceByKey("cancelProcess");

        // 初始化流程参数

        Map<String, Object> vars = new HashMap<String, Object>();

        vars.put("confirm", false);

        // 设置参数,完成用户确认的Task

        Task task = taskService.createTaskQuery().singleResult();

        taskService.complete(task.getId(), vars);

当图11-7的流程启动后,会经过“汇款操作”的ServiceTask,此时会输出“处理汇款业务”,然后到达“用户确认”的Task,代码清单11-18中将“用户确认”的Task完成并设置“confirm”参数为false,这个时候流程会到达取消结束事件,取消结束事件被触发并抛出,此时取消边界事件会捕获该抛出事件,触发事务子流程里面的补偿事件(此处为代码清单11-17中的)。事务子流程里面“取消汇款”(代码清单11-17中的)是“汇款操作”(代码清单11-17中的)的补偿,当补偿事件被触发后,“取消汇款”的ServiceTask被执行,输出“处理回滚汇款业务”。当整个事务子流程里面的补偿事件都处理完后,流程离开取消边界事件(代码清单11-17中的),到达“接收取消操作”的ServiceTask,输出“处理子流程取消后的业务”。运行代码清单11-18,输出结果如下:

处理汇款业务

处理回滚汇款业务

处理子流程取消后的业务

 

终止结束事件

当流程执行到终止结束事件,当前的流程将会被终结,该事件可以使用在嵌入子流程、调用子流程、事件子流程或者事务子流程中。终止结束事件使用terminateEventDefinition元素作为事件定义,如果将该元素的activiti:terminateAll属性设置为true,那么当终止结束事件被触发事时,流程实例的全部执行流均会被终结。图11-8是一个简单流程,对应的流程XML文件如代码清单11-19所示。


11-8 测试终止结束事件流程

代码清单11-19codes\11\11.4\end-event\resource\bpmn\TerminateEndEvent_TerminateAll.bpmn

    <process id="terminateAll" name="terminateAll" isExecutable="true">

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

        <subProcess id="subprocess1" name="Sub Process">

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

            <serviceTask id="servicetask2" name="子流程的ServiceTask"

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

            <endEvent id="terminateendevent1" name="TerminateEndEvent">

                <terminateEventDefinition

                    activiti:terminateAll="true"></terminateEventDefinition>

            </endEvent>

            <sequenceFlow id="flow7" sourceRef="startevent2"

                targetRef="servicetask2"></sequenceFlow>

            <sequenceFlow id="flow8" sourceRef="servicetask2"

                targetRef="terminateendevent1"></sequenceFlow>

        </subProcess>

        <userTask id="servicetask1" name="第一个用户任务"></userTask>

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

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

            targetRef="parallelgateway1"></sequenceFlow>

        <sequenceFlow id="flow2" sourceRef="parallelgateway1"

            targetRef="subprocess1"></sequenceFlow>

        <sequenceFlow id="flow3" sourceRef="parallelgateway1"

            targetRef="servicetask1"></sequenceFlow>

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

        <userTask id="usertask1" name="第二个用户任务"></userTask>

        <sequenceFlow id="flow4" sourceRef="subprocess1"

            targetRef="usertask1"></sequenceFlow>

        <sequenceFlow id="flow5" sourceRef="servicetask1"

            targetRef="usertask1"></sequenceFlow>

        <sequenceFlow id="flow6" sourceRef="usertask1" targetRef="endevent1"></sequenceFlow>

    </process>

代码清单11-19中的粗体字代码,定义了终止结束事件,并将activiti:terminateAll属性设置为true,流程启动后,会分为两个执行流,一个到达子流程,一个到达“第一个用户任务”,子流程会自动执行ServictTask并触发终止结束事件,由于设置了activiti:terminateAll属性,因此当子流程中的结束事件触发后,整个流程实例会被结束。代码清单11-20为测试代码。

代码清单11-20codes\11\11.4\end-event\src\org\crazyit\activiti\TerminateEndEvent.java

        // 创建流程引擎

        ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();

        // 得到流程存储服务组件

        RepositoryService repositoryService = engine.getRepositoryService();

        // 得到运行时服务组件

        RuntimeService runtimeService = engine.getRuntimeService();

        // 部署流程文件

        repositoryService

                .createDeployment()

                .addClasspathResource(

                        "bpmn/TerminateEndEvent_TerminateAll.bpmn")

                .addClasspathResource("bpmn/TerminateEndEvent.bpmn").deploy();

        // 启动含有terminateAll属性的流程

        ProcessInstance pi1 = runtimeService

                .startProcessInstanceByKey("terminateAll");

        // 查询执行流数量

        long exeCount = runtimeService.createExecutionQuery()

                .processInstanceId(pi1.getId()).count();

        System.out.println("含有 terminateAll 属性的流程,中断结束事件触发后执行流数量:" + exeCount);

        // 启动不含 有 terminateAll属性的流程

        ProcessInstance pi2 = runtimeService

                .startProcessInstanceByKey("terminateEvent");

        // 查询全部执行流数量

        exeCount = runtimeService.createExecutionQuery()

                .processInstanceId(pi2.getId()).count();

        System.out.println("不含有terminateAll 属性的流程,中断结束事件触发后执行流数量:" + exeCount);

注意代码清单10-20中加载了两份流程文件,一份将activiti:terminateAll设置为true而另外一份则没有设置该属性(默认值为false),两份流程文件的流程图与图11-8一致。代码清单10-20加载两份流程文件后,分别启动两个流程实例,然后再进行执行流查询。运行代码清单10-20,得到以下结果:

含有 terminateAll 属性的流程,中断结束事件触发后执行流数量:0

不含有terminateAll 属性的流程,中断结束事件触发后执行流数量:3

根据输出结果可知,如果终止结束事件的activiti:terminateAll属性被设置为true,终止结束事件触发后,整个流程实例将会被终结,查询不到任何执行流。

 

京东购买地址: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

猜你喜欢

转载自blog.csdn.net/boxiong86/article/details/78767144