jBPM-jPDL學習筆記——流程設計與控制

 相關資料:
《jBPM學習筆記(V3.2環境部署)》
《jBPM學習筆記(框架設計簡介)》
背景
本片文章,我們將從業務流程的設計開始,通過帶領大家完成一個完整工作流的程序設計,來學習jPDL的使用。

業務流程設計
這裏我們實現一個相對簡化的公司借款申請流程。流程圖如下: 
 

在jPDL中,與流程設計相關的文件有三個:processdefinition.xml、gdp.xml、processimage.jpg。其中processdefinition.xml是流程定義的描述文件;gpd.xml是對圖形界面呈現的XML描述;而processimage.jpg則是對圖形界面的快照。下面我們將展示本樣例的流程定義文件。

流程定義描述

processdefinition.xml文件

引用

<?xml version="1.0" encoding="UTF-8"?>

<process-definition   xmlns ="urn:jbpm.org:jpdl-3.2"  name="simple">
   <start-state name="開始">
      <transition name="借款發起" to="填寫借款申請">
<action name="Action_StartProcess" class="com.firstflow.action.StartProcessActionHander"></action>
</transition>
   </start-state>
   <task-node name="填寫借款申請">
<task name="Task_AssignToInitiator">
<assignment class="com.firstflow.task.NewApplicationAssignmentHandler"></assignment>
</task>
<transition to="部門經理審批" name="提交申請">
<action name="Action_SubmitApply" class="com.firstflow.action.SubmitApplyActionHandler"></action>
</transition>
</task-node>

<task-node name="部門經理審批">
<task name="Task_ManagerApprove">
<assignment class="com.firstflow.task.DepartmentManagerApproveAssignmentHandler"></assignment>
</task>
<transition to="金額判定" name="部門經理審批通過">
<action name="Task_ManagerApproved" class="com.firstflow.action.ManagerApprovedActionHandler"></action>
</transition>
<transition to="結束" name="部門經理駁回">
<action name="Action_ManagerDisapprove" class="com.firstflow.action.ManagerDisapproveActionHandler"></action>
</transition>
</task-node>

<node name="財務撥款">
<action name="Action_AccountantProcess" class="com.firstflow.action.AccountantProcessActoinHandler"></action>
<transition to="結束" name="郵件通知">
<action name="Action_Mail" class="com.firstflow.action.SendMailActionHandler"></action>
</transition>
</node>

<decision name="金額判定">
<handler class="com.firstflow.decision.MoneyCheckDecisionHandler"></handler>
<transition to="總經理審批" name="&gt;5000元總經理審批"></transition>
<transition to="財務撥款" name="&lt;5000元 財務撥款"></transition>
</decision>

<task-node name="總經理審批">
<task name="Task_PresidentApprove">
<assignment class="com.firstflow.task.PresidentApproveAssignmentHandler"></assignment>
</task>
<transition to="財務撥款" name="總經理審批通過">
<action name="Action_PresidentApproved" class="com.firstflow.action.PresidentApprovedActionHandler"></action>
</transition>
<transition to="結束" name="總經理駁回">
<action name="Action_PresidentDisapproved" class="com.firstflow.action.PresidentDisapprovedActionHandler"></action>
</transition>
</task-node>
   <end-state name="結束"></end-state>
</process-definition>


在樣例流程中,除了開始和結束結點外,我們定義了三種類型的結點:

任務結點<task-node>
任務結點是一個需要人工參與的結點類型。當流程進入結點時,會生成相應的任務實例(TaskInstatnce),並通過委派接口AssignmentHandler或jBPM表達式將任務委派給一個或多個特定的角色或參與者。結點自身進入等待狀態,直到任務被參與者完成或者跳過,流程繼續。

判定結點<decision>
判定結點的設計目標是根據上下文環境和程序邏輯,判定流程轉向。通過指定一個實現DecisionHandlder接口的Java委派類或jBPM表達式,來返回轉向(transition)的字符竄類型的名稱(可以是中文哦),來達到決定流程方向的功能。

普通結點<node>
普通結點也可以定義相應的處理任務,通過定義相應的ActioinHandler類。同任務結點不同的是,普通結點定義的任務是由流程自動執行的,無須人工干預。

三種結點都可定義結點事件(event):
node-enter,該事件在流程進入結點時觸發
node-leave,該事件在流程離開節點是觸發
可以在事件上掛接ActioinHandler接口的實現類來完成一些特定的功能。

三種節點都可以定義異步處理方式(async屬性):
異步處理意味着每個結點的事務處理是通過消息機制分離的,不再同一線程中統一調用執行。而是由消息監聽線程從消息隊列中取得消息體來運行相應得程序。
此外我們定義了結點間的轉向(transition),用來記錄和處理狀態的變遷。每個轉向中,可以委派一個或多個的ActioinHandler接口實現類,負責處理節點變遷時的上下文狀態變更及回調用戶定義的處理程序。

流程的程序接口說明

動作處理接口(ActioinHandler)
接口方法:
void execute( ExecutionContext executionContext ) throws Exception
該接口是jPDL中最常用的一個回調接口。從它的接口方法可以發現,它僅僅暴露了流程執行上下文變量ExecutionContext。用戶程序通過ExecutionContext來了解流程的執行狀態,並通過改變ExecutionContext中的屬性值來影響流程的執行。
ActioinHandler接口可以在所有能包含事件(event)、動作(action)元素的地方被回調。

判定處理接口(DecisionHandlder)
接口方法:
String decide(ExecutionContext executionContext) throws Exception
判定接口僅適用於判定節點(decision)中。從它的接口方法可以看出,方法要返回一個字符串型的結果,這個結果必須和判定節點擁有的轉向(transition)集合中的一條轉向名稱相匹配。
在DecisionHandlder的接口方法中一樣能訪問到ExecutionContext變量,這爲判定提供了執行上下文的根據。當然,如果有必要,用戶也可以在該接口中改變ExecutionContext中的變量值。

委派處理接口(AssignmentHandler)
接口方法:
void assign(Assignable assignable, ExecutionContext executionContext) throws Exception;
委派處理接口被用戶任務元素(task)的委派(assignment)子元素中,它的職責很明確,就是將任務分配給指定的人員或角色。
在AssignmentHandler接口的方法中,Assignable變量通常指任務實例(TaskInstance)。通過將ExecutionContext和TaskInstance兩個變量都暴露給接口方法,用戶就可以根據流程上下文情況,來決定要將指定的任務分配個誰。

流程的部署

用戶使用jPDL的流程設計器定義業務流程,當然,你也可以直接用文檔編輯器直接編輯processdefinition.xml定義文件。定義文檔是可以直接被ProcessDefinition類載入使用的,但在正式運行的系統中,流程定義信息更多是使用關係型數據庫來存儲。從流程定義文件將數據導入流程數據庫的過程,我們稱之爲流程部署。
jPDL的流程部署文件包含processdefinition.xml的定義部分和Java處理器的代碼部分,這些文件可以被一起打包成.jpdl的zip格式包而後上傳服務器端。這個過程可以在流程設計器界面的“deployment”標籤頁中操作: 
 
這裏我們着重要講述的是接受部署文件上載的服務器端配置。在jBPM3.2的包中帶着一個jPDL的管理控制檯web應用,默認名字爲jbpm-console。該應用帶有接受流程定義包部署的程序,但不是最小化的。實際上完成流程部署功能的,只是jbpm-jpdl.jar核心包中的一個servlet類:org.jbpm.web.ProcessUploadServlet . 完成這個Servlet的成功部署,需要以下工作:

1. 配置web.xml,將servlet配置成啓動時加載,如下:
引用

<web-app>
<servlet>
  <servlet-name>GDP Deployer Servlet</servlet-name>
  <servlet-class>org.jbpm.web.ProcessUploadServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
  </servlet>
 
<servlet-mapping>
  <servlet-name>GDP Deployer Servlet</servlet-name>
  <url-pattern>/upload/*</url-pattern>
  </servlet-mapping>
 
</web-app>


2. 建立流程定義存儲數據庫表:
Demo中,我們使用的數據庫是MySQL的,在E:\Java\tools\jbpm-jpdl-3.2.2\db\目錄下有個jbpm.jpdl.mysql.sql數據庫腳本文件。但我們不能直接導入該文件, 會提示有錯誤, 應爲該文件的SQL語句末尾少了分號,在批量執行時,MySQL報錯。需要在每一行SQL的末尾添加一個分號,這樣就可以用source命令導入了。

3. 配置Hibernate.cfg.xml
由於jBPM的數據庫持久化是依靠Hibernate進行的,因此需要配置Hibernate.cfg.xml使其適應我們的MySQL環境
引用

<!-- hibernate dialect -->
    <property name="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
    <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/linly</property>
    <property name="hibernate.connection.username">linly</property>
    <property name="hibernate.connection.password">coffee</property>
    <property name="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property>


4. Import需要的jar包
這裏的jar包包括三部分:jbpm的核心包;Hibernate及其支撐包;MySQL的JDBC驅動包。


到此,我們的配置工作完成,這是實現jBPM流程部署服務端的最小化應用配置。

流程控制及API使用

樣例程序中的Handler接口實現

下面,根據上述的接口分類,列出樣例程序中的類名及相應的功能說明,具體可參考源代碼。
動作處理接口(ActioinHandler)

這裏要提到一個很重要的區別,就是作用於Node上的ActoinHandler和作用於Transition上的ActoinHandler是有不同的。區別在於,Node上的ActoinHandler在結束業務邏輯處理後,必須調用executionContext.leaveNode();或executionContext.leaveNode(transition)來保證流程向下執行;而作用於Transition上的則不需要。

判定處理接口(DecisionHandlder)


委派處理接口(AssignmentHandler)


流程測試剖析

本章節,我們將給大家剖析兩個流程測試類。一個是簡單的基於內存模型的流程測試FirstFlowProcessTest;一個是更貼近實用的,基於MySQL數據庫操作的標準測試案例。通過對這兩個測試例程的分析,來直觀的學習如何通過Java API操作jPDL。

簡單流程測試案例
測試案例類:FirstFlowProcessTest.java
Java代碼
public class FirstFlowProcessTest extends TestCase {
	ProcessDefinition pdf ;
    ProcessInstance pi;       

	public void test4000YuanApplication() throws Exception {
		deployProcessDefinition();
		createProcessInstance("linly");
		submitApplication(4000);
		approveByManager(true);
		checkTasks();
	}

	public void test6000YuanApplication() throws Exception {
		deployProcessDefinition();
		createProcessInstance("linly");
		submitApplication(6000);
		approveByManager(true);
		approveByPresident(true);
		checkTasks();
	}
	
	public void test7000YuanApplication() throws Exception {
		deployProcessDefinition();
		createProcessInstance("linly");
		submitApplication(7000);
		approveByManager(true);
		approveByPresident(false);
		checkTasks();
	}
	
	/**
	 * 部署流程定義
	 * @throws Exception
	 */
    protected void deployProcessDefinition() throws Exception{
        System.out.println("==FirstFlowProcessTest.deployProcessDefinition()==");
        pdf = ProcessDefinition.parseXmlResource("firstflow/processdefinition.xml");
        assertNotNull("Definition should not be null", pdf);        
    }    
    /**
     * 生成流程實例 
     */
    protected void createProcessInstance(String user){
        System.out.println("==FirstFlowProcessTest.createProcessInstance()==");
    assertNotNull("Definition should not be null", pdf);
        //生成實例
    	pi = pdf.createProcessInstance();
    	assertNotNull("processInstance should not be null", pi);
    	//設置流程發起人
    	pi.getContextInstance().createVariable("initiator", user);
    	//觸發流程轉向
    	pi.signal();
    }    
    /**
     * 填寫提交申請單
     * @param money
     */
    protected void submitApplication(int money){    	
        System.out.println("==FirstFlowProcessTest.submitApplication()==");        
        TaskInstance ti = (TaskInstance)pi.getTaskMgmtInstance().getTaskInstances().iterator() .next() ;        
        System.out.println("ti.actor = " + ti.getActorId());        
        ContextInstance ci = ti.getContextInstance();
        ci.setVariable("money",new Integer(money));
        ti.end();    	
    }    
    /**
     * 部門經理審批
     * @param pass
     */    
    @SuppressWarnings("unchecked")
    protected void approveByManager(boolean pass){    	
        System.out.println("==FirstFlowProcessTest.approveByManager()==");        
        Iterator<TaskInstance> it = pi.getTaskMgmtInstance().getTaskInstances().iterator();        
        for( ;it.hasNext(); ){        	
        	TaskInstance ti = it.next();        	
        	if(ti.getActorId().equals("DepartmentManager")){
                List<Transition> transitions = ti.getToken().getNode().getLeavingTransitions();
                for(Transition t : transitions){
                	System.out.println("----Transition" + t.getName());
                }
                assertEquals("DepartmentManager",ti.getActorId());
                if(pass){
                	 ti.end("部門經理審批通過");    	
                }else{
                	ti.end("部門經理駁回");
                }
                return;
        	}
        }        
    }
    
    /**
     * 總經理審批
     * @param pass
     */
    @SuppressWarnings("unchecked")
    protected void approveByPresident(boolean pass){
    	   System.out.println("==FirstFlowProcessTest.approveByPresident()==");
    	   
           Iterator<TaskInstance> it = pi.getTaskMgmtInstance().getTaskInstances().iterator();
           
           for( ;it.hasNext(); ){
           	
	           	TaskInstance ti = it.next();
	           	
	           	if(ti.getActorId().equals("President")){
	                List<Transition> transitions = ti.getToken().getNode().getLeavingTransitions();
	                for(Transition t : transitions){
	                	System.out.println("----Transition" + t.getName());
	                }
	                assertEquals("President",ti.getActorId());
	                if(pass){
	                	 ti.end("總經理審批通過");    	
	                }else{
	                	ti.end("總經理駁回");
	                }
	                return;
	           	}
           }
    }    
    /**
     * 打印任務記錄
     */
    @SuppressWarnings("unchecked")
    protected void checkTasks(){       
        System.out.println("==FirstFlowProcessTest.checkTasks()==");
        Collection<TaskInstance> coll = pi.getTaskMgmtInstance().getTaskInstances();
        System.out.println("====Process has task:====");
        for(TaskInstance ti : coll){
               System.out.println("=="+ti.getName()+"==");
               System.out.println("=="+ti.getActorId()+"==");
               System.out.println("=="+ti.getVariables().toString() +"==");
        }
        System.out.println("====end====");   
    }     


該案例是在沒有數據庫支持的情況下,對報銷流程進行運行測試,測試邏輯如下:

1. 加載流程定義
Java代碼
ProcessDefinition.parseXmlResource("firstflow/processdefinition.xml")
代碼說明:
在沒有數據庫存儲的情況下,流程定義通過ProcessDefinition類直接從processdefinition.xml文件中解析加載。

2. 實例化流程對象
Java代碼
      //生成實例
       pi = pdf.createProcessInstance();
      assertNotNull("processInstance should not be null", pi);
      //設置流程發起人
       pi.getContextInstance().createVariable("initiator", user);
      //觸發流程轉向
       pi.signal();
代碼說明:
在獲得流程定義的實例後,可以用它生成流程實例,使用如下的語句:
pi = pdf.createProcessInstance();
流程實例擁有自己的ContextInstance環境變量對象。它實際上是一個HashMap,以key-value方式記錄了流程的上下文變量值,代碼中的
pi.getContextInstance().createVariable("initiator", user);就是向環境變量中添加一個key爲initiator的對象。
每個流程實例都擁有自己Token令牌對象,主流程有自己的RootToken,子流程也擁有自己的子Token。父流程的Token和子流程的Token相互關聯,形成Token樹。
Token對象表示流程運行的當前位置(運行到哪個節點了)。通過對Token對象的signal()方法調用,可以使流程向下運行。代碼中的pi.signal();實際上是間接調用了pi.getRootToken().signal();它使得新建的流程繼續向下個節點(即借款申請單填寫)進發。

3. 員工發起借款申請
Java代碼
    /**
     * 填寫提交申請單
     * @param money
     */
    protected void submitApplication(int money){
    		 System.out.println("==FirstFlowProcessTest.submitApplication()==");
        
        TaskInstance ti = (TaskInstance)pi.getTaskMgmtInstance().getTaskInstances().iterator() .next()         
        System.out.println("ti.actor = " + ti.getActorId());
        ContextInstance ci = ti.getContextInstance();
        ci.setVariable("money",new Integer(money));
        ti.end();
}
代碼說明:
在借款流程發起後,流程進入了申請單填寫階段。這個階段是個人工的任務,需要用戶的介入。因此,對於要借款的用戶而言,首先是獲取填寫申請單的任務實例:
     TaskInstance ti = (TaskInstance)pi.getTaskMgmtInstance().getTaskInstances().iterator() .next()        
在這個測試類中,由於沒有數據庫。對流程實例的引用是依靠了類的全局標量pi。這裏通過pi獲取全部的任務列表。實際上有且僅有一個任務,就是我們剛剛發起的申請單填寫任務。
接下來,我們獲取流程的上下文變量,將申請借款的數額記錄在上下文變量中ContextInstance ci = ti.getContextInstance();
    ci.setVariable("money",new Integer(money));
最後,我們要結束當前任務,告訴流程繼續下行,調用ti.end();這個方法的本質依然是調用了token.signal(),它選擇了一個默認的transition進行轉向。這裏要說明的是signal方法有多態的實現signal(Transition  transition),是可以指定具體轉向參數的。

4. 部門領導審批申請
Java代碼
     /**
     * 部門經理審批
     * @param pass
     */    
    @SuppressWarnings("unchecked")
    protected void approveByManager(boolean pass){    	
        System.out.println("==FirstFlowProcessTest.approveByManager()==");        
        Iterator<TaskInstance> it = pi.getTaskMgmtInstance().getTaskInstances().iterator();        
        for( ;it.hasNext(); ){        	
        	TaskInstance ti = it.next();        	
        	if(ti.getActorId().equals("DepartmentManager")){
                List<Transition> transitions = ti.getToken().getNode().getLeavingTransitions();
                for(Transition t : transitions){
                	System.out.println("----Transition" + t.getName());
                }
                assertEquals("DepartmentManager",ti.getActorId());
                if(pass){
                	 ti.end("部門經理審批通過");    	
                }else{
                	ti.end("部門經理駁回");
                }
                return;
        	}
        } 
}
代碼說明:
這裏,流程進入了部門經理審批階段。由於沒有數據庫支持,我們只能採取遍歷任務列表,並比對委派者ID的方式來確定委派給部門經理的任務實例。(在後面的基於數據庫的標準案例中,我們會看到如果根據用戶的ID來獲取分配給指定用戶的任務)
ti.getActorId().equals("DepartmentManager") // 比對任務的委派人。
ti.getToken().getNode().getLeavingTransitions();//獲取任務在當前節點上的所有轉向。
這裏我們要特別指出的是ti.end("部門經理審批通過")和ti.end("部門經理駁回")這實際上調用token.signal(transition);來完成任務的轉向,從而使流程繼續。

5. 總經理審批申請
Java代碼
     /**
     * 總經理審批
     * @param pass
     */
    @SuppressWarnings("unchecked")
    protected void approveByPresident(boolean pass){
    	   System.out.println("==FirstFlowProcessTest.approveByPresident()=="); 
           Iterator<TaskInstance> it = pi.getTaskMgmtInstance().getTaskInstances().iterator();
           
           for( ;it.hasNext(); ){           	
	           	TaskInstance ti = it.next();	           	
	           	if(ti.getActorId().equals("President")){
	                List<Transition> transitions = ti.getToken().getNode().getLeavingTransitions();
	                for(Transition t : transitions){
	                	System.out.println("----Transition" + t.getName());
	                }
	                assertEquals("President",ti.getActorId());
	                if(pass){
	                	 ti.end("總經理審批通過");    	
	                }else{
	                	ti.end("總經理駁回");
	                }
	                return;
	           	}
           }
}
代碼說明:
此步代碼同“部門經理審批”代碼相似,不作更多說明。

標準流程測試案例

該案例模擬了標準運行環境中,基於關係型數據庫的jBPM系統是如何執行流程的。
測試案例類:FirstFlowProcessDBTest.java
Java代碼
public class FirstFlowProcessDBTest {	
	/*
	 * 初始化jBPM配置
	 * 包含對Hibernate的數據庫初始化
	 */
    static JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();    
    public static void main(String[] args){
    	FirstFlowProcessDBTest test = new FirstFlowProcessDBTest();
    	test.test4000YuanApplication();
    	test.test6000YuanApplication();
    	test.test7000YuanApplication();
    }
    
	public void test4000YuanApplication(){
		ProcessInstance pi = createProcessInstance("linly");
		submitApplication("linly" , 4000);
		approveByManager(true);
		checkTasks(pi);
	}
	public void test6000YuanApplication() {
		ProcessInstance pi = createProcessInstance("linly");
		submitApplication("linly" , 6000);
		approveByManager(true);
		approveByPresident(true);
		checkTasks(pi);
	}	
	public void test7000YuanApplication() {
		ProcessInstance pi = createProcessInstance("linly");
		submitApplication("linly" , 7000);
		approveByManager(true);
		approveByPresident(false);
		checkTasks(pi);
	} 
    /**
     * 生成流程實例 
     */
    protected ProcessInstance createProcessInstance(String user){
        System.out.println("==FirstFlowProcessTest.createProcessInstance()==");
        JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
        try {
        	 GraphSession graphSession = jbpmContext.getGraphSession();
             /*
              * 從數據庫獲取指定的流程定義
              */
        	 ProcessDefinition pdf  = graphSession.findLatestProcessDefinition("simple");
        	 //生成流程實例
        	 ProcessInstance pi = pdf.createProcessInstance();
        	 //設置流程發起人
        	 pi.getContextInstance().createVariable("initiator", user);
        	 //觸發流程轉向
        	 pi.signal();
        	 /*
        	  * 保存流程實例 
        	  */
        	 jbpmContext.save(pi);        	 
        	 return pi;
        }finally{
        	jbpmContext.close();
        }
    }
    
    /**
     * 填寫提交申請單
     * @param money
     */
    @SuppressWarnings("unchecked")
    protected void submitApplication(String actorId , int money){
        System.out.println("==FirstFlowProcessTest.submitApplication()=="); 
    	JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
	    try {  	
            /*
             *根據操作者ID,獲取屬於該操作者的任務集
             */
	        List<TaskInstance> taskInstances = jbpmContext.getTaskList(actorId);	        
	        for(TaskInstance ti : taskInstances){
		        System.out.println("ti.name = " + ti.getName());
		        System.out.println("ti.actor = " + ti.getActorId());
		        ContextInstance ci = ti.getContextInstance();
		        ci.setVariable("money",new Integer(money));
		        ti.end();
	        }	        
	    }finally{
	    	jbpmContext.close();	
	    }
    }
    
    /**
     * 部門經理審批
     * @param pass
     */
    @SuppressWarnings("unchecked")
    protected void approveByManager(boolean pass){    	
        System.out.println("==FirstFlowProcessTest.approveByManager()==");
    	JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
    	try{
    		List<TaskInstance> taskInstances = jbpmContext.getTaskList("DepartmentManager");
    		for(TaskInstance ti : taskInstances){    		
				System.out.println("ti.name = " + ti.getName());
				System.out.println("ti.actor = " + ti.getActorId());
			     if(pass){
                	 ti.end("部門經理審批通過");    	
                }else{
                	ti.end("部門經理駁回");
                } 			
    		}
    	}finally{
    		jbpmContext.close();
    	}
    }
    
    /**
     * 總經理審批
     * @param pass
     */
    @SuppressWarnings("unchecked")
    protected void approveByPresident(boolean pass){
    	System.out.println("==FirstFlowProcessTest.approveByPresident()==");    	
    	JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
    	try{
    		List<TaskInstance> taskInstances = jbpmContext.getTaskList("President");
    		for(TaskInstance ti : taskInstances){
    			System.out.println("ti.name = " + ti.getName());
				System.out.println("ti.actor = " + ti.getActorId());
			     if(pass){
                	 ti.end("總經理審批通過");    	
                }else{
                	ti.end("總經理駁回");
                }
			}
    	}finally{
    		jbpmContext.close();
    	}
    }
    
    /**
     * 打印任務記錄
     */
    @SuppressWarnings("unchecked")
	protected void checkTasks(ProcessInstance pi){       
        System.out.println("==FirstFlowProcessTest.checkTasks()==");
        JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
    	try{    				
	        List<TaskInstance> coll = jbpmContext.getTaskMgmtSession().findTaskInstancesByProcessInstance(pi);	
	        System.out.println("====Process has task:====");	
	        for(TaskInstance ti : coll){	
	               System.out.println("=="+ti.getName()+"==");	
	               System.out.println("=="+ti.getActorId()+"==");	
	               System.out.println("=="+ti.getVariables().toString() +"==");	
	        }	
	        System.out.println("====end====");
    	}finally{
    		jbpmContext.close();
    	}
    }    
}

相對於簡單流程測試案例,標準流程的業務是相同的。它們的不同點在於:簡單流程通過XML載入流程定義,並使用類的全局變量來保存流程實例的引用;而標準流程則是通過數據庫載入流程定義,並使用數據庫的會話來維護流程的運行。

1. 從數據庫載入流程定義
Java代碼
	JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
從jBPM配置環境中獲取jBPM上下文實例,jbpmContext是對jbpm持久層操作API及上下文環境的封裝,它根據jBPM的配置文件生成。   
Java代碼
GraphSession graphSession = jbpmContext.getGraphSession();
生成對流程圖操作的持久層會話
Java代碼
ProcessDefinition pdf  = graphSession.findLatestProcessDefinition("simple");
從數據庫獲取命名爲“simple”的流程定義。

2. 新建流程實例,並存入數據庫持久化
Java代碼
ProcessInstance pi = pdf.createProcessInstance();
根據流程定義生成實例。
Java代碼
pi.getContextInstance().createVariable("initiator", user);
pi.signal();

設置實例的上下文變量,激活實例執行進程。
Java代碼
jbpmContext.save(pi);

保存實例到數據庫,持久化實例。

3. 從數據庫獲取屬於指定操作者的任務集
Java代碼
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try{
    	List<TaskInstance> taskInstances = jbpmContext.getTaskList("DepartmentManager");
    	for(TaskInstance ti : taskInstances){
		System.out.println("ti.name = " + ti.getName());
		System.out.println("ti.actor = " + ti.getActorId());
		if(pass){
            	 ti.end("部門經理審批通過");    	
         }else{
            	ti.end("部門經理駁回");
         }
	}	
}finally{
	jbpmContext.close();
}
通過JbpmContext對象,從數據庫獲取指定操作者的任務集合:
Java代碼
List<TaskInstance> taskInstances = jbpmContext.getTaskList("DepartmentManager");

注意,在每次的JbpmContext對象使用完畢後,一定要執行jbpmContext.close(),其本質是要釋放數據庫的操作連接。

(全文完)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章