轉載地址:http://www.mossle.com/docs/jbpm4devguide/html/bpmn2.html
第 3 章 BPMN 2.0
業務流程模型註解(Business Process Modeling Notation - BPMN)是 業務流程模型的一種標準圖形註解。這個標準 是由對象管理組(Object Management Group - OMG)維護的。
基本上,BPMN規範定義了任務看起來怎樣的,哪些結構可以 與其他進行連接,等等。這就意味着 意思不會被誤解。
標準的早期版本(1.2版以及之前)僅僅限制在模型上, 目標是在所有的利益相關者之間形成通用的理解, 在文檔,討論和實現業務流程之上。 BPMN標準證明了它自己,現在市場上許多建模工具 都使用了BPMN標準中的元素和結構。 實際上,現在的jPDL設計器也使用了 BPMN元素。
BPMN規範的2.0版本,當前已經處於最終階段了, 已經計劃不就就會完成,允許添加精確的技術細節 在BPMN的圖形和元素中, 同時制定BPMN元素的執行語法。 通過使用XML語言來指定業務流程的可執行語法, BPMN規範已經演變爲業務流程的語言, 可以執行在任何兼容BPMN2的流程引擎中, 同時依然可以使用強大的圖形註解。
jBPM BPMN2的實現是在jBPM 4.0發佈之後 在2009年8月,在與社區進行了緊密協作之後啓動的。 而後,我們決定了第一個發佈版(比如,文檔/QA) 涉及一部分BPMN2規範,將在jBPM 4.3發佈。
我們的目標是建立一個原生BPMN2運行引擎 (或者說實現’可執行的BPMN2’)基於流程虛擬機 (Process Virtual Machine - PVM)。 注意,這個版本的主要目標是原生可執行, 不是圖形註解 - 但是我們清楚 對於未來的版本是很重要的。
如果用戶已經瞭解了jBPM,就會發現
- 配置結構保持不變
- API與已經存在的完全一樣或者很類似
- 測試BPMN2流程也可以使用常用的java測試框架
- 數據庫表結構保持不變
所以,總體來說,我們的主要目標是保持所有在jBPM上好的事情, 加強它們,使用一個標準的流程語言。
第一個問題可能是,很正當的,映入腦海的是, 爲什麼已經有了jPDL還要實現BPMN2。它們兩個語言 的目標都是定義可執行的業務流程。從高層次來看, 兩個語言是等效的。主要的區別是 BPMN2是“廠商中立”的,你可以使用標準, 而jPDL是綁定在jBPM上的(雖然會有一些爭論 綁定在開源語言廠商比如jPDL 和綁定在閉源產品)。
在jBPM中,兩個語言實現都是建立在jBPM流程虛擬機上的 (PVM)。這意味着兩個語言共享通用功能 (持久化,事務,配置,也有基本流程結構,等等)。 結果就是,對jBPM核心的優化 會對兩個語言有益。依靠PVM,BPMN2實現 建立在基礎上,已經在過去證明了它自己, 並擁有了很大的最終用戶社區。
當執行語言,把它們相互比較的時候, 下面幾點必須納入考慮:
- BPMN2是基於被BPM工業接受的一個標準。
- BPMN2是與實現無關的。這一點的缺點是集成java技術 jPDL總會更早。 所以,從java開發者的角度,jPDL更簡單,感覺更自然 (一些BPEL/WSDL的“層次”也在BPMN中)。
- jPDL的一個目標是XML可讀,BPMN2流程在 一定程度上也是可讀的,但是工具和更多規範的細節 會要求實現同等級的 生產力。
- java開發者可以很快學會jPDL,因爲他們很瞭解jPDL語言, 會發現實用工具有時候很麻煩, 語言本身也過於複雜了。
- BPMN2包含一個很大的描述結構的集合,在規範中。 然而,對接口代碼的綁定在規範中是開放的 (與XPDL相比),即使WSDL通常會被默認使用。 這意味着流程的可移植性喪失了, 當我們把流程移植到一個引擎上,而這個引擎不支持同樣的綁定機制。 比如,調用java類通常是jBPM的默認實現 的綁定方式。
很自然的,因爲政治原因,BPMN2規範發展的會比較慢。 jPDL就可以快速變化,和新技術進行集成, 當他們發佈的時候, 與BPMN2相比可以加快步伐進行演化。 當然,因爲兩個都建立在同一個PVM上,jPDL中的邏輯 也可以一直到BPMN2上, 作爲一個擴展,不會出現很多麻煩。
BPMN2規範定義了非常豐富的語言,爲建模和執行業務流程。 然而,也意味着它非常困難總覽BPMN2可能是怎樣 爲了簡化這種情況,我們決定把 BPMN2結構分爲三個等級。 區分的方式主要基於Bruce Silver寫的 ‘BPMN method and Style’這本書(http://www.bpmnstyle.com/), Dr. Jim Arlow的培訓資料( http://www.slideshare.net/jimarlow/introductiontobpmn005), ‘How much BPMN do you need’( http://www.bpm-research.com/2008/03/03/how-much-bpmn-do-you-need/), 和我們自己的經驗。
我們定義了三種BPMN2結構分類:
- 基本:這個分類的結構很直接 並且容易瞭解。這個分類的結構可以用來爲 簡單的業務流程建模。
- 高級:包含更強大或更復雜的結構, 這些都提高了建模和執行語法的學習曲線。 業務流程的主要目標是使用這個 和之前的分類來實現結構。
- 複雜:這個分類的結構用來實現罕見的情況, 或者它們的語法難以理解。
在你的應用中使用BPMN 2.0是很簡單的:只要把下面一行 加入jbpm.cfg.xml文件。
<import resource="jbpm.bpmn.cfg.xml" />
這裏的引用會啓用BPMN 2.0的流程發佈,通過把BPMN 2.0發佈器安裝到流程引擎中。 注意流程引擎可以同時使用jPDL和BPMN 2.0流程。 這意味着在你的應用裏,一些流程可能是jPDL, 其他的可能是BPMN 2.0。
流程引擎是根據定義文件的後綴來區分流程定義的。 對於BPMN 2.0,使用*.bpmn.xml後綴 (jPDL使用*.jpdl.xml後綴)。
發佈中包含的例子也包含了下面章節中 討論的每個結構的實例。查看BPMN 2.0的流程實例 和測試用例, 在org.jbpm.examples.bpmn.* 包下。
參考用戶指南,第二章(安裝),研究一下如何導入實例。 查看章節‘導入實例’。
一個BPMN 2.0 XML流程的根是definitions元素。 在命名狀態,子元素會包含真正的業務流程定義。 每個process子元素 可以擁有一個id(必填)和 name(可選)。一個空的BPMN 2.0業務流程 看起來像下面這樣。也要注意把BPMN2.xsd放在classpath下, 來啓用XML自動補全。
<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"> ... </process> <definitions>
如果爲process元素定義了name,它會被用做流程的key。 (比如,啓動一個流程可以通過調用executionService.startProcessInstanceByKey(“myBusinessProcess”)。 如果沒有指定name,id會被用做key。所以只有id定義時, 會允許通過id來啓動一個流程實例。所以基本上name和key在使用上是等價的,比如搜索流程定義。 注意key的規則與jPDL一樣: 空格和非字母數字的字符會被下劃線代替。
與活動和網關一起,事件用來在實際的每個業務流程中。 事件讓業務建模工具用很自然的方式描述業務流程,比如 ‘當我接收到客戶的訂單,這個流程就啓動’, ‘如果兩天內任務沒結束,就終止流程’ 或者‘當我收到一封取消郵件,當流程在運行時, 使用子流程處理郵件’。注意典型的業務 通常使用這種事件驅動的方式。人們不會硬編碼順序創建, 但是他們傾向於使用在他們的環境中發生的事情(比如,事件)。 在BPMN規範中,描述了很多事件類型,爲了覆蓋可能的事情, 在業務環境中可能出現的情況。
一個啓動事件說明了流程的開始(或子流程)。圖形形式,它看起來 是一個圓(可能)內部有一個小圖標。圖標指定了事件的實際類型 會在流程實例創建時被觸發。
空啓動事件畫出來是一個圓,內部沒有圖標,意思是 這個觸發器是未知或者未指定的。jPDL的開始活動基本是一樣的語法。 流程實例的流程定義包含一個空啓動事件, 可以使用executionService的API調用創建。
一個空開始事件像下面這樣定義。id是必填的,name是可選的。
<startEvent id="start" name="myStart" />
結束事件指定了流程實例中一個流程路徑的結束。 圖形上,它看起來就是一個圓 擁有厚邊框(可能) 內部有小圖標。 圖標指定了結束的時候 會執行哪種操作。
空結束事件畫出來是一個圓,擁有厚邊框,內部沒有圖標, 這意味着當流程到達事件時,不會拋出任何信號。 jPDL中的結束事件與空結束事件語義相同。
空結束事件可以像下面一樣定義,id是必填的,name是可選的。
<endEvent id="end" name="myEnd" />
下面的例子顯示了只使用空開始和結束事件的流程:
這個流程對應的可執行XML像這樣 (忽略聲明用的definitions根元素)
<process id="noneStartEndEvent" name="BPMN2 Example none start and end event"> <startEvent id="start" /> <sequenceFlow id="flow1" name="fromStartToEnd" sourceRef="start" targetRef="end" /> <endEvent id="end" name="End" /> </process>
現在可以通過調用startProcessInstanceXXX操作, 創建一個流程實例。
ProcessInstance processInstance = executionService.startProcessInstanceByKey("noneStartEndEvent");
終止和空結束事件的區別是 實際中流程的路徑是如何處理的(或者使用BPMN 2.0的術語叫做token)。 終止結束事件會結束整個流程實例,而空結束事件只會結束當前流程路徑。 他們都不會拋出任何事情 當到達結束事件的時候。
一個終止結束事件可以像下面定義。id是必填的,name是可選的。
<endEvent id="terminateEnd" name="myTerminateEnd">
<terminateEventDefinition/>
</endEvent>
終止結束事件被描繪成結束事件一樣(圓,厚邊框), 內部圖標時一個完整的圓。在下面的例子中,完成task1 會結束流程實例,當完成task2時只會結束到達結束事件 的流程路徑,只剩下task1打開。
參考jBPM發佈包中的實例, 單元測試和業務流程對應XML。
順序流是事件,活動和網關之間的連線,顯示爲一條實線 帶有箭頭,在BPMN圖形中(jPDL中等效的是transition)。 每個順序流都有一個源頭和一個 目標引用,包含了 活動,事件或網關的id。
<sequenceFlow id="myFlow" name="My Flow" sourceRef="sourceId" targetRef="targetId" />
與jPDL的一個重要區別是多外向順序流的行爲。 在jPDL中,只有一個轉移會成爲外向轉移,除非活動是fork (或自定義活動擁有fork行爲)。然而,在BPMN中, 多外向順序流的默認行爲是切分進入的token(jBPM中術語叫做execution) 分成token集合,每個順序流一個。在下面情況中, 在完成第一個任務,就會激活三個任務。
爲了避免使用一個順序流,必須添加condition條件到順序流中。 在運行時,只有當condition條件結果爲true, 順序流纔會被執行。
爲了給順序流添加condition條件,添加一個conditionExpression 元素到順序流中。條件可以放在 ${}中。
<sequenceFlow id=....>
<conditionExpression xsi:type="tFormalExpression">${amount >= 500}</conditionExpression>
</sequenceFlow>
注意,當前必須把 xsi:type=”tFormalExpression”添加到 conditionExpression中。一個條件性的順序流可以看到一個小菱形圖片 在順序流的起點。記住表達式一直可以定義在順序流上, 但是一些結構不會解釋它(比如,並行網關)。
活動(比如用戶任務)和網關(比如唯一網關)可以用戶默認順序流。 默認順序流只會在活動或網關的 所有其他外向順序流的condition條件爲false時纔會使用。 默認順序流圖形像是順序流多了一個斜線標記。
默認順序流通過指定活動或網關的 ‘default’ 屬性 來使用。
也要注意,默認順序流上的表達式會被忽略。
BPMN中的網關是用來控制流程中的流向的。更確切的是, 當一個token(BPMN 2.0中execution的概念註解)到達一個網關, 它會根據網關的類型進行合併或切分。
網關描繪成一個菱形,使用一個內部圖標來指定類型 (唯一,廣泛,其他)。
所有網關類型,都可以設置gatewayDirection。 下面的值可以使用:
- unspecificed (默認):網關可能擁有多個 進入和外出順序流。
- mixed:網關必須擁有多個 進入和外出順序流。
- converging:網關必須擁有多個進入順序流, 但是只能有一個外出順序流。
- diverging:網關必須擁有一個進入順序流, 和多個外出順序流。
比如下面的例子:並行網關的gatewayDirection屬性爲’converging’, 會擁有json行爲。
<parallelGateway id="myJoin" name="My synchronizing join" gatewayDirection="converging" />
注意:gatewayDirection屬性根據規範是可選的。 這意味着我們不能通過這個屬性來 在運行時知道一個網關的行爲(比如,一個並行網關, 如果我們用夠切分和合並行爲)。然而,gatewayDirection屬性用在解析時 作爲約束條件對進入、外出順序流。所以使用這個屬性 會減低出錯的機會,當引用順序流時, 但不是必填的。
唯一網關表達了一個流程中的唯一決策。 會有一個外向順序流被使用,根據定義在 順序流中的條件。
對應的jPDL結構,相同的語法是 decision活動。唯一網關的 完全技術名稱是‘基於數據的唯一網關’, 但是也經常稱爲XOR 網關。 XOR網關被描繪爲一個菱形,內部有一個’X’, 一個空的菱形,沒有網關也象徵着唯一網關。
下面圖形顯示了唯一網關的用法:根據amount變量的值, 會選擇唯一網關外向的三個外向順序流 中的一個。
這個流程對應的可執行XML看起來像下面這樣。 注意定義在順序流中的條件。唯一網關會選擇一個順序流, 如果條件執行爲true。如果多個條件 執行爲true,第一個遇到的就會被使用 (日誌信息會顯示這種情況)。
<process id="exclusiveGateway" name="BPMN2 Example exclusive gateway"> <startEvent id="start" /> <sequenceFlow id="flow1" name="fromStartToExclusiveGateway" sourceRef="start" targetRef="decideBasedOnAmountGateway" /> <exclusiveGateway id="decideBasedOnAmountGateway" name="decideBasedOnAmount" /> <sequenceFlow id="flow2" name="fromGatewayToEndNotEnough" sourceRef="decideBasedOnAmountGateway" targetRef="endNotEnough"> <conditionExpression xsi:type="tFormalExpression"> ${amount < 100} </conditionExpression> </sequenceFlow> <sequenceFlow id="flow3" name="fromGatewayToEnEnough" sourceRef="decideBasedOnAmountGateway" targetRef="endEnough"> <conditionExpression xsi:type="tFormalExpression"> ${amount <= 500 && amount >= 100} </conditionExpression> </sequenceFlow> <sequenceFlow id="flow4" name="fromGatewayToMoreThanEnough" sourceRef="decideBasedOnAmountGateway" targetRef="endMoreThanEnough"> <conditionExpression xsi:type="tFormalExpression"> ${amount > 500} </conditionExpression> </sequenceFlow> <endEvent id="endNotEnough" name="not enough" /> <endEvent id="endEnough" name="enough" /> <endEvent id="endMoreThanEnough" name="more than enough" /> </process>
這個流程需要一個變量,這樣表達式就可以在運行期間執行。 變量可以被提供,當流程實例執行的時候(類似jPDL)。
Map<String, Object> vars = new HashMap<String, Object>(); vars.put("amount", amount); ProcessInstance processInstance = executionService.startProcessInstanceByKey("exclusiveGateway", vars);
唯一網關需要所有外向順序流上都定義條件。 對這種規則一種例外是默認順序流。 使用default 屬性來引用一個已存在的 順序流的id。這個順序流會被使用 當其他外向順序流的條件都執行爲false時。
<exclusiveGateway id="decision" name="decideBasedOnAmountAndBankType" default="myFlow"/>
<sequenceFlow id="myFlow" name="fromGatewayToStandard"
sourceRef="decision" targetRef="standard">
</sequenceFlow>
唯一網關可以同時實現匯聚和發散功能。這個邏輯很容易理解: 對於每個到達這個網關的分支流程,都會選擇一個外向順序流來繼續執行。 下面的圖形在BPMN 2.0中是完全合法的 (忽略名稱和聲明的條件)。
並行網關用來切分或同步相關的進入或外出 順序流。
- 並行網關擁有一個進入順序流的和多於一個的外出順序流 叫做‘並行切分或 ‘AND-split’。所有外出順序流都會 被並行使用。注意:像規範中定義的那樣, 外出順序流中的條件都會被忽略。
- 並行網關擁有多個進入順序流和一個外出順序流 叫做‘並行歸併’或 AND-join。所有進入順序流需要 到達這個並行歸併,在外向順序流使用之前。
並行網關像下面這樣定義:
<parallelGateway id="myParallelGateway" name="My Parallel Gateway" />
注意,gatewayDirection屬性可以被使用, 已獲得建模錯誤,在解析階段(參考上面)。
下面的圖形顯示了一個並行網關可以如何使用。在流程啓動後, ‘prepare shipment’ 和 ‘bill customer’用戶任務都會被激活。 並行網關被描繪爲一個菱形,內部圖標是一個十字, 對切分和歸併行爲都是一樣。
圖形對應的XML如下所示:
<process id="parallelGateway" name="BPMN2 example parallel gatewar"> <startEvent id="Start" /> <sequenceFlow id="flow1" name="fromStartToSplit" sourceRef="Start" targetRef="parallelGatewaySplit" /> <parallelGateway id="parallelGatewaySplit" name="Split" gatewayDirection="diverging"/> <sequenceFlow id="flow2a" name="Leg 1" sourceRef="parallelGatewaySplit" targetRef="prepareShipment" /> <userTask id="prepareShipment" name="Prepare shipment" implementation="other" /> <sequenceFlow id="flow2b" name="fromPrepareShipmentToJoin" sourceRef="prepareShipment" targetRef="parallelGatewayJoin" /> <sequenceFlow id="flow3a" name="Leg 2" sourceRef="parallelGatewaySplit" targetRef="billCustomer" /> <userTask id="billCustomer" name="Bill customer" implementation="other" /> <sequenceFlow id="flow3b" name="fromLeg2ToJoin" sourceRef="billCustomer" targetRef="parallelGatewayJoin" /> <parallelGateway id="parallelGatewayJoin" name="Join" gatewayDirection="converging"/> <sequenceFlow id="flow4" sourceRef="parallelGatewayJoin" targetRef="End"> </sequenceFlow> <endEvent id="End" name="End" /> </process>
一個並行網關(其實是任何網關)可以同時擁有切分和匯聚行爲。 下面的圖形在BPMN 2.0中是完全合法的。 在流程啓動之後,A和B任務都會激活。當A和B完成時,C,D和E 任務會被激活。
一個包含網關 - 也叫做OR-gateway - 被用來 進行“條件性”切分或匯聚順序流。它基本的行爲就和一個並行網關一樣, 但是它也可以統計條件,在外出順序流上(切分行爲) 和計算,如果這兒有流程離開,可以到達網關(合併行爲)。
包含網關顯示爲一個典型的網關圖形,裏邊有一個圓圈(參考’OR’的語法)。 和唯一網關不同,所有條件表達式被執行(發散或切分行爲)。 對於每個表達式結果爲true時,一個新的子流程分支就會被創建。 沒有定義條件的順序流會永遠被選擇(比如。一個子流程 在這種情況下總是會被創建)。
一個收斂的包含網關(合併行爲)有一個更困難的執行邏輯。 當一個執行(在BPMN 2.0的語法中叫做Token)到達一個合併包含網關。 就會進行下面的檢測(引用規範的文字):
對於每個空的進入順序流,這裏沒有Token 在順序流的圖形上面,比如,這裏有一個沒有直接的路徑 (由順序流組成)從Token到這個順序流,除非 a) 路徑到達了一個包含網關,或 b) 路徑到達了一個節點,直接到一個非空的 進入順序流的包含網關 "
簡單來說:當一個流程到達了這個網關,所有的激活流程會被檢測 它們是否可以到達包含網關,只是統計順序流 (注意:條件不會被執行!)。當包含網關被使用時, 它通常用在一個切分/匯聚包含網關對中。在其他情況, 流程行爲足夠簡單,只要通過看圖就可以理解了。
當然,不難想象情況,當流程切分和匯聚在複雜的組合, 使用大量的結構,其中包括包含網關。 在那些情況,很可能出現實際的流程行爲可能 與建模者的期望不符。所以,當使用包含網關時,要注意 通常的最佳實踐是讓包含網關成對使用。
下面的圖形演示瞭如何使用包含網關。 (例子來自於Bruce Silver的”BPMN method and style”)
我們可以區分下面的情況:
- 現金多於10000,不是國外銀行:只有 “Large deposit” 任務會被激活。
- 現金多於10000,是國外銀行: “Large deposit” 和 “Foreign deposit” 任務會被激活。
- 現金少於10000,是國外銀行: 只有 “Foreign deposit” 任務會被激活。
- 現金少於10000,不是國外銀行: 在這種情況 所有表達式的結果都是false,默認的順序流會被選擇。 在這個例子中國,這意味着”Standard deposit”任務會被激活。
無論在包含網關之後多少任務被激活,右側的收斂包含網關會等到 左側的包含網關所有外向順序流 到達合併網關(有時,只有一個,有時兩個)。 看一下org.jbpm.examples.bpmn.gateway.inclusive.InclusiveGatewayTest 可以看到在單元測試中是如何反應這個例子的。
這個例子的XML版本看起來像下面:
<process id="inclusiveGateway" name="BPMN2 Example inclusive gateway"> <startEvent id="start" /> <sequenceFlow id="flow1" sourceRef="start" targetRef="inclusiveGatewaySplit" /> <inclusiveGateway id="inclusiveGatewaySplit" default="flow3"/> <sequenceFlow id="flow2" sourceRef="inclusiveGatewaySplit" targetRef="largeDeposit"> <conditionExpression xsi:type="tFormalExpression">${cash > 10000}</conditionExpression> </sequenceFlow> <sequenceFlow id="flow3" sourceRef="inclusiveGatewaySplit" targetRef="standardDeposit" /> <sequenceFlow id="flow4" sourceRef="inclusiveGatewaySplit" targetRef="foreignDeposit"> <conditionExpression xsi:type="tFormalExpression">${bank == 'foreign'}</conditionExpression> </sequenceFlow> <userTask id="largeDeposit" name="Large deposit" /> <sequenceFlow id="flow5" sourceRef="largeDeposit" targetRef="inclusiveGatewayMerge" /> <userTask id="standardDeposit" name="Standard deposit" /> <sequenceFlow id="flow6" sourceRef="standardDeposit" targetRef="inclusiveGatewayMerge" /> <userTask id="foreignDeposit" name="Foreign deposit" /> <sequenceFlow id="flow7" sourceRef="foreignDeposit" targetRef="inclusiveGatewayMerge" /> <inclusiveGateway id="inclusiveGatewayMerge" /> <sequenceFlow id="flow8" sourceRef="inclusiveGatewayMerge" targetRef="theEnd" /> <endEvent id="theEnd" /> </process>
和其他網關類型一樣,包含網關類型可以同時擁有合併和切分行爲。 在這種情況下,包含網關將先等到所有分支流程到達, 在位每個順序流進行再次切分之前,這裏會有一個表達式執行 爲true(獲得沒有一個表達式)。
一個任務表示工作需要被外部實體完成, 比如人工或自動服務。
重要的是注意BPMN語法的’task’與jPDL語法的區別。 在jPDL中,’task’的概念總是用在人工做一些事情的環境。 的那個流程引擎遇到jPDL中的task,它會創建一個task, 交給一些人的任務列表,然後它會進入等待狀態。然而在BPMN 2.0中, 這裏有很多任務類型,一些表示等待狀態(比如,User Task 一些表示自動活動(比如,Service Task。 所以小心不要混淆了任務的概念,在切換語言的時候。
任務被描繪成一個圓角矩形,一般內部包含文字。 任務的類型(用戶任務,服務任務,腳本任務,等等)顯示在矩形的左上角,用小圖標區別。 根據任務的類型, 引擎會執行不同的功能。
user task是典型的’人工任務’, 實際中的每個workflow或BPMN軟件中都可以找到。當流程執行到達這樣一個user task時, 一個新人工任務就會被創建,交給用戶的任務列表。
和manual task的主要區別是 (也與人工工作對應)是流程引擎瞭解任務。 引擎可以跟蹤競爭,分配,時間,其他,這些不是manual task的情況。
user task描繪爲一個圓角矩形,在左上角是一個小用戶圖標。
user task被定義爲下面的BPMN 2.0 XML:
<userTask id="myTask" name="My task" />
根據規範,可以使用多種實現(WebService, WS-humantask,等等)。 通過使用implementation屬性。 當前,只有標準的jBPM任務機制纔可以用,所以這裏(還)沒有 定義’implementation’屬性的功能。
BPMN 2.0規範包含了一些方法把任務分配給用戶,組,角色等等。 當前的BPMN 2.0 jBPM實現允許使用一個 resourceAssignmentExpression來分配任務, 結合humanPerformer or PotentialOwner結構。 這部分希望在未來的版本里能夠進一步演化。
potentialOwner用來在你希望確定用戶,組,角色的時候。 這是一個task的候選人。 參考下面的例子。這裏的’My task’任務的候選人組是’management’用戶組。 也要注意,需要在流程外部定義一個資源, 這樣任務分配器可以引用到這個資源。 實際上,任何活動都可以引用一個或多個資源元素。 目前,只需要定義這個資源就可以了(因爲它是規範中的一個必須的元素), 但是在以後的發佈中會進行加強(比如,資源可以擁有運行時參數)。
<resource id="manager" name="manager" /> <process ...> ... <userTask id="myTask" name="My task"> <potentialOwner resourceRef="manager" jbpm:type="group"> <resourceAssignmentExpression> <formalExpression>management</formalExpression> </resourceAssignmentExpression> </potentialOwner> </userTask>
注意,我們使用了一個特定的後綴 (jbpm:type=”group”),來定義這是一個用戶組的分配方式。 如果刪除了這個屬性,就會默認使用用戶組的語法 (在這個例子中也是沒問題的)。 現在假設Peter和Mary是management組的成員 (這裏使用默認的身份服務):
identityService.createGroup("management"); identityService.createUser("peter", "Peter", "Pan"); identityService.createMembership("peter", "management"); identityService.createUser("mary", "Mary", "Littlelamb"); identityService.createMembership("mary", "management");
Peter和Mary都可以在他們的任務列表中看到這條任務 (代碼來自實例單元測試):
// Peter and Mary are both part of management, so they both should see the task
List<Task> tasks = taskService.findGroupTasks("peter");
assertEquals(1, tasks.size());
tasks = taskService.findGroupTasks("mary");
assertEquals(1, tasks.size());
// Mary claims the task
Task task = tasks.get(0);
taskService.takeTask(task.getId(), "mary");
assertNull(taskService.createTaskQuery().candidate("peter").uniqueResult());
taskService.completeTask(task.getId());
assertProcessInstanceEnded(processInstance);
當分配方式應該是候選用戶時, 只需要使用jbpm:type=”user”屬性。
<userTask id="myTask" name="My User task">
<potentialOwner resourceRef="employee" jbpm:type="user">
<resourceAssignmentExpression>
<formalExpression>peter</formalExpression>
</resourceAssignmentExpression>
</potentialOwner>
</userTask>
在這個例子裏,Peter將可以看到任務,因爲他是這個任務的候選用戶。
List<Task> tasks = taskService.createTaskQuery().candidate("peter").list();
human performer用來,當你想把一個任務直接分配給一個人, 組,角色時。這個方法的使用方式 看起來和potential owner很像。
<resource id="employee" name="employee" />
<process ...>
...
<userTask id="myTask" name="My User task">
<humanPerformer resourceRef="employee">
<resourceAssignmentExpression>
<formalExpression>mary</formalExpression>
</resourceAssignmentExpression>
</humanPerformer>
</userTask>
在這個例子中,任務會直接分配給Mary。 她可以在自己的任務列表中看到這個任務:
List<Task> tasks = taskService.findPersonalTasks("mary");
因爲任務分配已經完成,通過使用 formalExpression,它也可以定義表達式 在運行期解析。表達式本身需要放在 ${}中,這和jBPM一樣。 比如,如果流程變量’user’被定義了,然後,它可以用在表達式中。 當然也可以使用更復雜的表達式。
<userTask id="myTask" name="My User task">
<humanPerformer resourceRef="employee">
<resourceAssignmentExpression>
<formalExpression>${user}</formalExpression>
</resourceAssignmentExpression>
</humanPerformer>
</userTask>
注意不需要在humanPerformer元素中使用’jbpm:type’,因爲只能進行 直接用戶分配。如果任務需要被分配給一個角色或一個組, 使用potentialOwner和group類型(當你把任務分配給一個組時, 組中的所有成員都會成爲候選用戶 - 參考potentialOwner的用法)。
Service Task是一個自動活動,它會調用一些服務, 比如web service,Java service等等。當前jBPM引擎 只支持調用java service,但是web service的調用 已經在未來的版本中做了計劃。
定義一個服務任務需要好幾行XML(這裏就可以看到BPEL的影響力)。 當然,在不久的未來,我們希望有工具可以把這部分大量的簡化。 一個服務任務需要如下定義:
<serviceTask id="MyServiceTask" name="My service task" implementation="Other" operationRef="myOperation" />
服務任務需要一個必填的id和一個可選的 name。implementation元素 是用來表示調用服務的類型。可選值是WebService, Other或者Unspecified。 因爲我們只實現了Java調用, 現在只能選擇Other。
服務任務將調用一個操作,operation的id 會在operationRef屬性中引用。 這樣一個操作就是下面實例的 interface的一部分。每個操作都至少有一個 輸入信息,並且 最多有一個輸出信息。
<interface id="myInterface" name="org.jbpm.MyJavaServicek"> <operation id="myOperation2" name="myMethod"> <inMessageRef>inputMessage</inMessageRef> <outMessageRef>outputMessage</outMessageRef> </bpmn:operation> </interface>
對於java服務,接口的名稱用來 指定java類的全類名。操作的名稱 用來指定將要調用方法名。 輸入/輸出信息表示着java方法的參數/返回值, 定義如下所示:
<message id="inputMessage" name="input message" structureRef="myItemDefinition1" />
BPMN中很多元素叫做’item感知’,包括這個消息結構。 這意味着它們會在流程執行過程中保存或讀取item。 負責這些元素的數據結構需要使用ItemDefinition。 在這個環境下,消息指定了它的數據結構, 通過引用 structureRef屬性中定義的ItemDefinition。
<itemDefinition id="myItemDefinition1" > <jbpm:arg> <jbpm:object expr="#{var1}" /> </jbpm:arg> </itemDefinition> <itemDefinition id="myItemDefinition2"> <jbpm:var name="returnVar" /> </itemDefinition>
注意,這寫不是標準的BPMN 2.0標準(因此都有’jbpm’的前綴)。 實際上,根據標準,ItemDefinition不應該包含多餘一個數據結構定義。 實際在輸入參數的映射,使用一個數據結構, 在serviceTask的ioSpecification章節已經完成了。 然而,當前jBPM BPMN 2.0實現還沒有實現那個結構。 所以,這意味着當前使用的上面這種方法, 很可能在不久的未來就會出現變化。
重要提醒:接口,ItemDefinitions和消息需要定義在 <process>外邊。參考實例 ServiceTaskTest的實際流程和單元測試。
腳本任務時一個自動活動,當到達這個任務的時候 流程引擎會執行一個腳本。腳本任務使用方式如下:
<scriptTask id="scriptTask" name="Script Task" scriptLanguage="bsh"> <script><![CDATA[ for(int i=0; i < input.length; i++){ System.out.println(input[i] + " x 2 = " + (input[i]*2)); }]]> </script> </scriptTask>
腳本任務,除了必填id和可選的 name之外,還允許指定 scriptLanguage和script。 因爲我們使用了JSR-223(java平臺的腳本語言),修改腳本語言就需要:
- 把scriptLanguage 屬性修改爲JSR-223兼容的名稱
- 在classpath下添加JSR規範的ScriptEngine實現
上面的XML對應圖形如下所示(添加了空開始和結束事件)。
像上面例子中顯示的那樣,可以在腳本中使用流程變量。 我們現在可以啓動一個這個例子的流程,也要提供一些隨機生成的輸入變量:
Map<String, Object> variables = new HashMap<String, Object>(); Integer[] values = { 11, 23, 56, 980, 67543, 8762524 }; variables.put("input", values); executionService.startProcessInstanceBykey("scriptTaskExample", variables);
在輸出控制檯裏,我們現在可以看到執行的執行的腳本:
11 x 2 = 22 23 x 2 = 46 56 x 2 = 112 980 x 2 = 1960 67543 x 2 = 135086 8762524 x 2 = 17525048
手工任務時一個由外部人員執行的任務,但是沒有指定是 一個BPM系統或是一個服務會被調用。在真實世界裏,有很多例子: 安裝一個電話系統,使用定期郵件發送一封信, 用電話聯繫客戶,等等。
<manualTask id="myManualTask" name="Call customer" />
手工任務的目標更像 文檔/建模提醒的,因爲它 對流程引擎的運行沒有任何意義,因此,當流程引擎遇到一個手工任務時 會簡單略過。
receive task是一個任務會等到外部消息的到來。 除了廣泛使用的web service用例,規範在其他環境中的使用也是一樣的。 web service用例還沒有實現, 但是receive task已經可以在java環境中使用了。
receive task顯示爲一個圓角矩形(和task圖形一樣) 在左上角有一個小信封的圖標。
在java環境中,receive task沒有其他屬性,除了id和name(可選), 行爲就像是一個等待狀態。爲了在你的業務流程中使用等待狀態, 只需要加入如下幾行:
<receiveTask id="receiveTask" name="wait" />
流程執行會在這樣一個receive task中等待。流程會使用 熟悉的jBPM signal methods來繼續執行。 注意,這些可能在未來改變,因爲’signal’ 在BPMN 2.0中擁有完全不同的含義。
Execution execution = processInstance.findActiveExecutionIn("receiveTask"); executionService.signalExecutionById(execution.getId());
子流程的第一目的是實現流程的“繼承”,意味着 設計者可以創建多個不同“級別”的細節。頂級視圖理解爲做 一件事情的最高級別方式,最低的級別 就關注具體細節。
比如下面的圖形。在這個模型裏,只有最高級的步驟顯示出來。 實際的實現”Check credit”步驟隱藏在 摺疊子流程中,這可能是最完美的級別 細節來討論業務流程,與最終用戶。
子流程的第二種主要功能是子流程”容器”作爲 事件的作用域。當一個事件在子流程中觸發時,獲取事件 在子流程的邊界上就會首先獲得這個事件。
定義在頂級流程的子流程被稱爲內嵌子流程。 上級流程中的所有流程數據也可以在子流程中使用。 下面的圖形演示了 上面模型的展開形式。
這部分的XML內容看起來像是這樣:
<process id="embeddedSubprocess"> <startEvent id="theStart" /> <sequenceFlow id="flow1" sourceRef="theStart" targetRef="receiveOrder" /> <receiveTask name="Receive order" id="receiveOrder" /> <sequenceFlow id="flow2" sourceRef="receiveOrder" targetRef="checkCreditSubProcess" /> <subProcess id="checkCreditSubProcess" name="Credit check"> ... </subProcess> <sequenceFlow id="flow9" sourceRef="checkCreditSubProcess" targetRef="theEnd" /> <endEvent id="theEnd" /> </process>
注意在子流程內部,事件,活動,任務的定義與頂級流程中是一樣的。 (因此在上面的XML例子中是三個”…”) 子流程只允許有一個空開始事件。
結論,一個內嵌子流程會像下面這樣運行:當一個流程執行到子流程, 一個子分支會被創建。子分支以後還可以創建其他子分支, 比如,當一個併發網關使用在子流程中。 子流程,只會在沒有任何活動的分支時纔會完成。 這時,上級流程會 繼續執行。
比如,在下面的圖形中,”Third task” 只會在”First task”和”Second task”都完成時纔會到達。 子流程的其中一個任務不會觸發子流程向下運行, 因爲另一個分支在子流程中還是活動的。
子流程可以擁有多個開始事件。這種情況下,多個並行分支就在流程中存在。 子流程完成的規則沒有改變: 子流程只有在所有並行分支都完成時 纔會結束。
內嵌子流程也是可以的。這時,流程可以分散成多個不同級別的細節。 這裏沒有對內嵌級別做任何限制。
實現提醒:按照BPMN2規範,一個沒有外向順序流的活動會隱式結束當前分支。 然而當前,必須特別指定一個結束事件 在子流程中,來結束一個分支, 這會在未來的規範兼容過程中加強。
定時啓動事件用來表示流程需要在指定時間啓動。 可以指定一個特殊的時間點(比如,2010年10月10日下午5點), 但是也可以用一個通常的時間(比如,每個週五的半夜)。
定時啓動事件看起來是在圓圈中有一個表的圖標。
使用定時啓動事件,要添加一個timerEventDefinition元素 在開始事件元素下面:
<startEvent name="Every Monday morning" id="myStart">
<timerEventDefinition/>
</startEvent>
可以使用下面的時間定義:
- timeDate: 指定一個固定時間, 這時定時器會觸發,流程會繼續。默認的時間格式是 “dd/MM/yyyy hh:mm:ss”。這是引擎範圍的,可以通過設置 配置中的jbpm.duedatetime.format屬性來改變。
<startEvent id="myStartEvent" > <timerEventDefinition> <timeDate>10/10/2099 00:00:00</timeDate> </timerEventDefinition> </startEvent>
注意,在使用固定事件時,流程只用在一個單獨的事件。 在流程實例創建之後,定時啓動事件不會再次觸發。 - timeCycle: 指定一個延遲時間段, 相對於流程進入定時器事件時。可以用兩種定義方式:
時間段表達式:
quantity [business] {second | seconds | minute | minutes | hour | hours | day | days | week | weeks | month | months | year | years}
這與jPDL中的定時器時間段定義是完全相同的。注意, BPMN2定時啓動事件也可以理解”業務時間”。 這允許,比如定義一個”業務日期”作爲週期,從早九點到晚五點。 這樣,從下午5點到上午9點的時間就不會被計算, 當事件觸發的事件被計算的時候。 請參考jPDL用戶手冊,獲得更多信息,關於如何自定義業務日曆。 下面的例子演示了定時啓動事件會啓動給一個新流程實例 每隔5個小時。
<startEvent id="myStartEvent" > <timerEventDefinition> <timeCycle>5 hours</timeCycle> </timerEventDefinition> </startEvent>
Cron 表達式: 雖然時間段表達式已經很好的覆蓋了 延遲定義,有時,它們不太好用。 當,比如,一個流程實例應該在每個週五晚上23點執行, cron表達式允許一個更自然的方式來定義這種重複的行爲的發生。
下面的例子演示了定時啓動事件會在 每週五的23點啓動一個新的流程實例。
<startEvent id="myStartEvent" > <timerEventDefinition> <timeCycle>0 0 23 ? * FRI</timeCycle> </timerEventDefinition> </startEvent>
jBPM中實現的定時啓動事件也擁有如下的特性:
- 聲明瞭定時啓動事件的流程定義,也可以當做一個無啓動事件啓動。 這就是說,比如調用 executionService.startProcessInstanceByKey(key)也是可以的。
- 定時啓動事件的內部實現是一個定時任務。這意味着 必須配置job executor,定時啓動事件才能工作。 這種實現的優點是,定時啓動事件的觸發是事務性的 (比如,如果定時啓動事件後的一個服務任務失敗了, 事務就會回滾,定時啓動事件就會稍後執行) 並且可以應付服務器崩潰。(比如,當服務器備份時, 定時啓動事件會由job executor獲取, 就像什麼也沒有發生一樣)。
- 當一個擁有定時啓動事件的流程定義發佈新版本時, 舊版本的定時啓動事件的任務會被從系統中刪除。這意味着 只有最新版本的流程定義會被使用 來創建一個流程實例。
中間事件用來表示在流程執行過程中發生的事件(比如, 在流程啓動之後,在它完成之前)。中間事件看起來就像 一個有着雙邊線的圓圈,圓圈中的圖標表示了事件的類型。
這兒有好多種中間事件類型,比如定時器事件,觸發事件,傳播事件,等等。 中間事件既可以拋出也可以捕獲:
- 拋出:當一個流程到達事件中, 它會立刻觸發一個對應的觸發器(一個激活,一個錯誤,等等)。 拋出事件用圖形表示起來就是使用黑色填充的圖標。
- 捕獲:當一個流程到達事件中, 它會等待一個對應的觸發器發生(一個錯誤,一個定時器,等等)。 捕獲事件用圖形表示起來就是沒有使用黑色填充的圖標(比如,內部是白色的)。
內部定時器事件用來表示一個流程的延遲。 直接的用例是收集數據, 只在沒有人工作的晚上執行大量的邏輯,等等。
注意,一個內部定時器只能是一個捕獲事件(拋出一個定時器事件時沒有意義的)。 下面的圖形中演示了內部定時器事件的圖形形式。
定義一個內部定時器事件,在XML裏像是這樣:
<intermediateCatchEvent id="myTimer" name="Wait for an hour"> <timerEventDefinition> <timeCycle>1 hour</timeCycle> </timerEventDefinition> </intermediateCatchEvent>
有兩種方法可以來指定延遲,使用timeCycle 或 a timeDate。在上面例子中,使用的是 timeCycle 。
下面的延遲定義也是可以用的(這與啓動定時器是相同的)。
- timeDate: 指定一個固定時間, 這時定時器會觸發,流程會繼續。默認的時間格式是 “dd/MM/yyyy hh:mm:ss”。這是引擎範圍的,可以通過設置 配置中的jbpm.duedatetime.format屬性來改變。
<intermediateCatchEvent id="myTimer" > <timerEventDefinition> <timeDate>10/10/2099 00:00:00</timeDate> </timerEventDefinition> </intermediateCatchEvent>
- timeCycle: 指定一個延遲時間段, 相對於流程進入定時器事件時。可以用兩種定義方式:
時間段表達式:
quantity [business] {second | seconds | minute | minutes | hour | hours | day | days | week | weeks | month | months | year | years}
這與jPDL中的定時器時間段定義是完全相同的。注意, BPMN2的內部定時器事件也可以理解”業務時間”。 這允許,比如定義一個”業務日期”作爲週期,從早九點到晚五點。 定時器會從下午四點開始中間等待兩個小時,然後會在第二個工作日的上午十點觸發。 請參考jPDL用戶手冊,獲得更多信息, 關於如何自定義業務日曆。
<intermediateCatchEvent id="intermediateTimer" > <timerEventDefinition> <timeCycle>5 hours</timeCycle> </timerEventDefinition> </intermediateCatchEvent>
Cron 表達式: 雖然時間段表達式已經很好的覆蓋了 延遲定義,有時,它們不太好用。 當,比如,流程需要延遲到週五晚上23點,這樣流程可以在週末執行, 時間段表達式就難以使用了 (你需要一些東西,像”#{calculated_value} 秒”, 你可以先計算這個值)。
Cron 表達式 允許我們定義延遲,這種方式很多人都知道(因爲CRON表達式 在Unix中用來定義任務)。注意一個cron表達式 通常用來定義重複執行。在這個環境下,就是 第一個滿足cron表達式的時間點 用來設置定時器事件的持續時間(所以不會重複執行)。下面的例子 展示了一個內部定時器事件是如何執行流程 在下一個星期五晚上23點執行。
<intermediateCatchEvent id="intermediateTimer" > <timerEventDefinition> <timeCycle>0 0 23 ? * FRI</timeCycle> </timerEventDefinition> </intermediateCatchEvent>
前提條件:爲了運行實例,我們假設 已經在JBoss server中安裝了jBPM控制檯。如果沒有, 請先執行’demo.setup.jboss’安裝腳本。
我們實現的業務流程實現起來像下面這樣:
你可能已經看過這個例子了,因爲我們也在發佈包中的 實例中使用jPDL實現過它了。
業務流程很簡單:一個員工可以啓動一個新流程, 申請一定時間的假期。在請求任務完成之後, 經理會在任務列表中看到審覈任務。 經理可以決定批准或駁回這個申請。 根據outcome(那是外向順序流上的小菱形 - 這意味着在順序流上有條件表達式), 會發送一個駁回信息或者流程結束。注意,實際上我們這裏使用了簡寫: 不是在’verify request’任務的外向順序流上設置表達式, 我們可以在用戶任務之後使用一個唯一網關來控制流程的流向。 也要注意,因爲我們還沒有實現泳道(可能在下一個版本會實現), 所以很難看到誰在業務流程中。
流程的XML版本看起來像下面這樣:
<process id="vacationRequestProcess" name="BPMN2 Example process using task forms"> <startEvent id="start" /> <sequenceFlow id="flow1" name="fromStartToRequestVacation" sourceRef="start" targetRef="requestVacation" /> <userTask id="requestVacation" name="Request Vacation" implementation="other"> <potentialOwner resourceRef="user" jbpm:type="group"> <resourceAssignmentExpression> <formalExpression>user</formalExpression> </resourceAssignmentExpression> </potentialOwner> <rendering id="requestForm"> <jbpm:form>org/jbpm/examples/bpmn/usertask/taskform/request_vacation.ftl</jbpm:form> </rendering> </userTask> <sequenceFlow id="flow2" name="fromRequestVacationToVerifyRequest" sourceRef="requestVacation" targetRef="verifyRequest" /> <userTask id="verifyRequest" name="Verify Request" implementation="other"> <potentialOwner resourceRef="user" jbpm:type="group"> <resourceAssignmentExpression> <formalExpression>manager</formalExpression> </resourceAssignmentExpression> </potentialOwner> <rendering id="verifyForm"> <jbpm:form>org/jbpm/examples/bpmn/usertask/taskform/verify_request.ftl</jbpm:form> </rendering> </userTask> <sequenceFlow id="flow3" name="fromVerifyRequestToEnd" sourceRef="verifyRequest" targetRef="theEnd"> <conditionExpression xsi:type="tFormalExpression"> ${verificationResult == 'OK'} </conditionExpression> </sequenceFlow> <sequenceFlow id="flow4" name="fromVerifyRequestToSendRejectionMessage" sourceRef="verifyRequest" targetRef="sendRejectionMessage"> <conditionExpression xsi:type="tFormalExpression"> ${verificationResult == 'Not OK'} </conditionExpression> </sequenceFlow> <scriptTask id="sendRejectionMessage" name="Send rejection Message" scriptLanguage="bsh"> <script> <![CDATA[System.out.println("Vacation request refused!");]]> </script> </scriptTask> <sequenceFlow id="flow5" name="fromSendRejectionMessageToEnd" sourceRef="sendRejectionMessage" targetRef="theEnd" /> <endEvent id="theEnd" name="End" /> </process>
注意:當你在安裝demo時,自己都已經安裝了。 也要注意,我們這裏使用了腳本任務,爲了快速的編寫一些輸出, 而不是發送真實的信息(圖形顯示了一個service task)。 也要注意,我們這裏在任務分配中做了一些簡略 (會在下一個版本進行修復)。
在這個實現使用的結構中覆蓋了之前章節中的所有內容。 也要注意我們這裏使用了任務表單功能, 這是一個自定義jBPM擴展, 可以爲用戶任務渲染元素。
<userTask id="verifyRequest" name="Verify Request" implementation="other"> <potentialOwner resourceRef="user" jbpm:type="group"> <resourceAssignmentExpression> <formalExpression>user</formalExpression> </resourceAssignmentExpression> </potentialOwner> <rendering id="verifyForm"> <jbpm:form>org/jbpm/examples/bpmn/usertask/taskform/verify_request.ftl</jbpm:form> </rendering> </userTask>
BPMN 2.0裏任務表單的機制與jPDL裏完全一樣。 表單自身是一個Freemarker模板文件, 需要放在發佈中。比如,這個 ‘verify_request.ftl’ 看起來像下面這樣:
<html> <body> <form action="${form.action}" method="POST" enctype="multipart/form-data"> <h3>Your employee, ${employee_name} would like to go on vacation</h3> Number of days: ${number_of_days}<br/> <hr> In case you reject, please provide a reason:<br/> <input type="textarea" name="reason"/><br/> <input type="submit" name="verificationResult" value="OK"> <input type="submit" name="verificationResult" value="Not OK"> </form> </body> </html>
注意,流程變量可以使用 ${my_process_variable}來訪問。也要注意輸入控件的名稱。 (比如,輸入文本框,提交表單)可以用來 定義新流程變量。 比如,下面的輸入文本會被保存爲 流程變量’reason’。
<input type="textarea" name="reason"/>
注意這裏有兩個提交按鈕(這是當然的,如果你看到’OK’和’Not OK’兩個順序流 從’request vacation’任務裏出去了。通過點擊其中一個按鈕, 流程變量’verificationResult’會被保存起來。 它可以用來執行外出的順序流:
<sequenceFlow id="flow3" name="fromVerifyRequestToEnd"
sourceRef="verifyRequest" targetRef="theEnd">
<conditionExpression xsi:type="tFormalExpression">
${verificationResult == 'OK'}
</conditionExpression>
</sequenceFlow>
流程現在可以發佈了。你可以使用ant的發佈任務來做這些事情(參考實例), 或者你可以指定你的jBPM配置到控制檯的數據庫。 爲了用編程的方式發佈你的流程,你需要把任務表單添加到你的發佈中:
NewDeployment deployment = repositoryService.createDeployment(); deployment.addResourceFromClasspath("org/jbpm/examples/bpmn/usertask/taskform/vacationrequest.bpmn.xml"); deployment.addResourceFromClasspath("org/jbpm/examples/bpmn/usertask/taskform/request_vacation.ftl"); deployment.addResourceFromClasspath("org/jbpm/examples/bpmn/usertask/taskform/verify_request.ftl"); deployment.deploy();
你現在可以嵌入(或在單獨的服務器中)這個業務流程,使用熟悉的jBPM API操作。 比如,流程實例現在可以使用 key來啓動(比如,BPMN 2.0的流程id):
ProcessInstance pi = executionService.startProcessInstanceByKey("vacationRequestProcess");
任務列表可以這樣獲得:
Task requestTasktask = taskService.createTaskQuery().candidate("peter").uniqueResult();
當像jBPM控制器數據庫發佈時,你應該看到我們的新業務流程了。
在你啓動一個新流程後,一個新任務應該在員工的任務列表中了。 當點擊’view’以後,任務表單會被顯示出來, 在這裏填寫未來會在流程中使用的變量。
在任務結束之後,經理會在他的任務列表中看到新的審覈任務。 他現在可以通過或駁回請假申請,基於員工的輸入。
因爲數據庫表結構沒有變化,我們只是把BPMN 2.0添加到了jBPM PVM上面, 所有已存的報表都可以用於我們的新BPMN 2.0流程中。