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