activiti学习(二十七)——涉及并行网关的节点跳转

分析

关于并行任务的跳转,分享牛大神有他的代码。不过我觉得并行网关分支出来的任务,无论从现实业务还是逻辑实现上,都不应该可以随意跳转。

分享牛的跳转逻辑,无非是把并行分支的execution及其对应task都结束,然后跳转到目标节点上。然而这里其实涉及一些问题。从业务上来讲,某个分支流程,它自己是否要跳转到别的节点,不应该影响平行的其他任务节点,除非有明确的业务要求。从实现逻辑的角度来说,当存在多个并行网关多次分支的流程图中,跳转到某个节点时,到底需要提前结束哪些任务就会变得极难判断,如下图所示。综上所述,我认为并行网关中的任务不应该随意进行跳转。

实际业务可能不存在这样的流程,这里夸张了一点,纯粹举个例子。例如usertask6要跳转到usertask7,那根据图上来看,usertask5、usertask3的任务和execution必须删除。而且例如usertask4要跳转到usertask1,涉及的并行execution如何处理也是个问题。所以真不建议在并行网关中使用跳转。

只有一种情况的是可以进行跳转的,就是同一分支上的节点之间的跳转。例如下图这样:

usertask3跳转到usertask1,usertask2跳转到usertask6。类似这些在同一分支上的任务节点跳转,情况和普通的跳转类似,不过我们还是要对前面的例子改改

 

代码

这次我们的流程图如下,会有分支、以及多实例任务,我们计划是usertask1和usertask3之间可来回跳转,usertask2和usertask4也是如此:

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
  <process id="parallelJump" name="parallelJump" isExecutable="true">
    <startEvent id="startevent1" name="Start"></startEvent>
    <parallelGateway id="parallelgateway1" name="Parallel Gateway"></parallelGateway>
    <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="parallelgateway1"></sequenceFlow>
    <userTask id="usertask1" name="usertask1">
      <multiInstanceLoopCharacteristics isSequential="false">
        <loopCardinality>3</loopCardinality>
      </multiInstanceLoopCharacteristics>
    </userTask>
    <sequenceFlow id="flow2" sourceRef="parallelgateway1" targetRef="usertask1"></sequenceFlow>
    <userTask id="usertask2" name="usertask2"></userTask>
    <sequenceFlow id="flow3" sourceRef="parallelgateway1" targetRef="usertask2"></sequenceFlow>
    <userTask id="usertask3" name="usertask3"></userTask>
    <sequenceFlow id="flow4" sourceRef="usertask1" targetRef="usertask3"></sequenceFlow>
    <userTask id="usertask4" name="usertask4">
      <multiInstanceLoopCharacteristics isSequential="true">
        <loopCardinality>3</loopCardinality>
      </multiInstanceLoopCharacteristics>
    </userTask>
    <sequenceFlow id="flow5" sourceRef="usertask2" targetRef="usertask4"></sequenceFlow>
    <parallelGateway id="parallelgateway2" name="Parallel Gateway"></parallelGateway>
    <sequenceFlow id="flow6" sourceRef="usertask3" targetRef="parallelgateway2"></sequenceFlow>
    <sequenceFlow id="flow7" sourceRef="usertask4" targetRef="parallelgateway2"></sequenceFlow>
    <endEvent id="endevent1" name="End"></endEvent>
    <sequenceFlow id="flow8" sourceRef="parallelgateway2" targetRef="endevent1"></sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_parallelJump">
    <bpmndi:BPMNPlane bpmnElement="parallelJump" id="BPMNPlane_parallelJump">
      <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="110.0" y="250.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="parallelgateway1" id="BPMNShape_parallelgateway1">
        <omgdc:Bounds height="40.0" width="40.0" x="190.0" y="247.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
        <omgdc:Bounds height="55.0" width="105.0" x="250.0" y="180.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2">
        <omgdc:Bounds height="55.0" width="105.0" x="250.0" y="290.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="usertask3" id="BPMNShape_usertask3">
        <omgdc:Bounds height="55.0" width="105.0" x="410.0" y="180.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="usertask4" id="BPMNShape_usertask4">
        <omgdc:Bounds height="55.0" width="105.0" x="410.0" y="290.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="parallelgateway2" id="BPMNShape_parallelgateway2">
        <omgdc:Bounds height="40.0" width="40.0" x="540.0" y="247.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="625.0" y="250.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
        <omgdi:waypoint x="145.0" y="267.0"></omgdi:waypoint>
        <omgdi:waypoint x="190.0" y="267.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
        <omgdi:waypoint x="210.0" y="247.0"></omgdi:waypoint>
        <omgdi:waypoint x="210.0" y="207.0"></omgdi:waypoint>
        <omgdi:waypoint x="250.0" y="207.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
        <omgdi:waypoint x="210.0" y="287.0"></omgdi:waypoint>
        <omgdi:waypoint x="210.0" y="317.0"></omgdi:waypoint>
        <omgdi:waypoint x="250.0" y="317.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
        <omgdi:waypoint x="355.0" y="207.0"></omgdi:waypoint>
        <omgdi:waypoint x="410.0" y="207.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow5" id="BPMNEdge_flow5">
        <omgdi:waypoint x="355.0" y="317.0"></omgdi:waypoint>
        <omgdi:waypoint x="410.0" y="317.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow6" id="BPMNEdge_flow6">
        <omgdi:waypoint x="515.0" y="207.0"></omgdi:waypoint>
        <omgdi:waypoint x="560.0" y="207.0"></omgdi:waypoint>
        <omgdi:waypoint x="560.0" y="247.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow7" id="BPMNEdge_flow7">
        <omgdi:waypoint x="515.0" y="317.0"></omgdi:waypoint>
        <omgdi:waypoint x="560.0" y="317.0"></omgdi:waypoint>
        <omgdi:waypoint x="560.0" y="287.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow8" id="BPMNEdge_flow8">
        <omgdi:waypoint x="580.0" y="267.0"></omgdi:waypoint>
        <omgdi:waypoint x="625.0" y="267.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

跳转的代码与前面MultiInstanceJumpCmd类似,但是要还要进行一些修改。之前我们是通过getProcessInstance获取其流程实例,进而获得其父execution,另外task也是与流程实例id关联的,通过流程实例id去控制是一种相对简便的处理方式。但在并行网关中,如果我们只处理单个分支的情况,通过流程实例id去处理task和execution,很可能会把另一分支也处理了,这不符合我们的预期,我们仅仅是需要处理当前分支对应的task和execution而已,所以不能通过流程实例id去操作,要老老实实根据活动节点的分类,判断是否获取其父节点进行操作:

package jump;

import java.util.List;
import java.util.Map;

import org.activiti.engine.delegate.TaskListener;
import org.activiti.engine.delegate.event.ActivitiEventType;
import org.activiti.engine.delegate.event.impl.ActivitiEventBuilder;
import org.activiti.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
import org.activiti.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.ExecutionEntityManager;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntityManager;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.pvm.runtime.AtomicOperation;
import org.activiti.engine.runtime.Execution;

public class ParallelJumpCmd implements Command {

	private String taskId;
	private Map<String, Object> variables;
	private String desActivityId;
	private String scActivityId;

	public ParallelJumpCmd(String taskId, Map<String, Object> variables, String scActivityId, String desActivityId) {
		this.taskId = taskId;
		this.variables = variables;
		this.desActivityId = desActivityId;
		this.scActivityId = scActivityId;
	}

	public Object execute(CommandContext commandContext) {
		TaskEntityManager taskEntityManager = commandContext.getTaskEntityManager();
		TaskEntity taskEntity = taskEntityManager.findTaskById(taskId);
		ExecutionEntity parentExecutionEntity = taskEntity.getProcessInstance();
		String processDefinitionId = parentExecutionEntity.getProcessDefinitionId();
		ProcessDefinitionEntity processDefinitionEntity = Context.getProcessEngineConfiguration().getDeploymentManager()
				.findDeployedProcessDefinitionById(processDefinitionId);
		ActivityImpl curActivityImpl = processDefinitionEntity.findActivity(scActivityId);

		if(curActivityImpl.getActivityBehavior() instanceof SequentialMultiInstanceBehavior) {
			parentExecutionEntity = taskEntity.getExecution().getParent();
		}else if(curActivityImpl.getActivityBehavior() instanceof ParallelMultiInstanceBehavior) {
			parentExecutionEntity = taskEntity.getExecution().getParent().getParent();
		}else {
			parentExecutionEntity = taskEntity.getExecution();
		}
		
		// 设置流程变量
		parentExecutionEntity.setVariables(variables);
		parentExecutionEntity.setExecutions(null);
		parentExecutionEntity.setActivity(curActivityImpl);
		parentExecutionEntity.setEventSource(curActivityImpl);
		parentExecutionEntity.setActive(true);

		// 触发全局事件转发器TASK_COMPLETED事件
		if (Context.getProcessEngineConfiguration().getEventDispatcher().isEnabled()) {
			Context.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent(ActivitiEventBuilder
					.createEntityWithVariablesEvent(ActivitiEventType.TASK_COMPLETED, this, variables, false));
		}

		// 删除任务
		List<ExecutionEntity> childExecutionEntityList = parentExecutionEntity.getExecutions();
		if(childExecutionEntityList.size() == 0) {
			//普通任务
			taskEntityManager.deleteTask(taskEntity, TaskEntity.DELETE_REASON_COMPLETED, false);	
		}
		for(ExecutionEntity childExecutionEntity : childExecutionEntityList) {
			List<ExecutionEntity> childExecutionEntityList1 = childExecutionEntity.getExecutions();
			if(childExecutionEntityList1.size() == 0) {
				//串行多实例任务处理
				List<TaskEntity> taskList = childExecutionEntity.getTasks();
				for(TaskEntity task : taskList) {
					task.fireEvent(TaskListener.EVENTNAME_COMPLETE);
					taskEntityManager.deleteTask(task, TaskEntity.DELETE_REASON_COMPLETED, false);	
				}
			}else {
				//并行多实例任务处理
				for(ExecutionEntity childExecutionEntity1 : childExecutionEntityList1) {
					List<TaskEntity> taskList1 = childExecutionEntity1.getTasks();
					for(TaskEntity task : taskList1) {
						task.fireEvent(TaskListener.EVENTNAME_COMPLETE);
						taskEntityManager.deleteTask(task, TaskEntity.DELETE_REASON_COMPLETED, false);	
					}
				}
			}
		}

		ExecutionEntityManager executionEntityManager = Context.getCommandContext().getExecutionEntityManager();
		List<ExecutionEntity> childExecutionList = executionEntityManager.findChildExecutionsByParentExecutionId(parentExecutionEntity.getId());
		for(ExecutionEntity executionEntityChild : childExecutionList) {
			List<ExecutionEntity> childExecutionList1 = executionEntityManager.findChildExecutionsByParentExecutionId(executionEntityChild.getId());
			for(ExecutionEntity executionEntityChild1 : childExecutionList1) {
				executionEntityChild1.remove();
				Context.getCommandContext().getHistoryManager().recordActivityEnd(executionEntityChild1);
			}
			executionEntityChild.remove();
			Context.getCommandContext().getHistoryManager().recordActivityEnd(executionEntityChild);
		}
		
		commandContext.getIdentityLinkEntityManager().deleteIdentityLinksByProcInstance(parentExecutionEntity.getId());
		ActivityImpl desActivityimpl = processDefinitionEntity.findActivity(desActivityId);	
		
		parentExecutionEntity.removeVariable("nrOfInstances");
		parentExecutionEntity.removeVariable("nrOfActiveInstances");
		parentExecutionEntity.removeVariable("nrOfCompletedInstances");
		parentExecutionEntity.removeVariable("loopCounter");
		
		parentExecutionEntity.setActivity(desActivityimpl);
		parentExecutionEntity.performOperation(AtomicOperation.TRANSITION_CREATE_SCOPE);

		return null;
	}
}

46-52行判断节点是串行多实例、并行多实例还是普通任务节点,分情况获取分支的父execution。68-92行也是分情况删除其execution下对应的task。如果还是像之前多实例任务跳转那样处理,就会把另一条分支上的task也删除了。另外59行要把状态设置成活动,因为多实例任务的父execution的isActive是false,跳转到普通任务节点,之后会导致普通任务节点的execution的usActive状态也是false,这就不对了。其他操作与前面的跳转是类似的,就不重复叙述了。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值