一、启动事件
每个流程都需要从启动事件开始,根据不同的需求有空启动、定时启动、异常启动和消息启动。
异常启动事件不能用于主流程,必须嵌入到事件子流程中。
1、定时启动事件
定时启动标签timerEventDefinition嵌套在启动事件startEvent中就构成了定时启动事件。定时启动事件可以有三种启动属性:
如下定义了一个定时启动事件,部署流程5分钟后启动:
<startEvent id="timerstartevent1" name="Timer start">
<timerEventDefinition>
<timeDuration>PT5M</timeDuration>
</timerEventDefinition>
</startEvent>
定时启动的测试代码如下:
@Deployment(resources = "chapter11/timerEvent/timerStartEvent.bpmn")
public void testTriggerAutomatic() throws Exception {
// 部署之后引擎会自动创建一个定时启动事件的Job
JobQuery jobQuery = managementService.createJobQuery();
assertEquals(1, jobQuery.count());
// 模拟时间5分钟之后
SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss");
System.out.println(".... start ...." + sdf.format(new Date()));
Context.getProcessEngineConfiguration().getClock().setCurrentTime(new Date(System.currentTimeMillis() + ((50 * 60 * 1000) + 5000)));
waitForJobExecutorToProcessAllJobs(5000L, 1L);
System.out.println(".... end ...." + sdf.format(new Date()));
assertEquals(0, jobQuery.count());
// 检查是否启动了流程实例
long count = runtimeService.createProcessInstanceQuery().processDefinitionKey("timerStartEvent").count();
assertEquals(1, count);
}
Context.getProcessEngineConfiguration().getClock().setCurrentTime(new Date(System.currentTimeMillis() + ((50 * 60 * 1000) + 5000)));
waitForJobExecutorToProcessAllJobs(5000L, 1L);
这两行代码用activiti提供的测试工具用5s模拟5分钟,我自己运行的时候发现Context.getProcessEngineConfiguration()的结果是null,改用测试类继承的PluggableActivitiTestCase类的getProcessEngineConfiguration()后正常执行。
猜想是作者搞混了?Context.getProcessEngineConfiguration()是非测试时用的代码?
定时启动事件也可以手动触发,即API调用触发,测试代码如下:
@Deployment(resources = "chapter11/timerEvent/timerStartEvent.bpmn")
public void testTriggerManual() throws Exception {
// 部署之后引擎会自动创建一个定时启动事件的Job
JobQuery jobQuery = managementService.createJobQuery();
assertEquals(1, jobQuery.count());
// 手动触发作业的执行
Job job = jobQuery.singleResult();
managementService.executeJob(job.getId());
assertEquals(0, jobQuery.count());
// 检查是否启动了流程实例
long count = runtimeService.createProcessInstanceQuery().processDefinitionKey("timerStartEvent").count();
assertEquals(1, count);
}
定时启动的job呗记录在ACT_RU_JOB表中,以下是该表的一些主要字段:
2、消息启动事件
通过一个消息标志触发启动事件。
消息启动事件的相关代码:
<message id="MSG_001" name="启动XXX流程"></message>
<process id="messageStartEvent" name="messageStartEvent" isExecutable="true">
<startEvent id="messagestartevent1" name="Start">
<messageEventDefinition messageRef="MSG_001"></messageEventDefinition>
</startEvent>
<userTask id="usertask1" name="用户任务" activiti:assignee="kermit"></userTask>
<sequenceFlow id="flow1" sourceRef="messagestartevent1" targetRef="usertask1"></sequenceFlow>
<endEvent id="endevent1" name="End"></endEvent>
<sequenceFlow id="flow2" sourceRef="usertask1" targetRef="endevent1"></sequenceFlow>
</process>
以消息启动事件的测试代码:
public void testStartMessageEvent(){
ProcessInstance processInstance = this.runtimeService.startProcessInstanceByMessage("启动流程");
Assert.assertNotNull(processInstance);
}
也可以使用startProcessInstanceByKey方法来启动流程,但是这对于消息启动事件有一个限制:流程只能包含一个消息启动事件。测试代码如下:
public void testStartMessageEventByKey() throws Exception {
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("messageStartEvent");
assertNotNull(processInstance);
}
- EVENT_TYPE_:时间类型。message表示消息类型;
- EVENT_NAME_:消息名称。触发消息时引擎会根据消息名称参数和这个字段进行匹配;
- EXECUTION_ID_:实例执行ID。如果一个流程以消息启动事件引导,则此字段为空,如果在流程运行中注册了消息事件,则此字段保存流程执行ID;
- PROC_INST_ID_:流程实例ID。通EXECUTION_ID_,保存流程实例ID(与EXECUTION是一对多的关系);
- ACTIVITI_ID_:活动ID 。指示如果根据字段EVENT_NAME_匹配到消息就应该触发哪个活动;
- CONFIGURATION_:消息的匹配信息。对于消息类型来说这里保存的是流程定义ID。
测试消息注册成功的代码:
@Test
@Deployment(resources = "messageStartEvent.bpmn")
public void testMessageSubcription(){
EventSubscriptionQueryImpl query = new EventSubscriptionQueryImpl(this.processEngineConfiguration.getCommandExecutor());
EventSubscriptionEntity entity = query.eventName("启动流程").singleResult();
Assert.assertNotNull(entity);
}
没写完,要出门了,晚上回来继续。BTW,今天北京天气不错,这会出门还能晒到太阳,下午安。
回来了,笔记继续。
二、终止结束事件
终止结束事件和空结束事件的区别在于终止结束事件可以终止整个流程实例的执行,而空结束事件只结束一条输出流的执行。
上图的流程中启动事件后通过一个并行网关得到一个主任务流程和一个子任务流程。如果主任务完成后到达终止结束事件则整个流程都将结束(包括进行中的子任务),但如果子任务先于主任务结束,则主任务还未结束,整个流程还处于运行状态。
终止结束事件的XML定义如下:
<endEvent id="terminateendevent1" name="End">
<terminateEventDefinition></terminateEventDefinition>
</endEvent>
private void checkFinished(ProcessInstance processInstance) {
// 验证流程已结束
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
.processInstanceId(processInstance.getProcessInstanceId()).singleResult();
assertNotNull(historicProcessInstance.getEndTime());
// 查询历史任务
List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().list();
for (HistoricTaskInstance hti : list) {
System.out.println(hti.getName() + " " + hti.getDeleteReason());
}
// 流程结束后校验监听器设置的变量
HistoricVariableInstance variableInstance = historyService.createHistoricVariableInstanceQuery().variableName("settedOnEnd").singleResult();
assertEquals(true, variableInstance.getValue());
}
其中HistoricTaskInstance.getDeleteReason()得到任务的删除原因属性,如果任务正常完成则属性为complete,如果异常终止为delete。
三、边界事件
1、异常边界事件
下面的xml定义中在一个java service添加了异常边界事件
<process id="throwErrorManual" name="throwErrorManual" isExecutable="true">
<startEvent id="startevent1" name="Start"></startEvent>
<serviceTask id="servicetask1" name="自动执行系统任务" activiti:class="me.kafeitu.activiti.chapter11.listener.ThrowErrorManaualService"></serviceTask>
<sequenceFlow id="flow1" sourceRef="startevent1" targetRef="servicetask1"></sequenceFlow>
<endEvent id="endevent1" name="End"></endEvent>
<sequenceFlow id="flow2" sourceRef="servicetask1" targetRef="endevent1"></sequenceFlow>
<boundaryEvent id="boundaryerror1" name="Error" attachedToRef="servicetask1">
<errorEventDefinition errorRef="AIA_ERROR_99"></errorEventDefinition>
</boundaryEvent>
<userTask id="usertask1" name="处理异常"></userTask>
<sequenceFlow id="flow3" sourceRef="boundaryerror1" targetRef="usertask1"></sequenceFlow>
<sequenceFlow id="flow4" sourceRef="usertask1" targetRef="servicetask1"></sequenceFlow>
<textAnnotation id="textannotation1">
<text>异常代码:AIA_ERROR_99</text>
</textAnnotation>
<association id="association1" sourceRef="textannotation1" targetRef="boundaryerror1"></association>
</process>
public class ThrowErrorManaualService implements JavaDelegate {
@Override
public void execute(DelegateExecution execution) throws Exception {
if (execution.getVariable("pass") == null) {
throw new BpmnError("AIA_ERROR_99");
}
}
}
2、消息边界事件
定义消息边界事件的代码如下:
<process id="messageBoundaryEvent" name="messageBoundaryEvent" isExecutable="true">
<startEvent id="startevent1" name="Start"></startEvent>
<userTask id="usertask1" name="审核文件"></userTask>
<sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>
<endEvent id="endevent1" name="End"></endEvent>
<sequenceFlow id="flow2" sourceRef="usertask1" targetRef="endevent1"></sequenceFlow>
<boundaryEvent id="boundarymessage1" name="Message" attachedToRef="usertask1" cancelActivity="true">
<messageEventDefinition messageRef="MSG_HELP"></messageEventDefinition>
</boundaryEvent>
<userTask id="usertask2" name="协助处理"></userTask>
<sequenceFlow id="flow3" sourceRef="boundarymessage1" targetRef="usertask2"></sequenceFlow>
<sequenceFlow id="flow4" sourceRef="usertask2" targetRef="usertask1"></sequenceFlow>
</process>
ExecutionQuery executionQuery = runtimeService.createExecutionQuery().messageEventSubscriptionName("MSG_协助处理");
Execution execution = executionQuery.singleResult();
runtimeService.messageEventReceived("MSG_协助处理", execution.getId());
3、信号边界事件与消息边界事件的机制相似
<signal id="S_HELP" name="S_协助处理"></signal>
<process id="signalBoundaryEvent" name="Signal Boundary Event" isExecutable="true">
<startEvent id="startevent1" name="Start"></startEvent>
<userTask id="usertask1" name="审核文件"></userTask>
<sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>
<boundaryEvent id="boundarysignal1" name="Signal" attachedToRef="usertask1" cancelActivity="true">
<signalEventDefinition signalRef="S_HELP"></signalEventDefinition>
</boundaryEvent>
<userTask id="usertask2" name="协助处理"></userTask>
<sequenceFlow id="flow2" sourceRef="boundarysignal1" targetRef="usertask2"></sequenceFlow>
<sequenceFlow id="flow3" sourceRef="usertask2" targetRef="usertask1"></sequenceFlow>
<endEvent id="endevent1" name="End"></endEvent>
<sequenceFlow id="flow4" sourceRef="usertask1" targetRef="endevent1"></sequenceFlow>
</process>
分为捕获型和抛出型两种,定义如下:
<process id="throwAlert" name="My process" isExecutable="true">
<startEvent id="startevent1" name="Start"></startEvent>
<intermediateThrowEvent id="signalintermediatethrowevent1" name="SignalThrowEvent">
<signalEventDefinition signalRef="alert"></signalEventDefinition>
</intermediateThrowEvent>
<sequenceFlow id="flow1" sourceRef="startevent1" targetRef="signalintermediatethrowevent1"></sequenceFlow>
<endEvent id="endevent1" name="End"></endEvent>
<sequenceFlow id="flow2" sourceRef="signalintermediatethrowevent1" targetRef="endevent1"></sequenceFlow>
<textAnnotation id="textannotation1">
<text>抛出alert信号</text>
</textAnnotation>
<association id="association1" sourceRef="textannotation1" targetRef="signalintermediatethrowevent1"></association>
</process>
<signal id="alert" name="alert"></signal>
<signal id="abort" name="abort"></signal>
<process id="catchSignal" name="My process" isExecutable="true">
<startEvent id="startevent1" name="Start"></startEvent>
<userTask id="usertask1" name="附加边界用户任务"></userTask>
<sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>
<intermediateCatchEvent id="signalintermediatecatchevent1" name="SignalCatchEvent">
<signalEventDefinition signalRef="abort"></signalEventDefinition>
</intermediateCatchEvent>
<sequenceFlow id="flow2" sourceRef="startevent1" targetRef="signalintermediatecatchevent1"></sequenceFlow>
<userTask id="usertask2" name="被中间信号抛出事件触发"></userTask>
<sequenceFlow id="flow3" name="捕获abort信号" sourceRef="signalintermediatecatchevent1" targetRef="usertask2"></sequenceFlow>
<endEvent id="endevent1" name="End"></endEvent>
<sequenceFlow id="flow4" sourceRef="usertask2" targetRef="endevent1"></sequenceFlow>
<sequenceFlow id="flow5" sourceRef="usertask1" targetRef="endevent1"></sequenceFlow>
<boundaryEvent id="boundarysignal1" name="Signal" attachedToRef="usertask1" cancelActivity="true">
<signalEventDefinition signalRef="alert"></signalEventDefinition>
</boundaryEvent>
<sequenceFlow id="flow6" name="捕获alert信号" sourceRef="boundarysignal1" targetRef="usertask3"></sequenceFlow>
<userTask id="usertask3" name="通过信号边界事件触发"></userTask>
<sequenceFlow id="flow7" sourceRef="usertask3" targetRef="endevent1"></sequenceFlow>
</process>
来源:oschina
链接:https://my.oschina.net/u/2453016/blog/611928