Fork me on GitHub

Activiti(2)--BPMN2.0规范

BPMN2.0:

  • 是一套业务流程模型与符号建模标准
  • 精准的执行语义来描述元素的操作
  • 以 XML 为载体, 以符号可视化业务

BPMN2.0元素:

  • 流对象
  • 连接对象
  • 数据
  • 泳道
  • 描述对象

其中最重要的流对象, 流对象包括活动, 事件和网关, 通过连接对象连接起来, 用来表示数据的流转, 泳道用来对业务的范围做区分, 描述对象并不影响业务的进行, 为流程图的可读性做补充性的描述.

image
image

1. 事件

1.1 分类

按照位置分类:

  • 开始事件
  • 中间事件/边界事件(中间事件是指出现在流程中, 可以单独作为流程节点的事件, 而边界事件指的是附属于某个流程节点的事件)
  • 结束事件

按照特征分类:

  • 捕获时间, 捕获事件是一直在等待被触发的事件, 所有的开始事件都是等待被触发的事件
  • 抛出事件, 执行到节点会自动执行并抛出结果, 结束事件都属于抛出事件.

按照定义分类:

  • 定时事件
  • 错误事件
  • 信号事件
  • 消息事件

1.1.1 定时事件

在使用定时事件时, 首先需要流程引擎的异步开关打开

  • 指定时间(timeDate)
  • 指定持续时间(timeDuration)
  • 周期执行(timeCycle)

定时事件的定义方式:

1
2
3
4
<timerEventDefinition>
<!-- 指定定时时间的类型: timeDate -->
<timeDate>2018-01-01T10:10:10</timeDate>
</timerEventDefinition>

1.1.1.1 定时开始事件

image

1
2
3
4
5
6
<startEvent id="timerStartEvent" name="Timer Start">
<timerEventDefinition>
<!--在流程部署完成 5 分钟后, 执行5次 -->
<timeCycle>R5/PT5M</timeCycle>
</timerEventDefinition>
</startEvent>

1.1.1.2 定时边界事件

流程流转到一个普通任务时, 如果在指定时间内没有完成, 就会触发定时边界事件

image

流程定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<startEvent id="startEvent"/>
<userTask id="commonTask" name="Common Task"/>
<!-- 声明边界事件, attachedToRef 指定边界事件的宿主 -->
<boundaryEvent id="boundaryEvent" name="Timer" attachedToRef="commonTask" cancelActivity="true">
<timerEventDefinition>
<!-- 部署完 5 秒之后 -->
<timeDuration>PT5S</timeDuration>
</timerEventDefinition>
</boundaryEvent>
<userTask id="timeoutTask" name="Timeout Task"/>
<endEvent id="end1"/>
<endEvent id="end2"/>
<!-- 构建顺序流 -->
<sequenceFlow sourceRef="startEvent" targetRef="commonTask"/>
<sequenceFlow sourceRef="commonTask" targetRef="end1"/>
<sequenceFlow sourceRef="boundaryEvent" targetRef="timeoutTask"/>
<sequenceFlow sourceRef="timeoutTask" targetRef="end2"/>
测试代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 超时边界事件
* 流程启动之后, 流转到 commonTask, 此时让当前线程休眠 10 秒(5秒就会超时)
* 定时任务超时后执行 timeoutTask
*/
@Test
@Deployment(resources = {"org/destiny/activiti/my-process-time-boundary.bpmn20.xml"})
public void testTimerBoundary() throws InterruptedException {
activitiRule.getRuntimeService().startProcessInstanceByKey("my-process");
// task 列表
List<Task> taskList = activitiRule.getTaskService().createTaskQuery().list();
log.info("task 总数: {}", taskList.size());
for (Task task : taskList) {
log.info("task.name = {}", task.getName());
}
// 强制睡眠等待边界事件触发
Thread.sleep(10 * 1000);
// task 列表
taskList = activitiRule.getTaskService().createTaskQuery().list();
log.info("休眠后 task 总数: {}", taskList.size());
for (Task task : taskList) {
log.info("休眠后 task.name = {}", task.getName());
}
}
日志输出
11:06:06,568 [main] INFO  org.destiny.activiti.bpmn20.TimerEventTest  - task 总数: 1
11:06:06,568 [main] INFO  org.destiny.activiti.bpmn20.TimerEventTest  - task.name = Common Task
...
11:06:16,573 [main] INFO  org.destiny.activiti.bpmn20.TimerEventTest  - 休眠后 task 总数: 1
11:06:16,573 [main] INFO  org.destiny.activiti.bpmn20.TimerEventTest  - 休眠后 task.name = Timeout Task

1.1.2 错误事件

网关中由提交的表单信息判断正常结束还是异常结束, 错误事件会被错误信息所触发, 主要用于处理业务异常

image

流程图简单分析

提交一个新的 sales leader, 创建一个子流程, 在子流程中同时对 customer rating 以及 profitability 进行考察, 如果同时通过, 则结束子流程, 完成主流程的 Store lead in CRM system 任务以及结束节点

如果 profitability 任务没有提供足够的信息, 则会抛出错误事件, 错误事件被边界事件所捕获, 进入 Provide additional details 流程, 并进入开始子流程

流程定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<error id="notEnoughInfoError" errorCode="not_enough_info" />
<process id="reviewSaledLead" name="Review sales lead">
<startEvent id="theStart" activiti:initiator="initiator" />
<sequenceFlow id="flow1" sourceRef="theStart" targetRef="provideNewSalesLead"/>
<userTask id="provideNewSalesLead" name="Provide new sales lead" activiti:assignee="${initiator}">
<extensionElements>
<activiti:formProperty id="customerName" name="Customer name" type="string" required="true"/>
<activiti:formProperty id="potentialProfit" name="Potential profit" type="long" />
<activiti:formProperty id="details" name="Details" type="string"/>
</extensionElements>
</userTask>
<sequenceFlow id="flow2" sourceRef="provideNewSalesLead" targetRef="reviewSalesLeadSubProcess"/>
<!-- 子流程 -->
<subProcess id="reviewSalesLeadSubProcess" name="Review sales lead">
<startEvent id="subProcessStart" />
<sequenceFlow id="flow3" sourceRef="subProcessStart" targetRef="fork"/>
<sequenceFlow id="flow4" sourceRef="fork" targetRef="reviewProfitability"/>
<parallelGateway id="fork" />
<sequenceFlow id="flow5" sourceRef="fork" targetRef="reviewCustomerRating"/>
<userTask id="reviewCustomerRating" name="Review customer rating" activiti:candidateGroups="accountancy" />
<sequenceFlow id="flow6" sourceRef="reviewCustomerRating" targetRef="subProcessEnd1"/>
<endEvent id="subProcessEnd1" />
<userTask id="reviewProfitability" name="Review profitability" activiti:candidateGroups="management">
<documentation>
${initiator} has published a new sales lead: ${customerName}. Details: ${details}
</documentation>
<extensionElements>
<activiti:formProperty id="notEnoughInformation" name="Do you believe this customer is profitable?" type="enum" required="true">
<activiti:value id="false" name="Yes" />
<activiti:value id="true" name="No (= request more info)" />
</activiti:formProperty>
</extensionElements>
</userTask>
<sequenceFlow id="flow7" sourceRef="reviewProfitability" targetRef="enoughInformationCheck"/>
<exclusiveGateway id="enoughInformationCheck" name="Enough information?" />
<sequenceFlow id="flow8" sourceRef="enoughInformationCheck" targetRef="notEnoughInformationEnd">
<conditionExpression>${notEnoughInformation == 'true'}</conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow9" sourceRef="enoughInformationCheck" targetRef="subProcessEnd2">
<conditionExpression>${notEnoughInformation == 'false'}</conditionExpression>
</sequenceFlow>
<endEvent id="subProcessEnd2" />
<endEvent id="notEnoughInformationEnd">
<errorEventDefinition errorRef="notEnoughInfoError" />
</endEvent>
</subProcess>
<sequenceFlow id="flow10" sourceRef="reviewSalesLeadSubProcess" targetRef="storeLeadInCrmSystem"/>
<boundaryEvent attachedToRef="reviewSalesLeadSubProcess" cancelActivity="true" id="catchNotEnoughInformationError" >
<errorEventDefinition errorRef="notEnoughInfoError" />
</boundaryEvent>
<sequenceFlow id="flow11" sourceRef="catchNotEnoughInformationError" targetRef="provideAdditionalDetails"/>
<userTask id="provideAdditionalDetails" name="Provide additional details" activiti:assignee="${initiator}">
<documentation>Provide additional details for ${customerName}.</documentation>
<extensionElements>
<activiti:formProperty id="details" name="Additional details" type="string" required="true"/>
</extensionElements>
</userTask>
<sequenceFlow id="flow12" sourceRef="provideAdditionalDetails" targetRef="reviewSalesLeadSubProcess"/>
<task id="storeLeadInCrmSystem" name="Store lead in CRM system" />
<sequenceFlow id="flow13" sourceRef="storeLeadInCrmSystem" targetRef="processEnd"/>
<endEvent id="processEnd" />
</process>

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
public class BoundaryErrorEventTest extends PluggableActivitiTestCase {

@Rule
public ActivitiRule activitiRule = new ActivitiRule();

@Override
protected void setUp() throws Exception {
super.setUp();
// 设置当前用户
Authentication.setAuthenticatedUserId("destiny");
}

@Override
public void tearDown() throws Exception {
Authentication.setAuthenticatedUserId(null);
super.tearDown();
}

@Deployment(resources = {"org/destiny/activiti/reviewSalesLead.bpmn20.xml"})
public void testReviewSalesLeadProcess() {
Map<String, Object> variables = new HashMap<>();
variables.put("details", "interesting");
variables.put("customerName", "Camery");

ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("reviewSaledLead", variables);
log.info("processInstance: {}", ToStringBuilder.reflectionToString(processInstance, ToStringStyle.JSON_STYLE));

Task task = taskService.createTaskQuery()
.taskAssignee("destiny")
.singleResult();

log.info("task: {}", ToStringBuilder.reflectionToString(task, ToStringStyle.JSON_STYLE));
// 使用断言确认
assertEquals(task.getName(), "Provide new sales lead");

// 提交节点
taskService.complete(task.getId());

// 进入并行网关, 会同时生成两个 task
Task ratingTask = taskService.createTaskQuery().taskCandidateGroup("accountancy").singleResult();
log.info("ratingTask: {}", ToStringBuilder.reflectionToString(ratingTask, ToStringStyle.JSON_STYLE));
assertEquals(ratingTask.getName(), "Review customer rating");

Task profitabilityTask = taskService.createTaskQuery().taskCandidateGroup("management").singleResult();
log.info("profitabilityTask: {}", ToStringBuilder.reflectionToString(profitabilityTask, ToStringStyle.JSON_STYLE));
assertEquals(profitabilityTask.getName(), "Review profitability");

// Review profitability 提交后就会触发 errorEvent
variables = new HashMap<>();
variables.put("notEnoughInformation", true);
taskService.complete(profitabilityTask.getId(), variables);

// 查找流程发起者 destiny 对应的 task
// 此时 errorEvent 会被边界条件捕获, 流转到 Review profitability
Task provideDetailsTask = taskService.createTaskQuery().taskAssignee("destiny").singleResult();
log.info("provideDetailsTask: {}", ToStringBuilder.reflectionToString(provideDetailsTask, ToStringStyle.JSON_STYLE));
assertEquals(provideDetailsTask.getName(), "Provide additional details");

// 完成 Review profitability 节点后, 会重新进入子流程
taskService.complete(provideDetailsTask.getId());
List<Task> reviewTasks = taskService.createTaskQuery().orderByTaskName().asc().list();
for (Task reviewTask : reviewTasks) {
log.info("reviewTask: {}", ToStringBuilder.reflectionToString(reviewTask, ToStringStyle.JSON_STYLE));
}
assertEquals(reviewTasks.get(0).getName(), "Review customer rating");
assertEquals(reviewTasks.get(1).getName(), "Review profitability");

taskService.complete(reviewTasks.get(0).getId());
variables.put("notEnoughInformation", false);
taskService.complete(reviewTasks.get(1).getId(), variables);
assertProcessEnded(processInstance.getId());
}
}

日志输出

分析日志可以看到, 没有错误所有的断言全部通过, 证明整个流程无误(日志量比较大, 为了能够清晰描述流程特地将日志格式调整)

03:53:20,823 [main] INFO  org.activiti.engine.ProcessEngines  - initialised process engine default
03:53:23,006 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,008 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 10 : theStart (StartEvent, parent id 5 (active)

03:53:23,009 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation :
03:53:23,009 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 10 : theStart (StartEvent, parent id 5 (active)

03:53:23,010 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,010 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 10 : theStart -> provideNewSalesLead, parent id 5 (active)

03:53:23,010 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,011 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 10 : provideNewSalesLead (UserTask, parent id 5 (active)

03:53:23,053 [main] INFO  org.activiti.engine.impl.test.AbstractTestCase  - processInstance: {"currentFlowElement":null,"currentActivitiListener":null,"processInstance":"ProcessInstance[5]","parent":null,"executions":[Execution[ id '10' ] - activity 'provideNewSalesLead - parent '5'],"superExecution":null,"subProcessInstance":null,"tenantId":"","name":null,"description":null,"localizedName":null,"localizedDescription":null,"lockTime":null,"isActive":true,"isScope":true,"isConcurrent":false,"isEnded":false,"isEventScope":false,"isMultiInstanceRoot":false,"isCountEnabled":false,"eventName":null,"eventSubscriptions":[],"jobs":[],"timerJobs":[],"tasks":[],"identityLinks":[IdentityLinkEntity[id=7, type=starter, userId=destiny, processInstanceId=5]],"deleteReason":null,"suspensionState":1,"startUserId":"destiny","startTime":"Sat Dec 08 15:53:23 CST 2018","eventSubscriptionCount":0,"taskCount":0,"jobCount":0,"timerJobCount":0,"suspendedJobCount":0,"deadLetterJobCount":0,"variableCount":0,"identityLinkCount":0,"processDefinitionId":"reviewSaledLead:1:4","processDefinitionKey":"reviewSaledLead","processDefinitionName":"Review sales lead","processDefinitionVersion":1,"deploymentId":null,"activityId":null,"activityName":null,"processInstanceId":"5","businessKey":null,"parentId":null,"superExecutionId":null,"rootProcessInstanceId":"5","rootProcessInstance":null,"forcedUpdate":false,"queryVariables":null,"isDeleted":false,"variableInstances":{details=VariableInstanceEntity[id=8, name=details, type=string, textValue=interesting], customerName=VariableInstanceEntity[id=9, name=customerName, type=string, textValue=Camery], initiator=VariableInstanceEntity[id=6, name=initiator, type=string, textValue=destiny]},"usedVariablesCache":{},"transientVariabes":null,"cachedElContext":null,"id":"5","revision":1,"isInserted":true,"isUpdated":false,"isDeleted":false}
03:53:23,075 [main] INFO  org.activiti.engine.impl.test.AbstractTestCase  - task: {"owner":null,"assigneeUpdatedCount":1,"originalAssignee":null,"assignee":"destiny","delegationState":null,"parentTaskId":null,"name":"Provide new sales lead","localizedName":null,"description":null,"localizedDescription":null,"priority":50,"createTime":"Sat Dec 08 15:53:23 CST 2018","dueDate":null,"suspensionState":1,"category":null,"isIdentityLinksInitialized":false,"taskIdentityLinkEntities":[],"executionId":"10","execution":null,"processInstanceId":"5","processInstance":null,"processDefinitionId":"reviewSaledLead:1:4","taskDefinitionKey":"provideNewSalesLead","formKey":null,"isDeleted":false,"isCanceled":false,"eventName":null,"currentActivitiListener":null,"tenantId":"","queryVariables":null,"forcedUpdate":false,"claimTime":null,"variableInstances":null,"usedVariablesCache":{},"transientVariabes":null,"cachedElContext":null,"id":"13","revision":1,"isInserted":false,"isUpdated":false,"isDeleted":false}
03:53:23,081 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.TriggerExecutionOperation :
03:53:23,082 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 10 : provideNewSalesLead (UserTask, parent id 5 (active)

03:53:23,083 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation :
03:53:23,083 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 10 : provideNewSalesLead (UserTask, parent id 5 (active)

03:53:23,084 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,084 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 10 : provideNewSalesLead -> reviewSalesLeadSubProcess, parent id 5 (active)

03:53:23,084 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,084 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 10 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active)

03:53:23,086 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,087 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 10 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (ended)
└── 14 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 16 : catchNotEnoughInformationError (BoundaryEvent, parent id 14 (active)
    └── 17 : subProcessStart (StartEvent, parent id 14 (active)

03:53:23,087 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation :
03:53:23,087 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 10 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (ended)
└── 14 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 16 : catchNotEnoughInformationError (BoundaryEvent, parent id 14 (active)
    └── 17 : subProcessStart (StartEvent, parent id 14 (active)

03:53:23,087 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,088 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 10 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (ended)
└── 14 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 16 : catchNotEnoughInformationError (BoundaryEvent, parent id 14 (active)
    └── 17 : subProcessStart -> fork, parent id 14 (active)

03:53:23,088 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,088 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 10 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (ended)
└── 14 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 16 : catchNotEnoughInformationError (BoundaryEvent, parent id 14 (active)
    └── 17 : fork (ParallelGateway, parent id 14 (active)

03:53:23,088 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation :
03:53:23,089 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 10 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (ended)
└── 14 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 16 : catchNotEnoughInformationError (BoundaryEvent, parent id 14 (active)
    └── 17 : fork (ParallelGateway, parent id 14 (not active)

03:53:23,089 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,089 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 10 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (ended)
└── 14 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 16 : catchNotEnoughInformationError (BoundaryEvent, parent id 14 (active)
    ├── 17 : fork -> reviewProfitability, parent id 14 (active)
    └── 20 : fork -> reviewCustomerRating, parent id 14 (active)

03:53:23,089 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,090 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 10 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (ended)
└── 14 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 16 : catchNotEnoughInformationError (BoundaryEvent, parent id 14 (active)
    ├── 17 : reviewProfitability (UserTask, parent id 14 (active)
    └── 20 : fork -> reviewCustomerRating, parent id 14 (active)

03:53:23,090 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,090 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 10 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (ended)
└── 14 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 16 : catchNotEnoughInformationError (BoundaryEvent, parent id 14 (active)
    ├── 17 : reviewProfitability (UserTask, parent id 14 (active)
    └── 20 : reviewCustomerRating (UserTask, parent id 14 (active)

03:53:23,092 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,092 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 10 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (ended)
└── 14 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 16 : catchNotEnoughInformationError (BoundaryEvent, parent id 14 (active)
    ├── 17 : reviewProfitability (UserTask, parent id 14 (active)
    └── 20 : reviewCustomerRating (UserTask, parent id 14 (active)

03:53:23,114 [main] INFO  org.activiti.engine.impl.test.AbstractTestCase  - ratingTask: {"owner":null,"assigneeUpdatedCount":0,"originalAssignee":null,"assignee":null,"delegationState":null,"parentTaskId":null,"name":"Review customer rating","localizedName":null,"description":null,"localizedDescription":null,"priority":50,"createTime":"Sat Dec 08 15:53:23 CST 2018","dueDate":null,"suspensionState":1,"category":null,"isIdentityLinksInitialized":false,"taskIdentityLinkEntities":[],"executionId":"20","execution":null,"processInstanceId":"5","processInstance":null,"processDefinitionId":"reviewSaledLead:1:4","taskDefinitionKey":"reviewCustomerRating","formKey":null,"isDeleted":false,"isCanceled":false,"eventName":null,"currentActivitiListener":null,"tenantId":"","queryVariables":null,"forcedUpdate":false,"claimTime":null,"variableInstances":null,"usedVariablesCache":{},"transientVariabes":null,"cachedElContext":null,"id":"25","revision":1,"isInserted":false,"isUpdated":false,"isDeleted":false}
03:53:23,116 [main] INFO  org.activiti.engine.impl.test.AbstractTestCase  - profitabilityTask: {"owner":null,"assigneeUpdatedCount":0,"originalAssignee":null,"assignee":null,"delegationState":null,"parentTaskId":null,"name":"Review profitability","localizedName":null,"description":"destiny has published a new sales lead: Camery. Details: interesting","localizedDescription":null,"priority":50,"createTime":"Sat Dec 08 15:53:23 CST 2018","dueDate":null,"suspensionState":1,"category":null,"isIdentityLinksInitialized":false,"taskIdentityLinkEntities":[],"executionId":"17","execution":null,"processInstanceId":"5","processInstance":null,"processDefinitionId":"reviewSaledLead:1:4","taskDefinitionKey":"reviewProfitability","formKey":null,"isDeleted":false,"isCanceled":false,"eventName":null,"currentActivitiListener":null,"tenantId":"","queryVariables":null,"forcedUpdate":false,"claimTime":null,"variableInstances":null,"usedVariablesCache":{},"transientVariabes":null,"cachedElContext":null,"id":"22","revision":1,"isInserted":false,"isUpdated":false,"isDeleted":false}
03:53:23,122 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.TriggerExecutionOperation :
03:53:23,125 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 14 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 16 : catchNotEnoughInformationError (BoundaryEvent, parent id 14 (active)
    ├── 17 : reviewProfitability (UserTask, parent id 14 (active)
    └── 20 : reviewCustomerRating (UserTask, parent id 14 (active)

03:53:23,126 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation :
03:53:23,126 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 14 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 16 : catchNotEnoughInformationError (BoundaryEvent, parent id 14 (active)
    ├── 17 : reviewProfitability (UserTask, parent id 14 (active)
    └── 20 : reviewCustomerRating (UserTask, parent id 14 (active)

03:53:23,126 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,127 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 14 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 16 : catchNotEnoughInformationError (BoundaryEvent, parent id 14 (active)
    ├── 17 : reviewProfitability -> enoughInformationCheck, parent id 14 (active)
    └── 20 : reviewCustomerRating (UserTask, parent id 14 (active)

03:53:23,127 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,127 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 14 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 16 : catchNotEnoughInformationError (BoundaryEvent, parent id 14 (active)
    ├── 17 : enoughInformationCheck (ExclusiveGateway, parent id 14 (active)
    └── 20 : reviewCustomerRating (UserTask, parent id 14 (active)

03:53:23,131 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation :
03:53:23,131 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 14 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 16 : catchNotEnoughInformationError (BoundaryEvent, parent id 14 (active)
    ├── 17 : enoughInformationCheck -> notEnoughInformationEnd, parent id 14 (active)
    └── 20 : reviewCustomerRating (UserTask, parent id 14 (active)

03:53:23,132 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,132 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 14 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 16 : catchNotEnoughInformationError (BoundaryEvent, parent id 14 (active)
    ├── 17 : enoughInformationCheck -> notEnoughInformationEnd, parent id 14 (active)
    └── 20 : reviewCustomerRating (UserTask, parent id 14 (active)

03:53:23,132 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,132 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 14 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 16 : catchNotEnoughInformationError (BoundaryEvent, parent id 14 (active)
    ├── 17 : notEnoughInformationEnd (EndEvent, parent id 14 (active)
    └── 20 : reviewCustomerRating (UserTask, parent id 14 (active)

03:53:23,133 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.TriggerExecutionOperation :
03:53:23,134 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 14 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 16 : catchNotEnoughInformationError (BoundaryEvent, parent id 14 (active)
    ├── 17 : notEnoughInformationEnd (EndEvent, parent id 14 (active)
    └── 20 : reviewCustomerRating (UserTask, parent id 14 (active)

03:53:23,139 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation :
03:53:23,139 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 14 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (scope) (ended)
    ├── 17 : notEnoughInformationEnd (EndEvent, parent id 14 (not active) (ended)
    └── 20 : reviewCustomerRating (UserTask, parent id 14 (not active) (ended)
└── 16 : catchNotEnoughInformationError (BoundaryEvent, parent id 5 (active)

03:53:23,139 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,139 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 14 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (scope) (ended)
    ├── 17 : notEnoughInformationEnd (EndEvent, parent id 14 (not active) (ended)
    └── 20 : reviewCustomerRating (UserTask, parent id 14 (not active) (ended)
└── 16 : catchNotEnoughInformationError -> provideAdditionalDetails, parent id 5 (active)

03:53:23,139 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,140 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 14 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (scope) (ended)
    ├── 17 : notEnoughInformationEnd (EndEvent, parent id 14 (not active) (ended)
    └── 20 : reviewCustomerRating (UserTask, parent id 14 (not active) (ended)
└── 16 : provideAdditionalDetails (UserTask, parent id 5 (active)

03:53:23,150 [main] INFO  org.activiti.engine.impl.test.AbstractTestCase  - provideDetailsTask: {"owner":null,"assigneeUpdatedCount":1,"originalAssignee":null,"assignee":"destiny","delegationState":null,"parentTaskId":null,"name":"Provide additional details","localizedName":null,"description":"Provide additional details for Camery.","localizedDescription":null,"priority":50,"createTime":"Sat Dec 08 15:53:23 CST 2018","dueDate":null,"suspensionState":1,"category":null,"isIdentityLinksInitialized":false,"taskIdentityLinkEntities":[],"executionId":"16","execution":null,"processInstanceId":"5","processInstance":null,"processDefinitionId":"reviewSaledLead:1:4","taskDefinitionKey":"provideAdditionalDetails","formKey":null,"isDeleted":false,"isCanceled":false,"eventName":null,"currentActivitiListener":null,"tenantId":"","queryVariables":null,"forcedUpdate":false,"claimTime":null,"variableInstances":null,"usedVariablesCache":{},"transientVariabes":null,"cachedElContext":null,"id":"32","revision":1,"isInserted":false,"isUpdated":false,"isDeleted":false}
03:53:23,152 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.TriggerExecutionOperation :
03:53:23,153 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 16 : provideAdditionalDetails (UserTask, parent id 5 (active)

03:53:23,154 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation :
03:53:23,154 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 16 : provideAdditionalDetails (UserTask, parent id 5 (active)

03:53:23,155 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,155 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 16 : provideAdditionalDetails -> reviewSalesLeadSubProcess, parent id 5 (active)

03:53:23,155 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,155 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 16 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active)

03:53:23,156 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,157 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 16 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (ended)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (active)
    └── 36 : subProcessStart (StartEvent, parent id 33 (active)

03:53:23,157 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation :
03:53:23,157 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 16 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (ended)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (active)
    └── 36 : subProcessStart (StartEvent, parent id 33 (active)

03:53:23,157 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,158 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 16 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (ended)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (active)
    └── 36 : subProcessStart -> fork, parent id 33 (active)

03:53:23,158 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,158 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 16 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (ended)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (active)
    └── 36 : fork (ParallelGateway, parent id 33 (active)

03:53:23,159 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation :
03:53:23,159 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 16 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (ended)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (active)
    └── 36 : fork (ParallelGateway, parent id 33 (not active)

03:53:23,159 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,159 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 16 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (ended)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (active)
    ├── 36 : fork -> reviewProfitability, parent id 33 (active)
    └── 39 : fork -> reviewCustomerRating, parent id 33 (active)

03:53:23,159 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,159 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 16 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (ended)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (active)
    ├── 36 : reviewProfitability (UserTask, parent id 33 (active)
    └── 39 : fork -> reviewCustomerRating, parent id 33 (active)

03:53:23,159 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,160 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 16 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (ended)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (active)
    ├── 36 : reviewProfitability (UserTask, parent id 33 (active)
    └── 39 : reviewCustomerRating (UserTask, parent id 33 (active)

03:53:23,161 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,162 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 16 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (ended)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (active)
    ├── 36 : reviewProfitability (UserTask, parent id 33 (active)
    └── 39 : reviewCustomerRating (UserTask, parent id 33 (active)

03:53:23,180 [main] INFO  org.activiti.engine.impl.test.AbstractTestCase  - reviewTask: {"owner":null,"assigneeUpdatedCount":0,"originalAssignee":null,"assignee":null,"delegationState":null,"parentTaskId":null,"name":"Review customer rating","localizedName":null,"description":null,"localizedDescription":null,"priority":50,"createTime":"Sat Dec 08 15:53:23 CST 2018","dueDate":null,"suspensionState":1,"category":null,"isIdentityLinksInitialized":false,"taskIdentityLinkEntities":[],"executionId":"39","execution":null,"processInstanceId":"5","processInstance":null,"processDefinitionId":"reviewSaledLead:1:4","taskDefinitionKey":"reviewCustomerRating","formKey":null,"isDeleted":false,"isCanceled":false,"eventName":null,"currentActivitiListener":null,"tenantId":"","queryVariables":null,"forcedUpdate":false,"claimTime":null,"variableInstances":null,"usedVariablesCache":{},"transientVariabes":null,"cachedElContext":null,"id":"44","revision":1,"isInserted":false,"isUpdated":false,"isDeleted":false}
03:53:23,180 [main] INFO  org.activiti.engine.impl.test.AbstractTestCase  - reviewTask: {"owner":null,"assigneeUpdatedCount":0,"originalAssignee":null,"assignee":null,"delegationState":null,"parentTaskId":null,"name":"Review profitability","localizedName":null,"description":"destiny has published a new sales lead: Camery. Details: interesting","localizedDescription":null,"priority":50,"createTime":"Sat Dec 08 15:53:23 CST 2018","dueDate":null,"suspensionState":1,"category":null,"isIdentityLinksInitialized":false,"taskIdentityLinkEntities":[],"executionId":"36","execution":null,"processInstanceId":"5","processInstance":null,"processDefinitionId":"reviewSaledLead:1:4","taskDefinitionKey":"reviewProfitability","formKey":null,"isDeleted":false,"isCanceled":false,"eventName":null,"currentActivitiListener":null,"tenantId":"","queryVariables":null,"forcedUpdate":false,"claimTime":null,"variableInstances":null,"usedVariablesCache":{},"transientVariabes":null,"cachedElContext":null,"id":"41","revision":1,"isInserted":false,"isUpdated":false,"isDeleted":false}
03:53:23,183 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.TriggerExecutionOperation :
03:53:23,190 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 39 : reviewCustomerRating (UserTask, parent id 33 (active)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (active)
    └── 36 : reviewProfitability (UserTask, parent id 33 (active)

03:53:23,190 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation :
03:53:23,190 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 39 : reviewCustomerRating (UserTask, parent id 33 (active)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (active)
    └── 36 : reviewProfitability (UserTask, parent id 33 (active)

03:53:23,191 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,191 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 39 : reviewCustomerRating -> subProcessEnd1, parent id 33 (active)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (active)
    └── 36 : reviewProfitability (UserTask, parent id 33 (active)

03:53:23,191 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,191 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 39 : subProcessEnd1 (EndEvent, parent id 33 (active)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (active)
    └── 36 : reviewProfitability (UserTask, parent id 33 (active)

03:53:23,191 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation :
03:53:23,192 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 39 : subProcessEnd1 (EndEvent, parent id 33 (active)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (active)
    └── 36 : reviewProfitability (UserTask, parent id 33 (active)

03:53:23,192 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.EndExecutionOperation :
03:53:23,193 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 39 : subProcessEnd1 (EndEvent, parent id 33 (active)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (active)
    └── 36 : reviewProfitability (UserTask, parent id 33 (active)

03:53:23,203 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.TriggerExecutionOperation :
03:53:23,204 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (active)
    └── 36 : reviewProfitability (UserTask, parent id 33 (active)

03:53:23,205 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation :
03:53:23,205 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (active)
    └── 36 : reviewProfitability (UserTask, parent id 33 (active)

03:53:23,206 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,206 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (active)
    └── 36 : reviewProfitability -> enoughInformationCheck, parent id 33 (active)

03:53:23,206 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,206 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (active)
    └── 36 : enoughInformationCheck (ExclusiveGateway, parent id 33 (active)

03:53:23,206 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation :
03:53:23,206 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (active)
    └── 36 : enoughInformationCheck -> subProcessEnd2, parent id 33 (active)

03:53:23,207 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,207 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (active)
    └── 36 : enoughInformationCheck -> subProcessEnd2, parent id 33 (active)

03:53:23,207 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,207 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (active)
    └── 36 : subProcessEnd2 (EndEvent, parent id 33 (active)

03:53:23,207 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation :
03:53:23,207 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (active)
    └── 36 : subProcessEnd2 (EndEvent, parent id 33 (active)

03:53:23,208 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.EndExecutionOperation :
03:53:23,208 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active) (scope)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (active)
    └── 36 : subProcessEnd2 (EndEvent, parent id 33 (active)

03:53:23,211 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation :
03:53:23,211 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (scope) (ended)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (not active) (ended)
    └── 36 : subProcessEnd2 (EndEvent, parent id 33 (not active) (ended)
└── 49 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (active)

03:53:23,211 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,211 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (scope) (ended)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (not active) (ended)
    └── 36 : subProcessEnd2 (EndEvent, parent id 33 (not active) (ended)
└── 49 : reviewSalesLeadSubProcess -> storeLeadInCrmSystem, parent id 5 (active)

03:53:23,211 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,212 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (scope) (ended)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (not active) (ended)
    └── 36 : subProcessEnd2 (EndEvent, parent id 33 (not active) (ended)
└── 49 : storeLeadInCrmSystem (ManualTask, parent id 5 (active)

03:53:23,212 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation :
03:53:23,212 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (scope) (ended)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (not active) (ended)
    └── 36 : subProcessEnd2 (EndEvent, parent id 33 (not active) (ended)
└── 49 : storeLeadInCrmSystem (ManualTask, parent id 5 (active)

03:53:23,212 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,212 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (scope) (ended)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (not active) (ended)
    └── 36 : subProcessEnd2 (EndEvent, parent id 33 (not active) (ended)
└── 49 : storeLeadInCrmSystem -> processEnd, parent id 5 (active)

03:53:23,212 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.ContinueProcessOperation :
03:53:23,212 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (scope) (ended)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (not active) (ended)
    └── 36 : subProcessEnd2 (EndEvent, parent id 33 (not active) (ended)
└── 49 : processEnd (EndEvent, parent id 5 (active)

03:53:23,212 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.TakeOutgoingSequenceFlowsOperation :
03:53:23,213 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (scope) (ended)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (not active) (ended)
    └── 36 : subProcessEnd2 (EndEvent, parent id 33 (not active) (ended)
└── 49 : processEnd (EndEvent, parent id 5 (active)

03:53:23,213 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.EndExecutionOperation :
03:53:23,213 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - 
5 (process instance)
└── 33 : reviewSalesLeadSubProcess (SubProcess, parent id 5 (not active) (scope) (ended)
    ├── 35 : catchNotEnoughInformationError (BoundaryEvent, parent id 33 (not active) (ended)
    └── 36 : subProcessEnd2 (EndEvent, parent id 33 (not active) (ended)
└── 49 : processEnd (EndEvent, parent id 5 (active)

03:53:23,281 [main] INFO  org.activiti.engine.impl.test.AbstractTestCase  - database was clean

1.1.3. 信号事件

1.1.3.1. 信号开始事件

信号开始事件的启动方式与普通开始事件启动方式很接近, 需要发出对应的信号去启动它

image

1
2
3
4
5
6
<signal id="theSignal" name="The signal"/>
<process id="processWithSignalStart1">
<startEvent id="theStart">
<signalEventDefinition id="theSignalEventDefinition" signalRef="theSignal"/>
</startEvent>
</process>

1.1.3.2 信号中间事件

1
2
3
4
5
6
7
8
9
10
11
12
<signal id="alterSignal" name="alter"/>
<process id="catchSignal">
<!-- 信号抛出事件 -->
<intermediateThrowEvent id="throwSignalEvent" name="Alter">
<signalEventDefinition signalRef="alterSignal"/>
</intermediateThrowEvent>

<!-- 信号捕获事件 -->
<intermediateCatchEvent id="throwSignalEvent" name="On Alter">
<signalEventDefinition signalRef="alterSignal"/>
</intermediateCatchEvent>
</process>

1.1.4 消息事件

消息事件的定义和信号事件的定义非常相近

1
2
3
4
5
6
7
8
9
10
11
12
<message id="newInvoice" name="newInvoiceMessage"/>
<message id="payment" name="paymentMessage"/>
<process id="catchSignal">
<startEvent id="messageStart">
<messsageEventDefinition messageRef="newInvoice"/>
</startEvent>

<!-- 信号捕获事件 -->
<intermediateCatchEvent id="paymentEvt">
<messageEventDefinition messageRef="payment"/>
</intermediateCatchEvent>
</process>

3. 顺序流和和网关

顺序流的分类:

  • 顺序流
  • 条件顺序流
  • 默认顺序流

3.1. 网关

网关的分类:

  • 排他网关(Exclusive Gateway)
  • 并行网关(Parallel Gateway)
  • 包容网关(Inclusive Gateway), 类似排他网关和并行网关的组合体, 即支持多条路, 有支持表达式
  • 事件网关(Event-based Gateway), 每个分支都有一个捕获事件等待被触发, 一个触发后, 其他都会失效

3.1.1. 排他网关

分支判断
只能选一
支持默认

image

流程定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<exclusiveGateway id="exclusiveGateway1" name="Exclusive Gateway"/>
<userTask id="userTask1" name="精英"/>
<userTask id="userTask2" name="优秀"/>
<userTask id="userTask3" name="普通"/>
<sequenceFlow id="flow2" sourceRef="exclusiveGateway1" targetRef="userTask1">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[score >= 90]]>
</conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow3" sourceRef="exclusiveGateway1" targetRef="userTask2">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[score >= 75 && score < 90]]>
</conditionExpression>
</sequenceFlow>
<!-- 默认数据流 -->
<sequenceFlow id="flow4" sourceRef="exclusiveGateway1" targetRef="userTask1"/>

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
@Test
@Deployment(resources = {"org/destiny/activiti/my-process-exclusive.bpmn20.xml"})
public void testExclusiveGateway() {
Map<String, Object> variables = new HashMap<>();
variables.put("key1", 3);
variables.put("score", 91);
ProcessInstance processInstance = activitiRule.getRuntimeService().startProcessInstanceByKey("my-process", variables);
Task task = activitiRule.getTaskService().createTaskQuery().singleResult();
// org.destiny.activiti.bpmn20.GatewayTest - task.name = 精英
log.info("task.name = {}", task.getName());
assertEquals(task.getName(), "精英");
}

3.1.2. 并行网关

让流程从单线程变为并发情况, 原有的一条流转数据变为两条, 可以同时进行确认支付和确认收货, 当这两个条件同时满足时, 继续进行后面的操作

image

  • 流程并发
  • 分支
  • 合并
  • 忽略条件(即使填了条件也不会生效)
  • 非对称(不要求所有的分支最终都合并在一起)

流程定义

1
2
3
4
5
6
7
8
9
10
<parallelGateway id="parallelGateway1" name="Parallel Gateway"/>
<userTask id="userTask1" name="确认支付"/>
<userTask id="userTask2" name="确认收货"/>
<sequenceFlow id="flow3" sourceRef="parallelGateway1" targetRef="userTask1"/>
<sequenceFlow id="flow4" sourceRef="parallelGateway1" targetRef="userTask2"/>
<parallelGateway id="parallelGateway2" name="parallelGateway"/>
<sequenceFlow id="flow8" sourceRef="userTask1" targetRef="parallelGateway2"/>
<sequenceFlow id="flow9" sourceRef="userTask2" targetRef="parallelGateway2"/>
<userTask id="userTask3" name="订单完成"/>
<sequenceFlow id="flow10" sourceRef="parallelGateway2" targetRef="userTask3"/>

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Test
@Deployment(resources = {"org/destiny/activiti/my-process-parallel.bpmn20.xml"})
public void testParallelGateway() {
activitiRule.getRuntimeService().startProcessInstanceByKey("my-process");
List<Task> taskList = activitiRule.getTaskService().createTaskQuery().list();
log.info("过并行网关时的 task 数量: {}", taskList.size());
for (Task task : taskList) {
log.info("task name: {}", task.getName());
}
assertEquals(2, taskList.size());
activitiRule.getTaskService().complete(taskList.get(0).getId());
List<Task> taskList1 = activitiRule.getTaskService().createTaskQuery().list();
log.info("提交第 1 个任务时的 task 数量: [{}]", taskList1.size());
for (Task task : taskList1) {
log.info("task name: {}", task.getName());
}
assertEquals(1, taskList1.size());
activitiRule.getTaskService().complete(taskList.get(1).getId());
List<Task> taskList2 = activitiRule.getTaskService().createTaskQuery().list();
log.info("提交第 2 个任务时的 task 数量: [{}]", taskList2.size());
for (Task task : taskList2) {
log.info("task name: {}", task.getName());
}
assertEquals(1, taskList1.size());
}

日志输出

06:17:51,705 [main] INFO  org.destiny.activiti.bpmn20.GatewayTest  - 过并行网关时的 task 数量: 2
06:17:51,705 [main] INFO  org.destiny.activiti.bpmn20.GatewayTest  - task name: 确认支付
06:17:51,706 [main] INFO  org.destiny.activiti.bpmn20.GatewayTest  - task name: 确认收货
06:17:51,710 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.TriggerExecutionOperation :

06:17:51,721 [main] INFO  org.destiny.activiti.bpmn20.GatewayTest  - 提交第 1 个任务时的 task 数量: [1]
06:17:51,721 [main] INFO  org.destiny.activiti.bpmn20.GatewayTest  - task name: 确认收货
06:17:51,724 [main] INFO  org.activiti.engine.impl.interceptor.DebugCommandInvoker  - Execution tree while executing operation class org.activiti.engine.impl.agenda.TriggerExecutionOperation :

06:17:51,738 [main] INFO  org.destiny.activiti.bpmn20.GatewayTest  - 提交第 2 个任务时的 task 数量: [1]
06:17:51,738 [main] INFO  org.destiny.activiti.bpmn20.GatewayTest  - task name: 订单完成

3.1.3. 包容性网关

可以理解为排他网关和并行网关的结合体

  • 排他网关有条件, 只能选择一条路径
  • 并行网关不带条件, 所有路径都会执行
  • 包容性网关有条件, 且支持并行运行
  1. 并发
  2. 分支
  3. 合并
  4. 条件
  5. 非对称

事件网关

会根据连接的捕获时间决定流程的走向, 只能走一条线路

  1. 流程暂停
  2. 事件订阅
  3. 捕获事件
  4. 单一执行
>