流程引擎內(nèi)核僅是“滿足Process基本運(yùn)行”的最微小結(jié)構(gòu),而整個(gè)引擎則要復(fù)雜很多,包括“狀態(tài)存儲(chǔ)”、“事件處理”、“組織適配”、“時(shí)間調(diào)度”、“消息服務(wù)”等等外圍的服務(wù)性功能。引擎內(nèi)核,僅包含最基本的對(duì)象和服務(wù),以及用于解決流程運(yùn)行問題的調(diào)度機(jī)制和執(zhí)行機(jī)制。
如果,你掌握了一個(gè)流程引擎的靈魂,你才有能力理解它的全部。否則,一個(gè)引擎對(duì)你來(lái)說(shuō),可能只是一個(gè)復(fù)雜的結(jié)構(gòu),豐富多彩API、令人眼花繚亂的“功能”和“服務(wù)”而已。
本
身工作流這個(gè)領(lǐng)域就是一個(gè)很“狹窄”的領(lǐng)域,國(guó)內(nèi)的廠商也不是很多,其中有部分實(shí)現(xiàn)技術(shù)并不弱。但可能涉于安全等因素,并沒有多少技術(shù)人員探討“深度的工
作流技術(shù)實(shí)現(xiàn)問題”。而廣大的開發(fā)愛好者卻還在花費(fèi)大量的時(shí)間在摸索“如何理解工作流、如何應(yīng)用工作流”。
所以在此之前,國(guó)內(nèi)尚未有一篇技術(shù)文章探討工作流引擎內(nèi)核的實(shí)現(xiàn),當(dāng)然也沒有探討jBpm引擎內(nèi)核的文章了。在
www. 技術(shù)站點(diǎn)和我的blog(
http://blog.csdn.net/james999)上有幾篇專門探討jbpm應(yīng)用的文章,對(duì)于初步想了解如何使用jbpm的讀者來(lái)說(shuō),值得看看。
對(duì)于這方面的技術(shù)分享,開源是個(gè)不錯(cuò)的突破口。
本篇就是以jBpm為實(shí)例,來(lái)詮釋工作流引擎的內(nèi)核設(shè)計(jì)思路和結(jié)構(gòu)。但是這僅僅是從jBpm的實(shí)現(xiàn)角度來(lái)輔助大家理解,因?yàn)楣ぷ髁饕鎯?nèi)核的設(shè)計(jì)、實(shí)現(xiàn)是有很多方式:這會(huì)因所選的模型、調(diào)度算法、推進(jìn)機(jī)制、狀態(tài)變遷機(jī)制、執(zhí)行機(jī)制等多方面的不一樣,而會(huì)差別很大。比如基于Activity Diagram模型的jBpm和基于FSM模型的OSWorkflow引擎內(nèi)核之間就有很大的差別。
相比較而言,jBpm的模型比較復(fù)雜,而引擎內(nèi)核實(shí)現(xiàn)的比較“精簡(jiǎn)”,非常便于大家“由淺入深的理解”。
2 閱讀本篇的基礎(chǔ)準(zhǔn)備
本文的讀者群主要是面向有一定工作流基本概念的開發(fā)人員。所以本文認(rèn)為你已經(jīng)具備了如下基本工作流知識(shí):
(1) 初步了解工作流系統(tǒng)結(jié)構(gòu)。比如理解工作流引擎在工作流系統(tǒng)中所處的位置和作用
(2) 對(duì)流程定義(Process Definition)和流程實(shí)例(Process Instance)相關(guān)對(duì)象有所了解。比如理解Process Instance代表什么,工作項(xiàng)(WorkItem)代表什么。
在閱讀本篇的時(shí)候,如果你已經(jīng)搭建了一套jbpm的開發(fā)環(huán)境,那么將有助于你更容易理解本篇的很多內(nèi)容,也便于實(shí)際體驗(yàn)代碼。從
www.官方網(wǎng)站下載jbpm-starters-kit開發(fā)包,按照其參考手冊(cè),可以很容易在eclipse開發(fā)環(huán)境中建立項(xiàng)目,效果圖類似如下:
我比較推崇“微內(nèi)核的流程引擎構(gòu)架”,并在最近兩三年內(nèi)寫了兩篇探討此方面的文章:第一篇是寫于05年7月份的《微內(nèi)核流程引擎架構(gòu)體系》,第二篇是07年7月份的《微內(nèi)核過程引擎的設(shè)計(jì)思路和構(gòu)架》(受普元《銀彈》雜志約稿所寫,尚未對(duì)外公開)。
但至今對(duì)外闡述引擎內(nèi)核到底是什么。

正如上面的兩張圖所示,我們可以通過“微內(nèi)核”的構(gòu)架來(lái)使得流程引擎的結(jié)構(gòu)更加“清晰”。而能否實(shí)現(xiàn)“微內(nèi)核”的根本,則是看你是否能夠設(shè)計(jì)并抽象出“良好的引擎內(nèi)核結(jié)構(gòu)”。
很顯然,要想設(shè)計(jì)出一套結(jié)構(gòu)優(yōu)良的引擎內(nèi)核,首要條件就是:明白什么是引擎內(nèi)核。
首
先我們需要明白引擎是什么,引擎可以做什么。這在WfMC的《工作流參考模型》中已經(jīng)有很詳細(xì)的解答,本文不再重復(fù)。知道這個(gè)僅僅是不夠的,你還需要很清
晰的明白如何去“為流程建模”,而這則在Aalst大師所著的《工作流管理——模型、方法、系統(tǒng)》一書有細(xì)致闡述,本文也不再重復(fù)。
但很可惜,至今尚未有一本專門的書籍來(lái)論述“過程建模方法”的,或者說(shuō)如何利用這些既有的“過程建模方法(諸如FSM、PetriNet、EPC、Activity Diagram等等)”來(lái)解決流程問題。這個(gè)只能分別查閱相關(guān)資料,此處也不敘述。
因?yàn)槲谋局恢v“引擎內(nèi)核”。
如果我們暫且把那復(fù)雜的流程業(yè)務(wù)性問題,諸如“組織模型分配”、“分支條件計(jì)算”、“事件處理”、“消息調(diào)度”、“工作項(xiàng)處理”、“存儲(chǔ)”、“應(yīng)用處理”、以及那些“變態(tài)的諸如會(huì)簽、回退之類的模型”都統(tǒng)統(tǒng)的拋棄,只留下“最單純的過程性問題”,也就是“解決一個(gè)過程運(yùn)行問題,按秩序的從一個(gè)節(jié)點(diǎn)到另一個(gè)節(jié)點(diǎn)的執(zhí)行”。——這就是引擎內(nèi)核所關(guān)注的根本問題。
上面這句話,估計(jì)會(huì)引起很多人“拍磚”。在很多人看來(lái),工作流之所以看起來(lái)很“難”,就是因?yàn)檫@些復(fù)雜多變的“業(yè)務(wù)性問題”都統(tǒng)統(tǒng)綁在一個(gè)“引擎”上造成的。
其
實(shí),這是兩個(gè)“維度”的問題,也就是“引擎的抽象”和“引擎的應(yīng)用”這兩個(gè)不同維度,不同層面的問題。但這絕不是兩個(gè)獨(dú)立的問題,“引擎的抽象”的好與
壞,直接影響到“引擎的應(yīng)用”的可復(fù)雜度和可支持度,當(dāng)然我們也不能否認(rèn),“引擎的應(yīng)用”問題也是一個(gè)很復(fù)雜的問題。但本文是站在“引擎的抽象”這個(gè)維度
來(lái)闡述問題的。對(duì)于“引擎的應(yīng)用”問題,可參考我的前作:2003年11月份的《工作流模型分析》、2003年12月份的《工作流授權(quán)控制模型》、
2004年7月份的《工作流系統(tǒng)中組織模型應(yīng)用解決方案》。
也就是說(shuō),本文不是指導(dǎo)大家如何去“使用jbpm”,而是闡述“jbpm的引擎的內(nèi)核部分是如何構(gòu)建的”。但本文的主旨不是告訴大家“jBpm是如何設(shè)計(jì)引擎內(nèi)核的”,而是以jBpm為例,來(lái)介紹“引擎內(nèi)核”。
引擎內(nèi)核所關(guān)注的是一個(gè)非常“抽象”層面的問題,而不同引擎關(guān)注的“一套完整的執(zhí)行環(huán)境”?;蛘呶覀兛梢赃@么來(lái)說(shuō),引擎內(nèi)核的職責(zé)是非常“精簡(jiǎn)”的:確保流程按照既有的定義,從一個(gè)節(jié)點(diǎn)運(yùn)行到另一個(gè)節(jié)點(diǎn),并正確執(zhí)行當(dāng)前節(jié)點(diǎn)。
總的來(lái)說(shuō),引擎內(nèi)核主要關(guān)注四個(gè)方面的問題:
(1) 流程定義問題:不是說(shuō)如何圖形化的定義流程,而是如何用一套定義對(duì)象,來(lái)詮釋所定義的流程。
(2) 流程調(diào)度問題:提供什么的機(jī)制,可以確保流程能夠處理復(fù)雜的“流程圖結(jié)構(gòu)”,諸如串行、并行、分支、聚合等等,并在這復(fù)雜結(jié)構(gòu)中確保流程從一個(gè)節(jié)點(diǎn)運(yùn)行到另一個(gè)節(jié)點(diǎn)。
(3) 流程執(zhí)行問題:當(dāng)流程運(yùn)行到某個(gè)節(jié)點(diǎn)的時(shí)候,需要一套機(jī)制來(lái)解決:是否執(zhí)行此節(jié)點(diǎn),并如何執(zhí)行此節(jié)點(diǎn)的問題,并維持節(jié)點(diǎn)狀態(tài)生命周期。
(4) 流程實(shí)例對(duì)象:需要一整套流程實(shí)例對(duì)象來(lái)描述流程實(shí)例運(yùn)行的狀態(tài)和結(jié)果。
工作流引擎本身就是一種“base on model”的組件,流程實(shí)例的執(zhí)行都是依賴于所定義的“流程定義”,而工作流引擎則是提供了這樣一種環(huán)境,來(lái)維持流程實(shí)例的運(yùn)行。
所以引擎內(nèi)核,必須提供一套定義對(duì)象來(lái)描述“流程定義”,并且這些定義對(duì)象必須反映出一種“模型”。
比如jBpm的定義對(duì)象,是與其所基于的Activity Diagram模型相對(duì)應(yīng)的。
引擎內(nèi)核的另一個(gè)重要功能,就是保證流程實(shí)例準(zhǔn)確的從一個(gè)節(jié)點(diǎn)運(yùn)行到另一個(gè)節(jié)點(diǎn),而這則需要依賴于一套調(diào)度機(jī)制。
引擎的調(diào)度機(jī)制有很多種實(shí)現(xiàn)方法,有的甚至是與“所依賴的模型有關(guān)”。但普遍來(lái)講,很多引擎都受到Petri Net的影響,而采用token來(lái)調(diào)度。
jBpm本身就吸納的token這套機(jī)制,當(dāng)然,與Petri Net的調(diào)度機(jī)制還是有所區(qū)別。我們將在下面的章節(jié)詳細(xì)介紹。
經(jīng)過引擎的調(diào)度,實(shí)例運(yùn)行到某個(gè)節(jié)點(diǎn)了,此時(shí)必須必須提供一套機(jī)制,來(lái)判斷當(dāng)前節(jié)點(diǎn)是否可執(zhí)行,如果可執(zhí)行,那么需要提供一套runtime envrioment來(lái)執(zhí)行節(jié)點(diǎn)——這就是引擎的執(zhí)行機(jī)制。
復(fù)雜的流程引擎會(huì)依賴于“流程實(shí)例狀態(tài)”或“活動(dòng)實(shí)例狀態(tài)”的約束和變遷來(lái)進(jìn)行處理。之所有有時(shí)候我們會(huì)把一個(gè)流程引擎也叫做“狀態(tài)機(jī)”,很大程度上也是這個(gè)原因。
每個(gè)一個(gè)流程實(shí)例,必須維護(hù)一套屬于自己的“運(yùn)行環(huán)境和數(shù)據(jù)”,而這則是實(shí)例對(duì)象的責(zé)任了?;旧蠈?shí)例對(duì)象會(huì)包含如下信息:
(1) 與流程實(shí)例的狀態(tài)或控制信息
(2) 與活動(dòng)實(shí)例的狀態(tài)或控制信息。如果某些引擎不支持活動(dòng)實(shí)例,那么必然會(huì)有某些其他實(shí)例信息,可以當(dāng)前節(jié)點(diǎn)的狀或控制信息。
(3) 一些臨時(shí)的“執(zhí)行”信息,便于引擎針對(duì)某種情況進(jìn)行處理
好
的開源工作流引擎不多,jbpm和osworkflow算是其中兩個(gè)有特色而且比較容易實(shí)際應(yīng)用的。目前一些國(guó)內(nèi)的中小型流程應(yīng)用項(xiàng)目,就是在jbpm或
osworkflow的基礎(chǔ)上擴(kuò)展實(shí)現(xiàn)。jBpm采用了Activity Diagram的模型,而osworkflow則是FSM的模型。
當(dāng)然,這僅僅是jbpm3之后的事情。自從被Jboss收購(gòu)之后,jbpm對(duì)早先的2.0構(gòu)架進(jìn)行了重組,整個(gè)結(jié)構(gòu)完全本著“微內(nèi)核”的思想進(jìn)行設(shè)計(jì)。
現(xiàn)在這里從技術(shù)角度來(lái)分析jbpm3的優(yōu)點(diǎn),簡(jiǎn)單羅列幾個(gè)大家都容易看見的:
(1) jbpm的模型是采用UML Activity Diagram的語(yǔ)義,所以便于開發(fā)人員理解流程。
(2) jbpm提供了可擴(kuò)展的Event-Action機(jī)制,來(lái)輔助活動(dòng)的擴(kuò)展處理。
(3) jbpm提供了靈活的條件表達(dá)式機(jī)制,來(lái)輔助條件解析、腳本計(jì)算的處理。
(4) jbpm提供了可擴(kuò)展的Task及分配機(jī)制,來(lái)滿足復(fù)雜人工活動(dòng)的處理。
(5) 借助hibernate的ORM的優(yōu)勢(shì),jbpm能夠很容易支持多種數(shù)據(jù)庫(kù)。
當(dāng)然,還有一些優(yōu)點(diǎn),是很多開發(fā)人員并不太注意的,比如:
(1) jbpm的Node機(jī)制非常靈活,開發(fā)人員可以很容易定制“業(yè)務(wù)化語(yǔ)義的節(jié)點(diǎn)”,并滿足運(yùn)行時(shí)候處理的需要。
有很多靈活的優(yōu)點(diǎn),當(dāng)然也少不了存在一些“局限”。
(1) 很顯然,只能有一個(gè)start-state。
(2) jbpm依靠Token來(lái)調(diào)度和計(jì)算,在同一個(gè)時(shí)刻中,一個(gè)ProcessInstance只允許一個(gè)Token對(duì)象只存在一個(gè)Node中(分支當(dāng)然用Child Token對(duì)象處理)。所以本質(zhì)上就不支持“multi-instance”模式。
(3) jbpm作為一款開源的工作流引擎,其更多的是關(guān)注“如何輔助你更容易的讓流程運(yùn)行完成”,但是并不記錄“流程運(yùn)行的歷史和軌跡”。這一點(diǎn)可能是東西方文化的差異性所在,因?yàn)閲?guó)內(nèi)的流程應(yīng)用,比較關(guān)注“運(yùn)行軌跡”。
至
于其他的一些局限,比如不支持“回退”、“跳轉(zhuǎn)”等操作,這也是因?yàn)闁|西方文化的差異所在。西方人認(rèn)為“往回流轉(zhuǎn)的情況肯定也是一種業(yè)務(wù)規(guī)則所定義,那么
肯定可以通過分支或條件來(lái)解決”,而東方則把“回退作為一個(gè)人性化管理和處理的潛在特點(diǎn)”。所以諸如此類的一些“特定需求”,估計(jì)只能通過擴(kuò)展jbpm來(lái)
實(shí)現(xiàn)了,甚至有時(shí)候,簡(jiǎn)單的擴(kuò)展是無(wú)法解決問題的——正如上一節(jié)所說(shuō)的那樣,“引擎的抽象”會(huì)影響“引擎的應(yīng)用”的復(fù)雜度支持。
但是,當(dāng)你試圖修改jbpm代碼的時(shí)候,你會(huì)顧慮jbpm的LGPL協(xié)議嗎?(很多國(guó)內(nèi)企業(yè)從來(lái)不考慮這個(gè)協(xié)議問題,寒)。
這里說(shuō)的“定義流程”并不是說(shuō)jbpm3中那個(gè)基于eclipse plugin的圖形化建模工具。而是如何去解決“形式化的描述一個(gè)流程”的問題。
形
式化的描述流程并不是一個(gè)簡(jiǎn)單的問題,從上世紀(jì)七十開始,人們就在探索用各種各樣多的模型來(lái)描繪流程:Petri Net, FSM, EPC,
Activity Diagram, 以及近來(lái)的XPDL MetaModel等等,延伸到如今的BPEL,BPMN,BPMD等等。
jBpm采
用了Activity Diagram的模型語(yǔ)義:其將用Start State、State、Action State(Task
Node)、End State、Fork、Join、Decision、Merge、Process
State這幾個(gè)“元素”的組合來(lái)描述任何一個(gè)流程。其中Action State是Activity
Diagram中的標(biāo)準(zhǔn)語(yǔ)義,在jBpm為了便于大家理解和使用,jBpm采用了TaskNode這個(gè)語(yǔ)義。
在WfMC的Workflow Reference Model中,對(duì)流程引擎的功能描述,其中就包含一項(xiàng):解析流程定義。如果想滿足這這功能,前提條件就必須有最基本的兩個(gè):
(1) 有一套形式化的描述語(yǔ)言(通常為xml格式)。利用這個(gè)描述語(yǔ)言可以描述一個(gè)流程的定義。比如WfMC所提出的XPDL這個(gè)描述語(yǔ)言。當(dāng)然,jBpm也有自己的一套,名為jPDL,也是一個(gè)xml格式的。
(2) 有一套對(duì)象集可以反映流程的定義模型和結(jié)果,一般叫做定義對(duì)象。流程引擎就需要把“xml格式的流程定義”解析為一套對(duì)象,而這套對(duì)象的結(jié)構(gòu)則反映了流程的結(jié)構(gòu)。
我們暫且不去探討jPDL那個(gè)形式化的xml語(yǔ)言,而把重心放在jBpm那套定義對(duì)象中。因?yàn)檫@個(gè)定義對(duì)象是屬于Engine Kernel的一部分。
面向?qū)ο蟮睦^承性、多態(tài)性可以讓我們從最抽象的部分來(lái)描述對(duì)象。那么這套定義對(duì)象也需要從最基礎(chǔ)的“抽象”說(shuō)起。
process的本質(zhì)就是“節(jié)點(diǎn)”和“有向弧”,當(dāng)然你也可以說(shuō)是Node和Link,或者Node和Transition,或者Activity和Transition等等之類的。jBpm采用的是Node和Transition來(lái)表示“節(jié)點(diǎn)”和“有向弧”。
于是乎,在jbpm中你可以看到這樣的結(jié)構(gòu)關(guān)系:
對(duì)于一個(gè)節(jié)點(diǎn)來(lái)說(shuō),從定義角度,其只關(guān)心幾個(gè)事情:
(1) 這是個(gè)什么類型的節(jié)點(diǎn)。這個(gè)節(jié)點(diǎn)可能是start state,也可能是一個(gè)task node,或者是一個(gè)fork。
(2) 這個(gè)節(jié)點(diǎn)的轉(zhuǎn)入Transition和轉(zhuǎn)出Transition。
可
能有的人會(huì)說(shuō),還需要關(guān)心節(jié)點(diǎn)的轉(zhuǎn)入轉(zhuǎn)出的類型,比如And Splite或者Xor
Join之類。這個(gè)并沒有錯(cuò),因?yàn)楹芏嗔鞒棠P偷墓?jié)點(diǎn)元素需要考慮這個(gè),比如WfMC的XPDL模型。但是jBpm的節(jié)點(diǎn)是沒有這樣的屬性的,或者說(shuō)的更
準(zhǔn)確些,是Activity Diagram模型的節(jié)點(diǎn)沒有這樣的特性?;顒?dòng)圖是采用“Fork”、“Join”這樣的節(jié)點(diǎn)來(lái)解決“分支”問題。
僅利用節(jié)點(diǎn)和轉(zhuǎn)移的組合,就可以表達(dá)一個(gè)“過程(Process)”。當(dāng)然這個(gè)流程只能告訴人們“大概的業(yè)務(wù)過程”,當(dāng)然不包括很復(fù)雜的信息。如下圖所示:
這是一張非常標(biāo)準(zhǔn)的“活動(dòng)圖”,如果我們用jbpm的設(shè)計(jì)器,看看這樣一張“流程圖”:
不論你如何繪畫,改變不了這張圖的本質(zhì):它就只有兩個(gè)基本元素:節(jié)點(diǎn)和轉(zhuǎn)移。只是有的節(jié)點(diǎn)是start-state,有的是task-node,有的是join,有的是end state而已。
我
們可以通過定義自己的Node節(jié)點(diǎn)對(duì)象,來(lái)補(bǔ)充jbpm自定的節(jié)點(diǎn)對(duì)象。只需要extends
Node,并重寫讀寫xml的read和write方法,重寫負(fù)責(zé)執(zhí)行的execute方法,在
org/jbpm/graph/node/node.types.xml中配置即可,當(dāng)然,你可以寫的更加復(fù)雜,更加業(yè)務(wù)化的節(jié)點(diǎn)。
jBpm的過程調(diào)度機(jī)制是吸納了Petri Net的一些思想。
jBpm采用Token來(lái)表示當(dāng)前實(shí)例運(yùn)行的位置,也利用token在流程各個(gè)點(diǎn)之間的轉(zhuǎn)移來(lái)表示流程的推進(jìn),如下圖所示:
當(dāng)jbpm試圖去啟動(dòng)一個(gè)流程的時(shí)候,首先是構(gòu)造一個(gè)流程實(shí)例,并為此流程實(shí)例創(chuàng)建一個(gè)Root Token,并把這個(gè)Root Token放置在Start Node上。
以下截取部分代碼實(shí)現(xiàn),僅供參考。手頭有jbpm3相應(yīng)開發(fā)環(huán)境的朋友,可以打開ProcessInstance和Token這兩個(gè)類。(注:以下所有參考代碼,為了突出主題,都已經(jīng)將實(shí)際代碼中的event,log等處理刪除)
|
public ProcessInstance( ProcessDefinition processDefinition ) {
this.processDefinition = processDefinition;
this.rootToken = new Token(this);
|
|
public Token(ProcessInstance processInstance) {
this.processInstance = processInstance;
this.node = processInstance.getProcessDefinition().getStartState();
|
jbpm是允許在start-state執(zhí)行Task的,也允許在start-state創(chuàng)建工人任務(wù)。不過此處我們不予討論。
當(dāng)Token已經(jīng)在Start-State節(jié)點(diǎn)了,我們可以開始往前推進(jìn),來(lái)促使流程實(shí)例往前運(yùn)行。對(duì)于外部操作來(lái)說(shuō),觸發(fā)流程實(shí)例往下運(yùn)行的操作有兩個(gè):
(1) 強(qiáng)制執(zhí)行ProcessInstance的signal操作
(2) 執(zhí)行TaskInstance的end操作。
但是,這兩個(gè)操作,都是通過“當(dāng)前token的signal操作”來(lái)內(nèi)部實(shí)現(xiàn)的,如下圖所示:
Token的Signal操作表示:實(shí)例需要離開當(dāng)前token所在的節(jié)點(diǎn),轉(zhuǎn)移到下一個(gè)節(jié)點(diǎn)上。因?yàn)?/span>Node與Node之間是“Transition”這個(gè)橋梁,所以,在轉(zhuǎn)移過程中,會(huì)首先把Token放入相關(guān)連的Transtion對(duì)象中,再由Transition對(duì)象把Token交給下一個(gè)節(jié)點(diǎn)。
讓我們來(lái)看看Token類中signal方法的部分代碼實(shí)現(xiàn),僅供參考:
|
public void signal() {
//注意ExecutionContext對(duì)象
signal(node.getDefaultLeavingTransition(), new ExecutionContext(this));
}
void signal(Transition transition, ExecutionContext executionContext) {
// start calculating the next state
node.leave(executionContext, transition);
}
|
接下來(lái),請(qǐng)注意node.leave()這個(gè)操作。這是一個(gè)很有意思的語(yǔ)義轉(zhuǎn)換:我們是采用token的signal操作來(lái)表示往下一個(gè)節(jié)點(diǎn)推進(jìn),但是實(shí)際確實(shí)執(zhí)行的node.leave ()操作。
如果這地方讓你自己來(lái)實(shí)現(xiàn),代碼會(huì)不會(huì)就是這樣子呢?不妨此處想一想。
|
//假設(shè)代碼,僅供思考 void signal(Transition transition, ExecutionContext executionContext) {
transition.take(executionContext);
}
|
前
面說(shuō)過,jbpm的調(diào)度機(jī)制吸納的Petri Net的思想。在Petri
Net中,并沒有transition中駐留token這個(gè)語(yǔ)義,token只駐留在庫(kù)所(Place)中。所以,jbpm此處的設(shè)計(jì)思路,是于此有一定
關(guān)系的。所以只是把一個(gè)ExecutionContext對(duì)象放在了transition中,而不是一個(gè)token對(duì)象。
讓我們來(lái)看看node對(duì)象的leave方法:
|
public void leave(ExecutionContext executionContext, Transition transition) {
Token token = executionContext.getToken();
token.setNode(this);
executionContext.setTransition(transition);
executionContext.setTransitionSource(this);
transition.take(executionContext);
}
|
我們直接跟蹤進(jìn)Transition的take操作:
|
public void take(ExecutionContext executionContext) {
executionContext.getToken().setNode(null);
// pass the token to the destinationNode node
to.enter(executionContext);
}
|
經(jīng)過這么多的中間步驟,我們終于把ExecutionContext對(duì)象從一個(gè)node轉(zhuǎn)移到下一個(gè)node了。讓我們來(lái)看看Node對(duì)象的enter操作:
|
public void enter(ExecutionContext executionContext) {
Token token = executionContext.getToken();
token.setNode(this);
// remove the transition references from the runtime context
executionContext.setTransition(null);
executionContext.setTransitionSource(null);
// execute the node
if (isAsync) {
} else {
execute(executionContext);
}
}
|
至此,jBpm成功的從一個(gè)節(jié)點(diǎn)轉(zhuǎn)移到下一個(gè)節(jié)點(diǎn)了?!?這就是jbpm的調(diào)度機(jī)制。
怎么樣,是不是非常的簡(jiǎn)單?
讓我們把整個(gè)過程,用一張更清晰的“思維圖”來(lái)展示一下:
8 jBpm的過程執(zhí)行機(jī)制
前面我們的“過程調(diào)度機(jī)制”是為了讓流程可以正確的從“一個(gè)節(jié)點(diǎn)轉(zhuǎn)移到下一個(gè)節(jié)點(diǎn)”,而本節(jié)所要講解的jbpm“執(zhí)行機(jī)制”,則是為提供一個(gè)運(yùn)行機(jī)制,來(lái)保證“節(jié)點(diǎn)的正確執(zhí)行”。
首先我們需要明確如下的概念:
(1) 節(jié)點(diǎn)有很多中,每種節(jié)點(diǎn)的執(zhí)行方式肯定是不一樣的
(2) 節(jié)點(diǎn)有自己的生命周期,不同的生命周期階段,所處的狀態(tài)不同。
在WfMC的《工作流參考模型》文檔中,為活動(dòng)實(shí)例歸納了幾個(gè)可參考的生命周期。(僅供參考,實(shí)際很多工作流引擎的節(jié)點(diǎn)的生命周期要比這復(fù)雜)
但是,jbpm并沒有突出“節(jié)點(diǎn)生命周期”這個(gè)理念,僅僅只是在“Event”中體現(xiàn)出出來(lái)。在我看來(lái),可能的原因有兩個(gè):
(1) jBpm沒有NodeInstance這個(gè)概念。利用Token和TaskInstance,jBpm足以持久化足夠的信息,能夠讓流程實(shí)例迅速定位到當(dāng)前運(yùn)行的狀態(tài)。
(2) jBpm的Event已經(jīng)很豐富,并且這個(gè)Event是圍繞“Token的轉(zhuǎn)移”而設(shè)置的,并不是圍繞Node的生命周期設(shè)置的。
(3) 通常我們需要在Active和Completed的生命周期內(nèi)所要操作的分支與聚合,在jBpm模型中分別由Fork、Join之類的節(jié)點(diǎn)替代。所以jBpm過分關(guān)注Node生命周期的管理意義不是非常大。
作
為個(gè)人,我并不行賞jBpm這樣拋棄“節(jié)點(diǎn)生命周期管理”的實(shí)現(xiàn)方式,更行賞OBE(最早的基于XPDL模型的java工作流引擎之一)的生命周期約束和
管理。但是,也不得不承認(rèn),jBpm規(guī)避了“繁瑣的狀態(tài)維護(hù)”,反而讓處理變得“簡(jiǎn)易”,也更容易被大家所理解和接受,而這也正是OBE逐漸消失的一個(gè)原
因:過于復(fù)雜和臃腫。
讓我們?cè)谇懊婺菑坖Bpm的“調(diào)度機(jī)制思維圖”上,再稍稍補(bǔ)充一點(diǎn)(為了突出顯示,與上圖有所改動(dòng))。
這張圖應(yīng)該可以很好的詮釋出,jBpm是如何執(zhí)行各種節(jié)點(diǎn)的,這也是得益于OO的“多態(tài)與繼承”特性。
jBpm的執(zhí)行機(jī)制非常簡(jiǎn)單,但還是需要稍微補(bǔ)充一下有關(guān)“分支”方面的處理。
jBpm采用sub token的機(jī)制來(lái)解決分支方面的處理:當(dāng)遇到有分支的時(shí)候,會(huì)為每個(gè)分支節(jié)點(diǎn)創(chuàng)建一個(gè)child token。在聚合節(jié)點(diǎn)(Join或Merge),則依賴其同步或異步的聚合方式,來(lái)分別處理。
比如我們參看Fork節(jié)點(diǎn)的執(zhí)行代碼(為了突出重點(diǎn),省略部分代碼):
|
public void execute(ExecutionContext executionContext) {
Token token = executionContext.getToken();
Iterator iter = transitionNames.iterator();
while (iter.hasNext()) {
String transitionName = (String) iter.next();
forkedTokens.add(createForkedToken(token, transitionName));
}
iter = forkedTokens.iterator();
while( iter.hasNext() ) {
//省略部分代碼
ExecutionContext childExecutionContext = new ExecutionContext(childToken);
leave(childExecutionContext, leavingTransitionName);
}
}
protected ForkedToken createForkedToken(Token parent, String transitionName) {
Token childToken = new Token(parent, getTokenName(parent, transitionName));
forkedToken = new ForkedToken(childToken, transitionName);
return forkedToken;
}
|
至于Merge節(jié)點(diǎn),我想此處不用在累贅的展示,有興趣的,可以參看Merge類的execute方法,即可。
9 jBpm內(nèi)核結(jié)構(gòu)與實(shí)例對(duì)象
Jbpm引擎內(nèi)核的結(jié)構(gòu)非常“精簡(jiǎn)”。除了我們上面所說(shuō)的那些定義對(duì)象(各種Node節(jié)點(diǎn)和Transtion),還有幾個(gè)與“運(yùn)行實(shí)例”相關(guān)的對(duì)象。如下圖所示,jbpm引擎內(nèi)核對(duì)象主要是在org.jbpm.graph.def和org.jbpm.graph.exe包。
(1) 我們需要描述一個(gè)流程實(shí)例,所以需要一個(gè)ProcessInstance對(duì)象。
(2) 每個(gè)流程實(shí)例,都會(huì)維護(hù)一套屬于其自己的“執(zhí)行環(huán)境”,也就是ExecutionContext對(duì)象。注意,這里是一套,而不是一個(gè)。
上
半年寫了些bpm和SOA的文章,也被csdn的好友拉著忽悠了不少這方面的概念,弄的好像我開始搞這方面的工作似的。其實(shí)不然,本質(zhì)工作與這有“天壤之
別”,完全是非常底層的java技術(shù)應(yīng)用。而workflow,也有兩三年沒有從事這方面的開發(fā)了,所以寫此篇文章,著實(shí)費(fèi)了點(diǎn)功夫。
想痛痛快快寫篇有關(guān)“引擎內(nèi)核”的文章,這個(gè)想法由來(lái)以及了,卻擔(dān)心自己不足以詮釋清楚,反而容易誤導(dǎo)他人,遂中途多次放棄。
正如前面所說(shuō)的那樣,引擎內(nèi)核的實(shí)現(xiàn),并沒有一套“固定的模式”或者“固定的實(shí)現(xiàn)體系”,會(huì)因?yàn)楹芏嘁蛩囟斐蓪?shí)現(xiàn)不同。如果想把“引擎內(nèi)核”的實(shí)現(xiàn)真正詮釋清楚,必須把這些相關(guān)因素都詮釋明朗——但這依然是一個(gè)浩大的工程。
前些日子,受朋友所托,為他們的公司學(xué)員講了幾節(jié)工作流的課程,期間嘗試jBpm來(lái)詮釋了一下引擎的實(shí)現(xiàn)思路,發(fā)現(xiàn)效果不錯(cuò)。——受此引發(fā),遂萌發(fā)了以jBpm為實(shí)例,來(lái)簡(jiǎn)單詮釋“流程引擎內(nèi)核”想法。
耗時(shí)一周的業(yè)余時(shí)間,雖然還很難詮釋自己的全部想法,但“點(diǎn)出幾個(gè)要點(diǎn)”,還是應(yīng)該有了。
胡長(zhǎng)城
寫于2007年9月2日星期日 夜。