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

在這個(gè)模型里,轉(zhuǎn)換 ab 會(huì)使?fàn)顟B(tài) a1 或 a2 變成狀態(tài) b; 而轉(zhuǎn)換ac 會(huì)使?fàn)顟B(tài)從 a1 或 a2 變成 c。所有狀態(tài)的轉(zhuǎn)換toC 會(huì)使?fàn)顟B(tài)變成c。 在狀態(tài) a1里的轉(zhuǎn)換 ba 會(huì)形成新的狀態(tài) a。初始狀態(tài)是 b。狀態(tài)從 a1變?yōu)?/span>a2時(shí),要經(jīng)過轉(zhuǎn)換 a1a2,從 a2 變?yōu)?/span>a1時(shí),要經(jīng)過a2a1。轉(zhuǎn)換ca把狀態(tài)引到一個(gè)選擇點(diǎn):如果最后的狀態(tài)是a1,就變成a1,否則就變成 a2。
同樣的場(chǎng)景,用層次結(jié)構(gòu)來表示,理解起來就容易多了,如圖2所示:

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

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

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