- 1. 简介
- 2. 开始
- 3. 配置
- 4. Flowable API
- 5. 集成 Spring
- 6. 部署
- 7. BPMN 2.0 介绍
- 8. BPMN 2.0 结构
- 9. 表单
- 10. JPA
- 11. 历史
- 12. 身份管理
- 13. Eclipse Designer
- 14. Flowable UI 应用
- 15. REST API
- 16. 集成 CDI
- 17. 集成 LDAP
- 18. 高级
- 19. 工具
8.2. 事件
事件(event)通常用于为流程生命周期中发生的事情建模。事件总是图形化为圆圈。在BPMN 2.0中,有两种主要的事件分类:捕获(catching)与抛出(throwing)事件。
捕获: 当流程执行到达这个事件时,会等待直到触发器动作。触发器的类型由其中的图标,或者说XML中的类型声明而定义。捕获事件与抛出事件显示上的区别,是其内部的图标没有填充(即是白色的)。
抛出: 当流程执行到达这个事件时,会触发一个触发器。触发器的类型,由其中的图标,或者说XML中的类型声明而定义。抛出事件与捕获事件显示上的区别,是其内部的图标填充为黑色。
8.2.1. 事件定义
事件定义(event definition),用于定义事件的语义。没有事件定义的话,事件就“不做什么特别的事情”。例如,一个没有事件定义的开始事件,并不限定具体是什么启动了流程。如果为这个开始事件添加事件定义(例如定时器事件定义),就声明了启动流程的“类型”(例如对于定时器事件定义,就是到达了特定的时间点)。
8.2.2. 定时器事件定义
定时器事件(timer event definition),是由定时器所触发的事件。可以用于开始事件,中间事件,或边界事件。定时器事件的行为取决于所使用的业务日历(business calendar)。定时器事件有默认的业务日历,但也可以为每个定时器事件定义,单独定义业务日历。
<timerEventDefinition flowable:businessCalendarName="custom">
...
</timerEventDefinition>
其中businessCalendarName指向流程引擎配置中的业务日历。如果省略业务日历定义,则使用默认业务日历。
定时器定义必须且只能包含下列的一种元素:
timeDate。这个元素指定了ISO 8601格式的固定时间。在这个时间就会触发触发器。例如:
<timerEventDefinition>
<timeDate>2011-03-11T12:13:14</timeDate>
</timerEventDefinition>
timeDuration。要定义定时器需要等待多长时间再触发,可以用timerEventDefinition的子元素timeDuration。使用ISO 8601格式(BPMN 2.0规范要求)。例如(等待10天):
<timerEventDefinition>
<timeDuration>P10D</timeDuration>
</timerEventDefinition>
timeCycle。指定重复周期,可用于周期性启动流程,或者为超期用户任务多次发送提醒。这个元素可以使用两种格式。第一种是按照ISO 8601标准定义的循环时间周期。例如(三次重复间隔,每次间隔为10小时):
也可以使用timeCycle的可选属性endDate,或者像这样直接写在时间表达式的结尾:
R3/PT10H/${EndDate}
。 当到达endDate时,应用会停止,并为该任务创建其他作业。 可以使用ISO 8601标准的常量,如"2015-02-25T16:42:11+00:00"。也可以使用变量,如${EndDate}
<timerEventDefinition>
<timeCycle flowable:endDate="2015-02-25T16:42:11+00:00">R3/PT10H</timeCycle>
</timerEventDefinition>
<timerEventDefinition>
<timeCycle>R3/PT10H/${EndDate}</timeCycle>
</timerEventDefinition>
如果同时使用了两种格式,则系统会使用以属性方式定义的endDate。
目前只有BoundaryTimerEvents与CatchTimerEvent可以使用EndDate。
另外,也可以使用cron表达式指定定时周期。下面的例子展示了一个整点启动,每5分钟触发的触发器:
0 0/5 * * * ?
请参考这个教程了解如何使用cron表达式。
请注意: 与普通的Unix cron不同,第一个符号代表的是秒,而不是分钟。
重复时间周期更适合使用相对时间,也就是从某个特定时间点开始计算(比如用户任务开始的时间)。而cron表达式可以使用绝对时间,因此更适合用于定时启动事件。
可以在定时事件定义中使用表达式,也就是使用流程变量控制定时。这个流程变量必须是包含合适时间格式的字符串,ISO 8601(或者对于循环类型,cron)。
<boundaryEvent cancelActivity="true" attachedToRef="firstLineSupport">
<timerEventDefinition>
<timeDuration>${duration}</timeDuration>
</timerEventDefinition>
</boundaryEvent>
请注意:定时器只有在异步执行器启用时才能触发(需要在flowable.cfg.xml
中,将asyncExecutorActivate设置为true
。因为默认情况下异步执行器都是禁用的)。
8.2.3. 错误事件定义
(error event definition)
重要提示: BPMN错误与Java异常不是一回事。事实上,这两者毫无共同点。BPMN错误事件是建模业务异常(business exceptions)的方式。而Java异常会按它们自己的方式处理。
<endEvent>
<errorEventDefinition errorRef="myError" />
</endEvent>
8.2.4. 信号事件定义
信号事件(signal event),是引用具名信号的事件。信号是全局范围(广播)的事件,并会被传递给所有激活的处理器(等待中的流程实例/捕获信号事件 catching signal events)。
使用signalEventDefinition
元素声明信号事件定义。其signalRef
属性引用一个signal
元素,该signal
元素需要声明为definitions
根元素的子元素。下面摘录一个流程,使用中间事件(intermediate event)抛出与捕获信号事件。
<definitions... >
<!-- 声明信号 -->
<signal name="alert" />
<process>
<intermediateThrowEvent name="Alert">
<!-- 信号事件定义 -->
<signalEventDefinition signalRef="alertSignal" />
</intermediateThrowEvent>
...
<intermediateCatchEvent name="On Alert">
<!-- 信号事件定义 -->
<signalEventDefinition signalRef="alertSignal" />
</intermediateCatchEvent>
...
</process>
</definitions>
两个signalEventDefinition
引用同一个signal
元素。
抛出信号事件
信号可以由流程实例使用BPMN结构抛出(throw),也可以通过编程方式使用Java API抛出。下面org.flowable.engine.RuntimeService
中的方法可以用编程方式抛出信号:
RuntimeService.signalEventReceived(String signalName);
RuntimeService.signalEventReceived(String signalName, String executionId);
signalEventReceived(String signalName)
与signalEventReceived(String signalName, String executionId)
的区别,是前者在全局范围为所有已订阅处理器抛出信号(广播),而后者只为指定的执行传递信号。
捕获信号事件
可以使用信号捕获中间事件(intermediate catch signal event)或者信号边界事件(signal boundary event)捕获信号事件。
查询信号事件订阅
可以查询订阅了某一信号事件的所有执行:
List<Execution> executions = runtimeService.createExecutionQuery()
.signalEventSubscriptionName("alert")
.list();
可以使用signalEventReceived(String signalName, String executionId)
方法为这些执行传递这个信号。
信号事件的范围
默认情况下,信号事件在流程引擎全局广播。这意味着你可以在一个流程实例中抛出一个信号事件,而不同流程定义的不同流程实例都会响应这个事件。
但有时也会希望只在同一个流程实例中响应信号事件。例如,在流程实例中使用异步机制,而两个或多个活动彼此互斥的时候。
要限制信号事件的范围(scope),在信号事件定义中添加(非BPMN 2.0标准!)scope属性:
<signal name="alert" flowable:scope="processInstance"/>
这个属性的默认值为"global(全局)"。
信号事件示例
下面是一个不同流程通过信号通信的例子。第一个流程在保险政策更新或变更时启动。在变更由人工审核之后,会抛出信号事件,指出政策已经发生了变更:
这个事件可以被所有感兴趣的流程实例捕获。下面是一个订阅这个事件的流程的例子。
请注意:要理解信号事件会广播给所有激活的处理器,这很重要。这意味着在上面的例子中,所有订阅这个信号的流程实例都会接收这个信号。在这个例子中这就是我们期望的。然而,有的情况下,不希望使用广播方式。考虑下面的流程:
Flowable不支持上面流程中描述的模式。我们的想法是,在执行"do something"任务时抛出的错误,由错误边界事件捕获,并通过信号抛出事件传播至执行的并行分支,最终中断"do something in parallel"任务。到目前为止Flowable会按照预期效果执行。然而,由于信号的广播效应,它也会被传播至所有其他订阅了这个信号事件的流程实例。这可能并非我们希望的效果。
请注意:信号事件与特定的流程实例无关,而是会广播给所有流程实例。如果你需要只为某一特定的流程实例传递信号,则需要使用signalEventReceived(String signalName, String executionId)
手动建立关联,并使用适当的的查询机制。
Flowable提供了解决的方法。可以在信号事件上添加scope属性,并将其设置为processInstance。
8.2.5. 消息事件定义
消息事件(message event),是指引用具名消息的事件。消息具有名字与载荷。与信号不同,消息事件只有一个接收者。
消息事件定义使用messageEventDefinition
元素声明。其messageRef
属性引用一个message
元素,该message
元素需要声明为definitions
根元素的子元素。下面摘录一个流程,声明了两个消息事件,并由开始事件与消息捕获中间事件(intermediate catching message event)引用。
<definitions
xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:flowable="http://flowable.org/bpmn"
targetNamespace="Examples"
xmlns:tns="Examples">
<message name="newInvoiceMessage" />
<message name="paymentMessage" />
<process>
<startEvent >
<messageEventDefinition messageRef="newInvoice" />
</startEvent>
...
<intermediateCatchEvent >
<messageEventDefinition messageRef="payment" />
</intermediateCatchEvent>
...
</process>
</definitions>
抛出消息事件
作为嵌入式的流程引擎,Flowable并不关心实际如何接收消息。因为这可能与环境相关,或需要进行平台定义的操作。例如连接至JMS(Java Messaging Service,Java消息服务)队列(Queue)/主题(Topic),或者处理Webservice或者REST请求。因此接收消息需要作为应用的一部分,或者是流程引擎所嵌入的基础框架中的一部分,由你自行实现。
在应用中接收到消息后,需要决定如何处理它。如果这个消息需要启动新的流程实例,可以选择一种由runtime服务提供的方法:
ProcessInstance startProcessInstanceByMessage(String messageName);
ProcessInstance startProcessInstanceByMessage(String messageName, Map<String, Object> processVariables);
ProcessInstance startProcessInstanceByMessage(String messageName, String businessKey,
Map<String, Object> processVariables);
这些方法使用消息启动流程实例。
如果需要由已有的流程实例接收消息,需要首先将消息与特定的流程实例关联(查看后续章节),然后触发等待中的执行,让流程继续进行。runtime服务提供了下列方法,可以触发订阅了消息事件的执行:
void messageEventReceived(String messageName, String executionId);
void messageEventReceived(String messageName, String executionId, HashMap<String, Object> processVariables);
查询消息事件订阅
对于消息启动事件(message start event),消息事件的订阅与的流程定义相关。可以使用
ProcessDefinitionQuery
查询这种类型的消息订阅:
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.messageEventSubscription("newCallCenterBooking")
.singleResult();
因为一个消息只能被一个流程定义订阅,因此这个查询总是返回0或1个结果。如果流程定义更新了,只有该流程定义的最新版本会订阅这个消息事件。
对于消息捕获中间事件(intermediate catch message event),消息事件的订阅与执行相关。可以使用
ExecutionQuery
查询这种类型的消息订阅:
Execution execution = runtimeService.createExecutionQuery()
.messageEventSubscriptionName("paymentReceived")
.variableValueEquals("orderId", message.getOrderId())
.singleResult();
这种查询通常都会有关联查询,并且通常需要了解流程的情况(在这个例子里,对于给定的orderId,至多只有一个流程实例)。
消息事件示例
下面是一个流程的例子,可以使用两种不同的消息启动:
在流程需要通过不同的方式启动,但是后续使用统一的方式处理时,就可以使用这种方法。
8.2.6. 启动事件
启动事件(start event)是流程的起点。启动事件的类型(流程在消息到达时启动,在指定的时间间隔后启动,等等),定义了流程如何启动,并显示为启动事件中的小图标。在XML中,类型由子元素声明来定义。
启动事件随时捕获:启动事件(保持)等候,直到特定的触发器被触发。
在启动事件中,可以使用下列Flowable自定义参数:
initiator: 指明保存认证用户(authenticated user)ID用的变量名。在流程启动时,操作用户的ID会保存在这个变量中。例如:
<startEvent flowable:initiator="initiator" />
认证用户必须在try-finally块中调用IdentityService.setAuthenticatedUserId(String)
方法进行设置。像这样:
try {
identityService.setAuthenticatedUserId("bono");
runtimeService.startProcessInstanceByKey("someProcessKey");
} finally {
identityService.setAuthenticatedUserId(null);
}
这段代码已经集成在Flowable应用中,可以在表单中使用。
8.2.7. 空启动事件
描述
“空”启动事件(none Start Event),指的是未指定启动流程实例触发器的启动事件。引擎将无法预知何时启动流程实例。空启动事件用于流程实例通过调用下列startProcessInstanceByXXX API方法启动的情况。
ProcessInstance processInstance = runtimeService.startProcessInstanceByXXX();
请注意:子流程(sub-process)必须有空启动事件。
图示
空启动事件用空心圆圈表示,中间没有图标(也就是说,没有触发器)。
XML表示
空启动事件的XML表示格式,就是普通的启动事件声明,而没有任何子元素(其他种类的启动事件都有用于声明其类型的子元素)。
<startEvent name="my start event" />
空启动事件的自定义扩展
formKey: 引用表单定义,用户需要在启动新流程实例时填写该表单。可以在表单章节找到更多信息。例如:
<startEvent flowable:formKey="request" />
8.2.8. 定时器启动事件
描述
定时器启动事件(timer start event)在指定时间创建流程实例。在流程只需要启动一次,或者流程需要在特定的时间间隔重复启动时,都可以使用。
请注意:子流程不能有定时器启动事件。
请注意:定时器启动事件,在流程部署的同时就开始计时。不需要调用startProcessInstanceByXXX就会在时间启动。调用startProcessInstanceByXXX时会在定时启动之外额外启动一个流程。
请注意:当部署带有定时器启动事件的流程的更新版本时,上一版本的定时器作业会被移除。这是因为通常并不希望旧版本的流程仍然自动启动新的流程实例。
图示
定时器启动事件,用其中有一个钟表图标的圆圈来表示。
XML表示
定时器启动事件的XML表示格式,是普通的启动事件声明加上定时器定义子元素。请参考定时器定义了解详细配置方法。
示例:流程会启动4次,间隔5分钟,从2011年3月11日,12:13开始
<startEvent>
<timerEventDefinition>
<timeCycle>R4/2011-03-11T12:13/PT5M</timeCycle>
</timerEventDefinition>
</startEvent>
示例:流程会在设定的时间启动一次
<startEvent>
<timerEventDefinition>
<timeDate>2011-03-11T12:13:14</timeDate>
</timerEventDefinition>
</startEvent>
8.2.9. 消息启动事件
描述
消息启动事件(message start event)使用具名消息启动流程实例。消息名用于选择正确的启动事件。
当部署具有一个或多个消息启动事件的流程定义时,会做如下判断:
给定流程定义中,消息启动事件的名字必须是唯一的。一个流程定义不得包含多个同名的消息启动事件。如果流程定义中有两个或多个消息启动事件引用同一个消息,或者两个或多个消息启动事件引用了具有相同消息名字的消息,则Flowable会在部署这个流程定义时抛出异常。
在所有已部署的流程定义中,消息启动事件的名字必须是唯一的。如果在流程定义中,一个或多个消息启动事件引用了已经部署的另一流程定义中消息启动事件的消息名,则Flowable会在部署这个流程定义时抛出异常。
流程版本:在部署流程定义的新版本时,会取消上一版本的消息订阅,即使新版本中并没有这个消息事件)。
在启动流程实例时,可以使用下列RuntimeService
中的方法,触发消息启动事件:
ProcessInstance startProcessInstanceByMessage(String messageName);
ProcessInstance startProcessInstanceByMessage(String messageName, Map<String, Object> processVariables);
ProcessInstance startProcessInstanceByMessage(String messageName, String businessKey,
Map<String, Object< processVariables);
messageName
是由message
元素的name
属性给定的名字。messageEventDefinition
的messageRef
属性会引用message
元素。当启动流程实例时,会做如下判断:
只有顶层流程(top-level process)才支持消息启动事件。嵌入式子流程不支持消息启动事件。
如果一个流程定义中有多个消息启动事件,可以使用
runtimeService.startProcessInstanceByMessage(…)
选择合适的启动事件。如果一个流程定义中有多个消息启动事件与一个空启动事件,则
runtimeService.startProcessInstanceByKey(…)
与runtimeService.startProcessInstanceById(…)
会使用空启动事件启动流程实例。如果一个流程定义中有多个消息启动事件而没有空启动事件,则
runtimeService.startProcessInstanceByKey(…)
与runtimeService.startProcessInstanceById(…)
会抛出异常。如果一个流程定义中只有一个消息启动事件,则
runtimeService.startProcessInstanceByKey(…)
与runtimeService.startProcessInstanceById(…)
会使用这个消息启动事件启动新流程实例。如果流程由调用活动(call activity)启动,则只有在下列情况下才支持消息启动事件
除了消息启动事件之外,流程还有唯一的空启动事件
或者流程只有唯一的消息启动事件,而没有其他启动事件。
图示
消息启动事件用其中有一个消息事件标志的圆圈表示。这个标志并未填充,用以表示捕获(接收)行为。
XML表示
消息启动事件的XML表示格式,为普通启动事件声明加上messageEventDefinition子元素:
<definitions
xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:flowable="http://flowable.org/bpmn"
targetNamespace="Examples"
xmlns:tns="Examples">
<message name="newInvoiceMessage" />
<process>
<startEvent >
<messageEventDefinition messageRef="tns:newInvoice" />
</startEvent>
...
</process>
</definitions>
8.2.10. 信号启动事件
描述
信号启动事件(signal start event),使用具名信号启动流程实例。这个信号可以由流程实例中的信号抛出中间事件(intermediary signal throw event),或者API(runtimeService.signalEventReceivedXXX方法)触发。两种方式都会启动所有拥有相同名字信号启动事件的流程定义。
请注意可以选择异步还是同步启动流程实例。
需要为API传递的signalName
,是由signal
元素的name
属性决定的名字。signal
元素由signalEventDefinition
的signalRef
属性引用。
图示
信号启动事件用其中有一个信号事件标志的圆圈表示。这个标志并未填充,用以表示捕获(接收)行为。
XML表示
信号启动事件的XML表示格式,为普通启动事件声明,加上signalEventDefinition子元素:
<signal name="The Signal" />
<process>
<startEvent>
<signalEventDefinition signalRef="theSignal" />
</startEvent>
<sequenceFlow sourceRef="theStart" targetRef="theTask" />
<userTask name="Task in process A" />
<sequenceFlow sourceRef="theTask" targetRef="theEnd" />
<endEvent />
</process>
8.2.11. 错误启动事件
描述
错误启动事件(error start event),可用于触发事件子流程(Event Sub-Process)。错误启动事件不能用于启动流程实例。
错误启动事件总是中断。
图示
错误启动事件用其中有一个错误事件标志的圆圈表示。这个标志并未填充,用以表示捕获(接收)行为。
XML表示
错误启动事件的XML表示格式,为普通启动事件声明加上errorEventDefinition子元素:
<startEvent >
<errorEventDefinition errorRef="someError" />
</startEvent>
8.2.12. 结束事件
结束事件(end event)标志着流程或子流程中一个分支的结束。结束事件总是抛出(型)事件。这意味着当流程执行到达结束事件时,会抛出一个结果。结果的类型由事件内部的黑色图标表示。在XML表示中,类型由子元素声明给出。
8.2.13. 空结束事件
描述
“空”结束事件(none end event),意味着当到达这个事件时,没有特别指定抛出的结果。因此,引擎除了结束当前执行分支之外,不会多做任何事情。
图示
空结束事件,用其中没有图标(没有结果类型)的粗圆圈表示。
XML表示
空事件的XML表示格式为普通结束事件声明,没有任何子元素(其它种类的结束事件都有子元素,用于声明其类型)。
<endEvent name="my end event" />
8.2.14. 错误结束事件
描述
当流程执行到达错误结束事件(error end event)时,结束执行的当前分支,并抛出错误。这个错误可以由匹配的错误边界中间事件捕获。如果找不到匹配的错误边界事件,将会抛出异常。
图示
错误结束事件事件用内部有一个错误图标的标准结束事件(粗圆圈)表示。错误图标是全黑的,代表抛出的含义。
XML表示
错误结束事件表示为结束事件,加上errorEventDefinition子元素:
<endEvent>
<errorEventDefinition errorRef="myError" />
</endEvent>
errorRef属性可以引用在流程外定义的error元素:
<error errorCode="123" />
...
<process>
...
error的errorCode用于查找匹配的错误捕获边界事件。如果errorRef不匹配任何已定义的error,则该errorRef会用做errorCode的快捷方式。这个快捷方式是Flowable特有的。下面的代码片段在功能上是相同的。
<error errorCode="error123" />
...
<process>
...
<endEvent>
<errorEventDefinition errorRef="myError" />
</endEvent>
...
与下面的代码功能相同
<endEvent>
<errorEventDefinition errorRef="error123" />
</endEvent>
请注意errorRef必须遵从BPMN 2.0概要(schema),且必须是合法的QName。
8.2.15. 终止结束事件
描述
当到达终止结束事件(terminate end event)时,当前的流程实例或子流程会被终止。也就是说,当执行到达终止结束事件时,会判断第一个范围 scope(流程或子流程)并终止它。请注意在BPMN 2.0中,子流程可以是嵌入式子流程,调用活动,事件子流程,或事务子流程。有一条通用规则:当存在多实例的调用过程或嵌入式子流程时,只会终止一个实例,其他的实例与流程实例不会受影响。
可以添加一个可选属性terminateAll。当其为true时,无论该终止结束事件在流程定义中的位置,也无论它是否在子流程(甚至是嵌套子流程)中,都会终止(根)流程实例。
图示
终止结束事件用内部有一个全黑圆的标准结束事件(粗圆圈)表示。
XML表示
终止结束事件,表示为结束事件,加上terminateEventDefinition子元素。
请注意terminateAll属性是可选的(默认为false)。
<endEvent id="myEndEvent >
<terminateEventDefinition flowable:terminateAll="true"></terminateEventDefinition>
</endEvent>
8.2.16. 取消结束事件
描述
取消结束事件(cancel end event)只能与BPMN事务子流程(BPMN transaction subprocess)一起使用。当到达取消结束事件时,会抛出取消事件,且必须由取消边界事件(cancel boundary event)捕获。取消边界事件将取消事务,并触发补偿(compensation)。
图示
取消结束事件用内部有一个取消图标的标准结束事件(粗圆圈)表示。取消图标是全黑的,代表抛出的含义。
XML表示
取消结束事件,表示为结束事件,加上cancelEventDefinition子元素。
<endEvent>
<cancelEventDefinition />
</endEvent>
8.2.17. 边界事件
边界事件(boundary event)是捕获型事件,依附在活动(activity)上。边界事件永远不会抛出。这意味着当活动运行时,事件将监听特定类型的触发器。当捕获到事件时,会终止活动,并沿该事件的出口顺序流继续。
所有的边界事件都用相同的方式定义:
<boundaryEvent attachedToRef="theActivity">
<XXXEventDefinition/>
</boundaryEvent>
边界事件由下列元素定义:
(流程范围内)唯一的标识符
由attachedToRef属性定义的,对该事件所依附的活动的引用。请注意边界事件及其所依附的活动,应定义在相同级别(也就是说,边界事件并不包含在活动内)。
定义了边界事件的类型的,形如XXXEventDefinition的XML子元素(例如TimerEventDefinition,ErrorEventDefinition,等等)。查阅特定的边界事件类型,以了解更多细节。
8.2.18. 定时器边界事件
描述
定时器边界事件(timer boundary event)的行为像是跑表与闹钟。当执行到达边界事件所依附的活动时,将启动定时器。当定时器触发时(例如在特定时间间隔后),可以中断活动,并沿着边界事件的出口顺序流继续执行。
图示
定时器边界事件用内部有一个定时器图标的标准边界事件(圆圈)表示。
XML表示
定时器边界事件与一般边界事件一样定义。其中类型子元素为timerEventDefinition元素。
<boundaryEvent cancelActivity="true" attachedToRef="firstLineSupport">
<timerEventDefinition>
<timeDuration>PT4H</timeDuration>
</timerEventDefinition>
</boundaryEvent>
请参考定时器事件定义了解定时器配置的细节。
在图示中圆圈画为虚线,如下:
中断与非中断定时器事件是不同的。非中断意味着最初的活动不会被中断,而会保持原样。默认为中断行为。在XML表示中,cancelActivity属性设置为false。
一个典型使用场景,是在一段时间之后发送提醒邮件,但不影响正常的流程流向。
<boundaryEvent cancelActivity="false" attachedToRef="firstLineSupport"/>
请注意:定时器边界事件只有在异步执行器(async executor)启用时才能触发(也就是说,需要在flowable.cfg.xml
中,将asyncExecutorActivate设置为true
。因为异步执行器默认情况下是禁用的。)
边界事件的已知问题
所有类型的边界事件,都有一个关于并发的已知问题:不能在边界事件上附加多个出口顺序流。这个问题的解决方案,是使用一条出口顺序流,指向并行网关。
8.2.19. 错误边界事件
描述
在活动边界上的错误捕获中间(事件),或简称错误边界事件(error boundary event),捕获其所依附的活动范围内抛出的错误。
在嵌入式子流程或者调用活动上定义错误边界事件最有意义,因为子流程的范围会包括其中的所有活动。错误可以由错误结束事件抛出。这样的错误会逐层向其上级父范围传播,直到在范围内找到一个匹配错误事件定义的错误边界事件。
当捕获错误事件时,会销毁边界事件定义所在的活动,同时销毁其中所有的当前执行(例如,并行活动,嵌套子流程,等等)。流程执行将沿着边界事件的出口顺序流继续。
图示
错误边界事件用内部有一个错误图标的标准中间事件(两层圆圈)表示。错误图标是白色的,代表捕获的含义。
XML表示
错误边界事件与标准边界事件一样定义:
<boundaryEvent attachedToRef="mySubProcess">
<errorEventDefinition errorRef="myError"/>
</boundaryEvent>
在边界事件中,errorRef引用一个流程元素外定义的错误:
<error errorCode="123" />
...
<process>
...
errorCode用于匹配捕获的错误:
如果省略了errorRef,错误边界事件会捕获所有错误事件,无论error的errorCode是什么。
如果提供了errorRef,并且其引用了存在的error,则边界事件只会捕获相同错误代码的错误。
如果提供了errorRef,但BPMN 2.0文件中没有定义error,则errorRef会用作errorCode(与错误结束事件类似)。
示例
下面的示例流程展示了如何使用错误结束事件。当'Review profitability (审核盈利能力)'用户任务完成,并指出提供的信息不足时,会抛出错误。当这个错误被子流程边界捕获时,'Review sales lead (审核销售线索)'子流程中的所有运行中活动都会被销毁(即使'Review customer rating 审核客户等级'还没有完成),并会创建'Provide additional details (提供更多信息)'用户任务。
这个流程作为演示配置的示例提供。可以在org.flowable.examples.bpmn.event.error包中找到流程XML与单元测试。
8.2.20. 信号边界事件
描述
依附在活动边界上的信号捕获中间(事件),或简称信号边界事件(signal boundary event),捕获与其信号定义具有相同名称的信号。
请注意:与其他事件例如错误边界事件不同的是,信号边界事件不只是捕获其所依附范围抛出的信号。信号边界事件为全局范围(广播)的,意味着信号可以从任何地方抛出,甚至可以是不同的流程实例。
请注意:与其他事件(如错误事件)不同,信号在被捕获后不会被消耗。如果有两个激活的信号边界事件,捕获相同的信号事件,则两个边界事件都会被触发,哪怕它们不在同一个流程实例里。
图示
信号边界事件,用内部有一个信号图标的标准中间事件(两层圆圈)表示。信号图标是白色的,代表捕获的含义。
XML表示
信号边界事件与标准边界事件一样定义:
<boundaryEvent attachedToRef="task" cancelActivity="true">
<signalEventDefinition signalRef="alertSignal"/>
</boundaryEvent>
示例
参见信号事件定义章节。
8.2.21. 消息边界事件
描述
在活动边界上的消息捕获中间(事件),或简称消息边界事件(message boundary event),捕获与其消息定义具有相同消息名的消息。
图示
消息边界事件,用内部有一个消息图标的标准中间事件(两层圆圈)表示。信号图标是白色的,代表捕获的含义。
请注意消息边界事件既可以是中断型的(右图),也可以是非中断型的(左图)。
XML表示
消息边界事件与标准边界事件一样定义:
<boundaryEvent attachedToRef="task" cancelActivity="true">
<messageEventDefinition messageRef="newCustomerMessage"/>
</boundaryEvent>
示例
参见消息事件定义章节。
8.2.22. 取消边界事件
描述
依附在事务子流程边界上的取消捕获中间事件,或简称取消边界事件(cancel boundary event),在事务取消时触发。当取消边界事件触发时,首先会中断当前范围的所有活动执行。接下来,启动事务范围内所有有效的的补偿边界事件(compensation boundary event)。补偿会同步执行,也就是说在离开事务前,边界事件会等待补偿完成。当补偿完成时,沿取消边界事件的任何出口顺序流离开事务子流程。
请注意:一个事务子流程只允许使用一个取消边界事件。
请注意:如果事务子流程中有嵌套的子流程,只会对成功完成的子流程触发补偿。
请注意:如果取消边界事件放置在具有多实例特性的事务子流程上,如果一个实例触发了取消,则边界事件将取消所有实例。
图示
取消边界事件,用内部有一个取消图标的标准中间事件(两层圆圈)表示。取消图标是白色的(未填充),代表捕获的含义。
XML表示
取消边界事件与标准边界事件一样定义:
<boundaryEvent attachedToRef="transaction" >
<cancelEventDefinition />
</boundaryEvent>
因为取消边界事件总是中断型的,因此没有cancelActivity
属性。
8.2.23. 补偿边界事件
描述
依附在活动边界上的补偿捕获中间(事件),或简称补偿边界事件(compensation boundary event),可以为活动附加补偿处理器。
补偿边界事件必须使用直接关联的方式引用单个的补偿处理器。
补偿边界事件与其它边界事件的活动策略不同。其它边界事件,例如信号边界事件,在其依附的活动启动时激活;当该活动结束时会被解除,并取消相应的事件订阅。而补偿边界事件不是这样。补偿边界事件在其依附的活动成功完成时激活,同时创建补偿事件的相应订阅。当补偿事件被触发,或者相应的流程实例结束时,才会移除订阅。请考虑下列因素:
当补偿被触发时,会调用补偿边界事件关联的补偿处理器。调用次数与其依附的活动成功完成的次数相同。
如果补偿边界事件依附在具有多实例特性的活动上,则会为每一个实例创建补偿事件订阅。
如果补偿边界事件依附在位于循环内部的活动上,则每次该活动执行时,都会创建一个补偿事件订阅。
如果流程实例结束,则取消补偿事件的订阅。
请注意:嵌入式子流程不支持补偿边界事件。
图示
补偿边界事件,用内部有一个补偿图标的标准中间事件(两层圆圈)表示。补偿图标是白色的(未填充),代表捕获的含义。另外,补偿边界事件使用单向连接关联补偿处理器,如下图所示:
XML表示
补偿边界事件与标准边界事件一样定义:
<boundaryEvent attachedToRef="bookHotel" >
<compensateEventDefinition />
</boundaryEvent>
<association associationDirection="One"
sourceRef="compensateBookHotelEvt" targetRef="undoBookHotel" />
<serviceTask isForCompensation="true" flowable:class="..." />
补偿边界事件在活动完成后才激活,因此不支持cancelActivity
属性。
8.2.24. 捕获中间事件
所有的捕获中间事件(intermediate catching events)都使用相同方式定义:
<intermediateCatchEvent >
<XXXEventDefinition/>
</intermediateCatchEvent>
捕获中间事件由下列元素定义:
(流程范围内)唯一的标识符
定义了捕获中间事件类型的,形如XXXEventDefinition的XML子元素(例如TimerEventDefinition等)。查阅特定中间捕获事件类型,以了解更多细节。
8.2.25. 定时器捕获中间事件
描述
定时器捕获中间事件(timer intermediate catching event)的行为像是跑表。当执行到达捕获事件时,启动定时器;当定时器触发时(例如在一段时间间隔后),沿定时器中间事件的出口顺序流继续执行。
图示
定时器中间事件用内部有定时器图标的中间捕获事件表示。
XML表示
定时器中间事件与捕获中间事件一样定义。子元素为timerEventDefinition。
<intermediateCatchEvent>
<timerEventDefinition>
<timeDuration>PT5M</timeDuration>
</timerEventDefinition>
</intermediateCatchEvent>
查看定时器事件定义了解详细配置。
8.2.26. 信号捕获中间事件
描述
信号捕获中间事件(signal intermediate catching event),捕获与其引用的信号定义具有相同信号名称的信号。
请注意:与其他事件如错误事件不同,信号在被捕获后不会被消耗。如果有两个激活的信号中间事件,捕获相同的信号事件,则两个中间事件都会被触发,哪怕它们不在同一个流程实例里。
图示
信号捕获中间事件用内部有信号图标的标准中间事件(两层圆圈)表示。信号图标是白色的(未填充),代表捕获的含义。
XML表示
信号中间事件与捕获中间事件一样定义。子元素为signalEventDefinition。
<intermediateCatchEvent>
<signalEventDefinition signalRef="newCustomerSignal" />
</intermediateCatchEvent>
示例
参阅信号事件定义章节。
8.2.27. 消息捕获中间事件
描述
消息捕获中间事件(message intermediate catching event),捕获特定名字的消息。
图示
消息捕获中间事件用内部有消息图标的标准中间事件(两层圆圈)表示。消息图标是白色的(未填充),代表捕获的含义。
XML表示
消息中间事件与捕获中间事件一样定义。子元素为messageEventDefinition。
<intermediateCatchEvent>
<messageEventDefinition signalRef="newCustomerMessage" />
</intermediateCatchEvent>
示例
参阅消息事件定义章节。
8.2.28. 抛出中间事件
所有的抛出中间事件(intermediate throwing evnet)都使用相同方式定义:
<intermediateThrowEvent >
<XXXEventDefinition/>
</intermediateThrowEvent>
抛出中间事件由下列元素定义:
(流程范围内)唯一的标识符
定义了抛出中间事件类型的,形如XXXEventDefinition的XML子元素(例如signalEventDefinition等)。查阅特定中间抛出事件类型,以了解更多细节。
8.2.29. 空抛出中间事件
下面的流程图展示了空抛出中间事件(intermediate throwing none event)的简单例子。其用于指示流程已经到达了某种状态。
添加一个执行监听器后,空中间事件就可以成为很好的监视某些KPI(Key Performance Indicators 关键绩效指标)的钩子。
<intermediateThrowEvent>
<extensionElements>
<flowable:executionListener class="org.flowable.engine.test.bpmn.event.IntermediateNoneEventTest$MyExecutionListener" event="start" />
</extensionElements>
</intermediateThrowEvent>
你也可以添加一些自己的代码,将部分事件发送给你的BAM(Business Activity Monitoring 业务活动监控)工具,或者DWH(Data Warehouse 数据仓库)。在这种情况下,引擎本身不会做任何事情,只是从中穿过。
8.2.30. 信号抛出中间事件
描述
信号抛出中间事件(signal intermediate throwing event),抛出所定义信号的信号事件。
在Flowable中,信号会广播至所有的激活的处理器(也就是说,所有的信号捕获事件)。可以同步或异步地发布信号。
在默认配置中,信号同步地传递。这意味着抛出信号的流程实例会等待,直到信号传递至所有的捕获信号的流程实例。所有的捕获流程实例也会在与抛出流程实例相同的事务中,也就是说如果收到通知的流程实例中,有一个实例产生了技术错误(抛出异常),则所有相关的实例都会失败。
信号也可以异步地传递。这是由到达抛出信号事件时的发送处理器来决定的。对于每个激活的处理器,JobExecutor会为其存储并传递一个异步通知消息(asynchronous notification message),即作业(Job)。
图示
消息抛出中间事件用内部有信号图标的标准中间事件(两层圆圈)表示。信号图标是黑色的(已填充),代表抛出的含义。
XML表示
信号中间事件与抛出中间事件一样定义。子元素为signalEventDefinition。
<intermediateThrowEvent>
<signalEventDefinition signalRef="newCustomerSignal" />
</intermediateThrowEvent>
异步信号事件这样定义:
<intermediateThrowEvent>
<signalEventDefinition signalRef="newCustomerSignal" flowable:async="true" />
</intermediateThrowEvent>
示例
参阅信号事件定义章节。
8.2.31. 补偿抛出中间事件
描述
补偿抛出中间事件(compensation intermediate throwing event)用于触发补偿。
触发补偿:既可以为设计的活动触发补偿,也可以为补偿事件所在的范围触发补偿。补偿由活动所关联的补偿处理器执行。
活动抛出补偿时,活动关联的补偿处理器将执行的次数,为活动成功完成的次数。
抛出补偿时,当前范围中所有的活动,包括并行分支上的活动都会被补偿。
补偿分层触发:如果将要被补偿的活动是一个子流程,则该子流程中所有的活动都会触发补偿。如果该子流程有嵌套的活动,则会递归地抛出补偿。然而,补偿不会传播至流程的上层:如果子流程中触发了补偿,该补偿不会传播至子流程范围外的活动。BPMN规范指出,对“与子流程在相同级别”的活动触发补偿。
在Flowable中,补偿按照执行的相反顺序运行。这意味着最后完成的活动会第一个补偿。
可以使用补偿抛出中间事件补偿已经成功完成的事务子流程。
请注意:如果抛出补偿的范围中有一个子流程,而该子流程包含有关联了补偿处理器的活动,则当抛出补偿时,只有该子流程成功完成时,补偿才会传播至该子流程。如果子流程内嵌套的部分活动已经完成,并附加了补偿处理器,但包含这些活动的子流程还没有完成,则这些补偿处理器仍不会执行。参考下面的例子:
在这个流程中,有两个并行的执行:一个执行嵌入子流程,另一个执行“charge credit card(信用卡付款)”活动。假定两个执行都已开始,且第一个执行正等待用户完成“review bookings(检查预定)”任务。第二个执行进行了“charge credit card(信用卡付款)”活动的操作,抛出了错误,导致“cancel reservations(取消预订)”事件触发补偿。这时并行子流程还未完成,意味着补偿不会传播至该子流程,因此不会执行“cancel hotel reservation(取消酒店预订)”补偿处理器。而如果“cancel reservations(取消预订)”运行前,这个用户任务(因此该嵌入式子流程也)已经完成,则补偿会传播至该嵌入式子流程。
流程变量:当补偿嵌入式子流程时,用于执行补偿处理器的执行,可以访问子流程的局部流程变量在子流程完成时的值。为此,会对范围执行(为执行子流程所创建的执行)所关联的流程变量进行快照。意味着:
补偿执行器无法访问子流程范围内并行执行所添加的变量。
上层执行所关联的流程变量(例如流程实例关联的流程变量)不在该快照中。因为补偿处理器可以直接访问这些流程变量在抛出补偿时的值。
只会为嵌入式子流程进行变量快照。其他活动不会进行变量快照。
目前的限制:
目前不支持
waitForCompletion="false"
。当补偿抛出中间事件触发补偿时,只有在补偿成功完成时,才会离开该事件。补偿由并行执行运行。并行执行会按照补偿活动完成的逆序启动。
补偿不会传播至调用活动(call activity)生成的子流程。
图示
补偿抛出中间事件用内部有补偿图标的标准中间事件(两层圆圈)表示。补偿图标是黑色的(已填充),代表抛出的含义。
XML表示
补偿中间事件与抛出中间事件一样定义。子元素为compensateEventDefinition。
<intermediateThrowEvent>
<compensateEventDefinition />
</intermediateThrowEvent>
另外,activityRef
可选项用于为指定的范围或活动触发补偿:
<intermediateThrowEvent>
<compensateEventDefinition activityRef="bookHotel" />
</intermediateThrowEvent>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论