posts - 26,comments - 77,trackbacks - 0
          活動可以實現(xiàn)循環(huán),基于轉(zhuǎn)移或活動組合。 循環(huán)可以包含等待狀態(tài)。

          為了支持多次自動循環(huán)執(zhí)行,流程虛擬機 把執(zhí)行的傳播從尾部遞歸轉(zhuǎn)換成while循環(huán)。
          7.2. 子流程

          TODO: 子流程
          7.3. 默認執(zhí)行行為

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

              * waitForSignal()
              * take(Transition)
              * end(*)
              * execute(Activity)
              * createExecution(*)

          當Activity實現(xiàn)用做活動行為, 沒有調(diào)用任何下面的流程傳播方法,然后 在活動執(zhí)行時,執(zhí)行會使用默認執(zhí)行行為。

          默認執(zhí)行行為定義在下面:

              * 如果當前活動有一個默認向外轉(zhuǎn)移,選擇它。
              * 如果當前活動有一個父活動,回退到父活動。
              * 否則,結(jié)束這個執(zhí)行。

          流程語言可以重寫默認執(zhí)行行為, 通過重寫ExecutionImpl中的 proceed方法。
          7.4. 功能活動

          活動也可以用作事件監(jiān)聽器,被稱作功能活動。 自動活動的例子是發(fā)送郵件,執(zhí)行數(shù)據(jù)庫更新, 生成pdf,計算平均數(shù),等等。 所有這些都是自動活動,沒有改變執(zhí)行流向。 這里是這些活動如何實現(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方法獲得一個OpenExecution, 這是ActivityExecution和 EventListenerExecution的超類。 OpenExecution沒有提供任何特定目的的方法, 但是依舊是當前狀態(tài),流程定義可以通過變量檢驗, 這包含了環(huán)境信息 對應(yīng)流程執(zhí)行。

          這些方法其實都不能調(diào)用執(zhí)行傳播方法。 所以在perform方法完成后,執(zhí)行會 執(zhí)行默認的方式。
          7.5. 執(zhí)行和線程

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

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

          下一個流程會展示基本理論。 它有三個等待狀態(tài)和四個自動活動。
          有很多順序自動活動的流程。



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

          這里是如何構(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();

          啟動一個新執(zhí)行意味著初始活動被執(zhí)行。 所以如果一個自動活動是初始活動,這意味著第一個未命名的向外轉(zhuǎn)移會被立刻選擇。 這些都發(fā)生在startProcessInstance調(diào)用的內(nèi)部。

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



          圖 7.2. 一個新執(zhí)行會被定為到'wait 1'。

          然后一個外部觸發(fā)器會執(zhí)行signal方法。

          execution.signal();

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

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

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



          圖 7.3. 一個signal會把執(zhí)行從'initial'帶到'wait 2'。

          然后執(zhí)行會等待一個外部觸發(fā)器, 像是一個對象(更準確的是一個對象圖)在內(nèi)存中, 直到下一個外部觸發(fā)器執(zhí)行signal方法。

          execution.signal();

          第二個調(diào)用的signal會直接讓執(zhí)行進入wait 3, 在它返回之前。
          第二個signal讓執(zhí)行進入'wait 3'。



          圖 7.4. 第二個signal讓執(zhí)行進入'wait 3'。

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

          當在持久化模式下執(zhí)行一個流程,你如何綁定 流程執(zhí)行到數(shù)據(jù)庫的事務(wù)上。
          持久化模式下的事務(wù)超時



          圖 7.5. 持久化模式下的事務(wù)超時

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

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

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

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

          為了創(chuàng)建可執(zhí)行流程,開發(fā)者需要確切知道什么是自動活動, 什么是等待狀態(tài),哪些線程會被分配給流程執(zhí)行。 對于畫業(yè)務(wù)流程的業(yè)務(wù)分析人員,事件就很簡單了。 對于他們畫的活動,他們通常只要知道這是一個人或是一個系統(tǒng)響應(yīng)。 但是他們通常不知道如何轉(zhuǎn)換線程和事務(wù)。

          所以對于開發(fā)者,第一個任務(wù)是分析什么是流程控制的線程中需要執(zhí)行的, 什么是外部的。 查找外部觸發(fā)器是尋找一個流程中的等待狀態(tài)的很好的開始, 就像動詞和名詞可以在構(gòu)建UML類圖中的元素的規(guī)則。
          posted @ 2009-06-26 12:03 卡宴 閱讀(1160) | 評論 (0)編輯 收藏
          5.5. 基本流程執(zhí)行

          在下一個例子里,我們會結(jié)合自動活動和等待狀態(tài)。 這里例子構(gòu)建了貸款審批流程,使用WaitState 和Display活動,我們剛剛創(chuàng)建的。 貸款流程的圖形看起來像這樣:
          貸款流程



          圖 5.3. 貸款流程

          使用Java構(gòu)建流程圖形是很乏味的事情, 因為你必須在局部變量中跟蹤所有的引用。 為了解決這個問題,流程虛擬機提供了一個ProcessFactory。 ProcessFactory是一種領(lǐng)域特定語言(DSL),可以嵌入到Java中, 簡化流程圖形的結(jié)構(gòu)。這個模型也叫做 流暢接口。

          ClientProcessDefinition processDefinition = ProcessFactory.build("loan")
            .activity("submit loan request").initial().behaviour(new Display("loan request submitted"))
              .transition().to("evaluate")
            .activity("evaluate").behaviour(new WaitState())
              .transition("approve").to("wire money")
              .transition("reject").to("end")
            .activity("wire money").behaviour(new Display("wire the money"))
              .transition().to("archive")
            .activity("archive").behaviour(new WaitState())
              .transition().to("end")
            .activity("end").behaviour(new WaitState())
          .done();

          為了了解ProcessFactory的更多細節(jié),可以參考 api文檔。 ProcessFactory的另一種選擇是創(chuàng)建一個XML語言和一個XML解析器,來表示流程。 XML解析器可以直接實例化 org.jbpm.pvm.internal.model包中的類。 這種方式一般都被流程語言選擇使用。

          初始化活動submit loan request和 wire the money活動是自動活動。 在這個例子中,wire the money活動的 Display實現(xiàn) 使用Java API來把信息輸出到控制臺上。但是讀取器可以想象一個可選的 Activity實現(xiàn),使用支付流程庫的Java API 來實現(xiàn)一個真實的自動支付。

          上述流程的一個新執(zhí)行可以像下面這樣啟動

          ClientExecution execution = processDefinition.startProcessInstance();

          當startExecution方法返回時, submit loan request活動會被執(zhí)行, 執(zhí)行會位于evaluate活動。
          位于'evaluate'活動的執(zhí)行



          圖 5.4. 位于'evaluate'活動的執(zhí)行

          現(xiàn)在,執(zhí)行處在一個很有趣的點。這里有兩個轉(zhuǎn)移從evaluate指向外邊。 一個轉(zhuǎn)移叫approve 一個轉(zhuǎn)移叫reject。像我們上面解釋的, WaitState實現(xiàn)會根據(jù)執(zhí)行的signal選擇轉(zhuǎn)移。 讓我們像這樣執(zhí)行'approve' signal:

          execution.signal("approve");

          這個approve signal會導(dǎo)致執(zhí)行選擇approve轉(zhuǎn)移 它會到達wire money活動。

          在wire money活動中,信息會打印到控制臺里。 因為Display沒有調(diào)用execution.waitForSignal(), 也沒有調(diào)用其他執(zhí)行傳播方法, 默認流程行為只會讓執(zhí)行繼續(xù), 使用向外的轉(zhuǎn)移到達archive活動, 這也是一個WaitState。
          位于'archive'活動的執(zhí)行



          圖 5.5. 位于'archive'活動的執(zhí)行

          所以只有當archive到達時, signal("approve")會返回。

          另一個signal就像這樣:

          execution.signal("approve");

          將讓執(zhí)行最終到達結(jié)束狀態(tài)。
          位于'end'活動的執(zhí)行



          圖 5.6. 位于'end'活動的執(zhí)行

          5.6. 事件

          事件位于流程定義中, 一系列的EventListener可以進行注冊。

          public interface EventListener extends Serializable {

            void notify(EventListenerExecution execution) throws Exception;

          }

          事件的目的是讓開發(fā)者可以為流程添加程序邏輯, 不必改變流程圖。 這是非常有價值的機制,可以促進業(yè)務(wù)分析人員和開發(fā)者之間的協(xié)作。 業(yè)務(wù)分析人員負責描述需求。 當他們使用流程圖歸檔那些需求, 開發(fā)者可以獲得這些圖形,讓它可執(zhí)行化。 事件會非常方便,向一個流程中添加技術(shù)細節(jié)(比如一些數(shù)據(jù)庫插入操作) 這些都是業(yè)務(wù)分析人員不感興趣的東西。

          最常用的事件是由執(zhí)行自動觸發(fā)的:

          TODO: 在用戶手冊中解釋事件

          事件是由流程元素和事件名稱結(jié)合而成。 用戶和流程語言也可以出發(fā)事件, 使用編程的方式在流程中使用fire方法。

          public interface Execution extends Serializable {
            ...
            void fire(String eventName, ProcessElement eventSource);
            ...
          }

          可以把一系列的EventListeners分配給一個事件。 但是事件監(jiān)聽器不能控制執(zhí)行的流向, 因為它們僅僅是監(jiān)聽已經(jīng)執(zhí)行了的執(zhí)行。 這與活動處理活動的行為是不同的。 活動行為可以響應(yīng)執(zhí)行的傳播。

          我們會創(chuàng)建一個PrintLn事件監(jiān)聽器, 這與上面的Display活動是非常相似的。

          public class PrintLn implements EventListener {

            String message;

            public PrintLn(String message) {
              this.message = message;
            }

            public void notify(EventListenerExecution execution) throws Exception {
              System.out.println("message");
            }
          }

          多個PrintLn監(jiān)聽器 會在流程中注冊。
          PrintLn監(jiān)聽器流程



          圖 5.7. PrintLn監(jiān)聽器流程

          ClientProcessDefinition processDefinition = ProcessFactory.build()
            .activity("a").initial().behaviour(new AutomaticActivity())
              .event("end")
                .listener(new PrintLn("leaving a"))
                .listener(new PrintLn("second message while leaving a"))
              .transition().to("b")
                .listener(new PrintLn("taking transition"))
            .activity("b").behaviour(new WaitState())
              .event("start")
                .listener(new PrintLn("entering b"))
          .done();

          第一個事件演示如何為相同的事件注冊多個監(jiān)聽器。 它們會根據(jù)它們指定的順序依次執(zhí)行。

          然后,在轉(zhuǎn)椅上,這里的事件只有一種類型。 所以在那種情況下,事件類型不需要指定, 監(jiān)聽器可以直接添加到轉(zhuǎn)移上。

          一個監(jiān)聽器每次都會執(zhí)行,當一個執(zhí)行觸發(fā)事件時,如果這個監(jiān)聽器被注冊了。 執(zhí)行會作為一個參數(shù)提供給活動接口, 除了控制流程傳播的方法以外, 都可以被監(jiān)聽器使用。
          5.7. 事件傳播

          事件會默認傳播給最近的流程元素。 目的是允許監(jiān)聽器在流程定義或組合活動中 可以執(zhí)行所有發(fā)生在流程元素中的事件。 比如這個功能允許為end事件在流程定義或一個組合活動中注冊一個事件監(jiān)聽器。 這種動作會被執(zhí)行,如果一個活動離開。 如果事件監(jiān)聽器被注冊到一個組合活動中, 它也會被所有活動執(zhí)行,當組合活動中出現(xiàn)了離開事件。

          為了清楚地顯示這個,我們會創(chuàng)建一個DisplaySource事件監(jiān)聽器, 這會把leaving信息和事件源 打印到控制臺。

          public class DisplaySource implements EventListener {

            public void execute(EventListenerExecution execution) {
              System.out.println("leaving "+execution.getEventSource());
            }
          }

          注意事件監(jiān)聽器的目的不是可視化,這是為什么事件監(jiān)聽器本身 不應(yīng)該顯示在圖形中。一個DisplaySource事件監(jiān)聽器 會作為end事件的監(jiān)聽器添加到組合活動中。

          下一個流程展示了DisplaySource事件監(jiān)聽器如何 作為'end'事件的監(jiān)聽器注冊到composite活動:
          一個在組合活動中為end事件注冊了不可見的事件監(jiān)聽器的流程。



          圖 5.8. 一個在組合活動中為end事件注冊了不可見的事件監(jiān)聽器的流程。

          TODO 更新代碼片段

          下一步,我們會啟動一個執(zhí)行。

          ClientExecution execution = processDefinition.startProcessInstance();

          在啟動一個新執(zhí)行后,執(zhí)行將在a活動中 作為初始活動。沒有活動離開,所以沒有信息被記錄下來。 下一個signal會給與執(zhí)行, 導(dǎo)致它選擇從a到b。

          execution.signal();

          當signal方法返回,執(zhí)行會選擇轉(zhuǎn)移 然后end事件會被a活動觸發(fā)。 那個組合活動會被傳播到組合活動和流程定義中。 因為我們的DisplaySource 監(jiān)聽器放到 composite活動中, 它會接收事件,把下面的信息打印到控制臺中:

          leaving activity(a)

          另一個

          execution.signal();

          會選擇b到c的轉(zhuǎn)移。那會觸發(fā)兩個活動離開事件。 一個在b活動,一個在組合活動。 所以下面的幾行會添加到控制臺輸出中:

          leaving activity(b)
          leaving activity(composite)

          事件傳播建立在流程定義的繼承組合結(jié)構(gòu)中。 頂級元素總是流程定義。 流程定義包含一系列活動。每個活動可以是葉子活動或者可以是一個組合節(jié)點, 這意味著它包含了一系列內(nèi)嵌活動。 內(nèi)嵌活動可以被使用,比如超級狀態(tài)或組合活動,在內(nèi)嵌流程語言中,像BPEL。

          所以事件模型在組合活動和上面的流程定義中的功能是相似的。 想象'Phase one'模型一個超級狀態(tài)作為一個狀態(tài)機。 然后事件傳播允許在超級狀態(tài)中注冊所有事件。 這個主意是繼承組合響應(yīng)圖形展示。 如果一個'e'元素畫在另一個'p'元素中, 'p'是'e'的父節(jié)點。一個流程定義擁有一系列定義活動。 每個活動可以擁有一系列內(nèi)嵌活動。 一個轉(zhuǎn)移的父節(jié)點就是它的源頭和目的的第一個父節(jié)點。

          如果一個事件監(jiān)聽器對傳播的事件沒有興趣, 可以在構(gòu)建流程使用ProcessFactory的propagationDisabled()。 下一個流程是與上面相同的流程, 除了傳播的事件會被事件監(jiān)聽器禁用。 圖形還是一樣。
          注冊到'end'事件的事件監(jiān)聽器被禁用的流程。



          圖 5.9. 注冊到'end'事件的事件監(jiān)聽器被禁用的流程。

          使用流程工廠構(gòu)建流程:

          TODO 更新代碼

          所以當?shù)谝粋€signal在流程中調(diào)用時,end事件 會再次觸發(fā)在a活動上,但是現(xiàn)在在組合活動的事件監(jiān)聽器 不會被執(zhí)行,因為傳播的事件被禁用了。 禁用傳播是單獨的事件監(jiān)聽器的一個屬性, 不會影響其他監(jiān)聽器。事件會一直被觸發(fā), 傳播到整個父繼承結(jié)構(gòu)。

          ClientExecution execution = processDefinition.startProcessInstance();

          第一個signal會選擇從a到b的流程。 沒有信息會被打印到控制臺。

          execution.signal();

          下一步,第二個signal會選擇從b到c的轉(zhuǎn)移。

          execution.signal()

          還是兩個end事件被觸發(fā), 就像上面分別在b和composite活動中。 第一個事件是b活動上的 end事件。 那將被傳播給composite活動。 所以事件監(jiān)聽器不會為這個事件執(zhí)行,因為它已經(jīng)禁用了傳播。 但是事件監(jiān)聽器會在composite活動上 為end事件執(zhí)行。 那是不傳播的,但是直接在composite活動上觸發(fā)。 所以事件監(jiān)聽器現(xiàn)在會被執(zhí)行 一次,為組合活動,就像下面控制臺里顯示的那樣:

          leaving activity(composite)


          jBPM4.0中文開發(fā)指南完整版http://family168.com/tutorial/jbpm4devguide/html/index.html
          posted @ 2009-06-25 17:38 卡宴 閱讀(958) | 評論 (0)編輯 收藏
          這一章解釋了流程定義的基礎(chǔ),流程虛擬機給予的功能 以及活動實現(xiàn)是如何構(gòu)建的。 同時,客戶端API被用來執(zhí)行包含了那些活動實現(xiàn)的流程。
          5.1. ActivityBehaviour

          PVM庫沒有包含完整的流程結(jié)構(gòu)。 作為替代的是,活動的運行時行為被委派給一個ActivityBehaviour。 換句話講,ActivityBehaviour是一個接口, 它用來在純java環(huán)境實現(xiàn)流程結(jié)構(gòu)的運行時行為。

          public interface ActivityBehaviour extends Serializable {

            void execute(ActivityExecution execution) throws Exception;

          }

          當一個活動行為被調(diào)用時,它就處于執(zhí)行傳播的全部控制中。 換句話說,一個活動行為可以決定下一步應(yīng)該執(zhí)行什么執(zhí)行。 比如,可以使用execution.take(Transition)獲得一個轉(zhuǎn)移, 或者使用execution.waitForSignal()進入等待階段。 萬一活動行為沒有調(diào)用任何上述的執(zhí)行傳播方法, 執(zhí)行將 按默認方式執(zhí)行。
          5.2. ActivityBehaviour實例

          我們會啟動一個非常原始的hello world例子。 一個Display活動會將一條信息打印到控制臺:

          public class Display implements ActivityBehaviour {

            String message;

            public Display(String message) {
              this.message = message;
            }

            public void execute(ActivityExecution execution) {
              System.out.println(message);
            }
          }

          讓我們使用這個活動構(gòu)建我們第一個流程定義:
          Display實例流程



          圖 5.1. Display實例流程

          TODO add ProcessBuilder example code

          現(xiàn)在我們可以像下面這樣執(zhí)行流程:

          Execution execution = processDefinition.startExecution();

          startExecution的調(diào)用會在控制臺打印hello world:

          hello
          world

          一個總是值得提醒的事情是活動可以使用屬性進行配置。 在Display例子中,你可以看到message屬性在兩種使用方法中配置的不同。 通過配置屬性,我們可以寫出可復(fù)用的活動。 它們可以在以后每次使用在流程中都進行不同的配置。 這是一個基本的部分, 將流程語言構(gòu)建在流程虛擬機之上。

          其他需要解釋的部分是 這個活動實現(xiàn)沒有包含任何執(zhí)行傳播的功能。 當一個新流程實例啟動時, 執(zhí)行會定位到初始活動,那個活動會被執(zhí)行。 Display.execute方法用來決定默認的執(zhí)行傳播。 具體的,這意味著活動自己 沒有調(diào)用任何執(zhí)行傳播的方法。 那種情況下,默認的傳播會執(zhí)行。默認傳播會選擇第一個轉(zhuǎn)移,如果這個轉(zhuǎn)移存在的話。 如果沒有,它會結(jié)束這個執(zhí)行。 這揭示了為什么a活動和b活動都被執(zhí)行, 而在b活動執(zhí)行完執(zhí)行會停止。

          關(guān)于默認流程行為的更多細節(jié)可以 在第 7.3 節(jié) “默認執(zhí)行行為”找到。
          5.3. ExternalActivityBehaviour

          外部活動是負責流程執(zhí)行由外部轉(zhuǎn)移進來的活動, 外部的意思是來自流程系統(tǒng)的外部。 這意味著這個執(zhí)行流程對于系統(tǒng)來說,這是一個等待狀態(tài)。 這個執(zhí)行會一直等待到外部觸發(fā)器調(diào)用。

          為了處理外部觸發(fā)器,ExternalActivityBehaviour 為ActivityBehaviour添加了一個方法:

          public interface ExternalActivity extends Activity {

            void signal(Execution execution,
                        String signal,
                        Map<String, Object> parameters) throws Exception;

          }

          就像普通的活動,當一個執(zhí)行到達一個活動, 外部活動行為的execute方法會被調(diào)用。 在外部活動中,execute方法會傳遞另一個系統(tǒng)的響應(yīng), 然后通過調(diào)用execution.waitForSignal() 進入等待狀態(tài)。 比如在execute方法中,響應(yīng)可能是由一個人傳入, 通過在任務(wù)管理系統(tǒng)中創(chuàng)建一個任務(wù)入口, 然后等待到這個人完成這個任務(wù)。

          一旦活動行為已經(jīng)處于等待狀態(tài), 然后執(zhí)行會等待到調(diào)用signal方法。 執(zhí)行會委派signal給ExternalActivityBehaviour對象 分配給當前的活動。

          所以活動的signal方法 會在等待期間,在執(zhí)行獲得一個外部觸發(fā)器的時候調(diào)用。 signal方法中,響應(yīng)會傳遞給后面的流程執(zhí)行。 比如,當一個人完成了一個任務(wù),任務(wù)管理系統(tǒng) 會在執(zhí)行中調(diào)用signal方法。

          一個signal可選擇使用signal名字和一個參數(shù)map。 活動行為攔截signal和參數(shù)的最常用方式是 signal對應(yīng)選擇的外出轉(zhuǎn)移, 參數(shù)作為執(zhí)行中的變量。但那些只是例子, 它一直等到活動使用singal和它期望的參數(shù)。
          5.4. ExternalActivity實例

          這里是一個簡單等待狀態(tài)實現(xiàn)的第一個例子:

          public class WaitState implements ExternalActivity {

            public void execute(ActivityExecution execution) {
              execution.waitForSignal();
            }

            public void signal(ActivityExecution execution,
                               String signalName,
                               Map<String, Object> parameters) {
              execution.take(signalName);
            }
          }

          execute方法調(diào)用execution.waitForSignal()。 execution.waitForSignal()的調(diào)用 會使流程執(zhí)行進入等待狀態(tài), 直到一個外部觸發(fā)器出現(xiàn)。

          signal方法使用signal參數(shù)對應(yīng)的轉(zhuǎn)移名稱 來選擇轉(zhuǎn)移。所以當一個執(zhí)行獲得一個外部觸發(fā)器, signal名稱被攔截,作為外部轉(zhuǎn)移的名稱, 執(zhí)行會被傳播到那個轉(zhuǎn)移上。

          這里是從a到b有一個轉(zhuǎn)移的相同的流程。 這時候,兩個活動的行為都是WaitState。
          外部活動實例流程



          圖 5.2. 外部活動實例流程

          ClientProcessDefinition processDefinition = ProcessFactory.build()
              .activity("a").initial().behaviour(new WaitState())
                .transition().to("b")
              .activity("b").behaviour(new WaitState())
          .done();

          讓我們?yōu)榱鞒潭x啟動一個新流程實例:

          ClientExecution execution = processDefinition.startProcessInstance();

          啟動這個流程會執(zhí)行a中的WaitState活動。 WaitState.execute會調(diào)用 ActivityExecution.waitForSignal。 所以當processDefinition.startProcessInstance()返回, 執(zhí)行會一直處在a活動。

          assertEquals("a", execution.getActivityName());

          然后我們提供了外部觸發(fā)器, 通過調(diào)用signal方法。

          execution.signal();

          execution.signal()會委派給當前活動。 所以在這種情況下就是a活動里的 WaitState活動。WaitState.signal會調(diào)用 ActivityExecution.take(String transitionName)。 當我們沒有提供一個signal名稱,第一個名字是null會被選中。 我們指定的a的唯一轉(zhuǎn)移沒有名字,所以會選中這個。 然后這個轉(zhuǎn)移指向b。 當執(zhí)行到達b活動, b活動中的WaitState活動會被執(zhí)行。 就像我們上面看到的,執(zhí)行會在b一直等待, 這時signal會返回, 離開的執(zhí)行指向b活動。

          assertEquals("b", execution.getActivityName());


          jBPM4.0開發(fā)指南完整版http://family168.com/tutorial/jbpm4devguide/html/index.html
          posted @ 2009-06-25 17:37 卡宴 閱讀(966) | 評論 (0)編輯 收藏
            
            第1個示例Pay是我們family168所做的例子中我最喜歡的一個,這是一個簡易的信息統(tǒng)計查詢工具,它甚至沒有服務(wù)器端的代碼,完全依靠JavaScript提供各種數(shù)據(jù),在這個小系統(tǒng)中我們可以分類查看不同客戶的信息,以及由這這些信息匯總的圖形報表。
            其中包月包年的用戶情況的統(tǒng)計使用的是maven2中的cobertura里的效果,這樣哪些vip已經(jīng)過期,哪些vip快到期就一目了然,當然最重要的是視覺效果好。
            首頁效果如下圖所示:

          系統(tǒng)的最后一項功能是統(tǒng)計報表,我們可以按照用戶類型和是否過期生成兩種統(tǒng)計報表,報表圖形并不是使用Ext JS實現(xiàn)的,而是用svg畫的,不過我們在顯示報表頁面的時候使用了iframe,這樣做的好處是不用將所有代碼都加載到首頁中,雖然RIA宣揚one page one application,但是使用iframe可以在一定程度上避免一次加載過多的資源文件,在實際中依然擁有適用的場景。
              顯示效果如下圖所示:

          第2個示例Tracker是一個簡易的任務(wù)跟蹤系統(tǒng),它使用了最基本的ssh開發(fā)框架,通過嵌入式數(shù)據(jù)庫hsqldb保存數(shù)據(jù),依靠maven2管理項目流程。儼然已經(jīng)是一個小而全的企業(yè)系統(tǒng)了。
          Ext JS在系統(tǒng)中負責前臺展示的部分,后臺通過struts2結(jié)合json-lib與前臺的Ext JS進行交互,在開發(fā)過程中,我們封裝了JsonGrid和JsonTree這些基本組件,很大程度上減少了編碼的數(shù)量,提高的開發(fā)效率。
            系統(tǒng)界面如下:

            系統(tǒng)左側(cè)是以JsonTree為基礎(chǔ)生成的樹形菜單,顯示了所有工程的信息,我們可以直接在左側(cè)面板部分進行添加,修改,刪除等操作。
          進行詳細配置和右鍵功能菜單效果如下圖所示:


          統(tǒng)計系統(tǒng)下載:http://www.family168.com/demo/pay.rar
          任務(wù)追蹤系統(tǒng)下載:http://www.family168.com/demo/tracker.rar
          由于第2個示例是使用maven2構(gòu)建的,所以不會使用maven2的朋友可以查看我們的maven2教程http://family168.com/oa/maven2/html/index.html。如果想知道這2個示例的詳細講解,可以上我們的論壇http://family168.com/bbsindex.asp?boardid=13查看。
          posted @ 2009-06-25 15:12 卡宴 閱讀(1545) | 評論 (3)編輯 收藏
          4.1. APIs

          流程虛擬機包含4個集成的API,在不同的執(zhí)行模式下, 覆蓋完整的流程工作。 每個API都有特定的目的, 滿足下面的架構(gòu)。
          流程虛擬機中的4個API


          圖 4.1. 流程虛擬機中的4個API

          服務(wù)接口用在應(yīng)用代碼中,與流程虛擬機進行交互, 它將運行在支持事務(wù)的持久化模式下,后端基于數(shù)據(jù)庫。 這是用戶將PVM作為一個工作流引擎使用的最常用的方式。

          如果不想使用持久化方式執(zhí)行流程,可以直接使用客戶端API來處理流程和執(zhí)行對象。 客戶端API對外暴露了核心模型對象的方法。

          活動API用來實現(xiàn)活動在運行時的行為。 因此一個活動類型實際上是一個組件,核心是實現(xiàn)了ActivityBehaviour接口。 活動行為實現(xiàn)可以控制執(zhí)行的流程。

          事件監(jiān)聽器API用來編寫java代碼,它可以用來處理流程事件。 它比活動API類似, 唯一的差別是事件監(jiān)聽器不能控制執(zhí)行的流程。
          4.2. 活動API

          活動API允許使用java實現(xiàn)運行時的活動行為。

          public interface ActivityBehaviour extends Serializable {
            void execute(ActivityExecution execution) throws Exception;
          }

          一個活動就是分配給活動的一些行為。 提供的執(zhí)行就是到達這個活動的執(zhí)行。 ActivityExecution接口 暴露了控制執(zhí)行流程的方法。

          public interface ActivityExecution extends OpenExecution {

            void waitForSignal();
            void take(String transitionName);
            void execute(String activityName);

            ...

          }

          4.3. 事件監(jiān)聽API

          事件監(jiān)聽API允許使用java開發(fā)監(jiān)聽器, 并在特定的流程事件發(fā)生時調(diào)用,像進入一個活動或離開一個活動。 它與活動API類似, 不同的是不能控制執(zhí)行流程的傳播。 比如,當一個執(zhí)行選擇了一個轉(zhuǎn)移,一個對應(yīng)的監(jiān)聽器會被激活, 但是因為這個轉(zhuǎn)移已經(jīng)被選擇了, 執(zhí)行的流程無法被事件監(jiān)聽器改變。

          public interface EventListener extends Serializable {

            void notify(EventListenerExecution execution) throws Exception;

          }

          4.4. 客戶端API

          客戶端API是一套暴露了相關(guān)方法的接口, 它用來直接管理流程定義上的執(zhí)行和執(zhí)行對應(yīng)。

          最小的需求,客戶端API和活動API需要使用活動創(chuàng)建 流程定義并執(zhí)行它。
          4.5. 環(huán)境

          在持久化執(zhí)行環(huán)境下,環(huán)境的第一目的 是讓流程在不同的事務(wù)環(huán)境下執(zhí)行, 比如Java標準版,Java企業(yè)版,SEAM和Spring。

          PVM代碼自身只通過自身定義的接口來調(diào)用事務(wù)資源。 比如,PVM自身擁有一些建立在hibernate會話,異步消息會話 和定時任務(wù)會話的接口方法。

          環(huán)境允許為其配置真實的實現(xiàn), 在請求的基礎(chǔ)上實現(xiàn)服務(wù)的延遲加載, 為事務(wù)的持續(xù)獲得服務(wù)對象。

          一個環(huán)境工廠是靜態(tài)的,一個環(huán)境工廠 提供應(yīng)用中的所有線程。

          EnvironmentFactory environmentFactory = new PvmEnvironmentFactory("environment.cfg.xml");

          環(huán)境部分可以像這樣 圍繞在持久化流程操作周圍:

          Environment environment = environmentFactory.openEnvironment();
          try {

            ... inside the environment block...

          } finally {
            environment.close();
          }

          PVM自身會從環(huán)境中獲得所有事務(wù)資源和配置。 Activity實現(xiàn) 也可以做同樣的事情。

          org.jbpm.pvm.internal.cfg.JbpmConfiguration 這個類扮演著Configuration, ProcessEngine和EnvironmentFactory三個角色。
          4.6. 命令

          命令封裝了將被運行在環(huán)境塊中的操作。 命令的主要目的是獲得邏輯。

          public interface Command<T> extends Serializable {

            T execute(Environment environment) throws Exception;

          }

          4.7. 服務(wù)

          這里有三個主要服務(wù):RepositoryService, ExecutionService和ManagementService。 通常來說,服務(wù)是會話外觀,用來暴露PVM持久化應(yīng)用的方法。 下一部分用例子展示 這些服務(wù)中的基本方法。

          RepositoryService管理 流程定義的資源。

          public interface RepositoryService {

            Deployment createDeployment();

            ProcessDefinitionQuery createProcessDefinitionQuery();

            ...

          }

          ExecutionService管理 運行時的執(zhí)行。

          public interface ExecutionService {

            ProcessInstance startProcessInstanceById(String processDefinitionId);

            ProcessInstance signalExecutionById(String executionId);

            ...

          }

          ManagementService包含了所有管理操作 來保持系統(tǒng)啟動運行。

          public interface ManagementService {

            JobQuery createJobQuery();

            void executeJob(long jobDbid);

            ...

          }

          所有這些方法都封裝成Command。 這三個服務(wù)執(zhí)行的方法 都委派給一個CommandService:

          public interface CommandService {

            <T> T execute(Command<T> command);

          }

          CommandService被配置到環(huán)境中。 一個CommandService鏈可以看做環(huán)繞在一個命令周圍的一些攔截器。 這就是如何在不同的環(huán)境下 進行持久化和事務(wù)支持的核心機制。

          默認的配置文件jbpm.default.cfg.xml 包含了下面的配置服務(wù)。

          <jbpm-configuration>

            <process-engine>

              <repository-service />
              <repository-cache />
              <execution-service />
              <history-service />
              <management-service />
              <identity-service />
              <task-service />

          文件 jbpm.tx.hibernate.cfg.xml包含了 下面的command service配置:

          <jbpm-configuration>

            <process-engine-context>
              <command-service>
                <retry-interceptor />
                <environment-interceptor />
                <standard-transaction-interceptor />
              </command-service>
            </process-engine-context>

            ...

          這些服務(wù),比如repository-service,execution-service 和management-service將按照類型找到配置好的command-service。 command-service標簽符合默認的命令服務(wù), 基本上什么也不做, 只是在提供給它的環(huán)境上執(zhí)行命令。

          配置的command-service結(jié)果, 在默認的命令執(zhí)行期下面的三個攔截器鏈中。
          CommandService攔截器



          圖 4.2. CommandService攔截器

          retry攔截器是鏈中的第一個,它會被環(huán)境 當做CommandService.class暴露出來。 所以retry攔截器會分別提供給repository-service, execution-service和management-service這些服務(wù)。

          retry-interceptor會獲取hiberate的StaleObjectExceptions (因為樂觀鎖失敗)并重新嘗試執(zhí)行命令。

          environment-interceptor會把一個環(huán)境塊 放到命令執(zhí)行的周圍。

          standard-transaction-interceptor會初始化一個 StandardTransaction。hibernate會話/事務(wù)會被作為 標準事務(wù)的一個資源。

          這個攔截器棧的不同配置也可以使用:

              * 把執(zhí)行委派到一個本地ejb命令服務(wù), 這樣可以啟動一個內(nèi)容管理的事務(wù)。
              * 把執(zhí)行委派到一個遠程ejb命令服務(wù), 這樣命令實際執(zhí)行在一個不同的JVM上。
              * 把命令打包成一個異步消息, 這樣命令會異步執(zhí)行在一個不同的事務(wù)中。

          完整版內(nèi)容http://family168.com/tutorial/jbpm4devguide/html/index.html
          posted @ 2009-06-24 08:54 卡宴 閱讀(1414) | 評論 (0)編輯 收藏

          jbpm.jar包含了一些默認配置文件, 它們可以導(dǎo)入到用戶配置文件中。

          這樣,用戶很容易選擇包含或排除哪些功能。 而且這些配置信息也包含了實現(xiàn), 所以用戶可以只導(dǎo)入那些起作用的配置文件, 當我們發(fā)布的配置文件中出現(xiàn)了修改的時候。

          配置文件可以導(dǎo)入到用戶的jbpm.cfg.xml中:

          jbpm.default.cfg.xml
          jbpm.identity.cfg.xml
          jbpm.jbossremote.cfg.xml
          jbpm.jobexecutor.cfg.xml
          jbpm.tx.hibernate.cfg.xml
          jbpm.tx.jta.cfg.xml

          jbpm.default.cfg.xml:包含了默認的配置, 比如服務(wù),hibernate配置(來自jbpm.hibernate.cfg.xml中的配置), hibernate會話工廠,業(yè)務(wù)日歷等等。

          一個標準的java配置看起來像是這樣:

          <?xml version="1.0" encoding="UTF-8"?>

          <jbpm-configuration>

            <import resource="jbpm.default.cfg.xml" />
            <import resource="jbpm.tx.hibernate.cfg.xml" />
            <import resource="jbpm.jpdl.cfg.xml" />
            <import resource="jbpm.identity.cfg.xml" />
            <import resource="jbpm.jobexecutor.cfg.xml" />

          </jbpm-configuration>

          在一個JTA環(huán)境中,使用jbpm.tx.jta.cfg.xml 替換jbpm.tx.hibernate.cfg.xml。

          如果希望自定義這些配置中的任何部分,可以在jbpm.cfg.xml中 使用自定義的內(nèi)容替換引用部分。

          jbpm.jar也包含了下列hibernate映射配置文件:

          jbpm.execution.hbm.xml
          jbpm.history.hbm.xml
          jbpm.identity.hbm.xml
          jbpm.repository.hbm.xml
          jbpm.task.hbm.xml
          jbpm.jpdl.hbm.xml

          所有這些將java領(lǐng)域模型映射到一個關(guān)系數(shù)據(jù)庫中。

          jbpm.jar還包含的其他配置文件:

          jbpm.task.lifecycle.xml
          jbpm.variable.types.xml
          jbpm.wire.bindings.xml
          jbpm.jpdl.activities.xml
          jbpm.jpdl.eventlisteners.xml

          如何從配置文件開始進行解析,參考

              * 類 org.jbpm.pvm.internal.env.JbpmConfigurationParser
              * 資源 modules/pvm/src/main/resources/jbpm.wire.bindings.xml
              * 包 modules/pvm/src/main/java/org/jbpm/pvm/internal/wire/binding

          完整版內(nèi)容http://family168.com/tutorial/jbpm4devguide/html/index.html
          posted @ 2009-06-24 08:50 卡宴 閱讀(1326) | 評論 (0)編輯 收藏
          僅列出標題
          共3頁: 上一頁 1 2 3 
          主站蜘蛛池模板: 林州市| 崇州市| 洞口县| 通州市| 赞皇县| 泰州市| 韩城市| 寿阳县| 潮州市| 临夏市| 绥芬河市| 英山县| 子洲县| 同江市| 颍上县| 镇安县| 天气| 芦溪县| 枞阳县| 防城港市| 永善县| 潞西市| 仁化县| 乐昌市| 灌云县| 白沙| 肥城市| 克东县| 涟水县| 青川县| 青浦区| 松滋市| 东阳市| 都安| 仁寿县| 禄劝| 浙江省| 双桥区| 舒城县| 定安县| 大厂|