Jbpm用戶指南翻譯:第11章 任務管理

  
第11章 任務管理
Jbpm的核心業務是持久化流程執行的能力,對於管理任務和個人任務清單來說這是一個非常重要的特性,Jbpm允許指定一段軟件描述所有人的任務中處於等待狀態的流程。
11.1 任務
任務是流程定義的一部分,它們定義了在流程執行期間任務實例怎樣被創建和分配。
任務可以在task-node和process-definition中定義,通常使用的方式是在一個task-node裏定義一個或多個任務,這種情況下,task-node代表一個將由用戶完成的任務,並且流程執行將一直等待直到參與者完成這個任務,當參與者完成任務時,流程執行將繼續。當在一個task-node中指定多個任務時,默認的行爲是等待所有任務完成。
任務也可以被指定在process-definition。指定在process-definition的任務可以通過名稱查找到並且在task-node裏引用或者在內部動作中使用。事實上,所有給定名稱的任務(包括在task-node中定義的任務)都可以在流程定義(process-definition)中通過名字查找到。
任務名稱在整個流程定義中必須是唯一的。任務可以被指定一個優先級,這個優先級在任務的實例創建時將被作爲每個任務實例的初始優先級,任務實例的初始優先級可以在以後被修改。
11.2 任務實例
任務實例可以被分配一個actorId(java.lang.String)。所有的任務實例被存儲在數據庫中的一張表裏面(JBPM_TASKINSTANCE),通過查詢這張表可以得到給定actorId的所有任務實例,進而獲取特定用戶的任務清單。
Jbpm的任務清單機制可以組合Jbpm任務和其他任務,甚至那些任務與流程執行毫不相干,這樣,Jbpm開發者可以很方便的組合Jbpm流程任務和其他應用的任務到一個集中的任務清單倉庫中。
11.2.1 任務實例生命週期
任務實例的生命週期是很直觀的:創建後,任務實例可以被隨意地開始,然後任務實例可以被結束,這意味着任務實例被標記爲完成。
注意,爲了靈活性,分配不是生命週期的一部分,所以任務實例可以被分配也可以不被分配,任務實例的分配不會影響任務實例的生命週期。
任務實例典型的創建是通過流程執行進入一個task-node(使用方法TaskMgmtInstance.createTaskInstance(…)),然後,用戶接口組件會使用TaskMgmtSession.findTaskInstancesByActorId(…)查詢數據庫獲取任務列表,然後,在收集了用戶輸入後,UI組件調用TaskInstance.assign(String)、TaskInstance.start()或者TaskInstance.end(…)。
任務實例依靠日期屬性維護它的狀態:創建、開始和結束。這些屬性可以通過任務實例上它們各自的getter方法訪問。
通常,完成的任務實例用一個結束日期做了標記,所以它們不能被以後的任務列表查詢獲取,但是它們仍然存在於JBPM_TASKINSTANCE表中。
11.2.2 任務實例和圖執行
任務實例是參與者任務清單(tasklist)中的項目,任務實例可以作爲信號,當一個信號任務實例完成時,可以發送一個信號到它的令牌繼續流程執行。任務實例可以被阻塞,這意味着在任務實例完成之前相關令牌(=執行路徑)不允許離開任務節點。默認情況下,任務實例是信號非阻塞的。
如果多於一個任務實例與一個任務節點關聯,流程開發者可以指定任務實例的完成怎樣影響流程的繼續。下面是可以給任務節點的signal屬性設置的值:
l        last:這是默認值。當最後一個任務實例完成時繼續執行;當在節點入口處沒有任務創建時,繼續執行。
l        last-wait:當最後一個任務實例完成時繼續執行;當在節點入口處沒有任務創建時,執行在任務節點等待,直到任務被創建。
l        first:當第一個任務實例完成時繼續執行;當在節點入口處沒有任務創建時,繼續執行。
l        first-wait:當第一個任務實例完成時繼續執行;當在節點入口處沒有任務創建時,執行在任務節點等待,直到任務被創建。
l        unsynchronized:總是繼續執行,不管任務是否創建和完成。
l        never:執行不再繼續,不管任務是否創建和完成。
任務實例可以基於運行時的計算創建,如果那樣的話,需要添加一個ActionHandler到任務節點的node-enter事件,並且設置屬性create-tasks=“false”。下面是這樣一個動作的實現例子:
public class CreateTasks implements ActionHandler {
 public void execute(ExecutionContext executionContext) throws Exception {
    Token token = executionContext.getToken();
    TaskMgmtInstance tmi = executionContext.getTaskMgmtInstance();
     
    TaskNode taskNode = (TaskNode) executionContext.getNode();
    Task changeNappy = taskNode.getTask("change nappy");
 
    // 現在, 相同任務的兩個任務實例被創建
    tmi.createTaskInstance(changeNappy, token);
    tmi.createTaskInstance(changeNappy, token);
 }
}
如示例所展示,任務可以在指定的任務節點中創建,它們也可以被指定到process-definition,並且從TaskMgmtDefinition獲取。TaskMgmtDefinition用任務管理信息擴展了ProcessDefinition。
標識任務示例完成的API是TaskInstance.end(),你可以在end方法中指定一個轉換,如果這個任務的完成會觸發繼續執行,則會通過指定的轉換離開任務節點。
11.3 分配
流程定義包含任務節點,任務節點(task-node)包含一個或多個任務,任務作爲流程定義的一部分是靜態描述。在運行時,任務導致任務實例的創建,一個任務實例對應某人任務列表中的一個入口。
11.3.1 分配接口
通過接口AssignmentHandler進行任務實例分配:
public interface AssignmentHandler extends Serializable {
 void assign( Assignable assignable, ExecutionContext executionContext );
}
當任務實例被創建時分配處理的實現被調用,在那時,任務實例可以被分配到一個或多個參與者。AssignmentHandler實現將調用分配方法(setActorId或setPooledActors)分配任務,可以分配一個任務實例或者一個泳道實例swimlaneInstance(=流程角色)。
public interface Assignable {
 public void setActorId(String actorId);
 public void setPooledActors(String[] pooledActors);
}
任務實例和泳道實例都可以被分配到一個用戶或者共享參與者。分配任務實例到一個用戶,調用Assignable.setActorId(String actorId);分配任務實例到候選的共享參與者,調用Assignable.setPooledActors(String[] actorIds)。
流程定義中的每個任務都可以與一個分配處理實現相關聯,用來完成運行時的任務分配。
當流程中的多個任務將要分配給相同的人或者參與者組時,考慮使用泳道
考慮到AssignmentHandler的重用,每個AssignmentHandler的使用可以在processdefinition.xml中配置。請參考“16.2 委託”瞭解怎樣添加分配處理配置的更多信息。
11.3.2 分配數據模型
下面是管理分配任務實例和泳道實例到參與者的數據模型,每個任務實例擁有一個actorId或一組被共享的參與者。
11.1 分配模型類圖
actorId對任務負責,而共享的參與者表示候選者集合,如果它們獲取任務,則可以負責任務。actorId和共享參與者具體使用哪個是可選的,兩者也可以結合使用。
11.3.3 推模式
任務實例的actorId表明對給定任務負責,而任務實例的共享參與者是任務的候選參與者。典型情況下,任務實例的actorId指向一個用戶,共享參與者可以指向多個用戶和/或用戶組。
用戶的任務清單是所有以用戶作爲actorId的任務實例,這個清單可以使用TaskMgmtSession.findTaskInstances(String actorId)獲得。
11.3.4 拉模式
另一方面,共享的任務(譯者注:共享任務即不僅是由一個用戶負責的)是提供給共享參與者中所引用的用戶的。獲取共享任務一般需要兩步操作:1)從身份組件中獲取給定用戶的所有組2)爲結合了用戶的actorId和指向用戶組的actorId獲取所有共享任務清單(譯者注:這段話不太好理解,解釋如下――任務指向一個actorId,在Jbpm中並沒有強制限制該actorId必須爲單個用戶或者用戶組,所以我們在實際應用中也可以把actorId作爲一個用戶組。假如有一個用戶爲user1,他屬於用戶組group1,在流程中有些任務分配到了user1,而其它又有些任務分配到了group1,如果我們要取user1的所有共享任務,則即需要獲取分配到user1的共享任務,也需要獲取分配到group1的共享任務)。可以使用方法TaskMgmtSesion.findPooledTaskInstances(String actorId)或TaskMgmtSession.findPooledTaskInstances(List actorIds)獲取提供給指定用戶的共享任務清單,這些方法會返回actorId爲空(null)以及共享參與者中某個與給定的actorIds中的某個相匹配的任務實例。
爲了防止多個用戶在同一個共享任務上工作,使用用戶的actorId修改任務實例的actorId就可以了。這樣,任務實例將不會出現在共享任務清單中,而只會存在於用戶個人的任務清單裏。設置任務實例的actorId爲空(null),則會把任務實例放回共享任務裏。
11.4 任務實例變量
任務實例可以擁有它自己的變量,並且也可以“看到”流程變量。任務實例通常是在執行路徑(=令牌)中創建,與令牌之間的父子關係相似,這會在令牌和任務實例之間創建一個父子關係。正常的範圍規則適用於相關令牌的任務實例變量和流程變量之間,有關範圍的更多信息可以在“10.4 變量”找到。
這意味着任務實例可以“看到”它自己的變量,另外還有它所關聯令牌的所有變量。
控制器可以用來在任務實例範圍和流程範圍的變量間創建、組裝和提交變量。
11.5 任務控制器
在任務實例創建時,任務控制器可以組裝任務實例變量,並且當任務實例完成時,任務控制器可以提交任務實例的數據到流程變量。
注意,任務控制器不是強制使用的,即使不使用任務控制器,任務實例也能夠“看到”與它的令牌相關的流程變量,當你想要做如下事情時使用任務控制器:
l        在任務實例中創建變量的拷貝,這樣任務變量的中間更新不會影響到流程變量,而是直到處理完成後拷貝才被提交回流程變量。
l        任務實例變量與流程變量不是一一對應的。例如,假設流程有變量“sales in januari”“sales in februari”和“sales in march”,而任務實例所使用表單可能需要顯示的是三個月的平均銷售量。
任務用來收集用戶輸入,但是目前有很多可以向用戶展示任務的用戶接口,例如web應用、swing應用、及時消息、電子郵件表單…因此任務控制器在流程變量(=流程上下文)和用戶接口應用之間起到了橋的作用,任務控制器爲用戶接口應用提供流程變量的視圖。
任務控制器在流程變量到任務變量之間進行轉換(如果需要)。當任務實例被創建時,任務實例負責從流程變量提取信息,並且創建任務變量,任務變量作爲用戶接口表單是輸入,並且用戶輸入可以存儲在任務變量裏;當用戶結束任務時,任務控制器負責基於任務實例數據更新流程變量。
11.2 任務控制器
簡單的情形是,在流程變量和表單參數之間是一對一的映射,任務控制器在task元素中指定,這種情況下,默認的Jbpm任務管理器可以被使用,它包含一個variable元素列表,variable元素表示流程變量怎樣被拷貝到任務變量。
下面的例子展示了怎樣基於流程變量的拷貝創建任務實例變量:
<task name="clean ceiling">
 <controller>
    <variable name="a" access="read" mapped-name="x" />
    <variable name="b" access="read,write,required" mapped-name="y" />
    <variable name="c" access="read,write" />
 </controller>
</task>
name屬性指向流程變量的名稱,mapped-name屬性是任意的,用來指向任務實例變量的名稱。如果忽略mapped-name屬性,則mapped-name默認與name屬性相同。注意,mapped-name也被用來在web應用中作爲任務實例表單的域標籤。
access屬性指定了如果在任務實例創建時變量被拷貝,是否需要在任務結束時把它寫回流程變量。這個信息可以被用戶接口所使用,進行適當的表單控制。access屬性是可選的,默認值是“read,write”。
任務節點(task-node)可以擁有多個任務,而開始狀態(start-state)只能有一個任務。
如果在流程變量和表單參數之間簡單的一對一映射太過約束,你也可以編寫你自己的TaskControllerHandler實現,下面是TaskControllerHandler接口:
public interface TaskControllerHandler extends Serializable {
 void initializeTaskVariables(TaskInstance taskInstance, ContextInstance contextInstance, Token token);
 void submitTaskVariables(TaskInstance taskInstance, ContextInstance contextInstance, Token token);
}
下面展示了怎樣在任務中配置你自己定製的任務控制器:
<task name="clean ceiling">
 <controller class="com.yourcom.CleanCeilingTaskControllerHandler">
    -- here goes your task controller handler configuration --
 </controller>
</task>
 
11.6 泳道(swimlane)
泳道是流程角色,它是定義流程中的多個任務由相同參與者完成的一種機制。在第一個任務實例爲指定泳道創建後,參與者將被流程記住,以被在同一泳道中的後續任務所使用。泳道有一個分配,因此所有引用泳道的任務不需要再指定分配
當給定泳道的第一個任務被創建時,泳道的AssignmentHandler被調用,Assignable被傳遞到AssignmentHandler,將會成爲泳道實例(SwimlaneInstance)。需要知道的是,給定泳道中的所有任務實例的分配都被傳播到泳道實例,這個行爲是被作爲默認實現的,因爲獲取任務進行處理的人在履行某個流程角色(譯者注:也就是說在某個泳道內),因此所有後續對泳道的任務實例的分配會自動轉到用戶。
泳道是從UML活動圖中借來的術語。
11.7 開始任務中的泳道
泳道可以與開始任務關聯,用以捕獲流程的發起者。
任務可以在start-state中指定,該任務與泳道關聯,當這個任務的一個新的任務實例被創建時,當前已經過鑑定的參與者可以使用Authentication.getAuthenticatedActorId()(參考17.2 鑑定)獲取(譯者注:這時創建開始任務時,自動進行的),並且該參與者存儲在開始任務的泳道里。
例如:
<process-definition>
 <swimlane name='initiator' />
 <start-state>
    <task swimlane='initiator' />
    <transition to='...' />
 </start-state>
 ...
</process-definition>
象其它任何任務一樣,也可以向開始任務添加變量用來定義與任務關聯的表單。請參考“11.5 任務控制器”。
11.8 任務事件
任務可以擁有所關聯的動作。任務有四個標準的事件類型定義:task-create,task-assign,task-start,和task-end。
當一個任務實例被創建時task-create事件被激活。
當一個任務實例被分配時task-assign事件被激活。注意:在這個事件中執行的動作裏,你可以用executionContext.getTaskInstance().getPreviousActorId()訪問前一個參與者。
當TaskInstance.start()被調用時task-start事件被激活。這可以被用來標識用戶在這個任務實例上已經開始工作。是否開始一個任務是可選的。
當TaskInstance.end()被調用時task-end事件被激活。這標誌了任務的完成,如果任務與流程執行相關,這個調用會觸發流程繼續執行。
因爲任務可以擁有事件以及相關聯的動作,所以異常處理也可以在任務上被指定。有關異常處理的更多信息,請參考“9.7 異常處理”。
11.9 任務定時器
在節點上,定時器可以在任務上被指定。請參考“12.1 定時器”。
對於任務的定時器需要指明的是:任務定時器的cancel-event可以被定製。默認情況下,當任務被結束時(=完成)任務上的定時器將被取消,在是通過在定時器上使用cancel-event屬性,流程開發者可以定製諸如task-assign或task-start。cancel-event支持多個事件,通過在屬性中指定一個用逗號分割的列表,可以組合cancel-event的類型。
11.10 定製任務實例
任務實例可以被定製。最簡單的方式就是創建TaskInstance的子類,然後創建一個org.jbpm.taskmgmt.TaskInstanceFactory的實現,並在jbpm.cfg.xml中設置配置屬性jbpm.task.instance.factory爲該實現的完整類名。如果你使用一個TaskInstance的子類,還需要爲該子類創建一個hibernate映射文件(使用hibernate的extends=”org.jbpm.taskmgmt.exe.TaskInstance”),然後在hibernate.cfg.xml中添加該映射文件到映射文件列表。
11.11 身份組件
用戶、組和權限管理一般都被稱爲身份管理。Jbpm包含了一個可選的身份組件,它可以很容易的被企業自己的身份數據存儲所替換。
Jbpm的身份管理組件包含了組織知識模型。任務分配典型的需要由組織知識來完成,因此,這意味着組織知識模型描述了用戶、組、系統以及它們之間的關係。作爲可選的,權限和角色也可以被包含進組織模型。數個學術研究嘗試的失敗表明,沒有一個通用的組織模型可以適用於所有組織。
Jbpm的作法是定義一個參與者作爲流程的實際參與者,參與者通過它的ID進行標識,稱爲actorId,Jbpm只知道actorId並且爲了靈活性用java.lang.String表示,因此任何組織模型知識及其數據結構都在Jbpm核心引擎之外。
作爲對Jbpm的擴展,我們將提供(以後)管理簡單的“用戶-角色”模型的組件,用戶和角色之間的多對多關係與J2EE和servlet規範中定義的模型是一致的,這可以作爲一個新的開發起點,感興趣的話可以查看jboss jbpm jira中的問題跟蹤來獲取更多詳細資料。
注意,實際被用在servlet、ejb和portlet規範中的“用戶-角色”模型不足以滿足任務的分配處理,該模型在用戶和角色之間是一個多對多的關係,不包括流程中用戶有關的組和組織結構信息。
11.11.1 身份模型
11.3身份模型類圖
黃色的類是下面將要討論的有關表達式分配處理的相關類。
User表示用戶或服務。Group是任何類型的用戶組,Group可以被嵌套,用來建模一個團隊、一個業務單元、以及整個公司的關係,組有類型,用來在不同等級的組之間進行區分,例如haircolor組。Membership表示用戶和組之間的多對多關係,membership可以被用來表示公司中的一個職位,membership的名字可被用來表示用戶在組中的角色。
11.11.2 分配表達式
隨同身份組件提供了一種實現,即在進行任務分配期間通過表達式計算參與者。這裏有一個在流程定義中使用分配表達式的例子:
<process-definition>
 ...
 <task-node name='a'>
    <task name='laundry'>
      <assignment expression='previous --> group(hierarchy) --> member(boss)' />
    </task>
    <transition to='b' />
 </task-node>
 ...
分配表達式的語法如下:
first-term --> next-term --> next-term --> ... --> next-term
 
where
 
first-term ::= previous |
               swimlane(swimlane-name) |
               variable(variable-name) |
               user(user-name) |
               group(group-name)
 
and
 
next-term ::= group(group-type) |
              member(role-name)
 
11.11.2.1第一術語first-term
表達式以從左到右的順序被分解,first-term指定了身份模型中的一個用戶或組,後續的術語的計算基於這個中間的用戶或組。
privious的意思是任務被分配到當前已被鑑定的參與者,這意味着參與者完成了流程中的前一個步驟。
swimlane(swimlane-name)的意思是用戶或組從指定的泳道實例中取得。
variable(variable-name)的意思是用戶或組從指定變量實例中取得。變量實例可以包含一個java.lang.String,這種情況下,用戶或組從身份組件獲取;或者變量實例包含一個用戶或組對象。
user(user-name)的意思是給定的用戶從身份組件中取得。
group(group-name)的意思是給定的組從身份組件中取得。
11.11.2.2第二術語next-term
group(group-type)獲取用戶的組,這意味着前一個術語必須結果爲一個用戶,這個術語會使用給定的group-type在所有的membership中爲用戶搜索組。
member(role-name)從組裏得到用戶所履行的角色,前一個術語必須結果爲一個組,這個術語在組中爲用戶搜索與給頂的role-name相匹配的membership。
11.11.3 移除身份組件
當你想要爲組織信息使用自己的數據源時,例如你們公司的用戶數據庫或者ldap系統,你可以拔除身份組件,你唯一要做的就是在hibernate.cfg.xml中刪除下面的行…
<mapping resource="org/jbpm/identity/User.hbm.xml"/>
<mapping resource="org/jbpm/identity/Group.hbm.xml"/>
<mapping resource="org/jbpm/identity/Membership.hbm.xml"/>
ExpressionAssignmentHandler依賴於身份組件,因此你將不能使用它。萬一你想重新使用ExpressionAssignmentHandler並且綁定它到你自己的用戶數據存儲,你可以從ExpressionAssignmentHandler繼承並且重寫getExpressiontSession方法。
protected ExpressionSession getExpressionSession(AssignmentContext assignmentContext);
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章