E-State和工作流
我在前面的文章“狀態機與工作流(State Machines and Workflow)”(WLDJ,卷3,第1期)中討論過工作流和狀態機,它們是面向流程的應用程序的補充實現策略。 狀態技術是對許多業務流程中所采用的“里程碑”技術所做繼承的強大抽象。另一方面,工作流 - 這里特指BEA Weblogic Integration的Business Process Modeler(BPM)組件 - 提供了重要的企業級服務,例如外部系統集成、人員工作列表(worklist)和任務管理,事件和計時器,以及XML消息處理。在前面的文章中指出過,混合的狀態-工作流解決方案有以下幾個部分:
1. 狀態機框架,由以下部分構成:
- 狀態模型: 一套狀態和轉換,在XML文檔中表達。
- Actor數據庫: 主角(actor)是指具有狀態的實體。Actor的狀態由狀態機保存在數據庫里。
- 狀態機引擎: 把事件插入Actor的狀態模型中,并相應地更新狀態。它還會在進入、退出狀態或者發生轉換時,調用用戶自定義動作類。
- 動作類: 用戶自定義的Java類,負責響應某種狀態的進入或退出,或者某個Actor在特定狀態模型下執行過濾形態。
2. BPM工作流:負責接收事件,然后把事件插入狀態機。
3. BPM工作流:設置計時器,在時間用盡的時候把超時事件插入狀態機。
4. BPM工作流:某一狀態動作調用該工作流,給它分配一個工作列表任務或與外部系統交互。
E-State 是第一部分即狀態機框架的參考實現。本文來討論狀態機的體系結構。
E-State 體系結構
方法學
E-State的狀態模型基于實時面向對象建模(Real-time Object-Oriented Modeling -ROOM) 的方法。ROOM 的狀態圖是層次結構的,也就是說每個狀態可以擁有子狀態。這個主意很簡單,效果卻異常強大。 從人類思維的角度來看,平面狀態模型無法進行擴展。當狀態和轉換的數量增長時,平面模型就會變得難于理解。而層次狀態模型則可以分部分考察,每一部分理解起來相對就變得簡單了。例如,考慮圖 1。

在這個模型里,轉換 ab 會使狀態 a1 或 a2 變成狀態 b; 而轉換ac 會使狀態從 a1 或 a2 變成 c。所有狀態的轉換toC 會使狀態變成c。 在狀態 a1里的轉換 ba 會形成新的狀態 a。初始狀態是 b。狀態從 a1變為a2時,要經過轉換 a1a2,從 a2 變為a1時,要經過a2a1。轉換ca把狀態引到一個選擇點:如果最后的狀態是a1,就變成a1,否則就變成 a2。
同樣的場景,用層次結構來表示,理解起來就容易多了,如圖2所示:

首先,超級狀態 a被細分為狀態a1 和 a2(圖2中的右圖);這樣整個系統的狀態圖(圖2中的左圖)變得更簡潔。從狀態的高最層來看,轉換ab 和 ba只是在狀態a 和 b之間轉換;但是在狀態 a里,可以看到 ba 指向子狀態 a1,而 ab則來源于a1。 轉換ac 從 a1 或 a2 開始,指向 c;轉換 ca 從c開始,指向 狀態a的最后一個子狀態。另外,轉換toC不象圖1中那樣,要從每個狀態來開始;來自最高層狀態的非擴展轉換點toC 的事件指向狀態 c,就可以表示需要的行為。
作為層次結構設計的成果之一,ROOM提供了二個強大的特性:組轉換和歷史恢復。所謂組轉換是指:針對指定狀態發生的轉換,不論指定狀態處在什么子狀態當中;轉換 ac 把狀態從 a 變為 c,不論狀態a的子狀態是a1還是 a2。歷史恢復 就是變回指定狀態最近的子狀態;轉換 ca 把狀態 c 變回 a最近的子狀態.
可以選擇的方法還有UML和Petri-nets,它們都支持層次結構。
引擎
E-State的核心是一個無狀態的會話Enterprise JavaBean (EJB),它被稱為狀態機(StateMachine),如圖3中的陰影部分所示。

StateMachine EJB被配置成指向具體的狀態模型,使用XML文件來進行配置,配置文件中包含以下內容:
·一組狀態和一組轉換;
·唯一的命名空間,唯一命名空間有助于多重部署,稍后介紹。
·Java “動作”回調類的名稱,狀態機處理事件時,調用回調類。
EJB把模型用于“Actor”。在ROOM方法中,Actor指的是一個“活動”對象,狀態模型最好地描述了這個對象的行為。
(在 ROOM里,活動對象擁有自己的控制線程,以及一組自己的入站、出站消息接口。E-State里Actor的概念更嚴格) 在 E-State里,Actor是擁有狀態的實體,例如一個保險索賠。在一個模型里,從一個狀態到另一個狀態的轉換,反映了Actor的狀態變化;例如,索賠可能處在等待狀態,激活狀態,或者空閑狀態。 E-State 有三個表負責跟蹤Actor的狀態,這三個表是 Actor(主角)、Actor_Property(主角屬性)和 Actor_State(主角狀態),還有對應的實體 EJB (Actor,ActorProperty,和 ActorState) 來表示這三個表,如圖3所示。StateMachine EJB 某種程度上可以看作這些實體EJB的一個層面(facade)。StateMachine EJB的Actor管理方法有: createActor(),getCurrentState(),getChildState(), getActorProperty(),getActorProperties() 和 setActorProperty()。
狀態機余下的方法 (startMachine() 和 injectEvent()) 形成了狀態機引擎,驅動著Actor的狀態變化。實際上,startMachine()只是調用 injectEvent(),給它傳遞了一個特殊的“初始化”事件,由injectEvent()執行轉換操作,轉換操作的起點是模型中每個狀態的初始轉換點。所以, injectEvent() 方法是狀態機的核心,由它來驅動業務流程的動作前進。這個方法可以調用用戶自定義動作類,從而實現模型中所定義的狀態行動(StateAction)接口。動作類的功能是將重要的狀態機事件通知客戶,并向客戶請示邏輯決策。在表1里列出了動作類的方法。
表1 動作類的方法
方法 | 動作 |
OnStateEnter | 通知進入了一個狀態 |
OnStateExit | 通知退出了一個狀態 |
OnTransitionExecute | 通知執行了一個轉換。如果方法返回真,則允許該轉換發生,如果為假,則阻止轉換(在ROOM的概念里,稱為警衛(guard)) |
Choice | 要執行選擇點決策的請求。返回值為真或假,控制著狀態模型里控制分支的流轉方向。 |
在保險索賠的例子里,動作類啟動工作流,執行與任務相關的工作或者清理工作,或者啟動計數器。在 WebLogic Integration 7.0里,由BPM 的API啟動工作流。在In WebLogic Integration 8.1里,則用Web服務調用工作流。
表2里歸納了StateMachine EJB的方法。
表2 StateMachine EJB的方法
方法 | 動作 |
CreateActor | 在Actor表中為StateMachine EJB代表的模型建立一個新記錄 |
GetCurrentState | 取得StateMachine EJB代表的模型的Actor的當前葉子狀態 |
GetChildState | 取得StateMachine EJB代表的模型的Actor的指定狀態的當前子狀態 |
GetActorProperty | 取得StateMachine EJB代表的模型的Actor的指定屬性值 |
SetActorProperty | 設置StateMachine EJB代表的模型的Actor的指定屬性值 |
GetActorProperties | 取得StateMachine EJB代表的模型的Actor的名稱、類型和每個屬性的值 |
StartMachine | 執行模型里的每個狀態的初始化轉換,啟動StateMachine EJB代表的模型的Actor的狀態模型 |
InjectEvent | 把指定事件插入StateMachine EJB代表的模型的Actor的狀態模型里 |
在保險索賠的例子里,插入器(Injector)工作流調用狀態機的 injectEvent()方法。在WebLogic Integration 7.0里,工作流使用一個業務操作來調用這個方法,而在 WebLogic Integration 8.1,則用EJB控件來完成。
數據庫架構
圖4顯示了保持Actor持久狀態信息的表結構。.

在主表 Actor里,保存了特定 模型類型的Actor的當前狀態。當前狀態是指Actor目前所處的葉子狀態。這個表的主鍵由Actor的唯一標識符和它的模型命名空間組合而成的。一個Actor可能在多個 模型命名空間里具有狀態。特別的是,如果在不同的命名空間里存在著同一模型的二個版本,那么在每個命名空間里的Actor狀態都能在Actor表里表示。
Actor_State表捕捉特定命名空間里的特定Actor的復合狀態的活動子狀態。這個表僅供內部使用,狀態引擎用來來實現歷史恢復。在actor和actor_state表之間存在著一對多的關系。
Actor_Property 表保存特定命名空間里的特定Actor的用戶自定義屬性。每個屬性都有一個名字(對于每個命名空間的每個Actor,名字必須是唯一的),一個類型,和一個值。這個表為客戶應用程序提供了方便,可以把一組數據與角色關聯;更常見的情況是,應用程序的數據保存在應用程序的數據存儲機制里。
部署
StateMachine EJB會為每個狀態模型部署一個不同的實例。每個實例的源代碼是相同的(相同的home和 remote接口,相同的實現),但是具體的配置不同。StateMachine EJB的部署描述符指定了唯一的JNDI(Java命名和目錄接口)名(客戶用這個名字來定位EJB),還有一個對模型XML文件的引用。例如,保險狀態模型的StateMachine EJB可能有一個 JNDI 名"state_insurance" ,并指向文件 "Insurance.xml"。要與這個模型交互,客戶應用程序可以用"state-insurance"這個JNDI名來訪問模型的EJB并調用EJB的方法。這個特殊的方法有著顯著的優勢:
·生命周期:要想準備好一個可供處理的新狀態模型,需要部署一個指定該模型的StateMachine EJB。要想取消這個模型,需要取消EJB的部署。要把變化交給模型,需要用修改過的模型文件重新部署EJB。
·版本管理:如果現有的狀態模型有一個新的主版本,那么新版本可以部署成獨立的EJB,與以前的版本并存。
例如,“state-insurance-1.1”可以與“state-insurance”并存。
數據模型同樣支持版本管理。給定的Actor有多個模型的持久狀態,包括相同模型的不同版本,只有模型有不同的名稱。.
大多數業務流程要運行相當長的時候,所以應用程序升級的管理變得極富挑戰。有二個場景很難解決:
1.做了一個小補丁,但是有Actor正在用沒有打補丁的版本運行著。
2. 做了一個主要補丁,只有新Actor可用,舊的Actor仍然使用以前的版。.
E-State 是解決這些問題聰明的解決方案:
1.應用小補丁,意味著為模型重新部署現有EJB。Actor會在停止的地方重新開始,補丁同時發揮作用。.
2.應用主要補丁,意味著用一個獨立的命名空間部署新的EJB,而現在已經部署的EJB保持不變。模型彼此獨立,這樣老Actor用舊版本運行,新Actor用新版本運行。
結束語
E-State是由ROOM方法所啟發的一個企業級狀態機框架。它與BMP工作流集成在一起,提供了關鍵的集成服務,例如系統集成、事件、計時器、工作列表以及XML。這為開發面向流程的業務應用程序提供了強大的解決方案。E-State中包括:運行時引擎,狀態模型架構,持久性服務,用戶自定義“動作”類(在發生轉換時,在進入或退出狀態時,引擎會調用用戶自定義“動作”類。)動作類調用工作流來利用BPM服務;而工作流被事件觸發時,則調用引擎來觸發轉換。
參考資料
· Selic, Gullickson, and Ward. (1994). Real-Time Object-Oriented Modeling. Wiley.