flowable BPMN 2.0结构
阅读数:547 评论数:0
跳转到新版页面分类
python/Java
正文
一、自定义扩展
标准通常是不同公司(不同观点)大量讨论与妥协的结果。作为阅读BPMN 2.0 XML流程定义的开发者,有时会觉得某些结构或方法十分笨重。Flowable将开发者的感受放在最高优先,因此引入了一些'Flowable BPMN扩展(extensions)'。这些“扩展”并不在BPMN 2.0规格中,有些是新结构,有些是对特定结构的简化。
使用自定义扩展时,总是通过flowable:命名空间前缀,明确标识出XML元素、属性等。请注意Flowable引擎也支持activiti:命名空间前缀。
二、事件
事件(event)通常用于为流程生命周期中发生的事情建模。事件总是图形化为圆圈。在BPMN 2.0中,有两种主要的事件分类:捕获(catching)与抛出(throwing)事件。
事件分为五大类:开始事件、结束事件、中间事件、边界事件、边界补偿事件。
如何区分中间事件和边界事件
中间事件会以节点的形式出现在流程图中,但不会产生实际的节点。
边界事件与节点共存,中间事件可以理解为一个任务的发起前置监听,而边界事件可以理解为对整个节点的监听。
重点说一下补偿事件,主要用于对已完成的流程做回退处理。
当流程执行到达这个事件时,会等待直到触发器动作。触发器的类型由其中的图标,或者说XML中的类型声明而定义。捕获事件与抛出事件显示上的区别,是其内部的图标没有填充(即是白色的)。
当流程执行到达这个事件时,会触发一个触发器。触发器的类型,由其中的图标,或者说XML中的类型声明而定义。抛出事件与捕获事件显示上的区别,是其内部的图标填充为黑色。
事件定义(event definition),用于定义事件的语义。没有事件定义的话,事件就“不做什么特别的事情”。例如,一个没有事件定义的开始事件,并不限定具体是什么启动了流程。如果为这个开始事件添加事件定义(例如定时器事件定义),就声明了启动流程的“类型”(例如对于定时器事件定义,就是到达了特定的时间点)。
定时器事件(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标准定义的循环时间周期,也可以使用timeCycle的可选属性endDate,或者直接写在时间表达式的结尾。
<timerEventDefinition>
<timeCycle flowable:endDate="2015-02-25T16:42:11+00:00">R3/PT10H</timeCycle>
</timerEventDefinition>
<timerEventDefinition>
<timeCycle>R3/PT10H/${EndDate}</timeCycle>
</timerEventDefinition>
另外,也可以使用cron表达式指定定时周期。
0 0/5 * * * ?
注意定时器只有在异步执行器启用时才能触发。
BPMN错误事件是建模业务异常(business exceptions)的方式。
<endEvent id="myErrorEndEvent">
<errorEventDefinition errorRef="myError" />
</endEvent>
信号事件(signal event),是引用具名信号的事件。信号是全局范围(广播)的事件,并会被传递给所有激活的处理器(等待中的流程实例/捕获信号事件 catching signal events)。
使用signalEventDefinition元素声明信号事件定义。其signalRef属性引用一个signal元素,该signal元素需要声明为definitions根元素的子元素。下面摘录一个流程,使用中间事件(intermediate event)抛出与捕获信号事件。
<definitions... >
<!-- 声明信号 -->
<signal id="alertSignal" name="alert" />
<process id="catchSignal">
<intermediateThrowEvent id="throwSignalEvent" name="Alert">
<!-- 信号事件定义 -->
<signalEventDefinition signalRef="alertSignal" />
</intermediateThrowEvent>
...
<intermediateCatchEvent id="catchSignalEvent" name="On Alert">
<!-- 信号事件定义 -->
<signalEventDefinition signalRef="alertSignal" />
</intermediateCatchEvent>
...
</process>
</definitions>
抛出信号事件
信号可以由流程实例使用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();
信号事件的范围
默认情况下,信号事件在流程引擎全局广播。这意味着你可以在一个流程实例中抛出一个信号事件,而不同流程定义的不同流程实例都会响应这个事件。
但有时也会希望只在同一个流程实例中响应信号事件。例如,在流程实例中使用异步机制,而两个或多个活动彼此互斥的时候。
要限制信号事件的范围(scope),在信号事件定义中添加(非BPMN 2.0标准!)scope属性:
<signal id="alertSignal" name="alert" flowable:scope="processInstance"/>
这个属性的默认值为"global(全局)"。
信号事件示例
下面是一个不同流程通过信号通信的例子。第一个流程在保险政策更新或变更时启动。在变更由人工审核之后,会抛出信号事件,指出政策已经发生了变更:
这个事件可以被所有感兴趣的流程实例捕获。下面是一个订阅这个事件的流程的例子。
消息事件(message event),是指引用具名消息的事件。消息具有名字与载荷。与信号不同,消息事件只有一个接收者。
消息事件定义使用messageEventDefinition元素声明。其messageRef属性引用一个message元素,该message元素需要声明为definitions根元素的子元素。下面摘录一个流程,声明了两个消息事件,并由开始事件与消息捕获中间事件(intermediate catching message event)引用。
<definitions id="definitions"
xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:flowable="http://flowable.org/bpmn"
targetNamespace="Examples"
xmlns:tns="Examples">
<message id="newInvoice" name="newInvoiceMessage" />
<message id="payment" name="paymentMessage" />
<process id="invoiceProcess">
<startEvent id="messageStart" >
<messageEventDefinition messageRef="newInvoice" />
</startEvent>
...
<intermediateCatchEvent id="paymentEvt" >
<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();
启动事件(start event)是流程的起点。
启动事件随时捕获:启动事件(保持)等候,直到特定的触发器被触发。
在启动事件中,可以使用下列Flowable自定义参数:
initiator
指明保存认证用户(authenticated user)ID用的变量名。在流程启动时,操作用户的ID会保存在这个变量中。例如:
<startEvent id="request" flowable:initiator="initiator" />
空启动事件
“空”启动事件(none Start Event),指的是未指定启动流程实例触发器的启动事件。引擎将无法预知何时启动流程实例。空启动事件用于流程实例通过调用下列startProcessInstanceByXXX API方法启动的情况。
ProcessInstance processInstance = runtimeService.startProcessInstanceByXXX();
注意,子流程必须有空启动事件。
空启动事件用空心圆圈表示,中间没有图标(也就是说,没有触发器)。
空启动事件的XML表示格式,就是普通的启动事件声明,而没有任何子元素(其他种类的启动事件都有用于声明其类型的子元素)
<startEvent id="start" name="my start event" />
定时器启动事件
定时器启动事件(timer start event)在指定时间创建流程实例。在流程只需要启动一次,或者流程需要在特定的时间间隔重复启动时,都可以使用。
请注意:子流程不能有定时器启动事件。
请注意:定时器启动事件,在流程部署的同时就开始计时。不需要调用startProcessInstanceByXXX就会在时间启动。调用startProcessInstanceByXXX时会在定时启动之外额外启动一个流程。
请注意:当部署带有定时器启动事件的流程的更新版本时,上一版本的定时器作业会被移除。这是因为通常并不希望旧版本的流程仍然自动启动新的流程实例。
定时器启动事件,用其中有一个钟表图标的圆圈来表示。
<startEvent id="theStart">
<timerEventDefinition>
<timeCycle>R4/2011-03-11T12:13/PT5M</timeCycle>
</timerEventDefinition>
</startEvent>
消息启动事件
给定流程定义中,消息启动事件的名字必须是唯一的。一个流程定义不得包含多个同名的消息启动事件。
在启动流程实例时,可以使用下列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属性给定的名字。
-
只有顶层流程(top-level process)才支持消息启动事件。嵌入式子流程不支持消息启动事件。
-
如果一个流程定义中有多个消息启动事件,可以使用runtimeService.startProcessInstanceByMessage(...)选择合适的启动事件。
-
如果一个流程定义中有多个消息启动事件与一个空启动事件,则runtimeService.startProcessInstanceByKey(...)与runtimeService.startProcessInstanceById(...)会使用空启动事件启动流程实例。
-
如果一个流程定义中有多个消息启动事件而没有空启动事件,则runtimeService.startProcessInstanceByKey(...)与runtimeService.startProcessInstanceById(...)会抛出异常。
-
如果一个流程定义中只有一个消息启动事件,则runtimeService.startProcessInstanceByKey(...)与runtimeService.startProcessInstanceById(...)会使用这个消息启动事件启动新流程实例。
-
如果流程由调用活动(call activity)启动,则只有在下列情况下才支持消息启动事件
-
除了消息启动事件之外,流程还有唯一的空启动事件
-
或者流程只有唯一的消息启动事件,而没有其他启动事件。
-
消息启动事件用其中有一个消息事件标志的圆圈表示。这个标志并未填充,用以表示捕获(接收)行为。
消息启动事件的XML表示格式,为普通启动事件声明加上messageEventDefinition子元素:
<definitions id="definitions"
xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:flowable="http://flowable.org/bpmn"
targetNamespace="Examples"
xmlns:tns="Examples">
<message id="newInvoice" name="newInvoiceMessage" />
<process id="invoiceProcess">
<startEvent id="messageStart" >
<messageEventDefinition messageRef="tns:newInvoice" />
</startEvent>
...
</process>
</definitions>
信号启动事件
这个信号可以由流程实例中的信号抛出中间事件(intermediary signal throw event),或者API(runtimeService.signalEventReceivedXXX方法)触发。两种方式都会启动所有拥有相同名字信号启动事件的流程定义。
请注意可以选择异步还是同步启动流程实例。
需要为API传递的signalName,是由signal元素的name属性决定的名字。signal元素由signalEventDefinition的signalRef属性引用。
信号启动事件用其中有一个信号事件标志的圆圈表示。这个标志并未填充,用以表示捕获(接收)行为。
信号启动事件的XML表示格式,为普通启动事件声明,加上signalEventDefinition子元素:
<signal id="theSignal" name="The Signal" />
<process id="processWithSignalStart1">
<startEvent id="theStart">
<signalEventDefinition id="theSignalEventDefinition" signalRef="theSignal" />
</startEvent>
<sequenceFlow id="flow1" sourceRef="theStart" targetRef="theTask" />
<userTask id="theTask" name="Task in process A" />
<sequenceFlow id="flow2" sourceRef="theTask" targetRef="theEnd" />
<endEvent id="theEnd" />
</process>
错误启动事件
错误启动事件(error start event),可用于触发事件子流程(Event Sub-Process)。错误启动事件不能用于启动流程实例。
结束事件(end event)标志着流程或子流程中一个分支的结束。结束事件总是抛出(型)事件。这意味着当流程执行到达结束事件时,会抛出一个结果。结果的类型由事件内部的黑色图标表示。在XML表示中,类型由子元素声明给出。
空结束事件
“空”结束事件(none end event),意味着当到达这个事件时,没有特别指定抛出的结果。因此,引擎除了结束当前执行分支之外,不会多做任何事情。
空结束事件,用其中没有图标(没有结果类型)的粗圆圈表示。
<endEvent id="end" name="my end event" />
错误结束事件
当流程执行到达错误结束事件(error end event)时,结束执行的当前分支,并抛出错误。这个错误可以由匹配的错误边界中间事件捕获。如果找不到匹配的错误边界事件,将会抛出异常。
错误结束事件事件用内部有一个错误图标的标准结束事件(粗圆圈)表示。错误图标是全黑的,代表抛出的含义。
<endEvent id="myErrorEndEvent">
<errorEventDefinition errorRef="myError" />
</endEvent>
<error id="myError" errorCode="123" />
...
<process id="myProcess">
...
终止结束事件
当到达终止结束事件(terminate end event)时,当前的流程实例或子流程会被终止。也就是说,当执行到达终止结束事件时,会判断第一个范围 scope(流程或子流程)并终止它。请注意在BPMN 2.0中,子流程可以是嵌入式子流程,调用活动,事件子流程,或事务子流程。有一条通用规则:当存在多实例的调用过程或嵌入式子流程时,只会终止一个实例,其他的实例与流程实例不会受影响。
可以添加一个可选属性terminateAll。当其为true时,无论该终止结束事件在流程定义中的位置,也无论它是否在子流程(甚至是嵌套子流程)中,都会终止(根)流程实例。
终止结束事件用内部有一个全黑圆的标准结束事件(粗圆圈)表示。
终止结束事件,表示为结束事件,加上terminateEventDefinition子元素。
请注意terminateAll属性是可选的(默认为false)。
<endEvent id="myEndEvent >
<terminateEventDefinition flowable:terminateAll="true"></terminateEventDefinition>
</endEvent>
取消结束事件
取消结束事件(cancel end event)只能与BPMN事务子流程(BPMN transaction subprocess)一起使用。当到达取消结束事件时,会抛出取消事件,且必须由取消边界事件(cancel boundary event)捕获。取消边界事件将取消事务,并触发补偿(compensation)。
<endEvent id="myCancelEndEvent">
<cancelEventDefinition />
</endEvent>
边界事件(boundary event)是捕获型事件,依附在活动(activity)上。边界事件永远不会抛出。这意味着当活动运行时,事件将监听特定类型的触发器。当捕获到事件时,会终止活动,并沿该事件的出口顺序流继续。
所有的边界事件都用相同的方式定义:
<boundaryEvent id="myBoundaryEvent" attachedToRef="theActivity">
<XXXEventDefinition/>
</boundaryEvent>
定时器边界事件
定时器边界事件(timer boundary event)的行为像是跑表与闹钟。当执行到达边界事件所依附的活动时,将启动定时器。当定时器触发时(例如在特定时间间隔后),可以中断活动,并沿着边界事件的出口顺序流继续执行。
定时器边界事件用内部有一个定时器图标的标准边界事件(圆圈)表示。
<boundaryEvent id="escalationTimer" cancelActivity="true" attachedToRef="firstLineSupport">
<timerEventDefinition>
<timeDuration>PT4H</timeDuration>
</timerEventDefinition>
</boundaryEvent>
中断与非中断定时器事件是不同的。非中断意味着最初的活动不会被中断,而会保持原样。默认为中断行为。在XML表示中,cancelActivity属性设置为false。
定时器边界事件只有在异步执行器(async executor)启用时才能触发。
所有类型的边界事件,都有一个关于并发的已知问题:不能在边界事件上附加多个出口顺序流。这个问题的解决方案,是使用一条出口顺序流,指向并行网关。
错误边界事件
在活动边界上的错误捕获中间(事件),或简称错误边界事件(error boundary event),捕获其所依附的活动范围内抛出的错误。
在嵌入式子流程或者调用活动上定义错误边界事件最有意义,因为子流程的范围会包括其中的所有活动。错误可以由错误结束事件抛出。这样的错误会逐层向其上级父范围传播,直到在范围内找到一个匹配错误事件定义的错误边界事件。
当捕获错误事件时,会销毁边界事件定义所在的活动,同时销毁其中所有的当前执行(例如,并行活动,嵌套子流程,等等)。流程执行将沿着边界事件的出口顺序流继续。
<boundaryEvent id="catchError" attachedToRef="mySubProcess">
<errorEventDefinition errorRef="myError"/>
</boundaryEvent>
信号边界事件
与其他事件例如错误边界事件不同的是,信号边界事件不只是捕获其所依附范围抛出的信号。信号边界事件为全局范围(广播)的,意味着信号可以从任何地方抛出,甚至可以是不同的流程实例。
与其他事件(如错误事件)不同,信号在被捕获后不会被消耗。如果有两个激活的信号边界事件,捕获相同的信号事件,则两个边界事件都会被触发,哪怕它们不在同一个流程实例里。
<boundaryEvent id="boundary" attachedToRef="task" cancelActivity="true">
<signalEventDefinition signalRef="alertSignal"/>
</boundaryEvent>
消息边界事件
消息边界事件既可以是中断型的,也可以是非中断型的。
<boundaryEvent id="boundary" attachedToRef="task" cancelActivity="true">
<messageEventDefinition messageRef="newCustomerMessage"/>
</boundaryEvent>
取消边界事件
当取消边界事件触发时,首先会中断当前范围的所有活动执行。接下来,启动事务范围内所有有效的的补偿边界事件(compensation boundary event)。补偿会同步执行,也就是说在离开事务前,边界事件会等待补偿完成。当补偿完成时,沿取消边界事件的任何出口顺序流离开事务子流程。
一个事务子流程只允许使用一个取消边界事件。
如果事务子流程中有嵌套的子流程,只会对成功完成的子流程触发补偿。
<boundaryEvent id="boundary" attachedToRef="transaction" >
<cancelEventDefinition />
</boundaryEvent>
补偿边界事件
补偿边界事件必须使用直接关联的方式引用单个的补偿处理器。
补偿边界事件与其它边界事件的活动策略不同。其它边界事件,例如信号边界事件,在其依附的活动启动时激活;当该活动结束时会被解除,并取消相应的事件订阅。而补偿边界事件不是这样。补偿边界事件在其依附的活动成功完成时激活,同时创建补偿事件的相应订阅。当补偿事件被触发,或者相应的流程实例结束时,才会移除订阅。
<boundaryEvent id="compensateBookHotelEvt" attachedToRef="bookHotel" >
<compensateEventDefinition />
</boundaryEvent>
<association associationDirection="One" id="a1"
sourceRef="compensateBookHotelEvt" targetRef="undoBookHotel" />
<serviceTask id="undoBookHotel" isForCompensation="true" flowable:class="..." />
补偿边界事件在活动完成后才激活,因此不支持cancelActivity属性。
所有的捕获中间事件(intermediate catching events)都使用相同方式定义:
<intermediateCatchEvent id="myIntermediateCatchEvent" >
<XXXEventDefinition/>
</intermediateCatchEvent>
定时器捕获中间事件
当执行到达捕获事件时,启动定时器,当定时器触发时,沿定时器中间事件的出口顺序继续执行。
<intermediateCatchEvent id="timer">
<timerEventDefinition>
<timeDuration>PT5M</timeDuration>
</timerEventDefinition>
</intermediateCatchEvent>
信号捕获中间事件
<intermediateCatchEvent id="signal">
<signalEventDefinition signalRef="newCustomerSignal" />
</intermediateCatchEvent>
消息捕获中间事件
<intermediateCatchEvent id="message">
<messageEventDefinition signalRef="newCustomerMessage" />
</intermediateCatchEvent>
<intermediateThrowEvent id="myIntermediateThrowEvent" >
<XXXEventDefinition/>
</intermediateThrowEvent>
空抛出中间事件
<intermediateThrowEvent id="noneEvent">
<extensionElements>
<flowable:executionListener class="org.flowable.engine.test.bpmn.event.IntermediateNoneEventTest$MyExecutionListener" event="start" />
</extensionElements>
</intermediateThrowEvent>
信号抛出中间事件
<intermediateThrowEvent id="signal">
<signalEventDefinition signalRef="newCustomerSignal" />
</intermediateThrowEvent>
异步信号事件
<intermediateThrowEvent id="signal">
<signalEventDefinition signalRef="newCustomerSignal" flowable:async="true" />
</intermediateThrowEvent>
补偿抛出中间事件
<intermediateThrowEvent id="throwCompensation">
<compensateEventDefinition />
</intermediateThrowEvent>
另外,activityRef可选项用于为指定的范围或活动触发补偿:
<intermediateThrowEvent id="throwCompensation">
<compensateEventDefinition activityRef="bookHotel" />
</intermediateThrowEvent>
三、顺序流
顺序流(sequence flow)是流程中两个元素间的连接器。在流程执行过程中,一个元素被访问后,会沿着其所有出口顺序流继续执行。这意味着BPMN 2.0默认是并行执行的:两个出口顺序流就会创建两个独立的、并行的执行路径。
<sequenceFlow id="flow1" sourceRef="theStart" targetRef="theTask" />
<sequenceFlow id="flow" sourceRef="theStart" targetRef="theTask">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${order.price > 100 && order.price < 250}]]>
</conditionExpression>
</sequenceFlow>
可以省略xsi:type定义。
目前conditionExpressions只能使用UEL。
只有当没有其他顺序流可以选择时,才会选择默认顺序流作为活动的出口顺序流,流程会忽略默认顺序流上的条件。
活动的默认顺序流由该活动的default属性定义。
<exclusiveGateway id="exclusiveGw" name="Exclusive Gateway" default="flow2" />
<sequenceFlow id="flow1" sourceRef="exclusiveGw" targetRef="task1">
<conditionExpression xsi:type="tFormalExpression">${conditionA}</conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow2" sourceRef="exclusiveGw" targetRef="task2"/>
<sequenceFlow id="flow3" sourceRef="exclusiveGw" targetRef="task3">
<conditionExpression xsi:type="tFormalExpression">${conditionB}</conditionExpression>
</sequenceFlow>
四、网关
网关用于控制执行的流向。
当执行到这个网关时,会按照所有出口顺序流定义的顺序对它们进行计算,选择第一个条件计算为true的顺序流。
排他网关用内部带有“X”的标准网关(菱形)表示。
<exclusiveGateway id="exclusiveGw" name="Exclusive Gateway" />
<sequenceFlow id="flow2" sourceRef="exclusiveGw" targetRef="theTask1">
<conditionExpression xsi:type="tFormalExpression">${input == 1}</conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow3" sourceRef="exclusiveGw" targetRef="theTask2">
<conditionExpression xsi:type="tFormalExpression">${input == 2}</conditionExpression>
</sequenceFlow>
<sequenceFlow id="flow4" sourceRef="exclusiveGw" targetRef="theTask3">
<conditionExpression xsi:type="tFormalExpression">${input == 3}</conditionExpression>
</sequenceFlow>
这可以外将执行分支为多条路径 ,也可以合并多条入口路径的执行。
(1)所有的出口顺序流都并行执行,为每一条顺序流创建一个并行执行。
(2)所有到达并行网关的并行执行都会在网关处等待,直到每一条入口顺序流都到达有个执行。然后流程经过该合并网关继续。
并行网关,用内部带加事情图标的网关(菱形)表示。
<parallelGateway id="myParallelGateway" />
可以把包容网关看作排他网关与并行网关的组合,与排他网关一样,可以在包容网关的出口顺序流上定义条件,包容网关会计算条件。然而主要的区别是,包容网关与并行网关一样,可以同时选择多于一条出口顺序深沟高垒 。
包容网关,用内部带有圆圈的图标的网关(菱形)表示。
<inclusiveGateway id="myInclusiveGateway" />
当流程执行到达基于事件的网关时,与等待状态类似,网关会暂停执行,燕且为每个出口顺序流创建一个事件订阅。
基于事件的网磁,用内部有五边形加两圆的网关(菱形)表示。
XML元素为eventBasedGateway。
五、任务
用于对需要人工执行的任务进行建模,当流程执行到达用户任务时,会为指派至该任务的用户或组的任务列表创建一个新任务。
用户任务用左上角有一个小用户图标的标准任务(圆角矩形)表示。
<userTask id="theTask" name="Important task" />
任务BPMN 2.0元素都可以有描述,描述由documentation元素定义。
<userTask id="theTask" name="Schedule meeting" >
<documentation>
Schedule an engineering meeting for next week with the new hire.
</documentation>
可以使用标准java方式获取描述文本
task.getDescription()
(1)到期日期
可以在任务定义中使用表达式设定到期日期,该表达式必须解析为java.util.Date、java.util.String(ISO8601格式)、ISO8601时间长度(例如PT50M)或者null。
<userTask id="theTask" name="Important task" flowable:dueDate="${dateVariable}"/>
任务的到期日期也可以使用TaskService或者TaskListener中使用传递的DelegateTask修改。
(2)用户指派
可以定义humanPerformer子元素来实现,humanPerformer需要resourceAssignmentExpression来实际定义用户。目前,只支持formalExpressions。
<process >
...
<userTask id='theTask' name='important task' >
<humanPerformer>
<resourceAssignmentExpression>
<formalExpression>kermit</formalExpression>
</resourceAssignmentExpression>
</humanPerformer>
</userTask>
只能指定一个用户作为任务的humanPerformer,在Flowable术语中,这个用户被称为办理人(assignee)。可以通过TaskService获取特定用户办理的任务。
List<Task> tasks = taskService.createTaskQuery().taskAssignee("kermit").list();
任务也可以放在用户的候选任务列表中,需使用potentialOwner(潜在用户),用法与humanPerformer结构类似。
<process >
...
<userTask id='theTask' name='important task' >
<potentialOwner>
<resourceAssignmentExpression>
<formalExpression>user(kermit), group(management)</formalExpression>
</resourceAssignmentExpression>
</potentialOwner>
</userTask>
可用如下方法获取定义了potentialOwner结构的任务。
List<Task> tasks = taskService.createTaskQuery().taskCandidateUser("kermit");
如果未指定给定字符串是用户还是组,引擎默认其为组。
当指派关系不复杂时,为避免这种复杂性,可以在用户任务上使用自定义扩展。
<userTask id="theTask" name="my task" flowable:assignee="kermit" />
<userTask id="theTask" name="my task" flowable:candidateUsers="kermit, gonzo" />
<userTask id="theTask" name="my task" flowable:candidateGroups="management, accountancy" />
Flowable支持的身份关联类型有
public class IdentityLinkType {
/* Flowable内置角色 */
public static final String ASSIGNEE = "assignee";
public static final String CANDIDATE = "candidate";
public static final String OWNER = "owner";
public static final String STARTER = "starter";
public static final String PARTICIPANT = "participant";
}
当流程执行到达脚本任务时,会执行相应的脚本 。
脚本任务用左上角有一个小“脚本”的图标表示。
<scriptTask id="theScriptTask" name="Execute script" scriptFormat="groovy">
<script>
sum = 0
for ( i in inputArray ) {
sum += i
}
</script>
</scriptTask>
scriptFormat属性的值,必须是兼容JSR-223的名字,默认情况下,JavaScript包含在每一个JDK中,因此不需要添加任何jar。而使用groovy,需要添加如下依赖
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.x.x<version>
</dependency>
(1)脚本中的变量
到达脚本引擎的执行中,所有的流程变量都可以在脚本中使用。也可以简单地调用 execution.serVariable("variableName",variableValue)。
(2)脚本任务的结果
脚本任务的返回值 ,可以通过脚本任务定义的flowable:resultVariable属性设置为流程变量。
<scriptTask id="theScriptTask" name="Execute script" scriptFormat="juel" flowable:resultVariable="myVar">
<script>#{echo}</script>
</scriptTask>
用于调用Java类。
左上角有一个小齿轮图标的圆角矩形表示。
有四种方法声明如何调用 Java逻辑:
(1)指定实现了JavaDelegate或ActivityBehavior的类
(2)调用解析为委托对象(delegation object)的表达式
(3)调用方法表达式(method expression)
(4)对值表达式(value expression)求值
<serviceTask id="javaService"
name="My Java Service Task"
flowable:class="org.flowable.MyJavaDelegate" />
<serviceTask id="serviceTask" flowable:delegateExpression="${delegateExpressionBean}" />
<serviceTask id="javaService"
name="My Java Service Task"
flowable:expression="#{printer.printMessage()}" />
用于同步地调用外部的Web服务。
与Java服务任务图标一样。
使用Web服务之前,需要导入其操作及复杂的类型,可以使用导入标签指向Web服务的WSDL。
<import importType="http://schemas.xmlsoap.org/wsdl/"
location="http://localhost:63081/counter?wsdl"
namespace="http://webservice.flowable.org/" />
用于同步地执行一条或多条规则,Flowable使用名为Drools Expert的Drools规则引擎执行业务规则。
图标显示为带有表格图标的圆角矩形。
可以向一个或多个收信人发送邮件。
用于发出HTT请求,注意Http任务不是BPMN 2.0规范任务。
可以向Mule发送消息,不是BPMN 2.0规范任务。