jBPM4的高級(jí)圖形執(zhí)行 流程構(gòu)建
活動(dòng)可以實(shí)現(xiàn)循環(huán),基于轉(zhuǎn)移或活動(dòng)組合。 循環(huán)可以包含等待狀態(tài)。
為了支持多次自動(dòng)循環(huán)執(zhí)行,流程虛擬機(jī) 把執(zhí)行的傳播從尾部遞歸轉(zhuǎn)換成while循環(huán)。
7.2. 子流程
TODO: 子流程
7.3. 默認(rèn)執(zhí)行行為
當(dāng)一個(gè)Activity被用作活動(dòng)行為, 它可以使用下面的方法從外部控制流程:
◆waitForSignal()
◆take(Transition)
◆end(*)
◆execute(Activity)
◆createExecution(*)
當(dāng)Activity實(shí)現(xiàn)用做活動(dòng)行為, 沒有調(diào)用任何下面的流程傳播方法,然后 在活動(dòng)執(zhí)行時(shí),執(zhí)行會(huì)使用默認(rèn)執(zhí)行行為。
默認(rèn)執(zhí)行行為定義在下面:
◆如果當(dāng)前活動(dòng)有一個(gè)默認(rèn)向外轉(zhuǎn)移,選擇它。
◆如果當(dāng)前活動(dòng)有一個(gè)父活動(dòng),回退到父活動(dòng)。
◆否則,結(jié)束這個(gè)執(zhí)行。
流程語言可以重寫默認(rèn)執(zhí)行行為, 通過重寫ExecutionImpl中的 proceed方法。
7.4. 功能活動(dòng)
活動(dòng)也可以用作事件監(jiān)聽器,被稱作功能活動(dòng)。 自動(dòng)活動(dòng)的例子是發(fā)送郵件,執(zhí)行數(shù)據(jù)庫更新, 生成pdf,計(jì)算平均數(shù),等等。 所有這些都是自動(dòng)活動(dòng),沒有改變執(zhí)行流向。 這里是這些活動(dòng)如何實(shí)現(xiàn):
- 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方法獲得一個(gè)OpenExecution, 這是ActivityExecution和 EventListenerExecution的超類。 OpenExecution沒有提供任何特定目的的方法, 但是依舊是當(dāng)前狀態(tài),流程定義可以通過變量檢驗(yàn), 這包含了環(huán)境信息 對(duì)應(yīng)流程執(zhí)行。
這些方法其實(shí)都不能調(diào)用執(zhí)行傳播方法。 所以在perform方法完成后,執(zhí)行會(huì) 執(zhí)行默認(rèn)的方式。
7.5. 執(zhí)行和線程
這一章解釋流程虛擬機(jī)如何通過客戶端的線程, 把一個(gè)執(zhí)行從一個(gè)等待狀態(tài)帶到另一個(gè)。
當(dāng)一個(gè)客戶調(diào)用一個(gè)執(zhí)行的一個(gè)方法(比如signal方法)。 默認(rèn),流程虛擬機(jī)會(huì)使用線程執(zhí)行流程 直到它到達(dá)一個(gè)等待狀態(tài)。一旦下一個(gè)等待狀態(tài)到達(dá), 這個(gè)方法會(huì)返回,客戶端的線程就會(huì)返回。 這是流程虛擬機(jī)操作的默認(rèn)方式。 兩個(gè)更多的異步執(zhí)行可以補(bǔ)充默認(rèn)行為: 異步繼續(xù) 和異步命令服務(wù)。
下一個(gè)流程會(huì)展示基本理論。 它有三個(gè)等待狀態(tài)和四個(gè)自動(dòng)活動(dòng)。
圖 7.1. 有很多順序自動(dòng)活動(dòng)的流程。
這里是如何構(gòu)建流程:
- 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();
讓我們和你一起順著流程的執(zhí)行一起走。
- ClientExecution execution = processDefinition.startProcessInstance();
啟動(dòng)一個(gè)新執(zhí)行意味著初始活動(dòng)被執(zhí)行。 所以如果一個(gè)自動(dòng)活動(dòng)是初始活動(dòng),這意味著***個(gè)未命名的向外轉(zhuǎn)移會(huì)被立刻選擇。 這些都發(fā)生在startProcessInstance調(diào)用的內(nèi)部。
然而在這種情況下,初始活動(dòng)是一個(gè)等待狀態(tài)。 所以startProcessInstance方法會(huì)立刻返回, 執(zhí)行會(huì)定位到初始活動(dòng)'wait 1'。
一個(gè)新執(zhí)行會(huì)被定為到'wait 1'。
圖 7.2. 一個(gè)新執(zhí)行會(huì)被定為到wait 1。
然后一個(gè)外部觸發(fā)器會(huì)執(zhí)行signal方法。
- execution.signal();
像上面解釋的介紹WaitState, signal會(huì)導(dǎo)致選擇默認(rèn)的轉(zhuǎn)移。 轉(zhuǎn)移會(huì)把執(zhí)行移動(dòng)到automatic 1活動(dòng),并執(zhí)行它。 automatic 1中的Display活動(dòng)的execute方法, 向控制臺(tái)打印一行,它不會(huì) 調(diào)用execution.waitForSignal()。 因此,執(zhí)行會(huì)通過選擇automatic 1外部的默認(rèn)轉(zhuǎn)移進(jìn)行執(zhí)行。 在這種狀態(tài),signal方法一直阻塞著。另一個(gè)需要考慮的方式是執(zhí)行方法, 像signal會(huì)使用客戶端的線程 來攔截流程定義,直到到達(dá)一個(gè)等待狀態(tài)。
然后執(zhí)行到達(dá)wait 2, 執(zhí)行WaitState活動(dòng)。那個(gè)方法會(huì)調(diào)用 execution.waitForSignal(),這會(huì)導(dǎo)致signal方法返回。 線程會(huì)返回到調(diào)用signal方法 的客戶端。
所以,當(dāng)signal方法返回時(shí),執(zhí)行定義到wait 2。
一個(gè)signal會(huì)把執(zhí)行從'initial'帶到'wait 2'。
圖 7.3. 一個(gè)signal會(huì)把執(zhí)行從initial帶到wait 2。
然后執(zhí)行會(huì)等待一個(gè)外部觸發(fā)器, 像是一個(gè)對(duì)象(更準(zhǔn)確的是一個(gè)對(duì)象圖)在內(nèi)存中, 直到下一個(gè)外部觸發(fā)器執(zhí)行signal方法。
- execution.signal();
第二個(gè)調(diào)用的signal會(huì)直接讓執(zhí)行進(jìn)入wait 3, 在它返回之前。
第二個(gè)signal讓執(zhí)行進(jìn)入'wait 3'。
圖 7.4. 第二個(gè)signal讓執(zhí)行進(jìn)入wait 3。
使用這個(gè)范例的好處是相同的流程定義可以在 客戶執(zhí)行模式中執(zhí)行 (在內(nèi)存內(nèi)不使用持久化),就像在持久化執(zhí)行模式, 依賴應(yīng)用和環(huán)境。
當(dāng)在持久化模式下執(zhí)行一個(gè)流程,你如何綁定 流程執(zhí)行到數(shù)據(jù)庫的事務(wù)上。
持久化模式下的事務(wù)超時(shí)
圖 7.5. 持久化模式下的事務(wù)超時(shí)
在大多情況下,計(jì)算工作是流程需要完成的一部分, 在外部觸發(fā)器(紅色部分)之后的部分,其實(shí)很少。 一般來說,處理流程執(zhí)行和處理UI傳遞過來的請(qǐng)求 的事務(wù)不會(huì)超過一秒。 而業(yè)務(wù)流程中的等待狀態(tài)可能超過幾小時(shí),幾天甚至幾年。 當(dāng)?shù)却隣顟B(tài)啟動(dòng)后,線索就變得很清晰, 在等待狀態(tài)啟動(dòng)之前,只有計(jì)算工作的完成包含在事務(wù)中。
考慮一下這種方式: "當(dāng)?shù)竭_(dá)審批時(shí),所有的自動(dòng)流程需要做的是什么, 在流程系統(tǒng)需要等待另一個(gè)外部觸發(fā)器之前?"。 除非pdf需要被創(chuàng)建,或大郵件需要被發(fā)送, 大部分時(shí)候,它消耗的時(shí)間都是可以忽略的。 這就是為什么在默認(rèn)的持久化執(zhí)行模式下, 流程工作在客戶端線程下執(zhí)行。
這個(gè)原因也保證著流程同步路徑的情況。 當(dāng)一個(gè)執(zhí)行的單獨(dú)路徑切分成流程同步路徑, 流程花在計(jì)算上的時(shí)間是可忽略的。 所以為什么分支或切分活動(dòng)實(shí)現(xiàn)是有意義的, 目標(biāo)持久化模式產(chǎn)生的同步路徑在同一個(gè)線程中按順序執(zhí)行。 基本上它們都只是在同一個(gè)事務(wù)中的計(jì)算工作。 因?yàn)榉种Щ蚯蟹种烂總€(gè)執(zhí)行的同步路徑會(huì)返回,所以這只能被完成, 當(dāng)出現(xiàn)一個(gè)等待狀態(tài)的時(shí)候。
因?yàn)檫@里有一個(gè)困難的概念需要掌握,我會(huì)再次使用其他詞語來解釋它。 從頭再看一次在持久化執(zhí)行模式下被流程執(zhí)行創(chuàng)建出來的它。 如果在一個(gè)事務(wù)中,一個(gè)執(zhí)行被給與一個(gè)外部觸發(fā)器, 那導(dǎo)致執(zhí)行切分成多個(gè)執(zhí)行的同步路徑。 然后執(zhí)行在計(jì)算上的部分也可以忽略。 生成SQL的部分也可以忽略。 因?yàn)樗性谕椒种贤瓿傻墓δ?,必須在同一個(gè)事務(wù)中完成, 這里一般沒有指針在分支或切分實(shí)現(xiàn), 在多個(gè)線程中產(chǎn)生執(zhí)行的同步路徑。
為了創(chuàng)建可執(zhí)行流程,開發(fā)者需要確切知道什么是自動(dòng)活動(dòng), 什么是等待狀態(tài),哪些線程會(huì)被分配給流程執(zhí)行。 對(duì)于畫業(yè)務(wù)流程的業(yè)務(wù)分析人員,事件就很簡(jiǎn)單了。 對(duì)于他們畫的活動(dòng),他們通常只要知道這是一個(gè)人或是一個(gè)系統(tǒng)響應(yīng)。 但是他們通常不知道如何轉(zhuǎn)換線程和事務(wù)。
所以對(duì)于開發(fā)者,***個(gè)任務(wù)是分析什么是流程控制的線程中需要執(zhí)行的, 什么是外部的。 查找外部觸發(fā)器是尋找一個(gè)流程中的等待狀態(tài)的很好的開始, 就像動(dòng)詞和名詞可以在構(gòu)建UML類圖中的元素的規(guī)則。
【編輯推薦】