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

jBPM-4.0中文開發指南-第5章 實現基本活動

編輯:關於JAVA

第 5 章 實現基本活動

這一章解釋了流程定義的基礎,流程虛擬機給予的功能 以及活動實現是如何構建的。 同時,客戶端 API被用來執行包含了那些活動實現的流程。

5.1. ActivityBehaviour

PVM庫沒有包含完整的流程結構。 作為替代的是,活動的運行時行為被委派給一個 ActivityBehaviour. 換句話講,ActivityBehaviour是一個接口,它用來在純java環境實現流程結構的運 行時行為。

public interface ActivityBehaviour extends Serializable {

  void execute (ActivityExecution execution) throws Exception;

}

當一個活動行為被調用時,它就處於執行傳播的全部控制中。 換句話說,一個活動行為可以決定下一 步應該執行什麼執行。 比如,可以使用execution.take(Transition)獲得一個轉移,或者使用 execution.waitForSignal()進入等待階段。 萬一活動行為沒有調用任何上述的執行傳播方法,執行將 按默認方式執行。

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

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

圖 5.1. Display實例流程

TODO add ProcessBuilder example code

現在我們可以像下面這樣執行流程:

Execution execution = processDefinition.startExecution();

startExecution的調用會在控制台打印hello world:

hello

world

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

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

關於默認流程行為的更多細節可以 在第 7.3 節 “默認執行行為”找到。

5.3. ExternalActivityBehaviour

外部活動是負責流程執行由外部轉移進來的活動,外部的意思是來自流程系統的外部。 這意味著這個 執行流程對於系統來說,這是一個等待狀態。 這個執行會一直等待到外部觸發器調用。

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

public interface ExternalActivity extends Activity {

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

}

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

一旦活動行為已經處於等待狀態,然後執行會等待到調用signal方法。 執行會委派signal給 ExternalActivityBehaviour對象 分配給當前的活動。

所以活動的signal方法 會在等待期間,在執行獲得一個外部觸發器的時候調用。 signal方法中,響 應會傳遞給後面的流程執行。 比如,當一個人完成了一個任務,任務管理系統 會在執行中調用signal方 法。

一個signal可選擇使用signal名字和一個參數map. 活動行為攔截signal和參數的最常用方式是 signal對應選擇的外出轉移,參數作為執行中的變量。但那些只是例子,它一直等到活動使用singal和它 期望的參數。

5.4. ExternalActivity實例

這裡是一個簡單等待狀態實現的第一個例子:

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方法調用execution.waitForSignal()。 execution.waitForSignal()的調用 會使流程執 行進入等待狀態,直到一個外部觸發器出現。

signal方法使用signal參數對應的轉移名稱 來選擇轉移。所以當一個執行獲得一個外部觸發器, signal名稱被攔截,作為外部轉移的名稱,執行會被傳播到那個轉移上。

這裡是從a到b有一個轉移的相同的流程。 這時候,兩個活動的行為都是WaitState.

外部活動實例流程

圖 5.2. 外部活動實例流程

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

讓我們為流程定義啟動一個新流程實例:

ClientExecution execution = processDefinition.startProcessInstance();

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

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

然後我們提供了外部觸發器,通過調用signal方法。

execution.signal();

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

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

5.5. 基本流程執行

在下一個例子裡,我們會結合自動活動和等待狀態。 這裡例子構建了貸款審批流程,使用WaitState 和Display活動,我們剛剛創建的。 貸款流程的圖形看起來像這樣:

圖 5.3. 貸款流程

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

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的更多細節,可以參考 api文檔。 ProcessFactory的另一種選擇是創建一個 XML語言和一個XML解析器,來表示流程。 XML解析器可以直接實例化 org.jbpm.pvm.internal.model包中 的類。 這種方式一般都被流程語言選擇使用。

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

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

ClientExecution execution = processDefinition.startProcessInstance();

當startExecution方法返回時,submit loan request活動會被執行,執行會位於evaluate活動。

圖 5.4. 位於'evaluate'活動的執行

現在,執行處在一個很有趣的點。這裡有兩個轉移從evaluate指向外邊。 一個轉移叫approve 一個轉 移叫reject.像我們上面解釋的,WaitState實現會根據執行的signal選擇轉移。 讓我們像這樣執 行'approve' signal:

execution.signal("approve");

這個approve signal會導致執行選擇approve轉移 它會到達wire money活動。

在wire money活動中,信息會打印到控制台裡。 因為Display沒有調用execution.waitForSignal() ,也沒有調用其他執行傳播方法,默認流程行為只會讓執行繼續,使用向外的轉移到達archive活動,這 也是一個WaitState.

圖 5.5. 位於'archive'活動的執行

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

另一個signal就像這樣:

execution.signal("approve");

將讓執行最終到達結束狀態。

圖 5.6. 位於'end'活動的執行

5.6. 事件

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

public interface EventListener extends Serializable {

  void notify (EventListenerExecution execution) throws Exception;

}

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

最常用的事件是由執行自動觸發的:

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

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

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

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

我們會創建一個PrintLn事件監聽器,這與上面的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監聽器 會在流程中注冊。

圖 5.7. PrintLn監聽器流程

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

第一個事件演示如何為相同的事件注冊多個監聽器。 它們會根據它們指定的順序依次執行。

然後,在轉椅上,這裡的事件只有一種類型。 所以在那種情況下,事件類型不需要指定, 監聽器可以直接添加到轉移上。

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

5.7. 事件傳播

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

為了清楚地顯示這個,我們會創建一個DisplaySource事件監聽器, 這會把leaving信息和事件源 打印到控制台。

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

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

下一個流程展示了DisplaySource事件監聽器如何 作為'end'事件的監聽器注冊到composite活動:

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

ODO 更新代碼片段

下一步,我們會啟動一個執行。

ClientExecution execution = processDefinition.startProcessInstance();

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

execution.signal();

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

leaving activity(a)

另一個

execution.signal();

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

leaving activity(b)

leaving activity(composite)

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

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

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

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

使用流程工廠構建流程:

TODO 更新代碼

所以當第一個signal在流程中調用時,end事件 會再次觸發在a活動上,但是現在在組合活動的事件監聽器 不會被執行,因為傳播的事件被禁用了。 禁用傳播是單獨的事件監聽器的一個屬性, 不會影響其他監聽器。事件會一直被觸發, 傳播到整個父繼承結構。

ClientExecution execution = processDefinition.startProcessInstance();

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

execution.signal();

下一步,第二個signal會選擇從b到c的轉移。

execution.signal()

還是兩個end事件被觸發, 就像上面分別在b和composite活動中。 第一個事件是b活動上的 end事件。 那將被傳播給composite活動。 所以事件監聽器不會為這個事件執行,因為它已經禁用了傳播。 但是事件監聽器會在composite活動上 為end事件執行。 那是不傳播的,但是直接在composite活動上觸發。 所以事件監聽器現在會被執行 一次,為組合活動,就像下面控制台裡顯示的那樣:

leaving activity(composite)

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