程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> jBPM-4.0中文開發指南-第7章 高級圖形執行

jBPM-4.0中文開發指南-第7章 高級圖形執行

編輯:關於JAVA

第 7 章 高級圖形執行

7.1. 循環

活動可以實現循環,基於轉移或活動組合。 循環可以包含等待狀態。

為了支持多次自動循環執行,流程虛擬機 把執行的傳播從尾部遞歸轉換成while循環。

7.2. 子流程

TODO: 子流程

7.3. 默認執行行為

當一個Activity被用作活動行為, 它可以使用下面的方法從外部控制流程:

* waitForSignal()

* take(Transition)

* end(*)

* execute(Activity)

* createExecution(*)

當Activity實現用做活動行為, 沒有調用任何下面的流程傳播方法,然後 在活動執行時,執行會使用默認執行行為。

默認執行行為定義在下面:

* 如果當前活動有一個默認向外轉移,選擇它。

* 如果當前活動有一個父活動,回退到父活動。

* 否則,結束這個執行。

流程語言可以重寫默認執行行為, 通過重寫ExecutionImpl中的 proceed方法。

7.4. 功能活動

活動也可以用作事件監聽器,被稱作功能活動。 自動活動的例子是發送郵件,執行數據庫更新, 生成pdf,計算平均數,等等。 所有這些都是自動活動,沒有改變執行流向。 這裡是這些活動如何實現:

public class FunctionalActivity implements Activity, EventListener {
    public void execute(ActivityExecution execution) {
      perform(execution);
    }
    public void notify(EventListenerExecution execution) {
      perform(execution);
    }
    void perform(OpenExecution execution) {
      ...do functional work...
    }
  }

perform方法獲得一個OpenExecution, 這是ActivityExecution和 EventListenerExecution的超類。 OpenExecution沒有提供任何特定目的的方法, 但是依舊是當前狀態,流程定義可以通過變量檢驗, 這包含了環境信息 對應流程執行。

這些方法其實都不能調用執行傳播方法。 所以在perform方法完成後,執行會 執行默認的方式。

7.5. 執行和線程

這一章解釋流程虛擬機如何通過客戶端的線程, 把一個執行從一個等待狀態帶到另一個。

當一個客戶調用一個執行的一個方法(比如signal方法)。 默認,流程虛擬機會使用線程執行流程 直到它到達一個等待狀態。一旦下一個等待狀態到達, 這個方法會返回,客戶端的線程就會返回。 這是流程虛擬機操作的默認方式。 兩個更多的異步執行可以補充默認行為: 異步繼續 和異步命令服務。

下一個流程會展示基本理論。 它有三個等待狀態和四個自動活動。

有很多順序自動活動的流程。

圖 7.1. 有很多順序自動活動的流程。

這裡是如何構建流程:

ClientProcessDefinition processDefinition = ProcessFactory.build("automatic")
    .activity("wait 1").initial().behaviour(new WaitState())
      .transition().to("automatic 1")
    .activity("automatic 1").behaviour(new Display("one"))
      .transition().to("wait 2")
    .activity("wait 2").behaviour(new WaitState())
      .transition().to("automatic 2")
    .activity("automatic 2").behaviour(new Display("two"))
      .transition().to("automatic 3")
    .activity("automatic 3").behaviour(new Display("three"))
      .transition().to("automatic 4")
    .activity("automatic 4").behaviour(new Display("four"))
      .transition().to("wait 3")
    .activity("wait 3").behaviour(new WaitState())
.done();

讓我們和你一起順著流程的執行一起走。

ClientExecution execution = processDefinition.startProcessInstance();

啟動一個新執行意味著初始活動被執行。 所以如果一個自動活動是初始活動,這意味著第一個未命名的向外轉移會被立刻選擇。 這些都發生在startProcessInstance調用的內部。

然而在這種情況下,初始活動是一個等待狀態。 所以startProcessInstance方法會立刻返回, 執行會定位到初始活動'wait 1'.一個新執行會被定為到'wait 1'.

圖 7.2. 一個新執行會被定為到'wait 1'.

然後一個外部觸發器會執行signal方法。

execution.signal();

像上面解釋的介紹WaitState, signal會導致選擇默認的轉移。 轉移會把執行移動到automatic 1活動,並執行它。 automatic 1中的Display活動的execute方法, 向控制台打印一行,它不會 調用execution.waitForSignal()。 因此,執行會通過選擇automatic 1外部的默認轉移進行執行。 在這種狀態,signal方法一直阻塞著。另一個需要考慮的方式是執行方法, 像signal會使用客戶端的線程 來攔截流程定義,直到到達一個等待狀態。

然後執行到達wait 2, 執行WaitState活動。那個方法會調用 execution.waitForSignal(),這會導致signal方法返回。 線程會返回到調用signal方法 的客戶端。

所以,當signal方法返回時,執行定義到wait 2.一個signal會把執行從'initial'帶到'wait 2'.

圖 7.3. 一個signal會把執行從'initial'帶到'wait 2'.

然後執行會等待一個外部觸發器, 像是一個對象(更准確的是一個對象圖)在內存中, 直到下一個外部觸發器執行signal方法。

execution.signal();

第二個調用的signal會直接讓執行進入wait 3, 在它返回之前。

第二個signal讓執行進入'wait 3'.

圖 7.4. 第二個signal讓執行進入'wait 3'.

使用這個范例的好處是相同的流程定義可以在 客戶執行模式中執行 (在內存內不使用持久化),就像在持久化執行模式, 依賴應用和環境。

當在持久化模式下執行一個流程,你如何綁定 流程執行到數據庫的事務上。

持久化模式下的事務超時

圖 7.5. 持久化模式下的事務超時

在大多情況下,計算工作是流程需要完成的一部分, 在外部觸發器(紅色部分)之後的部分,其實很少。 一般來說,處理流程執行和處理UI傳遞過來的請求 的事務不會超過一秒。 而業務流程中的等待狀態可能超過幾小時,幾天甚至幾年。 當等待狀態啟動後,線索就變得很清晰, 在等待狀態啟動之前,只有計算工作的完成包含在事務中。

考慮一下這種方式: "當到達審批時,所有的自動流程需要做的是什麼, 在流程系統需要等待另一個外部觸發器之前?". 除非pdf需要被創建,或大郵件需要被發送, 大部分時候,它消耗的時間都是可以忽略的。 這就是為什麼在默認的持久化執行模式下, 流程工作在客戶端線程下執行。

這個原因也保證著流程同步路徑的情況。 當一個執行的單獨路徑切分成流程同步路徑, 流程花在計算上的時間是可忽略的。 所以為什麼分支或切分活動實現是有意義的, 目標持久化模式產生的同步路徑在同一個線程中按順序執行。 基本上它們都只是在同一個事務中的計算工作。 因為分支或切分知道每個執行的同步路徑會返回,所以這只能被完成, 當出現一個等待狀態的時候。

因為這裡有一個困難的概念需要掌握,我會再次使用其他詞語來解釋它。 從頭再看一次在持久化執行模式下被流程執行創建出來的它。 如果在一個事務中,一個執行被給與一個外部觸發器, 那導致執行切分成多個執行的同步路徑。 然後執行在計算上的部分也可以忽略。 生成SQL的部分也可以忽略。 因為所有在同步分支上完成的功能,必須在同一個事務中完成, 這裡一般沒有指針在分支或切分實現, 在多個線程中產生執行的同步路徑。

為了創建可執行流程,開發者需要確切知道什麼是自動活動, 什麼是等待狀態,哪些線程會被分配給流程執行。 對於畫業務流程的業務分析人員,事件就很簡單了。 對於他們畫的活動,他們通常只要知道這是一個人或是一個系統響應。 但是他們通常不知道如何轉換線程和事務。

所以對於開發者,第一個任務是分析什麼是流程控制的線程中需要執行的, 什麼是外部的。 查找外部觸發器是尋找一個流程中的等待狀態的很好的開始, 就像動詞和名詞可以在構建UML類圖中的元素的規則。

7.6. 流程同步

為了進行流程同步建模,在執行中這是一個父子樹形結構。 這個想法是執行主路徑是樹的根。 流程的主路徑也被稱作流程實例。 當在給定流程定義上啟動或創建一個新流程實例時, 執行便被創建。

現在,因為執行的主路徑和流程實例是相同對象, 這保證了用法的簡單, 在沒有同步情況的簡單流程下。

基本執行結構的UML類圖

圖 7.6. 基本執行結構的UML類圖

為了建立執行的多同步路徑,活動實現比如一個分支或切分 創建子執行, 使用ActivityExecution.createExecution方法。 活動實現比如結合或合並可以停止流程的這些同步路徑, 通過調用執行同步的stop方法。

只有葉子執行可以激活,非葉子執行應該不是激活的。 這個執行的樹形結構沒有堅持一個同步或結合行為的特殊類型。 它從事著分支或和切分 和結合或和合並來使用執行樹結構, 用任何方式,他們想定義期望的同步行為。 這裡我們看一個同步執行的例子。

執行的同步路徑

圖 7.7. 執行的同步路徑

這裡有執行的一個付款和一個發貨路徑。 在這種情況,水平線上的活動展示了分支和結合。這個執行顯示了三個執行。 執行的主路徑不是激活的(顯示成灰色) 執行的付款和發貨路徑是激活的,分別指向了 bill和ship活動。

從事活動行為的實現,是他們想使用的執行結構。 假設多個任務必須在執行進行之前完成。 活動行為可以為這個產生一系列子執行。 或者可以選擇,任務組件可以支持任務組, 分配給單獨的執行。在那種情況, 任務組件成為同步任務的響應, 因此把這個責任移動到執行樹形結構范圍之外。

7.7. 異常處理器

在所有分配到流程的代碼中,像 Activity,EventListeners和 Condition,可能分配給異常處理器。 這可以想成是把這些實現的方法實現包含在try-catch塊中。 但是為了構建更多可復用的構建塊, 為了委派類和異常處理邏輯, 異常處理器可以添加到核心流程模型中。

一個異常處理器可以分配給任何流程元素。 當一個異常發生在一個委派類中,一個匹配的異常處理器就會被找到。 如果找到了一個這樣的異常處理器,它會有一個處理這個異常的機會。

如果一個異常處理器處理完成,沒有出現問題,然後這個異常會 被認為是處理了,就會在委派代碼調用後繼續。 比如,一個轉移有三個動作,第二個動作拋出一個異常, 這個異常被異常處理器處理,然後

編寫自動活動,異常處理器提醒是很容易的。 默認是任意執行。沒有方法需要在執行中調用。 所以如果一個自動活動拋出一個異常,被異常處理器處理, 這個執行會在這個執行後繼續執行。這對於控制流向活動 就會有一個更大的困難。它們可能需要包含try-finally塊 來調用執行中對應的方法,在異常處理器 獲得一個機會來處理異常。比如,如果活動是等待狀態, 然後發生了一個異常,這裡就會有一個風險,線程會跳出 execution.waitForSignal()的調用, 導致執行在這個活動以後繼續執行。

TODO: exceptionhandler.isRethrowMasked

TODO: transactional exception handlers

TODO: we never catch errors

7.8. 流程修改

TODO: 流程修改

7.9. 鎖定和流程狀態

一個執行的狀態不是激活就是鎖定。 一個激活的執行不是執行就是等待外部觸發器。 如果一個執行不是STATE_ACTIVE,那麼它就是被鎖定。 一個鎖定的執行是只讀的,不能接受任何外部觸發器。

當一個新執行被創建時,它是STATE_ACTIVE. 為了把狀態修改成鎖定狀態,使用lock(String)。一些STATE_*常量 被提供了,它們演示了最常用的鎖定狀態。 但是在圖片中的'……'狀態展示了任何字符串 都可以作為狀態提供給lock方法。

執行的狀態

圖 7.8. 執行的狀態

如果一個執行被鎖定,修改執行的方法會 拋出一個PvmException,信息會引用真實的鎖定狀態。 觸發事件,更新變量,更新優先級,添加注釋 不會當做是修改執行。 子節點的創建和刪除也不會檢測, 這意味著那些方法可以被外部API客戶和活動行為調用, 即使執行在鎖定狀態。

確保比較getState()和STATE_*常量時 使用。equals,不要使用'==',因為如果執行從持久存儲加載。 會創建一個新字符串,而不是使用常量。

一個執行實現會被鎖定:

* 當它結束

* 當它暫停

* 在異步延續過程中

更多的,鎖定可以被活動實現使用, 讓執行在等待狀態下只讀,然後為這個執行傳遞 的外部實例就像這樣:

* 一個人員任務

* 一個服務調用

* 一個等待狀態當探測器檢測一個文件的出現時就結束

在這些情況,策略是外部實例應該獲得 執行的完全控制,因為它想要控制什麼應該允許,什麼不應該。 為了獲得那種控制,他們鎖定了執行,所以所有內部交互 必須通過外部實例傳遞。

一個創建外部實例的主要原因是, 它們可以在執行已經執行過還存在。比如, 在服務調用的情況,定時器可以導致執行獲得超時轉移。 當響應在超時後到達,服務調用實例應該 確認它沒有signal這個執行。所以服務調用可以看做 一個活動實例(活動實例) 是對活動每個執行的唯一實例。

外部實例它們自己負責管理執行鎖定。 如果定時器和客戶端應用結果是選擇 外部實例,而不是直接選擇執行,然後在理論上是不必要的。 它是從事活動行為實現,無論它希望 執行鎖定還是解鎖。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved