一個Jbpm員工請假流程的實例
作者:吳大愚
Email:[email protected]
適用於jbpm3.1版本
1. 概述
此實例包括的是一個員工請假審批的流程實例,和流程相關的代碼以及相應的測試代碼。此流程在Eclipse
說明,這篇文章說使用的流程實例是,學習《一個JBPM工作流管理示例》文章中的流程而來。原文中的流程實例不是jbpm3.1版本,不能適用於jbpm3.1。本人將其改寫,並加入自己的設計和實現。原文地址爲http://blogger.org.cn/blog/more.asp?name=lhwork&id=16137。可以對照學習。
2. 流程說明
假設應用背景如下:
在某一公司中,部門員工要休假的話需要部門主管的批准。如果休假天數大於10天的話,在部門主管的同意後,還必須老闆批准。如果是部門主管要休假只要老闆批准即可。在休假被批准之前,申請人可以撤銷休假申請。
每次休假申請結束之後,不管通過未通過或是否取消,都必須記錄下來。主管在批覆申請之後,系統要將批覆結果Email給申請人。對於大於10天的申請,如果部門主管已批准同意而上級主管還未批准,這時申請人撤銷申請後,系統應發Email通知部門主管申請已撤銷。
3. 流程定義
3.1. 原文件
<?xml version="1.0" encoding="UTF-8"?>
<process-definition
xmlns="urn:jbpm.org:jpdl-3.1" name="MyRequest">
<start-state name="SS_Request">
<transition name="" to="TN_WriteRequest"></transition>
</start-state>
<task-node name="TN_WriteRequest">
<task name="Task_WriteRequest">
<controller>
<variable name="dayCount" access="read,write,required"></variable>
</controller>
<assignment class="com.myrequest.task.WriteRequestAssignmentHandler"></assignment>
</task>
<transition name="Tr_WriteLeave" to="Fork_request">
<action name="Ac_WriteLeave" class="com.myrequest.action.WriteLeaveActionHandler"></action>
</transition>
</task-node>
<fork name="Fork_request">
<transition name="Tr_Cancel" to="TN_RequesterCancel"></transition>
<transition name="Tr_Request" to="Deci_IsChiefHere">
<action name="Ac_GetChiefState" class="com.myrequest.action.GetChiefStateActionHandler"></action>
</transition>
</fork>
<decision name="Deci_IsChiefHere">
<handler class="com.myrequest.decision.IsChiefHereDecisionHandler"/>
<transition name="Tr_Chief" to="TN_ChiefDecide"></transition>
<transition name="Tr_Boss" to="TN_BossDecide"></transition>
</decision>
<task-node name="TN_RequesterCancel">
<task name="Task_CancelRequest">
<assignment class="com.myrequest.task.CancelRequestAssignmentHandler"></assignment>
</task>
<transition name="Tr_RequestCancel" to="Join_Request">
<action name="Ac_RequestCancel" class="com.myrequest.action.RequestCancelActionHandler"></action>
</transition>
</task-node>
<task-node name="TN_ChiefDecide">
<task name="Task_ChiefDecide">
<assignment class="com.myrequest.task.ChiefDecideAssignmentHandler"></assignment>
</task>
<transition name="Tr_ChiefApprove" to="Deci_NeedBossDecide">
<action name="Ac_ChiefApprove" class="com.myrequest.action.ChiefApproveActionHandler"></action>
</transition>
<transition name="Tr_ChiefNotApprove" to="Join_Request">
<action name="Ac_ChiefNotApprove" class="com.myrequest.action.ChiefNotApproveActionHandler"></action>
</transition>
</task-node>
<join name="Join_Request">
<transition name="Tr_Join" to="Deci_DoSomething"></transition>
</join>
<decision name="Deci_NeedBossDecide">
<handler class="com.myrequest.decision.NeedBossDecideDecisionHandler"/>
<transition name="Tr_Need" to="TN_BossDecide"></transition>
<transition name="Tr_NotNeed" to="Join_Request">
<action name="Ac_NotNeed" class="com.myrequest.action.NotNeedActionHandler"></action>
</transition>
</decision>
<task-node name="TN_BossDecide">
<task name="Task_BossDecide">
<assignment class="com.myrequest.task.BossDecideAssignmentHandler"></assignment>
</task>
<transition name="Tr_BossApprove" to="Join_Request">
<action name="Ac_BossApprove" class="com.myrequest.action.BossApproveActionHandler"></action>
</transition>
<transition name="Tr_BossNotApprove" to="Join_Request">
<action name="Ac_BossNotApprove" class="com.myrequest.action.BossNotApproveActionHandler"></action>
</transition>
</task-node>
<decision name="Deci_DoSomething">
<handler class="com.myrequest.decision.DoSomethingDecisionHandler"/>
<transition name="Tr_Approve" to="ES_Finished">
<action name="Ac_Approve" class="com.myrequest.action.ApproveActionHandler"></action>
</transition>
<transition name="Tr_NotApprove" to="ES_Finished">
<action name="Ac_NotApprove" class="com.myrequest.action.NotApproveActionHandler"></action>
</transition>
<transition name="Tr_Cancel" to="ES_Finished">
<action name="Ac_Cancel" class="com.myrequest.action.CancelActionHandler"></action>
</transition>
</decision>
<end-state name="ES_Finished">
<event type="node-enter">
<action name="Ac_Finished" class="com.myrequest.action.FinishedActionHandler"></action>
</event>
</end-state>
</process-definition>
3.2. 流程圖片
3.3. 說明
3.3.1. 命名規則
start-state的定義爲SS_
end-state的定義爲ES_
task-node的定義爲TN_
fork的定義爲Fork_
join的定義爲Join_
task的定義爲Task_
transition的定義爲Tr_
action的定義爲Ac_
3.3.2. join節點類型
join結點Join_Request,採用的是Discriminator模式,即只要有一個fork發出的分支到達join,流程就可以向下進行。
Join共有三中模式:
l 默認的是所有fork發出的分支都到達,流程才向下進行;
l 第二種就是Discriminator模式,只要有一個fork發出的分支到達join,流程就可以向下進行;
l 第三種是設置當有n個分支到達之後,流程就可以向下進行。
但jpdl語言在jbpm3.1版本中還不支持對第二,第三兩種模式的設置。需要在流程實例化之後,來制定join的模式。具體如何實現,參見後面有關代碼部分。
3.3.3. 申請狀態
系統存在一個有關申請的狀態。系統用流程變量RequestState來存儲。共有五個狀態。存儲在com.myrequest.RequestState.java文件中。
在用戶啓動此請假流程,完成第一個填寫請假申請的任務實例後,此狀態置爲REQUEST。在完成取消請求任務之後,狀態改爲CANCEL。等等,讀者可以仔細閱讀流程定義和對應的代碼。
要說明的是所有的狀態修改都在完成任務之後的邊上執行Action中來修改的。這樣雖然增加了很多Action,但是狀態修改明確。但對於這樣的簡單問題,前臺代碼在實際任務完整之後,就可以調用修改此狀態變量。此處將這些修改都放在Action裏面,也有一定演示的含義。
3.3.4. 取消請假任務說明
在fork節點後,產生兩個並行分支。其中一個TN_RequestCancel任務節點包含一個Task_CancelRequest的任務。當此分支執行到這裏時,會把這個任務分配給啓動流程的用戶。在流程沒有結束的時候,如果用戶執行這個任務,就表示用戶要取消請假申請。在這個取消申請的任務會修改請假狀態,在此任務結束後,就會首先到達join節點。表示取消了此次申請操作。
4. 代碼說明
4.1. 代碼包結構
本實例是在Eclipse
在src/java目錄下面,有包:
l com.myrequest
n 存放流程的公共信息,包括:
n interface RequestState,用來存放請求狀態的5種狀態
n interface RequestVariable 存放流程實例的變量名
l com.myrequest.action
n 存放流程中所有的ActionHandler類
n 共有12個類,每個類對應流程中一個action的代碼。
l com.myrequest.task
n 存放流程中所有的task的AssignmentHandler分配類
n 共有4個類,對應4個task
l com.myrequest.decision
n 存放流程中所有的decision節點的判斷類
n 包括3個類,對應3個Decision節點
在src/test目錄下面,有包:
l com.myrequest
n 包含此流程的測試類 MyRequestProcessTest.java
在processes目錄下面,有:
l 流程定義文件夾MyRequest,包含:
n Gpd.xml
n Processdefinition.xml
n Processimage.jpg
4.2. 流程代碼說明
對流程中使用到的和Action,task,decision相關的類,以及測試類進行說明。
4.2.1. Action代碼說明
流程中的Action都使用指定類的形式來完成Action的操作。
例如:
<transition name="Tr_WriteLeave" to="Fork_request">
<action name="Ac_WriteLeave" class="com.myrequest.action.WriteLeaveActionHandler"></action>
</transition>
當流程執行到邊Tr_WriteLeave後,就會自動去執行Action裏面指定的類WriteLeaveActionHandler。
類WriteLeaveActionHandler實現了接口ActionHandler。此接口就一個函數
public void execute(ExecutionContext executionContext)
當流程執行這個類的時候,就會去調用這個函數。所以我們Action所要完成的工作也都寫在這個函數中。
在RequestBeginActionHandler.execute()中,我們只做了將整個流程的請求狀態設置爲REQEUST狀態。
4.2.2. Task代碼說明
所有Task都是在Task-node中描述的。Task都是人工任務,也就是說需要先將task分配給那個人,然後由這個人來完成。在人開始任務的時候,可以調用taskInstance的start()操作,表明任務開始。(start()操作是可選的,也可以不調用) 在任務結束後,可以調用taskInstance的end()操作,表示任務實例結束。如果是整個task-node中的最後一個task的end()操作,那麼就會這個end操作就會觸發流程繼續向下走。
例如:
<task name="Task_WriteRequest">
<controller>
<variable name="dayCount" access="read,write,required"></variable>
</controller>
<assignment class="com.myrequest.task.WriteRequestAssignmentHandler"></assignment>
</task>
當流程實例化Task_WriteRequest這個task後,首先會使用WriteRequestAssignmentHandler類來進行任務的分配。WriteRequestAssignmentHandler類實現了AssignmentHandler接口,包含一個
public void assign(Assignable assignable, ExecutionContext executionContext)接口。其中參數assignable就是此任務實例的引用(TaskInstance實現了Assignable接口)。因此我們只要調用assignable.setActorId(String userid),就可以把這個任務分配給userid所代表的用戶來執行了。
對於前臺,當用戶登陸後,通過org.jbpm.db.TaskMgmtSession.findTaskInstances(java.lang.String actorId)
就可以得到當前用戶所有要執行的任務了,這裏的任務是屬於多個不同的流程實例的。如果查看數據庫就會發現在jbpm_taskInstance表中,每一個taskinstance在actorId_字段記錄的是次任務實例分配人員的用戶名。這也就是上面所說的findTaskInstances方法如何工作的關鍵之處。
在
WriteRequestAssignmentHandler .assign()裏面,我們首先讀入流程變量userId,然後將任務分配給這個userId。
如何調用任務實例的end,來表示任務實例的完成呢?真正的系統中應該是在前臺,當客戶通過web或是客戶端觸發操作,然後執行對應的任務實例的end操作。但是在我們的代碼中只能通過在junit的測試代碼中來模擬。具體參見後面講解測試代碼部分。
4.2.3. Decision代碼說明
Decision節點可以有多種方式來進行條件判斷。
方法一是在每條出邊上加一個beanshell的表達式,jbpm引擎會按照流程定義文檔中邊的順序一次調用,來判斷那個表達式爲true,當發現第一個爲true的時候,流程就走這條邊了。
方法二就是對Decision節點配置一個handler。通過一個類來實現條件判斷。
例如:
<decision name="Deci_IsChiefHere">
<handler class="com.myrequest.decision.IsChiefHereDecisionHandler"/>
<transition name="Tr_Chief" to="TN_ChiefDecide"></transition>
<transition name="Tr_Boss" to="TN_BossDecide"></transition>
</decision>
表示當執行到Deci_IsChiefHere節點後,會自動執行IsChiefHereDecisionHandler類。IsChiefHereDecisionHandler實現了DecisionHandler接口。此接口包含一個方法public String decide(ExecutionContext executionContext) throws Exception 這個方法應該返回一個transition的name,表示選擇走那條邊。
這IsChiefHereDecisionHandler.decide()操作中,我們首先讀取流程變量isChiefHere,然後判斷走那條邊。
4.3. 流程測試類代碼說明
4.3.1. 測試類整體說明
測試類爲com.myrequest. MyRequestProcessTest
包含三個設施函數,分別爲
l test14DayAndBossNotApprove()
n 測試員工申請14天假期,部門主管批准,但老闆不批准
l test4DayAndChiefApprove()
n 測試員工申請14天假期,部門主管批准,(不需要老闆批准)
l test14DayAndChiefApproveAndUserCancel()
n 測試員工申請14天假期,部門主管批准,在老闆審批前員工自己撤銷申請
每個測試前都執行setUp()操作,這個操作用來設置流程中兩個變量,一個是用戶id,另外一個是部門主管是否在崗的狀態。可以修改這兩個參數,進行不同的測試。尤其是第二個參數,會影響流程的走向,可以分別設置爲true和false以觀察流程的走向和結果。
在測試類中還有7個輔助函數。分別爲:
l deployProcessDefinition()
n 流程部署
l createProcessInstance()
n 創建流程實例
n 設置join節點的性質爲Discriminator模式(參見流程定義部分)
n 設置流程相關變量
n 啓動流程
l userWriteRequest(int daycount)
n 模擬申請員工完成Task_WriteRequest任務,參數爲請假的天數
l chiefDecide(boolean isApprove)
n 模擬部門主管完成Task_ ChiefDecide任務,參數爲部門主管是否批准
l bossDecide(boolean isApprove)
n 模擬老闆完成Task_ BossDecide任務,參數爲老闆是否批准
l userCancel()
n 模擬申請員工完成Task_ CancelRequest任務
l checkTasks()
n 檢查整個流程中所有的任務的相關信息
4.3.2. checkTasks()說明
checkTasks()的核心是pi.getTaskMgmtInstance().getTaskInstances();返回流程實例的所有任務實例列表。但要說明的是所返回的任務實例列表和當前流程執行的位置有關,在流程開始處,流程執行中間,和流程執行結束處調用得到的任務實例列表不同。列表包含已經完成的任務實例和當前任務實例。
本來在此方法中還有現實每個任務實例起始時間和結束時間的操作。但發現返回全部爲null。這說明在沒有使用數據庫是,有關時間的屬性是不可用的。也就是說,這些時間屬性都是記錄在數據庫jbpm_taskInstance表中的。沒有用數據庫自然就得不到,它不會保留在內存的流程實例中。要說明的是jbpm_taskInstance表的start字段如果有時間,表示任務實例已經開始執行。如果end字段有時間,表示任務實例已經結束,此任務已經完成。
4.3.3. 測試中有關任務實例獲取的說明
因爲沒有使用數據庫,所以不能使用org.jbpm.db.TaskMgmtSession.findTaskInstances(java.lang.String actorId)得到不同用戶的當前任務。所以只能便利當前流程實例任務列表,從中找到相應的任務來操作。
如果有人有更好的辦法,請留言告知,謝謝:)
5. 實例不足和待學習地方
5.1. 不足
沒有使用swimlane,可以添加swimlane來進行任務分配。像websale就是有角色的。主要的原因是對swimlane的使用我還沒有搞清楚。我有關swimlane的學習心得會在下一篇對jbpm自帶實例websale的分析中來描述。
5.2. 學習點
通過這個實例,我發現org.jbpm.module.exe.ModuleInstance類有很多創建任務實例的方法,例如createTaskInstance(Task task)等等。我一直認爲任務的實例化是在流程執行過程中,又工作流引擎來做的工作。不知道在我們寫的代碼中需要用到創建流程這樣的方法嗎?如果要用的話,在什麼情況下會用到呢?
此外還有很多問題沒有搞懂,比如jbpm的模塊化思想,它的Ioc實現等等。慢慢學習吧。
6. 完整代碼
代碼太多,貼還是不貼呢,這是個問題啊。
6.1. MyRequestProcessTest
package com.myrequest;
import org.jbpm.graph.node.*;
import java.io.FileInputStream;
import java.util.*;
import org.jbpm.graph.def.ProcessDefinition;
import org.jbpm.graph.exe.ProcessInstance;
import org.jbpm.taskmgmt.*;
import org.jbpm.identity.*;
import org.jbpm.context.exe.ContextInstance;
import org.jbpm.taskmgmt.exe.*;
import junit.framework.TestCase;
import com.myrequest.*;
public class MyRequestProcessTest extends TestCase {
//static JbpmConfiguration cfg = JbpmConfiguration.getInstance();
ProcessDefinition pdf ;
ProcessInstance pi;
String userId;
boolean isChiefHere;
public void setUp(){
userId = "dust";
isChiefHere=true;
}
public void test14DayAndBossNotApprove() throws Exception {
this.deployProcessDefinition() ;
this.createProcessInstance() ;
this.userWriteRequest(14) ;
this.chiefDecide(true) ;
this.bossDecide(false);
this.checkTasks();
}
public void test4DayAndChiefApprove() throws Exception {
this.deployProcessDefinition() ;
this.createProcessInstance() ;
this.userWriteRequest(4) ;
this.chiefDecide(true) ;
this.checkTasks();
}
public void test14DayAndChiefApproveAndUserCancel() throws Exception {
this.deployProcessDefinition() ;
this.createProcessInstance() ;
this.userWriteRequest(14) ;
this.chiefDecide(true) ;
this.userCancel();
this.checkTasks();
}
protected void deployProcessDefinition() throws Exception{
System.out.println("==MyRequestProcessTest.deployProcessDefinition()==");
FileInputStream fis = new FileInputStream("processes/MyRequest/processdefinition.xml");
pdf = ProcessDefinition.parseXmlInputStream(fis);
assertNotNull("Definition should not be null", pdf);
}
protected void createProcessInstance() throws Exception{
System.out.println("==MyRequestProcessTest.createProcessInstance()==");
assertNotNull("Definition should not be null", pdf);
pi = new ProcessInstance(pdf);
assertNotNull("processInstance should not be null", pi);
Join join_Request = (Join)pi.getProcessDefinition().getNode("Join_Request");
assertNotNull("should find join_request node !",join_Request);
join_Request.setDiscriminator( true);
//設置申請人
pi.getContextInstance() .createVariable(RequestVariable.userId,this.userId);
//設置流程運行是,部門主管是否在崗
pi.getContextInstance() .createVariable(RequestVariable.isChiefHere,new Boolean(this.isChiefHere));
//啓動流程
pi.getRootToken().signal();
}
/**
* @param daycount 請假天數
* */
protected void userWriteRequest(int daycount){
System.out.println("==MyRequestProcessTest.userWriteRequest()==");
TaskInstance wr = (TaskInstance)pi.getTaskMgmtInstance().getTaskInstances().iterator() .next() ;
assertEquals(this.userId,wr.getActorId()) ;
ContextInstance ci = pi.getContextInstance();
ci.setVariable("dayCount",new Integer(daycount));
wr.end();
}
/**
* @param isApprove 部門主管是否同意請假
* */
protected void chiefDecide(boolean isApprove){
System.out.println("==MyRequestProcessTest.chiefDecide()==");
//String chiefId="today123";
/**
* 如果後臺使用數據庫的話,就可以使用
* org.jbpm.db.TaskMgmtSession.findTaskInstances(java.lang.String actorId) 得到所有分配給chiefId的taskInstance
* */
Collection coll = pi.getTaskMgmtInstance().getTaskInstances();
Iterator it = coll.iterator();
while(it.hasNext()){
TaskInstance ti = (TaskInstance)it.next();
if(ti.getName().equals("Task_ChiefDecide")){
assertEquals("today123",ti.getActorId());
if(isApprove)
ti.end("Tr_ChiefApprove");
else
ti.end("Tr_ChiefNotApprove");
return;
}
}
}
/**
* @param isApprove 老闆是否同意請假
* */
protected void bossDecide(boolean isApprove){
System.out.println("==MyRequestProcessTest.bossDecide()==");
//String bossId="elena";
/**
* 如果後臺使用數據庫的話,就可以使用
* org.jbpm.db.TaskMgmtSession.findTaskInstances(java.lang.String actorId) 得到所有分配給bossId的taskInstance
* */
Collection coll = pi.getTaskMgmtInstance().getTaskInstances();
Iterator it = coll.iterator();
while(it.hasNext()){
TaskInstance ti = (TaskInstance)it.next();
if(ti.getName().equals("Task_BossDecide")){
assertEquals("elena",ti.getActorId());
if(isApprove)
ti.end("Tr_BossApprove");
else
ti.end("Tr_BossNotApprove");
return;
}
}
}
protected void userCancel(){
System.out.println("==MyRequestProcessTest.userCancel()==");
/**
* 如果後臺使用數據庫的話,就可以使用
* org.jbpm.db.TaskMgmtSession.findTaskInstances(java.lang.String actorId) 得到所有分配給userid的taskInstance
* */
Collection coll = pi.getTaskMgmtInstance().getTaskInstances();
Iterator it = coll.iterator();
while(it.hasNext()){
TaskInstance ti = (TaskInstance)it.next();
if(ti.getName().equals("Task_CancelRequest")){
assertEquals(this.userId,ti.getActorId());
ti.end();
return;
}
}
}
protected void checkTasks(){
System.out.println("==MyRequestProcessTest.checkTasks()==");
Collection coll = pi.getTaskMgmtInstance().getTaskInstances();
Iterator it = coll.iterator();
System.out.println("====Process has task:====");
while(it.hasNext()){
TaskInstance ti = (TaskInstance)it.next();
System.out.println("=="+ti.getName()+"==");
System.out.println("=="+ti.getActorId()+"==");
System.out.println("=="+ti.getVariables().toString() +"==");
}
System.out.println("====end====");
}
}
6.2. ApproveActionHandler
package com.myrequest.action;
import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.exe.ExecutionContext;
import com.myrequest.*;
public class ApproveActionHandler implements ActionHandler {
public void execute(ExecutionContext executionContext) throws Exception {
// TODO Auto-generated method stub
System.out.println("==ApproveActionHandler.execute()==");
String user =(String)executionContext.getContextInstance().getVariable(RequestVariable.userId);
int dayCount = ((Integer)executionContext.getContextInstance().getVariable(RequestVariable.dayCount)).intValue();
/**
* 發送郵件給申請人user,告知其請假被批准。
*
* */
System.out.println("==發送郵件給"+user+",告知其"+dayCount+"天的請假申請,已經被批准。==");
}
}
6.3. BossApproveActionHandler
package com.myrequest.action;
import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.exe.ExecutionContext;
import com.myrequest.*;
public class BossApproveActionHandler implements ActionHandler {
public void execute(ExecutionContext executionContext) throws Exception {
// TODO Auto-generated method stub
executionContext.getContextInstance().setVariable(RequestVariable.requestState,RequestState.APPROVE );
System.out.println("==BossApproveActionHandler.execute()==");
}
}
6.4. BossNotApproveActionHandler
package com.myrequest.action;
import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.exe.ExecutionContext;
import com.myrequest.*;
public class BossNotApproveActionHandler implements ActionHandler {
public void execute(ExecutionContext executionContext) throws Exception {
// TODO Auto-generated method stub
executionContext.getContextInstance().setVariable(RequestVariable.requestState,RequestState.DISAPPROVE );
System.out.println("==BossNotApproveActionHandler.execute()==");
}
}
6.5. CancelActionHandler
package com.myrequest.action;
import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.exe.ExecutionContext;
import com.myrequest.RequestVariable;
import com.myrequest.*;
public class CancelActionHandler implements ActionHandler {
public void execute(ExecutionContext executionContext) throws Exception {
// TODO Auto-generated method stub
System.out.println("==CancelActionHandler.execute()==");
String user =(String)executionContext.getContextInstance().getVariable(RequestVariable.userId);
//如果取消的時候,部門主管已經批准過,那麼需要給部門主管發送郵件,通知其請假已經取消
if(executionContext.getContextInstance().hasVariable(RequestVariable.isChiefHere) && ((Boolean)executionContext.getContextInstance().getVariable(RequestVariable.isChiefHere)).booleanValue()){
int dayCount = ((Integer)executionContext.getContextInstance().getVariable(RequestVariable.dayCount)).intValue();
//通過user找到對應的chief
/**
* 發送郵件給chief,告知其請假被批准。
*
* */
System.out.println("==發送郵件給部門主管,告知其部門員工"+user+"的"+dayCount+"天請假申請,已經取消。==");
}
}
}
6.6. ChiefApproveActionHandler
package com.myrequest.action;
import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.exe.ExecutionContext;
import com.myrequest.RequestState;
import com.myrequest.RequestVariable;
import com.myrequest.*;
public class ChiefApproveActionHandler implements ActionHandler {
public void execute(ExecutionContext executionContext) throws Exception {
// TODO Auto-generated method stub
executionContext.getContextInstance().setVariable(RequestVariable.requestState,RequestState.CHIEFAPPROVE );
System.out.println("==ChiefApproveActionHandler.execute()==");
}
}
6.7. ChiefNotApproveActionHandler
package com.myrequest.action;
import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.exe.ExecutionContext;
import com.myrequest.*;
public class ChiefNotApproveActionHandler implements ActionHandler {
public void execute(ExecutionContext executionContext) throws Exception {
// TODO Auto-generated method stub
executionContext.getContextInstance().setVariable(RequestVariable.requestState,RequestState.DISAPPROVE );
System.out.println("==ChiefNotApproveActionHandler.execute()==");
}
}
6.8. FinishedActionHandler
package com.myrequest.action;
import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.exe.ExecutionContext;
import com.myrequest.*;
public class FinishedActionHandler implements ActionHandler {
public void execute(ExecutionContext executionContext) throws Exception {
System.out.println("==FinishedActionHandler.execute()==");
String user = (String)executionContext.getContextInstance().getVariable( RequestVariable.userId);
Integer dayCount = (Integer)executionContext.getContextInstance().getVariable( RequestVariable.dayCount);
String requestState = (String)executionContext.getContextInstance().getVariable( RequestVariable.requestState);
/**
* 將上述信息進行記錄,保存每一次員工請假的信息,無論是否批准還是取消請求
*
* */
System.out.print("==在系統中記錄:"+user+",申請請假"+dayCount+"天,");
if(requestState.compareTo(RequestState.APPROVE)==0)
System.out.print("被批准。");
else if(requestState.compareTo(RequestState.DISAPPROVE)==0)
System.out.print("未被批准。");
else if(requestState.compareTo(RequestState.CANCLE)==0)
System.out.print("已取消。");
else{
System.out.println("");
System.out.println("====系統出現問題,最終申請狀態爲:"+requestState+"====");
System.out.println("");
}
System.out.println("==");
}
}
6.9. NotApproveActionHandler
package com.myrequest.action;
import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.exe.ExecutionContext;
import com.myrequest.RequestVariable;
public class NotApproveActionHandler implements ActionHandler {
public void execute(ExecutionContext executionContext) throws Exception {
// TODO Auto-generated method stub
System.out.println("==NotApproveActionHandler.execute()==");
String user =(String)executionContext.getContextInstance().getVariable(RequestVariable.userId);
int dayCount = ((Integer)executionContext.getContextInstance().getVariable(RequestVariable.dayCount)).intValue();
/**
* 發送郵件給申請人user,告知其請假沒有被批准。
*
* */
System.out.println("==發送郵件給"+user+",告知其"+dayCount+"天的請假申請,沒有被批准。==");
}
}
6.10. NotNeedActionHandler
package com.myrequest.action;
import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.exe.ExecutionContext;
import com.myrequest.*;
public class NotNeedActionHandler implements ActionHandler {
public void execute(ExecutionContext executionContext) throws Exception {
// TODO Auto-generated method stub
executionContext.getContextInstance().setVariable(RequestVariable.requestState,RequestState.APPROVE );
System.out.println("==NotNeedActionHandler.execute()==");
}
}
6.11. RequestCancelActionHandler
package com.myrequest.action;
import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.exe.ExecutionContext;
import com.myrequest.*;
public class RequestCancelActionHandler implements ActionHandler {
public void execute(ExecutionContext executionContext) throws Exception {
// TODO Auto-generated method stub
String requestState = (String)executionContext.getContextInstance().getVariable(RequestVariable.requestState);
//只有兩種狀態下允許撤銷
if(requestState.compareTo(RequestState.REQEUST)==0 || requestState.compareTo(RequestState.CHIEFAPPROVE)==0 ){
//如果撤銷請求的時候,請求狀態爲“部門主管批准”狀態
if(requestState.compareTo(RequestState.CHIEFAPPROVE)==0){
executionContext.getContextInstance().createVariable(RequestVariable.isChiefHere,new Boolean(true));
}
executionContext.getContextInstance().setVariable(RequestVariable.requestState,RequestState.CANCLE );
}
else{
System.out.println("==can't canel request==");
}
System.out.println("==RequestCancelActionHandler.execute()==");
}
}
6.12. WriteLeaveActionHandler
package com.myrequest.action;
import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.exe.ExecutionContext;
import com.myrequest.RequestState;
import com.myrequest.RequestVariable;
import com.myrequest.*;
public class WriteLeaveActionHandler implements ActionHandler {
/**
* 設置請假狀態爲請求
* */
public void execute(ExecutionContext executionContext) throws Exception {
// TODO Auto-generated method stub
//設置請求狀態爲請求
executionContext.getContextInstance().createVariable(RequestVariable.requestState,RequestState.REQEUST);
System.out.println("==RequestBeginActionHandler.execute()==");
}
}
6.13. DoSomethingDecisionHandler
package com.myrequest.decision;
import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.graph.node.DecisionHandler;
import com.myrequest.*;
public class DoSomethingDecisionHandler implements DecisionHandler {
public String decide(ExecutionContext executionContext) throws Exception {
String requestState = (String)executionContext.getContextInstance().getVariable(RequestVariable.requestState);
if(requestState.compareTo(RequestState.APPROVE)==0){
return "Tr_Approve";
}
else if(requestState.compareTo(RequestState.DISAPPROVE)==0){
return "Tr_NotApprove";
}
else if(requestState.compareTo(RequestState.CANCLE)==0){
return "Tr_Cancel";
}
else{
System.out.println("==狀態不對==");
throw new Exception("到達的狀態不對,爲:"+requestState);
}
}
}
6.14. IsChiefHereDecisionHandler
package com.myrequest.decision;
import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.graph.node.DecisionHandler;
import com.myrequest.RequestVariable;
public class IsChiefHereDecisionHandler implements DecisionHandler {
public String decide(ExecutionContext executionContext) throws Exception {
/**
* 根據主管是否休假來判斷走哪條邊
* */
System.out.println("==IsChiefHereDecisionHandler.decide()==");
//主管在
if(((Boolean)executionContext.getContextInstance().getVariable(RequestVariable.isChiefHere)).booleanValue())
return "Tr_Chief";
else
return "Tr_Boss";
}
}
6.15. NeedBossDecideDecisionHandler
package com.myrequest.decision;
import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.graph.node.DecisionHandler;
import com.myrequest.*;
public class NeedBossDecideDecisionHandler implements DecisionHandler {
public String decide(ExecutionContext executionContext) throws Exception {
// TODO Auto-generated method stub
int dayCount =((Integer)executionContext.getContextInstance().getVariable(RequestVariable.dayCount)).intValue();
int dayLevel= 10;//是否需要boss批准的分界
if(dayCount>=dayLevel){
return "Tr_Need";
}
else{
return "Tr_NotNeed";
}
}
}
6.16. BossDecideAssignmentHandler
package com.myrequest.task;
import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.taskmgmt.def.AssignmentHandler;
import org.jbpm.taskmgmt.exe.Assignable;
public class BossDecideAssignmentHandler implements AssignmentHandler {
public void assign(Assignable assignable, ExecutionContext executionContext)
throws Exception {
// TODO Auto-generated method stub
/**
* 以角色boss到數據庫中查找,找到userid elena
* */
String bossId="elena";
//elena is boss
assignable.setActorId(bossId);
}
}
6.17. CancelRequestAssignmentHandler
package com.myrequest.task;
import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.taskmgmt.def.AssignmentHandler;
import org.jbpm.taskmgmt.exe.Assignable;
import com.myrequest.*;
public class CancelRequestAssignmentHandler implements AssignmentHandler {
public void assign(Assignable assignable, ExecutionContext executionContext)
throws Exception {
// TODO Auto-generated method stub
String userid=(String)executionContext.getContextInstance() .getVariable(RequestVariable.userId);
/**
* 撤銷請假請求任務可以由請假發起人執行,demo裏面是dust
* */
assignable.setActorId(userid);
System.out.println("==assign user is:"+userid+" ==");
System.out.println("==CancelRequestAssignmentHandler.assign()==");
}
}
6.18. ChiefDecideAssignmentHandler
package com.myrequest.task;
import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.taskmgmt.def.AssignmentHandler;
import org.jbpm.taskmgmt.exe.Assignable;
import com.myrequest.*;
public class ChiefDecideAssignmentHandler implements AssignmentHandler {
public void assign(Assignable assignable, ExecutionContext executionContext)
throws Exception {
// TODO Auto-generated method stub
String userid=(String)executionContext.getContextInstance().getVariable( RequestVariable.userId);
/**
* 通過userid得到user的上級chief是誰,這裏假設是today123
* */
String chiefId ="today123";
assignable.setActorId(chiefId);
System.out.println("==assign user is:"+userid+" ==");
System.out.println("==ChiefDecideAssignmentHandler.assign()==");
}
}
6.19. WriteRequestAssignmentHandler
package com.myrequest.task;
import com.myrequest.*;
import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.taskmgmt.def.AssignmentHandler;
import org.jbpm.taskmgmt.exe.Assignable;
public class WriteRequestAssignmentHandler implements AssignmentHandler {
public void assign(Assignable assignable, ExecutionContext executionContext)
throws Exception {
// TODO Auto-generated method stub
String userid =(String)executionContext.getContextInstance().getVariable(RequestVariable.userId);
assignable.setActorId(userid);
System.out.println("==assign user is:"+userid+" ==");
System.out.println("==WriteRequestAssignmentHandler.assign()==");
}
}
6.20. RequestState
package com.myrequest;
public interface RequestState {
final static String REQEUST="request";
final static String APPROVE="approve";
final static String DISAPPROVE="disapprove";
final static String CANCLE="cancel";
final static String CHIEFAPPROVE="chiefapprove";
}
6.21. RequestVariable
package com.myrequest;
public interface RequestVariable {
//請求狀態
final static String requestState="RequestState";
//主管是否在崗
final static String isChiefHere="isChiefHere";
//要請假的日期天數
final static String dayCount = "dayCount";
//啓動流程,也就是要申請請假的工人的id
final static String userId ="userId";
//部門主管是否同意
final static String isChiefApprove="isChiefApprove";
}
6.22. Processdefinition.xml
<?xml version="1.0" encoding="UTF-8"?>
<process-definition
xmlns="urn:jbpm.org:jpdl-3.1" name="MyRequest">
<start-state name="SS_Request">
<transition name="" to="TN_WriteRequest"></transition>
</start-state>
<task-node name="TN_WriteRequest">
<task name="Task_WriteRequest">
<controller>
<variable name="dayCount" access="read,write,required"></variable>
</controller>
<assignment class="com.myrequest.task.WriteRequestAssignmentHandler"></assignment>
</task>
<transition name="Tr_WriteLeave" to="Fork_request">
<action name="Ac_WriteLeave" class="com.myrequest.action.WriteLeaveActionHandler"></action>
</transition>
</task-node>
<fork name="Fork_request">
<transition name="Tr_Cancel" to="TN_RequesterCancel"></transition>
<transition name="Tr_Request" to="Deci_IsChiefHere"></transition>
</fork>
<decision name="Deci_IsChiefHere">
<handler class="com.myrequest.decision.IsChiefHereDecisionHandler"/>
<transition name="Tr_Chief" to="TN_ChiefDecide"></transition>
<transition name="Tr_Boss" to="TN_BossDecide"></transition>
</decision>
<task-node name="TN_RequesterCancel">
<task name="Task_CancelRequest">
<assignment class="com.myrequest.task.CancelRequestAssignmentHandler"></assignment>
</task>
<transition name="Tr_RequestCancel" to="Join_Request">
<action name="Ac_RequestCancel" class="com.myrequest.action.RequestCancelActionHandler"></action>
</transition>
</task-node>
<task-node name="TN_ChiefDecide">
<task name="Task_ChiefDecide">
<assignment class="com.myrequest.task.ChiefDecideAssignmentHandler"></assignment>
</task>
<transition name="Tr_ChiefApprove" to="Deci_NeedBossDecide">
<action name="Ac_ChiefApprove" class="com.myrequest.action.ChiefApproveActionHandler"></action>
</transition>
<transition name="Tr_ChiefNotApprove" to="Join_Request">
<action name="Ac_ChiefNotApprove" class="com.myrequest.action.ChiefNotApproveActionHandler"></action>
</transition>
</task-node>
<join name="Join_Request">
<transition name="Tr_Join" to="Deci_DoSomething"></transition>
</join>
<decision name="Deci_NeedBossDecide">
<handler class="com.myrequest.decision.NeedBossDecideDecisionHandler"/>
<transition name="Tr_Need" to="TN_BossDecide"></transition>
<transition name="Tr_NotNeed" to="Join_Request">
<action name="Ac_NotNeed" class="com.myrequest.action.NotNeedActionHandler"></action>
</transition>
</decision>
<task-node name="TN_BossDecide">
<task name="Task_BossDecide">
<assignment class="com.myrequest.task.BossDecideAssignmentHandler"></assignment>
</task>
<transition name="Tr_BossApprove" to="Join_Request">
<action name="Ac_BossApprove" class="com.myrequest.action.BossApproveActionHandler"></action>
</transition>
<transition name="Tr_BossNotApprove" to="Join_Request">
<action name="Ac_BossNotApprove" class="com.myrequest.action.BossNotApproveActionHandler"></action>
</transition>
</task-node>
<decision name="Deci_DoSomething">
<handler class="com.myrequest.decision.DoSomethingDecisionHandler"/>
<transition name="Tr_Approve" to="ES_Finished">
<action name="Ac_Approve" class="com.myrequest.action.ApproveActionHandler"></action>
</transition>
<transition name="Tr_NotApprove" to="ES_Finished">
<action name="Ac_NotApprove" class="com.myrequest.action.NotApproveActionHandler"></action>
</transition>
<transition name="Tr_Cancel" to="ES_Finished">
<action name="Ac_Cancel" class="com.myrequest.action.CancelActionHandler"></action>
</transition>
</decision>
<end-state name="ES_Finished">
<event type="node-enter">
<action name="Ac_Finished" class="com.myrequest.action.FinishedActionHandler"></action>
</event>
</end-state>
</process-definition>
6.23. gpd.xml
<?xml version="1.0" encoding="UTF-8"?>
<process-diagram name="MyRequest" width="804" height="613">
<node name="SS_Request" x="147" y="5" width="140" height="40">
<transition name="">
<label x="5" y="-10"/>
</transition>
</node>
<node name="TN_WriteRequest" x="145" y="71" width="140" height="40">
<transition name="Tr_WriteLeave">
<label x="5" y="-10"/>
</transition>
</node>
<node name="Fork_request" x="168" y="134" width="200" height="25">
<transition name="Tr_Cancel">
<label x="5" y="-10"/>
</transition>
<transition name="Tr_Request">
<label x="5" y="-10"/>
</transition>
</node>
<node name="Deci_IsChiefHere" x="311" y="179" width="140" height="40">
<transition name="Tr_Chief">
<label x="5" y="-10"/>
</transition>
<transition name="Tr_Boss">
<label x="5" y="-10"/>
</transition>
</node>
<node name="TN_RequesterCancel" x="9" y="181" width="140" height="40">
<transition name="Tr_RequestCancel">
<label x="-73" y="-5"/>
</transition>
</node>
<node name="TN_ChiefDecide" x="159" y="253" width="140" height="40">
<transition name="Tr_ChiefApprove">
<label x="-42" y="-9"/>
</transition>
<transition name="Tr_ChiefNotApprove">
<label x="-28" y="-5"/>
</transition>
</node>
<node name="Join_Request" x="115" y="388" width="200" height="25">
<transition name="Tr_Join">
<label x="5" y="-10"/>
</transition>
</node>
<node name="Deci_NeedBossDecide" x="415" y="243" width="166" height="40">
<transition name="Tr_Need">
<label x="-30" y="-11"/>
</transition>
<transition name="Tr_NotNeed">
<label x="5" y="-10"/>
</transition>
</node>
<node name="TN_BossDecide" x="663" y="241" width="140" height="40">
<transition name="Tr_BossApprove">
<label x="31" y="-13"/>
</transition>
<transition name="Tr_BossNotApprove">
<label x="-19" y="4"/>
</transition>
</node>
<node name="Deci_DoSomething" x="146" y="460" width="140" height="40">
<transition name="Tr_Approve">
<label x="5" y="-10"/>
</transition>
<transition name="Tr_NotApprove">
<label x="2" y="5"/>
</transition>
<transition name="Tr_Cancel">
<label x="4" y="-25"/>
</transition>
</node>
<node name="ES_Finished" x="147" y="572" width="140" height="40"/>
</process-diagram>