活動可以實現循環,基於轉移或活動組合。 循環可以包含等待狀態。
為了支持多次自動循環執行,流程虛擬機 把執行的傳播從尾部遞歸轉換成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類圖中的元素的規則。