第 3 章 BPMN 2.0

http://www.mossle.com/docs/jbpm4devguide/html/bpmn2.html

第 3 章 BPMN 2.0

3.1. 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的流程引擎中, 同時依然可以使用強大的圖形註解。

3.2. 歷史和目標

jBPM BPMN2的實現是在jBPM 4.0發佈之後 在2009年8月,在與社區進行了緊密協作之後啓動的。 而後,我們決定了第一個發佈版(比如,文檔/QA) 涉及一部分BPMN2規範,將在jBPM 4.3發佈。

我們的目標是建立一個原生BPMN2運行引擎 (或者說實現'可執行的BPMN2')基於流程虛擬機 (Process Virtual Machine - PVM)。 注意,這個版本的主要目標是原生可執行, 不是圖形註解 - 但是我們清楚 對於未來的版本是很重要的。

如果用戶已經瞭解了jBPM,就會發現

  • 配置結構保持不變
  • API與已經存在的完全一樣或者很類似
  • 測試BPMN2流程也可以使用常用的java測試框架
  • 數據庫表結構保持不變

所以,總體來說,我們的主要目標是保持所有在jBPM上好的事情, 加強它們,使用一個標準的流程語言。

 

3.3. JPDL vs BPMN 2.0

第一個問題可能是,很正當的,映入腦海的是, 爲什麼已經有了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上, 作爲一個擴展,不會出現很多麻煩。

3.4. Bpmn 2.0 執行

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結構分類:

  • 基本:這個分類的結構很直接 並且容易瞭解。這個分類的結構可以用來爲 簡單的業務流程建模。
  • 高級:包含更強大或更復雜的結構, 這些都提高了建模和執行語法的學習曲線。 業務流程的主要目標是使用這個 和之前的分類來實現結構。
  • 複雜:這個分類的結構用來實現罕見的情況, 或者它們的語法難以理解。

 

3.5. 配置

在你的應用中使用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後綴)。

3.6. 實例

發佈中包含的例子也包含了下面章節中 討論的每個結構的實例。查看BPMN 2.0的流程實例 和測試用例, 在org.jbpm.examples.bpmn.* 包下。

參考用戶指南,第二章(安裝),研究一下如何導入實例。 查看章節'導入實例'

3.7. 流程根元素

一個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一樣: 空格和非字母數字的字符會被下劃線代替。

3.8. 基本結構

3.8.1. 事件

與活動和網關一起,事件用來在實際的每個業務流程中。 事件讓業務建模工具用很自然的方式描述業務流程,比如 '當我接收到客戶的訂單,這個流程就啓動', '如果兩天內任務沒結束,就終止流程' 或者'當我收到一封取消郵件,當流程在運行時, 使用子流程處理郵件'。注意典型的業務 通常使用這種事件驅動的方式。人們不會硬編碼順序創建, 但是他們傾向於使用在他們的環境中發生的事情(比如,事件)。 在BPMN規範中,描述了很多事件類型,爲了覆蓋可能的事情, 在業務環境中可能出現的情況。

3.8.2. 事件:空啓動事件

一個啓動事件說明了流程的開始(或子流程)。圖形形式,它看起來 是一個圓(可能)內部有一個小圖標。圖標指定了事件的實際類型 會在流程實例創建時被觸發。

空啓動事件畫出來是一個圓,內部沒有圖標,意思是 這個觸發器是未知或者未指定的。jPDL的開始活動基本是一樣的語法。 流程實例的流程定義包含一個空啓動事件, 可以使用executionService的API調用創建。

一個空開始事件像下面這樣定義。id是必填的,name是可選的。

<startEvent id="start"  name="myStart" />
          

 

3.8.3. 事件:空結束事件

結束事件指定了流程實例中一個流程路徑的結束。 圖形上,它看起來就是一個圓 擁有厚邊框(可能) 內部有小圖標。 圖標指定了結束的時候 會執行哪種操作。

空結束事件畫出來是一個圓,擁有厚邊框,內部沒有圖標, 這意味着當流程到達事件時,不會拋出任何信號。 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");
          

 

3.8.4. 事件:終止結束事件

終止和空結束事件的區別是 實際中流程的路徑是如何處理的(或者使用BPMN 2.0的術語叫做token)。 終止結束事件會結束整個流程實例,而空結束事件只會結束當前流程路徑。 他們都不會拋出任何事情 當到達結束事件的時候。

一個終止結束事件可以像下面定義。id是必填的,name是可選的。

<endEvent id="terminateEnd" name="myTerminateEnd">
  <terminateEventDefinition/>
</endEvent>
          

 

終止結束事件被描繪成結束事件一樣(圓,厚邊框), 內部圖標時一個完整的圓。在下面的例子中,完成task1 會結束流程實例,當完成task2時只會結束到達結束事件 的流程路徑,只剩下task1打開。

 

參考jBPM發佈包中的實例, 單元測試和業務流程對應XML。

3.8.5. 順序流

順序流是事件,活動和網關之間的連線,顯示爲一條實線 帶有箭頭,在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' 屬性 來使用。

也要注意,默認順序流上的表達式會被忽略。

3.8.6. 網關

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屬性用在解析時 作爲約束條件對進入、外出順序流。所以使用這個屬性 會減低出錯的機會,當引用順序流時, 但不是必填的。

3.8.7. 網關:唯一網關

唯一網關表達了一個流程中的唯一決策。 會有一個外向順序流被使用,根據定義在 順序流中的條件。

對應的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中是完全合法的 (忽略名稱和聲明的條件)。

 

 

3.8.8. 網關:並行網關

並行網關用來切分或同步相關的進入或外出 順序流。

  • 並行網關擁有一個進入順序流的和多於一個的外出順序流 叫做'並行切分或 '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 任務會被激活。

 

 

3.8.9. 網關:包含網關

一個包含網關 - 也叫做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(獲得沒有一個表達式)。

 

 

3.8.10. 任務

一個任務表示工作需要被外部實體完成, 比如人工或自動服務。

重要的是注意BPMN語法的'task'與jPDL語法的區別。 在jPDL中,'task'的概念總是用在人工做一些事情的環境。 的那個流程引擎遇到jPDL中的task,它會創建一個task, 交給一些人的任務列表,然後它會進入等待狀態。然而在BPMN 2.0中, 這裏有很多任務類型,一些表示等待狀態(比如,User Task 一些表示自動活動(比如,Service Task。 所以小心不要混淆了任務的概念,在切換語言的時候。

任務被描繪成一個圓角矩形,一般內部包含文字。 任務的類型(用戶任務,服務任務,腳本任務,等等)顯示在矩形的左上角,用小圖標區別。 根據任務的類型, 引擎會執行不同的功能。

3.8.11. 任務:人工任務

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的用法)。

3.8.12. 任務:Java服務任務

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和一個可選的 nameimplementation元素 是用來表示調用服務的類型。可選值是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的實際流程和單元測試。

3.8.13. 任務:腳本任務

腳本任務時一個自動活動,當到達這個任務的時候 流程引擎會執行一個腳本。腳本任務使用方式如下:

<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之外,還允許指定 scriptLanguagescript。 因爲我們使用了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
        

 

3.8.14. 任務:手工任務

 

 

手工任務時一個由外部人員執行的任務,但是沒有指定是 一個BPM系統或是一個服務會被調用。在真實世界裏,有很多例子: 安裝一個電話系統,使用定期郵件發送一封信, 用電話聯繫客戶,等等。

<manualTask id="myManualTask" name="Call customer" />
         

 

手工任務的目標更像 文檔/建模提醒的,因爲它 對流程引擎的運行沒有任何意義,因此,當流程引擎遇到一個手工任務時 會簡單略過。

3.8.15. 任務:java接收任務

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());
        

 

3.9. 高級結構

3.9.1. 內嵌子流程

子流程的第一目的是實現流程的“繼承”,意味着 設計者可以創建多個不同“級別”的細節。頂級視圖理解爲做 一件事情的最高級別方式,最低的級別 就關注具體細節。

比如下面的圖形。在這個模型裏,只有最高級的步驟顯示出來。 實際的實現"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規範,一個沒有外向順序流的活動會隱式結束當前分支。 然而當前,必須特別指定一個結束事件 在子流程中,來結束一個分支, 這會在未來的規範兼容過程中加強。

3.9.2. 定時啓動事件

定時啓動事件用來表示流程需要在指定時間啓動。 可以指定一個特殊的時間點(比如,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獲取, 就像什麼也沒有發生一樣)。
  • 當一個擁有定時啓動事件的流程定義發佈新版本時, 舊版本的定時啓動事件的任務會被從系統中刪除。這意味着 只有最新版本的流程定義會被使用 來創建一個流程實例。

 

3.9.3. 中間事件

中間事件用來表示在流程執行過程中發生的事件(比如, 在流程啓動之後,在它完成之前)。中間事件看起來就像 一個有着雙邊線的圓圈,圓圈中的圖標表示了事件的類型。

這兒有好多種中間事件類型,比如定時器事件,觸發事件,傳播事件,等等。 中間事件既可以拋出也可以捕獲:

  • 拋出:當一個流程到達事件中, 它會立刻觸發一個對應的觸發器(一個激活,一個錯誤,等等)。 拋出事件用圖形表示起來就是使用黑色填充的圖標。
  • 捕獲:當一個流程到達事件中, 它會等待一個對應的觸發器發生(一個錯誤,一個定時器,等等)。 捕獲事件用圖形表示起來就是沒有使用黑色填充的圖標(比如,內部是白色的)。

 

3.9.4. 內部捕獲事件:定時器

內部定時器事件用來表示一個流程的延遲。 直接的用例是收集數據, 只在沒有人工作的晚上執行大量的邏輯,等等。

注意,一個內部定時器只能是一個捕獲事件(拋出一個定時器事件時沒有意義的)。 下面的圖形中演示了內部定時器事件的圖形形式。

定義一個內部定時器事件,在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>
                     

     

 

3.10. 完全的實例(包括控制檯任務表單)

前提條件:爲了運行實例,我們假設 已經在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流程中。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章