1、BPMN簡介
業務流程模型和標記法(BPMN, Business Process Model and Notation)是一套圖形化表示法,用於以業務流程模型詳細說明各種業務流程。
它最初由業務流程管理倡議組織(BPMI, Business Process Management Initiative)開發,名稱爲"Business Process Modeling Notation",即“業務流程建模標記法”。BPMI於2005年與對象管理組織(OMG, Object Management Group)合併。2011年1月OMG發佈2.0版本,同時改爲現在的名稱。
2、BPMN基本要素
BPMN包含四種要素:
流對象(Flow Object):
- 事件(Events)
- 活動(Activities)
- 網關(Gateways)
連接對象(Connecting Objects):
- 順序流(Sequence Flow)
- 消息流(Message Flow)
- 關聯(Association)
泳道(Swimlanes):
- 池(Pool)
- 道(Lane)
附加工件(Artifacts/Artefacts):
- 數據對象(Data Object)
- 組(Group)
- 註釋(Annotation)
在這裏需要重要關注4個基本對象,
- 事件(Event):用來表明流程的生命週期中發生了什麼。
- 活動(Activity):活動(Activities)是業務流程定義的核心元素,中文稱爲“活動”、“節點”、“步驟”。一個活動可以是流程的基本處理單元(如人工任務、服務任務),也可以是一個組合單元(如外部子流程、嵌套子流程)。
- 網關(Gateway):用來控制流程的流向。
- 流向/順序流(Flow):是連接兩個流程節點的連線。
3、流程根元素
一個BPMN 2.0 XML流程的根是definitions元素。 在命名狀態,子元素會包含真正的業務流程定義。 每個process子元素 可以擁有一個id(必填)和 name(可選)。下面是一個空的BPMN 2.0業務流程 。
<definitions id="myProcesses"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://schema.omg.org/spec/BPMN/2.0 BPMN20.xsd"
xmlns="http://schema.omg.org/spec/BPMN/2.0"
typeLanguage="http://www.w3.org/2001/XMLSchema"
expressionLanguage="http://www.w3.org/1999/XPath"
targetNamespace="http://jbpm.org/example/bpmn2">
<process id="My business processs" name="myBusinessProcess">
4、BPMN結構
在BPMN中,流對象是用於定義業務流程行爲的主要圖形元素。需要重點理解三個流對象。
4.1、事件
事件包含啓動事件、結束事件、中間事件,還有一類邊界事件,屬於中間中間事件的一種。
4.1.1、啓動事件(startEvent)
啓動事件(start event)(有的譯爲開始時間)是流程的起點。啓動事件的類型(例如流程在消息到達時啓動,在指定的時間間隔後啓動,等等),定義了流程如何啓動,並顯示爲啓動事件中的小圖標。在XML中,類型由子元素聲明來定義。
啓動事件隨時捕獲:啓動事件(保持)等候,直到特定的觸發器被觸發。
4.1.1.1、空啓動事件
- 描述:空”啓動事件(none Start Event),指的是未指定啓動流程實例觸發器的啓動事件。引擎將無法預知何時啓動流程實例。空啓動事件用於流程實例通過調用下列startProcessInstanceByXXX API方法啓動的情況。
ProcessInstance processInstance = runtimeService.startProcessInstanceByXXX();
- 圖示:空啓動事件用空心圓圈表示,中間沒有圖標(也就是說,沒有觸發器):
- xml表示:
<startEvent id="start" name="my start event" />
4.1.1.2、定時器啓動事件
- 描述:定時器啓動事件(timer start event)在指定時間創建流程實例。在流程只需要啓動一次,或者流程需要在特定的時間間隔重複啓動時,可以使用定時器啓動事件。
請注意:子流程不能有定時器啓動事件。
請注意:定時器啓動事件,在流程部署的同時就開始計時。不需要調用startProcessInstanceByXXX就會在時間啓動。調用startProcessInstanceByXXX時會在定時啓動之外額外啓動一個流程。
請注意:當部署帶有定時器啓動事件的流程的更新版本時,上一版本的定時器作業會被移除。這是因爲通常並不希望舊版本的流程仍然自動啓動新的流程實例。
-
圖示:定時器啓動事件,用其中有一個鐘錶圖標的圓圈來表示。
-
XML表示:定時器啓動事件的XML表示格式,是普通的啓動事件聲明加上定時器定義子元素。
示例:流程會啓動4次,間隔5分鐘,從2011年3月11日,12:13開始
<startEvent id="theStart">
<timerEventDefinition>
<timeCycle>R4/2011-03-11T12:13/PT5M</timeCycle>
</timerEventDefinition>
</startEvent>
示例:流程會在設定的時間啓動一次
<startEvent id="theStart">
<timerEventDefinition>
<timeDate>2011-03-11T12:13:14</timeDate>
</timerEventDefinition>
</startEvent>
4.1.1.3、消息啓動事件
- 描述:
消息啓動事件(message start event)使用具名消息啓動流程實例。消息名用於選擇正確的啓動事件。
當部署具有一個或多個消息啓動事件的流程定義時,會做如下判斷:
>> 給定流程定義中,消息啓動事件的名字必須是唯一的。一個流程定義不得包含多個同名的消息啓動事件。如果流程定義中有兩個或多個消息啓動事件引用同一個消息,或者兩個或多個消息啓動事件引用了具有相同消息名字的消息,則Flowable會在部署這個流程定義時拋出異常。
>> 在所有已部署的流程定義中,消息啓動事件的名字必須是唯一的。如果在流程定義中,一個或多個消息啓動事件引用了已經部署的另一流程定義中消息啓動事件的消息名,則Flowable會在部署這個流程定義時拋出異常。
>> 流程版本:在部署流程定義的新版本時,會取消上一版本的消息訂閱,即使新版本中並沒有這個消息事件)。
-
圖示:消息啓動事件用其中有一個消息事件標誌的圓圈表示。這個標誌並未填充,用以表示捕獲(接收)行爲。
-
XML表示:
消息啓動事件的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>
4.1.1.4、信號啓動事件
- 描述:
信號啓動事件(signal start event),使用具名信號啓動流程實例。這個信號可以由流程實例中的信號拋出中間事件(intermediary signal throw event),或者API(runtimeService.signalEventReceivedXXX方法)觸發。兩種方式都會啓動所有擁有相同名字信號啓動事件的流程定義。
請注意可以選擇異步還是同步啓動流程實例。
需要爲API傳遞的signalName,是由signal元素的name屬性決定的名字。signal元素由signalEventDefinition的signalRef屬性引用。
- 圖示:
信號啓動事件用其中有一個信號事件標誌的圓圈表示。這個標誌並未填充,用以表示捕獲(接收)行爲。
- XML表示:
信號啓動事件的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>
4.1.1.5、錯誤啓動事件
-
描述:
錯誤啓動事件(error start event),可用於觸發事件子流程(Event Sub-Process)。錯誤啓動事件不能用於啓動流程實例。
錯誤啓動事件總是中斷。 -
圖示:
錯誤啓動事件用其中有一個錯誤事件標誌的圓圈表示。這個標誌並未填充,用以表示捕獲(接收)行爲。
- XML表示:
錯誤啓動事件的XML表示格式,爲普通啓動事件聲明加上errorEventDefinition子元素:
<startEvent id="messageStart" >
<errorEventDefinition errorRef="someError" />
</startEvent>
4.1.2、結束事件
結束事件(end event)標誌着流程或子流程中一個分支的結束。結束事件總是拋出(型)事件。這意味着當流程執行到達結束事件時,會拋出一個結果。結果的類型由事件內部的黑色圖標表示。在XML表示中,類型由子元素聲明給出。
4.1.2.1、空結束事件
-
描述:
“空”結束事件(none end event),意味着當到達這個事件時,沒有特別指定拋出的結果。因此,引擎除了結束當前執行分支之外,不會多做任何事情。 -
圖示:
空結束事件,用其中沒有圖標(沒有結果類型)的粗圓圈表示。
- xml表示:
空事件的XML表示格式爲普通結束事件聲明,沒有任何子元素(其它種類的結束事件都有子元素,用於聲明其類型)。
<endEvent id="end" name="my end event" />
4.1.2.2、錯誤結束事件
-
描述:
當流程執行到達錯誤結束事件(error end event)時,結束執行的當前分支,並拋出錯誤。這個錯誤可以由匹配的錯誤邊界中間事件捕獲。如果找不到匹配的錯誤邊界事件,將會拋出異常。 -
圖示:
錯誤結束事件事件用內部有一個錯誤圖標的標準結束事件(粗圓圈)表示。錯誤圖標是全黑的,代表拋出的含義。
- XML表示:
錯誤結束事件表示爲結束事件,加上errorEventDefinition子元素:
<endEvent id="myErrorEndEvent">
<errorEventDefinition errorRef="myError" />
</endEvent>
errorRef屬性可以引用在流程外定義的error元素:
<error id="myError" errorCode="123" />
...
<process id="myProcess">
...
4.1.2.3、終止結束事件
-
描述:
當到達終止結束事件(terminate end event)時,當前的流程實例或子流程會被終止。也就是說,當執行到達終止結束事件時,會判斷第一個範圍 scope(流程或子流程)並終止它。在BPMN 2.0中,子流程可以是嵌入式子流程,調用活動,事件子流程,或事務子流程。有一條通用規則:當存在多實例的調用過程或嵌入式子流程時,只會終止一個實例,其他的實例與流程實例不會受影響。
可以添加一個可選屬性terminateAll。當其爲true時,無論該終止結束事件在流程定義中的位置,也無論它是否在子流程(甚至是嵌套子流程)中,都會終止(根)流程實例。 -
圖示:
終止結束事件用內部有一個全黑圓的標準結束事件(粗圓圈)表示。
-
xml表示:
終止結束事件,表示爲結束事件,加上terminateEventDefinition子元素。
terminateAll屬性是可選的(默認爲false)。
<endEvent id="myEndEvent >
<terminateEventDefinition flowable:terminateAll="true"></terminateEventDefinition>
</endEvent>
4.1.2.4、取消結束事件
-
描述:
取消結束事件(cancel end event)只能與BPMN事務子流程(BPMN transaction subprocess)一起使用。當到達取消結束事件時,會拋出取消事件,且必須由取消邊界事件(cancel boundary event)捕獲。取消邊界事件將取消事務,並觸發補償(compensation)。 -
圖示:
取消結束事件用內部有一個取消圖標的標準結束事件(粗圓圈)表示。取消圖標是全黑的,代表拋出的含義。
-
xml表示:
取消結束事件,表示爲結束事件,加上cancelEventDefinition子元素。
<endEvent id="myCancelEndEvent">
<cancelEventDefinition />
</endEvent>
4.1.3、邊界事件
邊界事件(boundary event)是捕獲型事件,依附在活動(activity)上。邊界事件永遠不會拋出。這意味着當活動運行時,事件將監聽特定類型的觸發器。當捕獲到事件時,會終止活動,並沿該事件的出口順序流繼續。
所有的邊界事件都用相同的方式定義:
<boundaryEvent id="myBoundaryEvent" attachedToRef="theActivity">
<XXXEventDefinition/>
</boundaryEvent>
邊界事件由下列元素定義:
-
(流程範圍內)唯一的標識符
-
由attachedToRef屬性定義的,對該事件所依附的活動的引用。邊界事件及其所依附的活動,應定義在相同級別(也就是說,邊界事件並不包含在活動內)。
-
定義了邊界事件的類型的,形如XXXEventDefinition的XML子元素(例如TimerEventDefinition,ErrorEventDefinition,等等)。查閱特定的邊界事件類型,以瞭解更多細節。
4.1.3.1、定時器邊界事件
-
描述:
定時器邊界事件(timer boundary event)的行爲像是跑表與鬧鐘。當執行到達邊界事件所依附的活動時,將啓動定時器。當定時器觸發時(例如在特定時間間隔後),可以中斷活動,並沿着邊界事件的出口順序流繼續執行。 -
圖示:
定時器邊界事件用內部有一個定時器圖標的標準邊界事件(圓圈)表示。
- XML表示:
定時器邊界事件與一般邊界事件一樣定義。其中類型子元素爲timerEventDefinition元素。
<boundaryEvent id="escalationTimer" cancelActivity="true" attachedToRef="firstLineSupport">
<timerEventDefinition>
<timeDuration>PT4H</timeDuration>
</timerEventDefinition>
</boundaryEvent>
4.1.3.2、錯誤邊界事件
-
描述
在活動邊界上的錯誤捕獲中間(事件),或簡稱錯誤邊界事件(error boundary event),捕獲其所依附的活動範圍內拋出的錯誤。
在嵌入式子流程或者調用活動上定義錯誤邊界事件最有意義,因爲子流程的範圍會包括其中的所有活動。錯誤可以由錯誤結束事件拋出。這樣的錯誤會逐層向其上級父範圍傳播,直到在範圍內找到一個匹配錯誤事件定義的錯誤邊界事件。
當捕獲錯誤事件時,會銷燬邊界事件定義所在的活動,同時銷燬其中所有的當前執行(例如,並行活動,嵌套子流程,等等)。流程執行將沿着邊界事件的出口順序流繼續。 -
圖示:
錯誤邊界事件用內部有一個錯誤圖標的標準中間事件(兩層圓圈)表示。錯誤圖標是白色的,代表捕獲的含義。
-
xml表示:
錯誤邊界事件與標準邊界事件一樣定義:
<boundaryEvent id="catchError" attachedToRef="mySubProcess">
<errorEventDefinition errorRef="myError"/>
</boundaryEvent>
4.1.3.3、信號邊界事件
- 描述:
依附在活動邊界上的信號捕獲中間(事件),或簡稱信號邊界事件(signal boundary event),捕獲與其信號定義具有相同名稱的信號。
與其他事件例如錯誤邊界事件不同的是,信號邊界事件不只是捕獲其所依附範圍拋出的信號。信號邊界事件爲全局範圍(廣播)的,意味着信號可以從任何地方拋出,甚至可以是不同的流程實例。
- 圖示:
信號邊界事件,用內部有一個信號圖標的標準中間事件(兩層圓圈)表示。信號圖標是白色的,代表捕獲的含義。
- xml表示:
信號邊界事件與標準邊界事件一樣定義:
<boundaryEvent id="boundary" attachedToRef="task" cancelActivity="true">
<signalEventDefinition signalRef="alertSignal"/>
</boundaryEvent>
4.1.3.4、消息邊界事件
-
描述:
在活動邊界上的消息捕獲中間(事件),或簡稱消息邊界事件(message boundary event),捕獲與其消息定義具有相同消息名的消息。 -
圖示:
消息邊界事件,用內部有一個消息圖標的標準中間事件(兩層圓圈)表示。信號圖標是白色的,代表捕獲的含義。
息邊界事件既可以是中斷型的(右圖),也可以是非中斷型的(左圖)。 -
XML表示:
消息邊界事件與標準邊界事件一樣定義:
<boundaryEvent id="boundary" attachedToRef="task" cancelActivity="true">
<messageEventDefinition messageRef="newCustomerMessage"/>
</boundaryEvent>
4.1.3.5、取消邊界事件
- 描述
依附在事務子流程邊界上的取消捕獲中間事件,或簡稱取消邊界事件(cancel boundary event),在事務取消時觸發。當取消邊界事件觸發時,首先會中斷當前範圍的所有活動執行。接下來,啓動事務範圍內所有有效的的補償邊界事件(compensation boundary event)。補償會同步執行,也就是說在離開事務前,邊界事件會等待補償完成。當補償完成時,沿取消邊界事件的任何出口順序流離開事務子流程。
>>> 一個事務子流程只允許使用一個取消邊界事件。
>>> 如果事務子流程中有嵌套的子流程,只會對成功完成的子流程觸發補償。
>>> 如果取消邊界事件放置在具有多實例特性的事務子流程上,如果一個實例觸發了取消,則邊界事件將取消所有實例。
- 圖示:
取消邊界事件,用內部有一個取消圖標的標準中間事件(兩層圓圈)表示。取消圖標是白色的(未填充),代表捕獲的含義。
- xml表示:
取消邊界事件與標準邊界事件一樣定義:
<boundaryEvent id="boundary" attachedToRef="transaction" >
<cancelEventDefinition />
</boundaryEvent>
4.1.3.6、補償邊界事件
-
描述:
依附在活動邊界上的補償捕獲中間(事件),或簡稱補償邊界事件(compensation boundary event),可以爲活動附加補償處理器。
補償邊界事件必須使用直接關聯的方式引用單個的補償處理器。
補償邊界事件與其它邊界事件的活動策略不同。其它邊界事件,例如信號邊界事件,在其依附的活動啓動時激活;當該活動結束時會被解除,並取消相應的事件訂閱。而補償邊界事件不是這樣。補償邊界事件在其依附的活動成功完成時激活,同時創建補償事件的相應訂閱。當補償事件被觸發,或者相應的流程實例結束時,纔會移除訂閱。請考慮下列因素:>>> 當補償被觸發時,會調用補償邊界事件關聯的補償處理器。調用次數與其依附的活動成功完成的次數相同。
>>>如果補償邊界事件依附在具有多實例特性的活動上,則會爲每一個實例創建補償事件訂閱。
>>> 如果補償邊界事件依附在位於循環內部的活動上,則每次該活動執行時,都會創建一個補償事件訂閱。
>>> 如果流程實例結束,則取消補償事件的訂閱。
-
圖示:
補償邊界事件,用內部有一個補償圖標的標準中間事件(兩層圓圈)表示。補償圖標是白色的(未填充),代表捕獲的含義。另外,補償邊界事件使用單向連接關聯補償處理器,如下圖所示:
-
xml表示:
補償邊界事件與標準邊界事件一樣定義:
<boundaryEvent id="compensateBookHotelEvt" attachedToRef="bookHotel" >
<compensateEventDefinition />
</boundaryEvent>
<association associationDirection="One" id="a1"
sourceRef="compensateBookHotelEvt" targetRef="undoBookHotel" />
<serviceTask id="undoBookHotel" isForCompensation="true" flowable:class="..." />
4.1.4、中間事件
在開始事件和結束事件之間發生的事件都稱爲中間事件。中間事件會影響流程的流轉路線,但不會啓動或直接終止流程的執行。
中間事件按照其特性可以分爲兩類:中間Catching(捕獲)事件和中間Throwing(拋出)事件,當流程到達中間Catching事件時,它會一直在等待被觸發,直接接收到的信息,纔會被觸發,而當流程到達中間Throwing事件時,該事件會自動被觸發並拋出相應的結果或者信息。
4.1.4.1、捕獲中間事件
所有的捕獲中間事件(intermediate catching events)都使用相同方式定義:
<intermediateCatchEvent id="myIntermediateCatchEvent" >
<XXXEventDefinition/>
</intermediateCatchEvent>
捕獲中間事件由下列元素定義:
-
(流程範圍內)唯一的標識符
-
定義了捕獲中間事件類型的,形如XXXEventDefinition的XML子元素(例如TimerEventDefinition等)。查閱特定中間捕獲事件類型,以瞭解更多細節。
4.1.4.1.1、定時器捕獲中間事件
-
描述:
定時器捕獲中間事件(timer intermediate catching event)的行爲像是跑表。當執行到達捕獲事件時,啓動定時器;當定時器觸發時(例如在一段時間間隔後),沿定時器中間事件的出口順序流繼續執行。 -
圖示:
定時器中間事件用內部有定時器圖標的中間捕獲事件表示。
-
XML表示:
定時器中間事件與捕獲中間事件一樣定義。子元素爲timerEventDefinition。
<intermediateCatchEvent id="timer">
<timerEventDefinition>
<timeDuration>PT5M</timeDuration>
</timerEventDefinition>
</intermediateCatchEvent>
4.1.4.1.2、信號捕獲中間事件
- 描述:
信號捕獲中間事件(signal intermediate catching event),捕獲與其引用的信號定義具有相同信號名稱的信號。
>>> 與其他事件如錯誤事件不同,信號在被捕獲後不會被消耗。如果有兩個激活的信號中間事件,捕獲相同的信號事件,則兩個中間事件都會被觸發,哪怕它們不在同一個流程實例裏。
- 圖示:
信號捕獲中間事件用內部有信號圖標的標準中間事件(兩層圓圈)表示。信號圖標是白色的(未填充),代表捕獲的含義。
- xml表示:
信號中間事件與捕獲中間事件一樣定義。子元素爲signalEventDefinition。
<intermediateCatchEvent id="signal">
<signalEventDefinition signalRef="newCustomerSignal" />
</intermediateCatchEvent>
4.1.4.1.3、信號捕獲中間事件
-
描述:
消息捕獲中間事件(message intermediate catching event),捕獲特定名字的消息。 -
圖示:
消息捕獲中間事件用內部有消息圖標的標準中間事件(兩層圓圈)表示。消息圖標是白色的(未填充),代表捕獲的含義。
- xml表示:
消息中間事件與捕獲中間事件一樣定義。子元素爲messageEventDefinition。
<intermediateCatchEvent id="message">
<messageEventDefinition signalRef="newCustomerMessage" />
</intermediateCatchEvent>
4.1.4.2、拋出中間事件
所有的拋出中間事件(intermediate throwing evnet)都使用相同方式定義:
<intermediateThrowEvent id="myIntermediateThrowEvent" >
<XXXEventDefinition/>
</intermediateThrowEvent>
拋出中間事件由下列元素定義:
-
(流程範圍內)唯一的標識符
-
定義了拋出中間事件類型的,形如XXXEventDefinition的XML子元素(例如signalEventDefinition等)。查閱特定中間拋出事件類型,以瞭解更多細節。
4.1.4.2.1、空拋出中間事件
下面的流程圖展示了空拋出中間事件(intermediate throwing none event)的簡單例子。其用於指示流程已經到達了某種狀態。
添加一個執行監聽器後,空中間事件就可以成爲很好的監視某些KPI(Key Performance Indicators 關鍵績效指標)的鉤子。
<intermediateThrowEvent id="noneEvent">
<extensionElements>
<flowable:executionListener class="org.flowable.engine.test.bpmn.event.IntermediateNoneEventTest$MyExecutionListener" event="start" />
</extensionElements>
</intermediateThrowEvent>
4.1.4.2.2、信號拋出中間事件
- 描述:
信號拋出中間事件(signal intermediate throwing event),拋出所定義信號的信號事件。
在Flowable中,信號會廣播至所有的激活的處理器(也就是說,所有的信號捕獲事件)。可以同步或異步地發佈信號。
-
- 在默認配置中,信號同步地傳遞。這意味着拋出信號的流程實例會等待,直到信號傳遞至所有的捕獲信號的流程實例。所有的捕獲流程實例也會在與拋出流程實例相同的事務中,也就是說如果收到通知的流程實例中,有一個實例產生了技術錯誤(拋出異常),則所有相關的實例都會失敗。
-
- 信號也可以異步地傳遞。這是由到達拋出信號事件時的發送處理器來決定的。對於每個激活的處理器,JobExecutor會爲其存儲並傳遞一個異步通知消息(asynchronous notification message),即作業(Job)。
-
圖示:
消息拋出中間事件用內部有信號圖標的標準中間事件(兩層圓圈)表示。信號圖標是黑色的(已填充),代表拋出的含義。
-
xml表示:
信號中間事件與拋出中間事件一樣定義。子元素爲signalEventDefinition。
<intermediateThrowEvent id="signal">
<signalEventDefinition signalRef="newCustomerSignal" />
</intermediateThrowEvent>
異步信號事件這樣定義:
<intermediateThrowEvent id="signal">
<signalEventDefinition signalRef="newCustomerSignal" flowable:async="true" />
</intermediateThrowEvent>
4.1.4.2.3、補償拋出中間事件
- 描述:
補償拋出中間事件(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 id="throwCompensation">
<compensateEventDefinition />
</intermediateThrowEvent>
另外,activityRef可選項用於爲指定的範圍或活動觸發補償:
<intermediateThrowEvent id="throwCompensation">
<compensateEventDefinition activityRef="bookHotel" />
</intermediateThrowEvent>
4.2、順序流
-
描述:
順序流(sequence flow)是流程中兩個元素間的連接器。在流程執行過程中,一個元素被訪問後,會沿着其所有出口順序流繼續執行。這意味着BPMN 2.0的默認是並行執行的:兩個出口順序流就會創建兩個獨立的、並行的執行路徑。 -
圖示:
順序流,用從源元素指向目標元素的箭頭表示。箭頭總是指向目標元素。
-
xml表示:
順序流需要有流程唯一的id,並引用存在的源與目標元素。
<sequenceFlow id="flow1" sourceRef="theStart" targetRef="theTask" />
4.2.1、條件順序流
- 描述:
在順序流上可以定義條件(conditional sequence flow)。當離開BPMN 2.0活動時,默認行爲是計算其每個出口順序流上的條件。當條件計算爲true時,選擇該出口順序流。如果該方法選擇了多條順序流,則會生成多個執行,流程會以並行方式繼續。
>>> 上面的介紹針對BPMN 2.0活動(與事件),但不適用於網關(gateway)。不同類型的網關,會用不同的方式處理帶有條件的順序流。
- 圖示:
條件順序流用起點帶有小菱形的順序流表示。在順序流旁顯示條件表達式。
- xml表示:
條件順序流的XML表示格式爲含有conditionExpression(條件表達式)子元素的普通順序流。請注意目前只支持tFormalExpressions。可以省略xsi:type=""定義,默認爲唯一支持的表達式類型。
<sequenceFlow id="flow" sourceRef="theStart" targetRef="theTask">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${order.price > 100 && order.price < 250}]]>
</conditionExpression>
</sequenceFlow>
>>> 目前conditionalExpressions只能使用UEL。使用的表達式需要能解析爲boolean值,否則當計算條件時會拋出異常。
- 下面的例子,通過典型的JavaBean的方式,使用getter引用流程變量的數據。
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${order.price > 100 && order.price < 250}]]>
</conditionExpression>
- 這個例子調用了一個解析爲boolean值的方法。
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[${order.isStandardOrder()}]]>
</conditionExpression>
Flowable發行版中包含了下列示例流程,用於展示值表達式與方法表達式的使用。
4.2.2、默認順序流
-
描述:
所有的BPMN 2.0任務與網關都可以使用默認順序流(default sequence flow)。只有當沒有其他順序流可以選擇時,纔會選擇默認順序流作爲活動的出口順序流。流程會忽略默認順序流上的條件。 -
圖示:
默認順序流用起點帶有“斜線”標記的一般順序流表示。
- XML表示:
活動的默認順序流由該活動的default屬性定義。下面的XML片段展示了一個排他網關(exclusive gateway),帶有默認順序流flow 2。只有當conditionA與conditionB都計算爲false時,纔會選擇默認順序流作爲網關的出口順序流。
<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>
對應下面的圖示:
4.3、網關
網關(gateway)用於控制執行的流向(或者按BPMN 2.0的用詞:執行的“標誌(token)”)。網關可以消費(consuming)與生成(generating)標誌。
網關用其中帶有圖標的菱形表示。該圖標顯示了網關的類型。
這裏出口順序流的含義與BPMN 2.0中的一般情況不一樣。一般情況下,會選擇所有條件計算爲true的順序流,並行執行。而使用排他網關時,只會選擇一條順序流。當多條順序流的條件都計算爲true時,會且僅會選擇在XML中最先定義的順序流繼續流程。如果沒有可選的順序流,會拋出異常。
4.3.1、排他網關
-
描述:
排他網關(exclusive gateway)(也叫異或網關 XOR gateway,或者更專業的,基於數據的排他網關 exclusive data-based gateway),用於對流程中的決策建模。當執行到達這個網關時,會按照所有出口順序流定義的順序對它們進行計算。選擇第一個條件計算爲true的順序流(當沒有設置條件時,認爲順序流爲true)繼續流程。 -
圖示:
排他網關用內部帶有’X’圖標的標準網關(菱形)表示,'X’圖標代表異或的含義。請注意內部沒有圖標的網關默認爲排他網關。BPMN 2.0規範不允許在同一個流程中混合使用有及沒有X的菱形標誌。
-
xml表示:
排他網關的XML表示格式很簡潔:一行定義網關的XML。條件表達式定義在其出口順序流上。
以下面的模型爲例:
其xml表示如下:
<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>
4.3.2、並行網關
- 描述:
網關也可以建模流程中的並行執行。在流程模型中引入並行的最簡單的網關,就是並行網關(parallel gateway)。它可以將執行分支(fork)爲多條路徑,也可以合併(join)多條入口路徑的執行。
並行網關的功能取決於其入口與出口順序流:
-
- 分支:所有的出口順序流都並行執行,爲每一條順序流創建一個並行執行。
-
- 合併:所有到達並行網關的並行執行都會在網關處等待,直到每一條入口順序流都到達了有個執行。然後流程經過該合併網關繼續。
>>> 如果並行網關同時具有多條入口與出口順序流,可以同時具有分支與合併的行爲。在這種情況下,網關首先合併所有入口順序流,然後分裂爲多條並行執行路徑。
與其他網關類型有一個重要區別:並行網關不計算條件。如果連接到並行網關的順序流上定義了條件,會直接忽略該條件。
- 圖示:
並行網關,用內部帶有’加號’圖標的網關(菱形)表示,代表與(AND)的含義。
- xml表示:
定義並行網關只需要一行XML:
<parallelGateway id="myParallelGateway" />
實際行爲(分支,合併或兩者皆有),由連接到該並行網關的順序流定義。
例如,上面的模型表示爲下面的XML:
<startEvent id="theStart" />
<sequenceFlow id="flow1" sourceRef="theStart" targetRef="fork" />
<parallelGateway id="fork" />
<sequenceFlow sourceRef="fork" targetRef="receivePayment" />
<sequenceFlow sourceRef="fork" targetRef="shipOrder" />
<userTask id="receivePayment" name="Receive Payment" />
<sequenceFlow sourceRef="receivePayment" targetRef="join" />
<userTask id="shipOrder" name="Ship Order" />
<sequenceFlow sourceRef="shipOrder" targetRef="join" />
<parallelGateway id="join" />
<sequenceFlow sourceRef="join" targetRef="archiveOrder" />
<userTask id="archiveOrder" name="Archive Order" />
<sequenceFlow sourceRef="archiveOrder" targetRef="theEnd" />
<endEvent id="theEnd" />
在上面的例子中,當流程啓動後會創建兩個任務:
ProcessInstance pi = runtimeService.startProcessInstanceByKey("forkJoin");
TaskQuery query = taskService.createTaskQuery()
.processInstanceId(pi.getId())
.orderByTaskName()
.asc();
List<Task> tasks = query.list();
assertEquals(2, tasks.size());
Task task1 = tasks.get(0);
assertEquals("Receive Payment", task1.getName());
Task task2 = tasks.get(1);
assertEquals("Ship Order", task2.getName());
當這兩個任務完成後,第二個並行網關會合並這兩個執行。由於它只有一條出口順序流,因此就不會再創建並行執行路徑,而只是激活Archive Order(存檔訂單)任務。
並行網關不需要“平衡”(也就是說,前後對應的兩個並行網關,其入口/出口順序流的數量不需要一致)。每個並行網關都會簡單地等待所有入口順序流,併爲每一條出口順序流創建並行執行,而不受流程模型中的其他結構影響。因此,下面的流程在BPMN 2.0中是合法的:
4.3.3、包容網關
- 描述:
可以把包容網關(inclusive gateway)看做排他網關與並行網關的組合。與排他網關一樣,可以在包容網關的出口順序流上定義條件,包容網關會計算條件。然而主要的區別是,包容網關與並行網關一樣,可以同時選擇多於一條出口順序流。
包容網關的功能取決於其入口與出口順序流:
-
- 分支:流程會計算所有出口順序流的條件。對於每一條計算爲true的順序流,流程都會創建一個並行執行。
-
- 合併:所有到達包容網關的並行執行,都會在網關處等待。直到每一條具有流程標誌(process token)的入口順序流,都有一個執行到達。這是與並行網關的重要區別。換句話說,包容網關只會等待可以被執行的入口順序流。在合併後,流程穿過合併並行網關繼續。
>>> 如果包容網關同時具有多條入口與出口順序流,可以同時具有分支與合併的行爲。在這種情況下,網關首先合併所有具有流程標誌的入口順序流,然後爲每一個條件計算爲true的出口順序流分裂出並行執行路徑。
包容網關的匯聚行爲比並行網關更復雜。所有到達包容網關的並行執行,都會在網關等待,直到所有“可以到達”包容網關的執行都“到達”包容網關。 判斷方法爲:計算當前流程實例中的所有執行,檢查從其位置是否有一條到達包容網關的路徑(忽略順序流上的任何條件)。如果存在這樣的執行(可到達但尚未到達),則不會觸發包容網關的匯聚行爲。
- xml表示:
定義包容網關需要一行XML:
<inclusiveGateway id="myInclusiveGateway" />
實際行爲(分支,合併或兩者皆有),由連接到該包容網關的順序流定義。
例如,上面的模型表現爲下面的XML:
<startEvent id="theStart" />
<sequenceFlow id="flow1" sourceRef="theStart" targetRef="fork" />
<inclusiveGateway id="fork" />
<sequenceFlow sourceRef="fork" targetRef="receivePayment" >
<conditionExpression xsi:type="tFormalExpression">${paymentReceived == false}</conditionExpression>
</sequenceFlow>
<sequenceFlow sourceRef="fork" targetRef="shipOrder" >
<conditionExpression xsi:type="tFormalExpression">${shipOrder == true}</conditionExpression>
</sequenceFlow>
<userTask id="receivePayment" name="Receive Payment" />
<sequenceFlow sourceRef="receivePayment" targetRef="join" />
<userTask id="shipOrder" name="Ship Order" />
<sequenceFlow sourceRef="shipOrder" targetRef="join" />
<inclusiveGateway id="join" />
<sequenceFlow sourceRef="join" targetRef="archiveOrder" />
<userTask id="archiveOrder" name="Archive Order" />
<sequenceFlow sourceRef="archiveOrder" targetRef="theEnd" />
<endEvent id="theEnd" />
在上面的例子中,當流程啓動後,如果流程變量paymentReceived == false且shipOrder == true,會創建兩個任務。如果只有一個流程變量等於true,則只會創建一個任務。如果沒有條件計算爲true,會拋出異常(可通過指定默出口順序流避免)。在下面的例子中,只會創建ship order(傳遞訂單)一個任務:
HashMap<String, Object> variableMap = new HashMap<String, Object>();
variableMap.put("receivedPayment", true);
variableMap.put("shipOrder", true);
ProcessInstance pi = runtimeService.startProcessInstanceByKey("forkJoin");
TaskQuery query = taskService.createTaskQuery()
.processInstanceId(pi.getId())
.orderByTaskName()
.asc();
List<Task> tasks = query.list();
assertEquals(1, tasks.size());
Task task = tasks.get(0);
assertEquals("Ship Order", task.getName());
當這個任務完成後,第二個包容網關會合並這兩個執行。並且由於它只有一條出口順序流,所有不會再創建並行執行路徑,而只會激活Archive Order(存檔訂單)任務。
>>> 包容網關不需要“平衡”(也就是說,對應的包容網關,其入口/出口順序流的數量不需要匹配)。包容網關會簡單地等待所有入口順序流,併爲每一條出口順序流創建並行執行,不受流程模型中的其他結構影響。
>>> 包容網關不需要“平衡”(也就是說,前後對應的兩個包容網關,其入口/出口順序流的數量不需要一致)。每個包容網關都會簡單地等待所有入口順序流,併爲每一條出口順序流創建並行執行,不受流程模型中的其他結構影響。
4.3.4、 基於事件的網關
- 描述:
基於事件的網關(event-based gateway)提供了根據事件做選擇的方式。網關的每一條出口順序流都需要連接至一個捕獲中間事件。當流程執行到達基於事件的網關時,與等待狀態類似,網關會暫停執行,並且爲每一條出口順序流創建一個事件訂閱。
>>> 基於事件的網關的出口順序流與一般的順序流不同。這些順序流從不實際執行。相反,它們用於告知流程引擎:當執行到達一個基於事件的網關時,需要訂閱什麼事件。有以下限制:
-
- 一個基於事件的網關,必須有兩條或更多的出口順序流。
-
- 基於事件的網關,只能連接至intermediateCatchEvent(捕獲中間事件)類型的元素(Flowable不支持在基於事件的網關之後連接“接收任務 Receive Task”)。
-
- 連接至基於事件的網關的intermediateCatchEvent,必須只有一個入口順序流。
-
圖示:
基於事件的網關,用內部帶有特殊圖標的網關(菱形)表示。
-
xml表示:
用於定義基於事件的網關的XML元素爲eventBasedGateway。 -
示例:
下面是一個帶有基於事件的網關的示例流程。當執行到達基於事件的網關時,流程執行暫停。流程實例訂閱alert信號事件,並創建一個10分鐘後觸發的定時器。流程引擎會等待10分鐘,並同時等待信號事件。如果信號在10分鐘內觸發,則會取消定時器,流程沿着信號繼續執行,激活Handle alert用戶任務。如果10分鐘內沒有觸發信號,則會繼續執行,並取消信號訂閱。
<definitions id="definitions"
xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:flowable="http://flowable.org/bpmn"
targetNamespace="Examples">
<signal id="alertSignal" name="alert" />
<process id="catchSignal">
<startEvent id="start" />
<sequenceFlow sourceRef="start" targetRef="gw1" />
<eventBasedGateway id="gw1" />
<sequenceFlow sourceRef="gw1" targetRef="signalEvent" />
<sequenceFlow sourceRef="gw1" targetRef="timerEvent" />
<intermediateCatchEvent id="signalEvent" name="Alert">
<signalEventDefinition signalRef="alertSignal" />
</intermediateCatchEvent>
<intermediateCatchEvent id="timerEvent" name="Alert">
<timerEventDefinition>
<timeDuration>PT10M</timeDuration>
</timerEventDefinition>
</intermediateCatchEvent>
<sequenceFlow sourceRef="timerEvent" targetRef="exGw1" />
<sequenceFlow sourceRef="signalEvent" targetRef="task" />
<userTask id="task" name="Handle alert"/>
<exclusiveGateway id="exGw1" />
<sequenceFlow sourceRef="task" targetRef="exGw1" />
<sequenceFlow sourceRef="exGw1" targetRef="end" />
<endEvent id="end" />
</process>
</definitions>
4.4、任務
一個任務表示工作需要被外部實體完成, 比如人工或自動服務。
任務被描繪成一個圓角矩形,一般內部包含文字。 任務的類型(用戶任務,服務任務,腳本任務,等等)顯示在矩形的左上角,用小圖標區別。 根據任務的類型, 引擎會執行不同的功能。
4.4.1、用戶任務
-
描述:
“用戶任務(user task)”,也叫人工任務,見名知意,是用於對需要人工執行的任務進行建模。當流程執行到達用戶任務時,會爲指派至該任務的用戶或組的任務列表創建一個新任務。 -
圖示:
用戶任務用左上角有一個小用戶圖標的標準任務(圓角矩形)表示。
-
xml表示:
用戶任務在XML中如下定義。其中id是必須屬性,name是可選屬性。
<userTask id="theTask" name="Important task" />
也可以爲用戶任務添加描述(description)。事實上任何BPMN 2.0元素都可以有描述。描述由documentation元素定義。
<userTask id="theTask" name="Schedule meeting" >
<documentation>
Schedule an engineering meeting for next week with the new hire.
</documentation>
- 到期日期
每個任務都可以使用一個字段標誌該任務的到期日期(due date)。可以使用查詢API,查詢在給定日期前或後到期的任務。
可以在任務定義中使用擴展指定表達式,以在任務創建時設定到期日期。該表達式必須解析爲java.util.Date,java.util.String (ISO8601格式),ISO8601時間長度(例如PT50M),或者null。例如,可以使用在流程裏前一個表單中輸入的日期,或者由前一個服務任務計算出的日期。如果使用的是時間長度,則到期日期基於當前時間加上給定長度計算。例如當dueDate使用“PT30M”時,任務在從現在起30分鐘後到期。
<userTask id="theTask" name="Important task" flowable:dueDate="${dateVariable}"/>
任務的到期日期也可以使用TaskService,或者在TaskListener中使用傳遞的DelegateTask修改。
- 用戶指派:
用戶任務可以直接指派(assign)給用戶。可以定義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");
將獲取所有kermit作爲候選用戶的任務,也就是說,表達式含有user(kermit)的任務。同時也將獲取所有指派給kermit爲其成員的組的任務(例如,kermit時management組的成員,且任務指派給management組)。組在運行時解析,並可通過身份服務管理。
如果並未指定給定字符串是用戶還是組,引擎默認其爲組。下列代碼與聲明group(accountancy)效果一樣。
<formalExpression>accountancy</formalExpression>
-
用於任務指派的Flowable擴展
很明顯,當指派關係不復雜時,這種用戶與組的指派方式十分笨重。爲避免這種複雜性,可以在用戶任務上使用自定義擴展。 -
- assignee(辦理人)屬性:這個自定義擴展用於直接將用戶指派至用戶任務。
<userTask id="theTask" name="my task" flowable:assignee="kermit" />
與上面定義的humanPerformer結構效果完全相同。
-
- candidateUsers(候選用戶)屬性:這個自定義擴展用於爲任務指定候選用戶。
<userTask id="theTask" name="my task" flowable:candidateUsers="kermit, gonzo" />
與使用上面定義的potentialOwner結構效果完全相同。請注意不需要像在potentialOwner中一樣,使用user(kermit)的聲明,因爲這個屬性只能用於用戶。
-
- candidateGroups(候選組)attribute:這個自定義擴展用於爲任務指定候選組。
<userTask id="theTask" name="my task" flowable:candidateGroups="management, accountancy" />
與使用上面定義的potentialOwner結構效果完全相同。請注意不需要像在potentialOwner中一樣,使用group(management)的聲明,因爲這個屬性只能用於組。
-
- 可以定義在一個用戶任務上同時定義candidateUsers與candidateGroups。
4.4.2、腳本任務
-
描述:
腳本任務(Script Task)是一個自動化任務。當流程到達腳本任務時,自動執行編寫的腳本,完畢後繼續執行後繼路線。 -
圖示:
腳本任務用左上角有一個小“腳本”圖標的標準BPMN 2.0任務(圓角矩形)表示。
-
xml表示:
腳本任務使用script與scriptFormat元素定義。
<scriptTask id="theScriptTask" name="Execute script" scriptFormat="groovy">
<script>
sum = 0
for ( i in inputArray ) {
sum += i
}
</script>
</scriptTask>
scriptFormat屬性的值,必須是兼容JSR-223(Java平臺腳本)的名字。默認情況下,JavaScript包含在每一個JDK中,因此不需要添加任何JAR文件。如果想使用其它(兼容JSR-223的)腳本引擎,則需要在classpath中添加相應的jar,並使用適當的名字。例如,Flowable單元測試經常使用Groovy,因爲其語法與Java十分相似。
請注意Groovy腳本引擎與groovy-all JAR捆綁在一起。在Groovy 2.0版本以前,腳本引擎是Groovy JAR的一部分。因此,必須添加如下依賴:
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.x.x<version>
</dependency>
- 腳本中的變量:
到達腳本引擎的執行中,所有的流程變量都可以在腳本中使用。在這個例子裏,腳本變量’inputArray’實際上就是一個流程變量(一個integer的數組)。
<script>
sum = 0
for ( i in inputArray ) {
sum += i
}
</script>
也可以簡單地調用execution.setVariable(“variableName”, variableValue),在腳本中設置流程變量。默認情況下,變量不會自動儲存(請注意,在一些早期版本中是會儲存的!)。可以將scriptTask的autoStoreVariables參數設置爲true,以自動保存任何在腳本中定義的變量(例如上例中的sum)。
<scriptTask id="script" scriptFormat="JavaScript" flowable:autoStoreVariables="false">
這個參數的默認值爲false。也就是說如果在腳本任務定義中忽略這個參數,則腳本聲明的所有變量將只在腳本執行期間有效。
在腳本中設置變量的例子:
<script>
def scriptVar = "test123"
execution.setVariable("myVar", scriptVar)
</script>
>>> 下列名字是保留字,不能用於變量名:out,out:print,lang:import,context,elcontext。
- 腳本任務的結果:
腳本任務的返回值,可以通過爲腳本任務定義的’flowable:resultVariable’屬性設置爲流程變量。可以是已經存在的,或者新的流程變量。如果指定爲已存在的流程變量,則流程變量的值會被腳本執行的結果值覆蓋。如果不指定結果變量名,則腳本結果值將被忽略。
<scriptTask id="theScriptTask" name="Execute script" scriptFormat="juel" flowable:resultVariable="myVar">
<script>#{echo}</script>
</scriptTask>
在上面的例子中,腳本執行的結果(解析表達式’#{echo}'的值),將在腳本完成後,設置爲名爲’myVar’的流程變量。
4.4.3、服務任務
服務任務(Service Task)是一個自動化任務。當流程到達系統任務時,它會調用一些服務(例如web service,java service等等),完畢後繼續執行後繼路線。
4.4.3.1、Java服務任務
-
描述:
Java服務任務(Java service task)用於調用Java類。 -
圖示:
服務任務用左上角有一個小齒輪圖標的圓角矩形表示。
- xml表示:
有四種方法聲明如何調用Java邏輯:
-
- 指定實現了JavaDelegate或ActivityBehavior的類
-
- 調用解析爲委託對象(delegation object)的表達式
-
- 調用方法表達式(method expression)
-
- 對值表達式(value expression)求值
使用flowable:class屬性提供全限定類名(fully qualified classname),指定流程執行時調用的類。
<serviceTask id="javaService"
name="My Java Service Task"
flowable:class="org.flowable.MyJavaDelegate" />
也可以使用解析爲對象的表達式。該對象必須遵循的規則,與使用flowable:class創建的對象規則相同。
<serviceTask id="serviceTask" flowable:delegateExpression="${delegateExpressionBean}" />
delegateExpressionBean是一個實現了JavaDelegate接口的bean,定義在Spring容器中。
使用flowable:expression屬性指定需要計算的UEL方法表達式。
<serviceTask id="javaService"
name="My Java Service Task"
flowable:expression="#{printer.printMessage()}" />
將在名爲printer的對象上調用printMessage方法(不帶參數)。
也可以爲表達式中使用的方法傳遞變量。
<serviceTask id="javaService"
name="My Java Service Task"
flowable:expression="#{printer.printMessage(execution, myVar)}" />
將在名爲printer的對象上調用printMessage方法。傳遞的第一個參數爲DelegateExecution,名爲execution,在表達式上下文中默認可用。傳遞的第二個參數,是當前執行中,名爲myVar變量的值。
可以使用flowable:expression屬性指定需要計算的UEL值表達式。
<serviceTask id="javaService"
name="My Java Service Task"
flowable:expression="#{split.ready}" />
會調用名爲split的bean的ready參數的getter方法,getReady(不帶參數)。該對象會被解析爲執行的流程變量或(如果可用的話)Spring上下文中的bean。
- 實現:
要實現可以在流程執行中調用的類,需要實現org.flowable.engine.delegate.JavaDelegate接口,並在execute方法中提供所需邏輯。當流程執行到達該活動時,會執行方法中定義的邏輯,並按照BPMN 2.0的默認方法離開活動。
下面是一個Java類的示例,用於將流程變量String改爲大寫。這個類需要實現org.flowable.engine.delegate.JavaDelegate接口,因此需要實現execute(DelegateExecution)方法。這個方法就是引擎將調用的方法,需要實現業務邏輯。可以通過DelegateExecution接口(點擊鏈接獲取該接口操作的詳細Javadoc)訪問流程實例信息,如流程變量等。
public class ToUppercase implements JavaDelegate {
public void execute(DelegateExecution execution) {
String var = (String) execution.getVariable("input");
var = var.toUpperCase();
execution.setVariable("input", var);
}
}
- 服務任務的結果:
服務執行的返回值(僅對使用表達式的服務任務),可以通過爲服務任務定義的’flowable:resultVariable’屬性設置爲流程變量。可以是已經存在的,或者新的流程變量。 如果指定爲已存在的流程變量,則流程變量的值會被服務執行的結果值覆蓋。 如果使用’flowable:useLocalScopeForResultVariable’,則會將結果值設置爲局部變量。 如果不指定結果變量名,則服務任務的結果值將被忽略。
<serviceTask id="aMethodExpressionServiceTask"
flowable:expression="#{myService.doSomething()}"
flowable:resultVariable="myVar" />
4.4.3.2、Web服務任務
-
描述:
Web服務任務(Web service task)用於同步地調用外部的Web服務。 -
圖示:
Web服務任務與Java服務任務圖標一樣。
- xml表示:
使用Web服務之前,需要導入其操作及複雜的類型。可以使用導入標籤(import tag)指向Web服務的WSDL,自動處理:
<import importType="http://schemas.xmlsoap.org/wsdl/"
location="http://localhost:63081/counter?wsdl"
namespace="http://webservice.flowable.org/" />
按照上面的聲明,Flowable會導入定義,但不會創建條目定義(item definition)與消息。如果需要調用一個名爲’prettyPrint’的方法,則需要先爲請求及回覆消息創建對應的消息與條目定義:
<message id="prettyPrintCountRequestMessage" itemRef="tns:prettyPrintCountRequestItem" />
<message id="prettyPrintCountResponseMessage" itemRef="tns:prettyPrintCountResponseItem" />
<itemDefinition id="prettyPrintCountRequestItem" structureRef="counter:prettyPrintCount" />
<itemDefinition id="prettyPrintCountResponseItem" structureRef="counter:prettyPrintCountResponse" />
在聲明服務任務前,需要定義實際引用Web服務的BPMN接口與操作。基本上,是定義“接口”與所需的“操作”。對每一個操作都可以重複使用之前定義的“傳入”與“傳出”消息。例如,下面的聲明定義了“counter”接口及“prettyPrintCountOperation”操作:
<interface name="Counter Interface" implementationRef="counter:Counter">
<operation id="prettyPrintCountOperation" name="prettyPrintCount Operation"
implementationRef="counter:prettyPrintCount">
<inMessageRef>tns:prettyPrintCountRequestMessage</inMessageRef>
<outMessageRef>tns:prettyPrintCountResponseMessage</outMessageRef>
</operation>
</interface>
這樣就可以使用##WebService實現,聲明Web服務任務,並引用Web服務操作。
<serviceTask id="webService"
name="Web service invocation"
implementation="##WebService"
operationRef="tns:prettyPrintCountOperation">
參考:
【1】:Flowable BPMN 用戶手冊 (v 6.3.0)
【2】:第 3 章 BPMN 2.0
【3】:BPMN 2.0 / Flowable
【4】:業務流程模型和標記法
【5】:BPMN2.0十分鐘就夠了
【6】:基於BPMN2.0的工作流(Workflow)
【7】: AWS BPMN2 Event參考指南
【8】:【activiti 入門】activiti6.0的中間事件,包含信號事件(捕獲與拋出)
【9】:AWS BPMN2 Activity參考指南
【10】:AWS BPMN2 Gateway參考指南