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)事件。

事件分为五大类:开始事件、结束事件、中间事件、边界事件、边界补偿事件。

如何区分中间事件和边界事件

中间事件会以节点的形式出现在流程图中,但不会产生实际的节点。

边界事件与节点共存,中间事件可以理解为一个任务的发起前置监听,而边界事件可以理解为对整个节点的监听。

重点说一下补偿事件,主要用于对已完成的流程做回退处理。

1、捕获

当流程执行到达这个事件时,会等待直到触发器动作。触发器的类型由其中的图标,或者说XML中的类型声明而定义。捕获事件与抛出事件显示上的区别,是其内部的图标没有填充(即是白色的)。

2、抛出

当流程执行到达这个事件时,会触发一个触发器。触发器的类型,由其中的图标,或者说XML中的类型声明而定义。抛出事件与捕获事件显示上的区别,是其内部的图标填充为黑色。

3、事件定义

事件定义(event definition),用于定义事件的语义。没有事件定义的话,事件就“不做什么特别的事情”。例如,一个没有事件定义的开始事件,并不限定具体是什么启动了流程。如果为这个开始事件添加事件定义(例如定时器事件定义),就声明了启动流程的“类型”(例如对于定时器事件定义,就是到达了特定的时间点)。

(1)定时器事件定义

定时器事件(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 * * * ?

注意定时器只有在异步执行器启用时才能触发。

(2)错误事件定义

BPMN错误事件是建模业务异常(business exceptions)的方式。

<endEvent id="myErrorEndEvent">
  <errorEventDefinition errorRef="myError" />
</endEvent>

(3)信号事件定义

信号事件(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(全局)"。

信号事件示例

下面是一个不同流程通过信号通信的例子。第一个流程在保险政策更新或变更时启动。在变更由人工审核之后,会抛出信号事件,指出政策已经发生了变更:

这个事件可以被所有感兴趣的流程实例捕获。下面是一个订阅这个事件的流程的例子。

(4)消息事件定义

消息事件(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();

(5)启动事件

启动事件(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时会在定时启动之外额外启动一个流程。

请注意:当部署带有定时器启动事件的流程的更新版本时,上一版本的定时器作业会被移除。这是因为通常并不希望旧版本的流程仍然自动启动新的流程实例。

定时器启动事件,用其中有一个钟表图标的圆圈来表示。

定时器启动事件的XML表示格式,是普通的启动事件声明加上定时器定义子元素。
示例:流程会启动4次,间隔5分钟,从2011年3月11日,12:13开始
<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)。错误启动事件不能用于启动流程实例。

错误启动事件总是中断。

错误启动事件用其中有一个错误事件标志的圆圈表示。这个标志并未填充,用以表示捕获(接收)行为。

错误启动事件的XML表示格式,为普通启动事件声明加上errorEventDefinition子元素:

<startEvent id="messageStart" >
	<errorEventDefinition errorRef="someError" />
</startEvent>

(6)结束事件

结束事件(end event)标志着流程或子流程中一个分支的结束。结束事件总是抛出(型)事件。这意味着当流程执行到达结束事件时,会抛出一个结果。结果的类型由事件内部的黑色图标表示。在XML表示中,类型由子元素声明给出。

空结束事件

“空”结束事件(none end event),意味着当到达这个事件时,没有特别指定抛出的结果。因此,引擎除了结束当前执行分支之外,不会多做任何事情。

空结束事件,用其中没有图标(没有结果类型)的粗圆圈表示。

空事件的XML表示格式为普通结束事件声明,没有任何子元素(其它种类的结束事件都有子元素,用于声明其类型)。
<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>

(7)边界事件

边界事件(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属性。

(8)捕获中间事件

所有的捕获中间事件(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>

(9)抛出中间事件

<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" />

1、条件顺序流

<sequenceFlow id="flow" sourceRef="theStart" targetRef="theTask">
  <conditionExpression xsi:type="tFormalExpression">
    <![CDATA[${order.price > 100 && order.price < 250}]]>
  </conditionExpression>
</sequenceFlow>

可以省略xsi:type定义。

目前conditionExpressions只能使用UEL。

2、默认顺序流

只有当没有其他顺序流可以选择时,才会选择默认顺序流作为活动的出口顺序流,流程会忽略默认顺序流上的条件。

活动的默认顺序流由该活动的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>

四、网关

网关用于控制执行的流向。

1、排他网关

当执行到这个网关时,会按照所有出口顺序流定义的顺序对它们进行计算,选择第一个条件计算为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>

2、并行网关

这可以外将执行分支为多条路径 ,也可以合并多条入口路径的执行。

(1)所有的出口顺序流都并行执行,为每一条顺序流创建一个并行执行。

(2)所有到达并行网关的并行执行都会在网关处等待,直到每一条入口顺序流都到达有个执行。然后流程经过该合并网关继续。

并行网关,用内部带加事情图标的网关(菱形)表示。

<parallelGateway id="myParallelGateway" />

3、包容网关

可以把包容网关看作排他网关与并行网关的组合,与排他网关一样,可以在包容网关的出口顺序流上定义条件,包容网关会计算条件。然而主要的区别是,包容网关与并行网关一样,可以同时选择多于一条出口顺序深沟高垒 。

包容网关,用内部带有圆圈的图标的网关(菱形)表示。

<inclusiveGateway id="myInclusiveGateway" />

4、基于事件的网关

当流程执行到达基于事件的网关时,与等待状态类似,网关会暂停执行,燕且为每个出口顺序流创建一个事件订阅。

基于事件的网磁,用内部有五边形加两圆的网关(菱形)表示。

XML元素为eventBasedGateway。

五、任务

1、用户任务

用于对需要人工执行的任务进行建模,当流程执行到达用户任务时,会为指派至该任务的用户或组的任务列表创建一个新任务。

用户任务用左上角有一个小用户图标的标准任务(圆角矩形)表示。

<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";
}

2、脚本任务

当流程执行到达脚本任务时,会执行相应的脚本 。

脚本任务用左上角有一个小“脚本”的图标表示。

<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>

3、Java服务任务

用于调用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()}" />

4、Web服务任务

用于同步地调用外部的Web服务。

与Java服务任务图标一样。

使用Web服务之前,需要导入其操作及复杂的类型,可以使用导入标签指向Web服务的WSDL。

<import importType="http://schemas.xmlsoap.org/wsdl/"
	location="http://localhost:63081/counter?wsdl"
	namespace="http://webservice.flowable.org/" />

5、业务规则任务

用于同步地执行一条或多条规则,Flowable使用名为Drools Expert的Drools规则引擎执行业务规则。

图标显示为带有表格图标的圆角矩形。

6、邮件任务

可以向一个或多个收信人发送邮件。

7、Http任务

用于发出HTT请求,注意Http任务不是BPMN 2.0规范任务。

8、Mule任务

可以向Mule发送消息,不是BPMN 2.0规范任务。

9、Camel任务,可以向Camel发送消息,不是BPMN 2.0规范任务。




相关推荐

Flowable是一个采用Java开发的业务流程引擎,通过Flowable流程引擎,我们可以部署BPMN2.0的流程定义(一般为XML文件),通过流程定义创建流程实例,查询和访问流程相关的实例与数据,

1、连接的URL可能需要变更,如下面的方式 jdbc:mysql://localhost/flowable?char

一、数据库表类别 flowable是基于Activiti开发的流程引擎,表名中ACT_就是基于Activiti的,而FLW_开头的,是特定于Flowable Work或Engage的数据库表。

一、配置 <dependency> <groupId>org.flowable</groupId> <artifactId>flowable-spring-boot-starter</

一、使用文件流部署工作流 //resourceName是文件的全名称(包括文件后缀) String resourceName = multipartFile.getOriginalFilename()

一、简介 BPMN是一个广泛接受与支持的,展现流程的注记方法。 BPMN 2.0概要(schema)的根元素(root element)是definitions元

一、简介 Flowable-ui.war是一个web应用,有以下几个功能: 1、Flowable IDM 身份管理应用,为所有Flowable UI

一、下载 从官网可以下载到flowable,其中有两个war包:flowable-ui.war和flowable-rest.war。 flowable-ui.wa

一、简介 如果需要人工执行的任务,就需要用到用户任务建模。 二、常用配置 1、执行人Assignments (1)执行人类型: Assignee 代理执行人,一般一个用户。 Candidat

通常我们会需要给任务运行表设置内容,因为默认启动任务或完成任务的操作时有些值为空,但我们需要给我们的字段设置值,我想给form_keys设置值,可以通过以下几种方式: 1、常用情况