標(biāo)題:JBPM的一些資料
出處:汐憶時(shí)空::CHIRS::蝶衣人生
時(shí)間:Tue, 20 Jun 2006 09:53:24 +0000
作者:chirs@zhoujin.com
地址:www.zhoujin.com/read.php/18.htm
內(nèi)容:
jPDL的流程定義元素
第一層級(jí):GraphElement
這個(gè)容易理解,因?yàn)樵诋嬃鞒潭x時(shí),每個(gè)拖拉的對(duì)象都是一個(gè)graph的元素.
GraphElement有四個(gè)屬性:1)processDefine 表示當(dāng)前元素屬于哪個(gè)流程定義
2)events 表示可以接收哪些event
3)name 名字
4)exceptionHandlers 異常處理類集合(List)
第二層級(jí):node;processDefinition;Transition;Task 它們都inherited from GraphElement
1)processDefinition表示流程定義(implements NodeCollection),它有下面的屬性:name,version,nodes,startState nodes表示流程中所有的node,startState用于啟動(dòng)流程時(shí)找到首節(jié)點(diǎn)
2)Transition表示轉(zhuǎn)移,它有三個(gè)屬性:from(Node),to(Node),supportedEventTypes表示支持的event類型
3)node表示節(jié)點(diǎn),它有四個(gè)屬性:leaving transitions;arriving transitions;action;superState
4)Task 定義任務(wù)
第三層級(jí):各種不同的node,它們都inherited from node decision;EndState;Fork;Join;Merge;Milestone; InterleaveEnd;InterleaveStart;ProcessState;State 這些都是見名知義,和xpdl差不多.
jbpm的Token
jbpm中最重要的概念,應(yīng)該是令牌(Token)和信令(Signal)
在整個(gè)流程實(shí)例運(yùn)行過程中,我們可以迅速的利用token得到其當(dāng)前的current state。在解決“并行”等(比如Fork)問題時(shí),jBpm讓Token對(duì)象維護(hù)了父子關(guān)系,這種關(guān)系在涉及到Fork的時(shí)候會(huì)產(chǎn)生。 jBpm讓Token這個(gè)對(duì)象身兼了多種使命:(1)快速定位current state (2)用于fork,join算法 (3)用于告知任務(wù)執(zhí)行者的任務(wù)索引。
如下代碼:
//pd是process definition,pi是process instance ProcessInstance
pi = new ProcessInstance( pd );
//得到根令牌
Token token = pi.getRootToken();
//發(fā)信令
token.signal();
Token的signal方法也可以傳入transition參數(shù),這個(gè)方法把信令發(fā)送給Token,這樣,令牌將被激活,并沿指定的transition離開當(dāng)前的狀態(tài)(如果沒有指定transition,將沿缺省的transition 離開當(dāng)前狀態(tài)).
jbpm是怎么實(shí)現(xiàn)的呢?其實(shí)很簡(jiǎn)單:
1)Token記錄了當(dāng)前的狀態(tài)(current state),只有當(dāng)前的狀態(tài)(或稱節(jié)點(diǎn))擁有該令牌
2)向TOKEN發(fā)signal后,當(dāng)前狀態(tài)收到該signal
3)當(dāng)前狀態(tài)把令牌傳給signal中指定的transition
4)transition收到令牌后,不強(qiáng)占,馬上把令牌傳給下個(gè)狀態(tài).
5)根據(jù)令牌的位置,流程的狀態(tài)已經(jīng)發(fā)生改變.
1、process definition
一個(gè)process definition代表了一個(gè)正式的業(yè)務(wù)流程,它以一個(gè)流程圖為基礎(chǔ)。這個(gè)流程圖由 許多node和transition組成。每個(gè)node在這個(gè)流程圖里都有著各自特殊的類型,這些不同的類型決定了node在運(yùn)行時(shí)的不同行為。一個(gè)process definition只有一個(gè)start state 。
2、token
一個(gè)token代表了一條執(zhí)行路徑,它包含了這條執(zhí)行路徑的當(dāng)前的執(zhí)行狀態(tài)(current state)。
3、process instance
一個(gè)process instance(流程實(shí)例)即一個(gè)process definition(流程定義)的流程執(zhí)行實(shí)例。一個(gè)process definition可以對(duì)應(yīng)多個(gè)process instance。當(dāng)一個(gè)process instance被創(chuàng)建的時(shí)候,一個(gè)主執(zhí)行路徑token同時(shí)被創(chuàng)建,這個(gè)token叫做root token,它指向流程定義的start state(processDefinition.getStartState()==token.getNode())。
4、signal
一個(gè)signal 發(fā)送給token通知token 繼續(xù)流程的執(zhí)行。如果signal 沒有指定transition,token將沿缺省的transition離開當(dāng)前狀態(tài),如果signal 指定transition,token將沿指定的transition離開當(dāng)前的狀態(tài)。看源代碼可以看到發(fā)給process instance的signal 其實(shí)都是發(fā)送給了root token。
5、Actions
jbpm提供了靈活的action ,當(dāng)流程執(zhí)行,token 進(jìn)入node和transition時(shí),會(huì)觸發(fā)相應(yīng)的一些event(事件)。在這些event上附上我們自己寫的action,就會(huì)帶動(dòng)action 的執(zhí)行。action里是我們自己的相關(guān)java操作代碼,非常方便。注意的是event(事件)是內(nèi)置的,無法擴(kuò)展。另外,action也可以直接掛在node上,而不依賴于event(事件)的觸發(fā),這個(gè)很重要!
Node
一個(gè)流程圖由許多node和transition組成。每個(gè)node都有一種類型,這個(gè)類型決定了當(dāng)流程執(zhí)行到這個(gè)node時(shí)的不同行為。jbpm有一組node type可以供你選擇,當(dāng)然你可以定制自己node 。
node的作用
node有兩個(gè)主要的作用:
1)執(zhí)行java代碼,比如說創(chuàng)建task instance(任務(wù)實(shí)例)、發(fā)出通知、更新數(shù)據(jù)庫(kù)等等。很典型的就是在node 上掛上我們的action
2) 控制流程的執(zhí)行:
A、等待狀態(tài)
流程進(jìn)入到這個(gè)node時(shí)將處于等待狀態(tài),直到一個(gè)signal 的發(fā)出
B、流程將沿著一個(gè)leaving transition越過這個(gè)node
這種情況特殊一點(diǎn),需要有個(gè)action掛在這個(gè)node上(注意這個(gè)action不是event觸發(fā)的!),action中將會(huì)調(diào)用到API里
executionContext.leaveNode(String transitionName),transitionName即這里的leaving transition名字。
C、創(chuàng)建新的執(zhí)行路徑
很典型的就是fork node。流程在這里會(huì)分叉,產(chǎn)生新的執(zhí)行路徑。這樣就創(chuàng)建了新的token,每個(gè)新的token代表一個(gè)新的執(zhí)行路徑。注意的是,這些新的token和產(chǎn)生前的token是父子關(guān)系!
D、結(jié)束執(zhí)行路徑
一個(gè)node可以結(jié)束一條執(zhí)行路徑,這同樣意味著相應(yīng)的token的結(jié)束和流程的結(jié)束。
流程圖中的node type
1、task-node
一個(gè)task-node可以包含一個(gè)或多個(gè)task,這些task分配給特定的user。當(dāng)流程執(zhí)行到task-node時(shí),task instance將會(huì)被創(chuàng)建,一個(gè)task對(duì)應(yīng)一個(gè)task instance。task instances 創(chuàng)建后,task-node就處于等待狀態(tài)。當(dāng)所有的task instances被特定的user執(zhí)行完畢后,將會(huì)發(fā)出一個(gè)新的signal 到token,即流程繼續(xù)執(zhí)行。
2、state
state是一個(gè)純粹的wait state(等待狀態(tài))。它和task-node的區(qū)別就是它不會(huì)創(chuàng)建task instances。很典型的用法是,當(dāng)進(jìn)入這個(gè)節(jié)點(diǎn)時(shí)(通過綁定一個(gè)action到node-enter event),發(fā)送一條消息到外部的系統(tǒng),然后流程就處于等待狀態(tài)。外部系統(tǒng)完成一些操作后返回一條消息,這個(gè)消息觸發(fā)一個(gè)signal 到token,然后流程繼續(xù)執(zhí)行。(不常用)
3、decision
當(dāng)需要在流程中根據(jù)不同條件來判斷執(zhí)行不同路徑時(shí),就可以用decision節(jié)點(diǎn)。兩種方法:最簡(jiǎn)單的是在transitions里增加condition elements(條件),condition是beanshell script寫的,它返回一個(gè)boolean。當(dāng)運(yùn)行的時(shí)候,decision節(jié)點(diǎn)將會(huì)在它的 leaving transitions里循環(huán),同時(shí)比較 leaving transitions里的condition,最先返回'true'的condition,那個(gè)leaving transitions將會(huì)被執(zhí)行;作為選擇,你可以實(shí)現(xiàn)DecisionHandler接口,它有一個(gè)decide()方法,該方法返回一個(gè)String(leaving transition的名字)。
4、fork
fork節(jié)點(diǎn)把一條執(zhí)行路徑分離成多條同時(shí)進(jìn)行(并發(fā))的執(zhí)行路徑,每條離開fork節(jié)點(diǎn)的路徑產(chǎn)生一個(gè)子token。
5、join
默認(rèn)情況下,join節(jié)點(diǎn)會(huì)認(rèn)為所有到達(dá)該節(jié)點(diǎn)的token都有著相同的父token。join 節(jié)點(diǎn)會(huì)結(jié)束每一個(gè)到達(dá)該節(jié)點(diǎn)的token,當(dāng)所有的子token都到達(dá)該節(jié)點(diǎn)后,父token會(huì)激活。當(dāng)仍然有子token處于活動(dòng)狀態(tài)時(shí),join 節(jié)點(diǎn)是wait state(等待狀態(tài))。
6、node
node節(jié)點(diǎn)就是讓你掛自己的action用的(注意:不是event觸發(fā)!!),當(dāng)流程到達(dá)該節(jié)點(diǎn)時(shí),action會(huì)被執(zhí)行。你的action要實(shí)現(xiàn)ActionHandler接口。同樣,在你的action里要控制流程!
Actions的說明
存在兩種action,一種是 event觸發(fā)的action,一種是掛在node 節(jié)點(diǎn)的action。要注意它們的區(qū)別,event觸發(fā)的action無法控制流程,也就是說它無法決定流程經(jīng)過這個(gè)節(jié)點(diǎn)后下一步將到哪一個(gè)leaving transition;而掛在node 節(jié)點(diǎn)的action就不同,它可以控制流程。不管是哪一種action都要實(shí)現(xiàn)ActionHandler接口。
variable的管理
流程實(shí)例中,存有contextInstance來管理token和variable.
contextInstance是通過一個(gè)map來進(jìn)行管理的,這個(gè)map的key是token,value是一個(gè)TokenVariableMap的對(duì)象.
TokenVariableMap本身并不是個(gè)map,而是一個(gè)普通的Object
TokenVariableMap有三個(gè)屬性,一個(gè)是contextInstance,一個(gè)是Token對(duì)象本身,還有一個(gè)是
一個(gè)Map,用來放variableInstance,這個(gè)Map的名稱為variableInstances.
variableInstances是一個(gè)map,它的key是variable的名稱,如"a",value是一個(gè)VariableInstance對(duì)象,VariableInstance對(duì)象放了四個(gè)屬性,一個(gè)是token對(duì)象本身,一個(gè)是variable的名稱,一個(gè)是TokenVariableMap對(duì)象,還有一個(gè)是processInsance,你可能會(huì)奇怪:variable的value放到哪里呢?實(shí)際上VariableInstance是一個(gè)抽象類,具體的實(shí)現(xiàn)是它根據(jù)value的class類型選擇它的子類,子類中有個(gè)屬性叫value。這個(gè)variable的value,如 new Integer(3).
一個(gè)流程實(shí)例可以有多個(gè)Token,Token間是有父子關(guān)系的:
Token tokenAB=new Token(tokenA,"ab");
上行代碼的意思是在tokenA下面建立一個(gè)TokenAB,該新建的
Token的名字是"ab".
ci.createVariable(tokenA, "a", new Integer(3));
表示在tokenA范圍內(nèi)建立一個(gè)variable,它的名稱為"a",
值為new Integer(3)
建立這個(gè)variable后,tokenA下面的token都可以看到該
variable,而上面的token則看不到.
只有createVariable可以在某個(gè)token上建立variable,其它的
方法只可以在rootToken上建立variable.
如:ci.setVariable(tokenA, "a", new Integer(3));
其實(shí)是在rootToken上建立了一個(gè)名稱為"a"的variable
Map variables = new HashMap();
variables.put("a", new Integer(3));
variables.put("b", new Integer(4));
ci.addVariables(variables);
上面四行代碼在rootToken上建立了兩個(gè)variable.
實(shí)際上在ExecutionContext中只有兩個(gè)方法:
public void setVariable(String name, Object value) {
getContextInstance().setVariable(name, value, token);
}
public Object getVariable(String name) {
return getContextInstance().getVariable(name, token);
}
可以在子token中修改父token中建立的variable.
Task(任務(wù))
jbpm一個(gè)相當(dāng)重要的功能就是對(duì)任務(wù)進(jìn)行管理。
Task(任務(wù))是流程定義里的一部分,它決定了task instance的創(chuàng)建和分配。
Task(任務(wù))可以在task-node節(jié)點(diǎn)下定義,也可以掛在process-definition節(jié)點(diǎn)下。最普遍的方式是在task-node節(jié)點(diǎn)下定義一個(gè)或多個(gè)任務(wù)。默認(rèn)情況下,流程在task-node節(jié)點(diǎn)會(huì)處于等待狀態(tài),直到所有的任務(wù)被執(zhí)行完畢。任務(wù)的名稱在整個(gè)流程中必須是唯一的。
一個(gè)TaskNode對(duì)應(yīng)多個(gè)Task
對(duì)于這樣的流程定義:
只有當(dāng)節(jié)點(diǎn)中的三個(gè)任務(wù)都完成后,流程才進(jìn)入后面的節(jié)點(diǎn)
對(duì)于這樣的流程定義:
>
當(dāng)?shù)谝粋€(gè)任務(wù)完成后,token就指向后面的節(jié)點(diǎn)
對(duì)于這樣的流程定義:
>
三個(gè)任務(wù)都完成后,token仍然不會(huì)指向后面的節(jié)點(diǎn);需要自己手動(dòng)調(diào)用
processInstance.signal()才會(huì)驅(qū)動(dòng)流程到下面的節(jié)點(diǎn)
對(duì)于這樣的流程定義:
>
token不會(huì)在本節(jié)點(diǎn)停留,而是直接到后面的節(jié)點(diǎn)
jbpm的任務(wù)管理實(shí)現(xiàn)
一個(gè)Task instance(任務(wù)實(shí)例)可以被分配給一個(gè)actorId (java.lang.String)。所有的Task instance都被保存在數(shù)據(jù)庫(kù)中的表jbpm_taskinstance里。當(dāng)你想得到特定用戶的任務(wù)清單時(shí),你就可以通過一個(gè)與用戶關(guān)聯(lián)的actorId來查詢這張表。
一個(gè)流程定義有一個(gè)TaskMgmtDefinition;一個(gè)TaskMgmtDefinition對(duì)應(yīng)多個(gè)swimlane,同時(shí)對(duì)應(yīng)多個(gè)task;一個(gè)swimlane有多個(gè)task,可以從TaskMgmtDefinition中通過task的名稱直接獲取相應(yīng)的task;
swimlane對(duì)象有四個(gè)屬性,分別是name(名字)、assignmentDelegation(分配代理類)、taskMgmtDefinition、tasks(Set 對(duì)應(yīng)多個(gè)task),可以增加task
task對(duì)象主要的屬性:taskMgmtDefinition、swimlane、assignmentDelegation、taskNode,需要注意的是swimlane和assignmentDelegation中間只是可以一個(gè)屬性有值,因?yàn)樗鼈兌己腿蝿?wù)的分配有關(guān)系。
一個(gè)流程實(shí)例有一個(gè)TaskMgmtInstance;一個(gè)TaskMgmtInstance對(duì)應(yīng)多個(gè)swimlaneInstance,同時(shí)對(duì)應(yīng)多個(gè)taskInstance;一個(gè)swimlaneInstance有多個(gè)taskInstance,可以從TaskMgmtInstance中直接獲取相應(yīng)的taskInstance;
swimlaneInstance對(duì)象主要有五個(gè)屬性,分別是name、actorId、pooledActors(Set)、swimlane、taskMgmtInstance。
taskInstance對(duì)象的主要屬性:name、actorId、task、swimlaneInstance、taskMgmtInstance、pooledActors。
當(dāng)對(duì)任務(wù)進(jìn)行分配時(shí),一般需要實(shí)現(xiàn)AssignmentHandler這個(gè)接口,這個(gè)接口的方法只有一個(gè):
void assign( Assignable assignable, ExecutionContext executionContext ) throws Exception;
一個(gè)典型的實(shí)現(xiàn)(把名字是'change nappy'的任務(wù)交給NappyAssignmentHandler這個(gè)類來分配)
NappyAssignmentHandler類:
public void assign(Assignable assignable, ExecutionContext executionContext) {
assignable.setActorId("papa");
}
同樣,Assignable只是一個(gè)接口,它有兩個(gè)方法:setActorId()和setPooledActors(),Assignable的具體實(shí)現(xiàn)類也是兩個(gè)
swimlaneInstancehe和taskInstance。這樣就不不難理解整個(gè)任務(wù)分配流程了:
1、流程進(jìn)入TaskNode節(jié)點(diǎn),執(zhí)行TaskNode類的execute()方法,該方法首先獲得TaskMgmtInstance實(shí)例,然后通過它來創(chuàng)建TaskInstance。taskMgmtInstance.createTaskInstance(task, executionContext);
2、在上面的createTaskInstance(task, executionContext)里,該方法調(diào)用了taskInstance.assign(executionContext)對(duì)taskInstance進(jìn)行分配。
3、在assign(executionContext)方法里,首先會(huì)判斷task屬性里是否存在swimlane,如果有的話,這個(gè)taskInstance就會(huì)分配給swimlane指定的ActorId或 PooledActors;如果不存在,再去找task屬性里 assignmentDelegation(分配代理類)通過代理類(即我們自己寫的實(shí)現(xiàn)AssignmentHandler這個(gè)接口的類)指定ActorId或 PooledActors。
jbpm的用戶角色管理
jbpm在用戶角色管理上共設(shè)計(jì)了四個(gè)類:Entity、 Membership、 Group、 User
Entity類是其他三個(gè)類的父類,它包含了兩個(gè)屬性:name(String)、 permissions(Set)
User類繼承Entity類,包含三個(gè)屬性:password(String)、 email(String)、 memberships(Set)
Group類繼承Entity類,包含四個(gè)屬性: type(String) 、parent(Group)、 children(Set)、 memberships(Set)
Membership類繼承Entity類,包含三個(gè)屬性:role(String)、 user(User)、 group(Group)
很明顯,一個(gè)user對(duì)應(yīng)一個(gè)用戶,一個(gè)group對(duì)應(yīng)一個(gè)用戶組,它們之間通過membership關(guān)聯(lián),并且一個(gè)user可以屬于多個(gè)不同類型(type)的group,user和 group之間是多對(duì)多的關(guān)系。
Membership類的role屬性個(gè)人感覺用途不大,反倒是name屬性代表了user在group里的role(角色)!
Generated by Bo-blog 2.0.2 RC 1