`
linliangyi2007
  • 浏览: 1009237 次
  • 性别: Icon_minigender_1
  • 来自: 福州
社区版块
存档分类
最新评论

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
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. 加载流程定义
ProcessDefinition.parseXmlResource("firstflow/processdefinition.xml")
代码说明:
在没有数据库存储的情况下,流程定义通过ProcessDefinition类直接从processdefinition.xml文件中解析加载。

2. 实例化流程对象
      //生成实例
       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. 员工发起借款申请
    /**
     * 填写提交申请单
     * @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. 部门领导审批申请
     /**
     * 部门经理审批
     * @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. 总经理审批申请
     /**
     * 总经理审批
     * @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
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. 从数据库载入流程定义
	JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
从jBPM配置环境中获取jBPM上下文实例,jbpmContext是对jbpm持久层操作API及上下文环境的封装,它根据jBPM的配置文件生成。   
GraphSession graphSession = jbpmContext.getGraphSession();
生成对流程图操作的持久层会话
ProcessDefinition pdf  = graphSession.findLatestProcessDefinition("simple");
从数据库获取命名为“simple”的流程定义。

2. 新建流程实例,并存入数据库持久化
ProcessInstance pi = pdf.createProcessInstance();
根据流程定义生成实例。
pi.getContextInstance().createVariable("initiator", user);
pi.signal();

设置实例的上下文变量,激活实例执行进程。
jbpmContext.save(pi);

保存实例到数据库,持久化实例。

3. 从数据库获取属于指定操作者的任务集
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对象,从数据库获取指定操作者的任务集合:
List<TaskInstance> taskInstances = jbpmContext.getTaskList("DepartmentManager");

注意,在每次的JbpmContext对象使用完毕后,一定要执行jbpmContext.close(),其本质是要释放数据库的操作连接。

(全文完)













  • FirstFlow样例代码.rar (85.9 KB)
  • 描述: jBPM Eclipse源码工程包(由于附件大小限制,去掉了lib中的jar包) 修订过的 MySQL建库脚本
  • 下载次数: 6307
分享到:
评论
69 楼 bolingsky 2009-02-21  
这个例子真是好,对初学者来说太有用了,我学了两天的JBPM,感觉你这个例子比网上任何的文字教程都要来得实在! 非常感谢楼主!
68 楼 yanghaofeilong 2009-01-22  
lrh800300 写道
linliangyi2007 写道
相关资料:
《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
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. 加载流程定义
ProcessDefinition.parseXmlResource("firstflow/processdefinition.xml")
代码说明:
在没有数据库存储的情况下,流程定义通过ProcessDefinition类直接从processdefinition.xml文件中解析加载。

2. 实例化流程对象
      //生成实例
       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. 员工发起借款申请
    /**
     * 填写提交申请单
     * @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. 部门领导审批申请
     /**
     * 部门经理审批
     * @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. 总经理审批申请
     /**
     * 总经理审批
     * @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
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. 从数据库载入流程定义
	JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
从jBPM配置环境中获取jBPM上下文实例,jbpmContext是对jbpm持久层操作API及上下文环境的封装,它根据jBPM的配置文件生成。   
GraphSession graphSession = jbpmContext.getGraphSession();
生成对流程图操作的持久层会话
ProcessDefinition pdf  = graphSession.findLatestProcessDefinition("simple");
从数据库获取命名为“simple”的流程定义。

2. 新建流程实例,并存入数据库持久化
ProcessInstance pi = pdf.createProcessInstance();
根据流程定义生成实例。
pi.getContextInstance().createVariable("initiator", user);
pi.signal();

设置实例的上下文变量,激活实例执行进程。
jbpmContext.save(pi);

保存实例到数据库,持久化实例。

3. 从数据库获取属于指定操作者的任务集
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对象,从数据库获取指定操作者的任务集合:
List<TaskInstance> taskInstances = jbpmContext.getTaskList("DepartmentManager");

注意,在每次的JbpmContext对象使用完毕后,一定要执行jbpmContext.close(),其本质是要释放数据库的操作连接。

(全文完)















大哥:支持你,希望以后还多出点这样的有关JBPM的好文章啊!!!
67 楼 lrh800300 2009-01-10  
linliangyi2007 写道
相关资料:
《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
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. 加载流程定义
ProcessDefinition.parseXmlResource("firstflow/processdefinition.xml")
代码说明:
在没有数据库存储的情况下,流程定义通过ProcessDefinition类直接从processdefinition.xml文件中解析加载。

2. 实例化流程对象
      //生成实例
       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. 员工发起借款申请
    /**
     * 填写提交申请单
     * @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. 部门领导审批申请
     /**
     * 部门经理审批
     * @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. 总经理审批申请
     /**
     * 总经理审批
     * @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
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. 从数据库载入流程定义
	JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
从jBPM配置环境中获取jBPM上下文实例,jbpmContext是对jbpm持久层操作API及上下文环境的封装,它根据jBPM的配置文件生成。   
GraphSession graphSession = jbpmContext.getGraphSession();
生成对流程图操作的持久层会话
ProcessDefinition pdf  = graphSession.findLatestProcessDefinition("simple");
从数据库获取命名为“simple”的流程定义。

2. 新建流程实例,并存入数据库持久化
ProcessInstance pi = pdf.createProcessInstance();
根据流程定义生成实例。
pi.getContextInstance().createVariable("initiator", user);
pi.signal();

设置实例的上下文变量,激活实例执行进程。
jbpmContext.save(pi);

保存实例到数据库,持久化实例。

3. 从数据库获取属于指定操作者的任务集
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对象,从数据库获取指定操作者的任务集合:
List<TaskInstance> taskInstances = jbpmContext.getTaskList("DepartmentManager");

注意,在每次的JbpmContext对象使用完毕后,一定要执行jbpmContext.close(),其本质是要释放数据库的操作连接。

(全文完)














66 楼 linliangyi2007 2009-01-08  
wjzliu0064 写道
   该流程应用与实际项目集成的例子有吗,例如与struts+hibernate+spring框架集成,系统启动后,自动加载流程定义文件保存数据到数据库,还有每个用户的待办任务和已办任务信息怎样取出显示在页面上


1.首先,我以为流程定义不应该在系统启动时加载到数据库的,应该是通过流程发布方式保存于数据库中。
2.获取用户待办任务和已办任务的API jBPM文档中都有demo,其使用方式与普通的api完全一样。

很多朋友都提出相同的问题,我感觉,大家深受SSH的思维结构的禁锢,除了在页面上增删改查,其他的api调用就变的似乎很困难了


65 楼 wjzliu0064 2009-01-08  
   该流程应用与实际项目集成的例子有吗,例如与struts+hibernate+spring框架集成,系统启动后,自动加载流程定义文件保存数据到数据库,还有每个用户的待办任务和已办任务信息怎样取出显示在页面上
64 楼 linliangyi2007 2009-01-07  
shuaijie506 写道
org.jbpm.JbpmException: task node does not have leaving transition '主管同意'

查询这个错误时找到这里,看到很多人都有这个错误,原因大侠们说的也很一致,就是流程定义文件中transition name="主管同意"找不到。

但是问题是我的流程定义文件里有transition name="主管同意"这个东西呀???

十分迷惑……
下附流程定义文件的这一部分的定义:

   <task-node name="部门主管审核">
      <task name="主管审核">
         <controller>
            <variable name="baixiaoId" access="read" mapped-name="报销ID"></variable>
         </controller>
         <assignment class="org.springmodules.workflow.jbpm31.JbpmHandlerProxy" config-type="bean">
            <targetBean>managerAssignment</targetBean>
            <factoryKey>jbpmConfiguration</factoryKey>
         </assignment>
      </task>
      <transition name="要求修改" to="申请人修改报销单"></transition>
      <transition name="主管同意" to="判断走向"></transition>
   </task-node>

调试时是执行到以下代码报错的:
TaskInstance ti=jbpmContext.getTaskMgmtSession().getTaskInstance(Long.parseLong(tid));
if(result.equals("主管同意")||result.equals("主管不同意"))
{
ti.end("主管同意");
}
这一句ti.end("主管同意");完事就跳到catch块去了。
不知道怎么回事。楼主可以帮忙解答一下吗,不胜感谢!


因为transition是汉字的,请注意你java文件的编码是不是用UTF-8的
63 楼 shuaijie506 2009-01-06  
org.jbpm.JbpmException: task node does not have leaving transition '主管同意'

查询这个错误时找到这里,看到很多人都有这个错误,原因大侠们说的也很一致,就是流程定义文件中transition name="主管同意"找不到。

但是问题是我的流程定义文件里有transition name="主管同意"这个东西呀???

十分迷惑……
下附流程定义文件的这一部分的定义:

   <task-node name="部门主管审核">
      <task name="主管审核">
         <controller>
            <variable name="baixiaoId" access="read" mapped-name="报销ID"></variable>
         </controller>
         <assignment class="org.springmodules.workflow.jbpm31.JbpmHandlerProxy" config-type="bean">
            <targetBean>managerAssignment</targetBean>
            <factoryKey>jbpmConfiguration</factoryKey>
         </assignment>
      </task>
      <transition name="要求修改" to="申请人修改报销单"></transition>
      <transition name="主管同意" to="判断走向"></transition>
   </task-node>

调试时是执行到以下代码报错的:
TaskInstance ti=jbpmContext.getTaskMgmtSession().getTaskInstance(Long.parseLong(tid));
if(result.equals("主管同意")||result.equals("主管不同意"))
{
ti.end("主管同意");
}
这一句ti.end("主管同意");完事就跳到catch块去了。
不知道怎么回事。楼主可以帮忙解答一下吗,不胜感谢!
62 楼 linliangyi2007 2009-01-02  
楼上的兄弟,不要随便删除hibernate.queries.hbm.xml中的sql,有些是目前没有用的,但是为了后期jBPM的扩展,有用的。

看了你的异常,很多是数据库的session被关闭的原因。查查代码吧,有没有在关闭session后又对数据库进行操作的。

至于servlet部署异常,这个比较奇怪,不过尽量不要用“GDP Deployer Servlet”这种带空格的名字,我自己没有试过这个行不行,但最好不要吧。

这个servlet没有必要<load-on-startup>1</load-on-startup> 的。
61 楼 xiatiaohcx 2009-01-02  
我觉得还是没有部署成功的问题,我在下载的文件中没有发现web.xml,于是新建了一个web.xml在webapp/WEB-INF/web.xml,然后把
<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>
加入到web.xml中,这样做不知道有没有问题
然后在流程设计器界面的“deployment”标签页中操作:
点击Test Connection会出这样的错
An exception happened while testing the server connection.
Resaon:
An unexcepted exception caused the test connection operation to fail
不明白是怎么回事,我初学java还望指点一下
60 楼 xiatiaohcx 2009-01-02  
我看了一下在org.jbpm.db中hibernate.queries.hbm.xml中有这样一个查询
<query name="GraphSession.findTokensForProcessInNode">
    <![CDATA[
      select token
      from org.jbpm.graph.exe.Token token
      where token.processInstance.processDefinition.name = :processDefinitionName
        and node.name = :nodeName
    ]]>
  </query>
也不明白这个查询是做什么用的,直接删除了,部署成功,可在执行代码时出现
Exception in thread "main" org.jbpm.JbpmException: task node does not have leaving transition '部门经理审批通过'
at org.jbpm.taskmgmt.exe.TaskInstance.end(TaskInstance.java:425)
at com.firstflow.FirstFlowProcessDBTest.approveByManager(FirstFlowProcessDBTest.java:138)
at com.firstflow.FirstFlowProcessDBTest.test4000YuanApplication(FirstFlowProcessDBTest.java:38)
at com.firstflow.FirstFlowProcessDBTest.main(FirstFlowProcessDBTest.java:32)
楼主能看一下是什么问题吗
59 楼 xiatiaohcx 2009-01-02  
执行查询时是这个错
org.hibernate.hql.ast.QuerySyntaxException: Invalid path: 'node.name' [
   
      select token
      from org.jbpm.graph.exe.Token token
      where token.processInstance.processDefinition.name = :processDefinitionName
        and node.name = :nodeName
   
  ]
58 楼 xiatiaohcx 2009-01-02  
能帮我看一下这是什么错吗,谢谢了
Exception in thread "main" 00:00:21,171 [main] DEBUG JbpmContext : closing jbpmContext org.jbpm.JbpmContext@e48e1b
00:00:21,171 [main] DEBUG Services : closing service 'persistence': org.jbpm.persistence.db.DbPersistenceService@17f11fb
org.hibernate.HibernateException: Errors in named queries: GraphSession.findTokensForProcessInNode
at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:341)
57 楼 xuantian868 2008-12-30  
学习了。很好,很强大。
56 楼 swims 2008-12-21  
 




              谢谢分享,收益匪浅 ,临走之时,不忘回帖!
55 楼 linliangyi2007 2008-12-13  
sevencolor 写道

按林兄写的代码测试,已经在数据库部署成功,但第一个4000元测试就过不去了,林兄能麻烦你看下是什么问题造成异常吗?是在'com.firstflow.task.NewApplicationAssignmentHandler' 出现问题的
Thu Dec 11 09:59:15 CST 2008员工linly发起借款流程
09:59:15,671 [main] DEBUG Token : token[10] is unlocked by token[10]
09:59:15,671 [main] DEBUG NullableType : binding '11' to parameter: 1
09:59:15,671 [main] DEBUG NullableType : returning 'K' as column: CLASS2_1_0_
09:59:15,671 [main] DEBUG NullableType : returning '填写借款申请' as column: NAME3_1_0_
09:59:15,671 [main] DEBUG NullableType : returning null as column: DESCRIPT4_1_0_
09:59:15,671 [main] DEBUG NullableType : returning '2' as column: PROCESSD5_1_0_
09:59:15,687 [main] DEBUG NullableType : returning 'false' as column: ISASYNC6_1_0_
09:59:15,687 [main] DEBUG NullableType : returning 'false' as column: ISASYNCE7_1_0_
09:59:15,687 [main] DEBUG NullableType : returning null as column: ACTION8_1_0_
09:59:15,687 [main] DEBUG NullableType : returning null as column: SUPERSTATE9_1_0_
09:59:15,687 [main] DEBUG NullableType : returning '4' as column: SIGNAL15_1_0_
09:59:15,687 [main] DEBUG NullableType : returning 'true' as column: CREATET16_1_0_
09:59:15,687 [main] DEBUG NullableType : returning 'false' as column: ENDTASKS17_1_0_
09:59:15,703 [main] DEBUG GraphElement : event 'node-enter' on 'TaskNode(填写借款申请)' for 'Token(/)'
09:59:15,703 [main] DEBUG NullableType : binding '11' to parameter: 1
09:59:15,703 [main] DEBUG NullableType : binding '11' to parameter: 1
09:59:15,718 [main] DEBUG NullableType : returning '5' as column: ID1_10_0_
09:59:15,718 [main] DEBUG NullableType : returning 'Task_AssignToInitiator' as column: NAME2_10_0_
09:59:15,718 [main] DEBUG NullableType : returning null as column: DESCRIPT3_10_0_
09:59:15,718 [main] DEBUG NullableType : returning '2' as column: PROCESSD4_10_0_
09:59:15,718 [main] DEBUG NullableType : returning 'false' as column: ISBLOCKING5_10_0_
09:59:15,718 [main] DEBUG NullableType : returning 'true' as column: ISSIGNAL6_10_0_
09:59:15,718 [main] DEBUG NullableType : returning null as column: CONDITION7_10_0_
09:59:15,718 [main] DEBUG NullableType : returning null as column: DUEDATE8_10_0_
09:59:15,718 [main] DEBUG NullableType : returning '3' as column: PRIORITY9_10_0_
09:59:15,718 [main] DEBUG NullableType : returning null as column: ACTORID10_10_0_
09:59:15,718 [main] DEBUG NullableType : returning null as column: POOLEDA11_10_0_
09:59:15,718 [main] DEBUG NullableType : returning '6' as column: TASKMGM12_10_0_
09:59:15,718 [main] DEBUG NullableType : returning '11' as column: TASKNODE13_10_0_
09:59:15,718 [main] DEBUG NullableType : returning null as column: STARTSTATE14_10_0_
09:59:15,734 [main] DEBUG NullableType : returning '6' as column: ASSIGNM15_10_0_
09:59:15,734 [main] DEBUG NullableType : returning null as column: SWIMLANE16_10_0_
09:59:15,734 [main] DEBUG NullableType : returning null as column: TASKCON17_10_0_
09:59:15,734 [main] DEBUG NullableType : returning '11' as column: TASKNODE13_1_
09:59:15,734 [main] DEBUG NullableType : returning '5' as column: ID1_1_
09:59:15,734 [main] WARN&nbsp; StatefulPersistenceContext : Narrowing proxy to class org.jbpm.graph.node.TaskNode - this operation breaks ==
09:59:15,765 [main] DEBUG NullableType : binding '5' to parameter: 1
09:59:15,781 [main] DEBUG NullableType : binding '0' to parameter: 1
09:59:15,781 [main] DEBUG NullableType : binding 'Task_AssignToInitiator' to parameter: 2
09:59:15,796 [main] DEBUG NullableType : binding null to parameter: 3
09:59:15,796 [main] DEBUG NullableType : binding null to parameter: 4
09:59:15,796 [main] DEBUG NullableType : binding null to parameter: 5
09:59:15,796 [main] DEBUG NullableType : binding null to parameter: 6
09:59:15,796 [main] DEBUG NullableType : binding null to parameter: 7
09:59:15,796 [main] DEBUG NullableType : binding null to parameter: 8
09:59:15,796 [main] DEBUG NullableType : binding '3' to parameter: 9
09:59:15,796 [main] DEBUG NullableType : binding 'false' to parameter: 10
09:59:15,796 [main] DEBUG NullableType : binding 'false' to parameter: 11
09:59:15,796 [main] DEBUG NullableType : binding 'true' to parameter: 12
09:59:15,796 [main] DEBUG NullableType : binding 'true' to parameter: 13
09:59:15,796 [main] DEBUG NullableType : binding 'false' to parameter: 14
09:59:15,812 [main] DEBUG NullableType : binding '5' to parameter: 15
09:59:15,812 [main] DEBUG NullableType : binding null to parameter: 16
09:59:15,812 [main] DEBUG NullableType : binding '10' to parameter: 17
09:59:15,812 [main] DEBUG NullableType : binding null to parameter: 18
09:59:15,812 [main] DEBUG NullableType : binding null to parameter: 19
09:59:15,828 [main] DEBUG GraphElement : event 'task-create' on 'Task(Task_AssignToInitiator)' for 'Token(/)'
09:59:15,828 [main] DEBUG NullableType : binding '5' to parameter: 1
09:59:15,828 [main] DEBUG NullableType : binding '6' to parameter: 1
09:59:15,843 [main] DEBUG NullableType : returning 'com.firstflow.task.NewApplicationAssignmentHandler' as column: CLASSNAME2_6_0_
09:59:15,843 [main] DEBUG NullableType : returning null as column: CONFIGUR3_6_0_
09:59:15,843 [main] DEBUG NullableType : returning 'field' as column: CONFIGTYPE4_6_0_
09:59:15,843 [main] DEBUG NullableType : returning '2' as column: PROCESSD5_6_0_
09:59:15,843 [main] DEBUG JbpmContext : closing jbpmContext org.jbpm.JbpmContext@1f26605
09:59:15,843 [main] DEBUG Services : closing service 'persistence': org.jbpm.persistence.db.DbPersistenceService@1ac1e22
09:59:15,859 [main] DEBUG DbPersistenceService : committing hibernate transaction org.hibernate.transaction.JDBCTransaction@cd4544
09:59:15,859 [main] DEBUG NullableType : binding '0' to parameter: 1
09:59:15,859 [main] DEBUG NullableType : binding '10' to parameter: 2
09:59:15,859 [main] DEBUG NullableType : binding '6' to parameter: 3
09:59:15,859 [main] DEBUG NullableType : binding '0' to parameter: 1
09:59:15,875 [main] DEBUG NullableType : binding '10' to parameter: 2
09:59:15,875 [main] DEBUG NullableType : binding '0' to parameter: 1
09:59:15,875 [main] DEBUG NullableType : binding '10' to parameter: 2
09:59:15,875 [main] DEBUG NullableType : binding '20' to parameter: 3
09:59:15,906 [main] DEBUG NullableType : binding '0' to parameter: 1
09:59:15,906 [main] DEBUG NullableType : binding 'initiator' to parameter: 2
09:59:15,906 [main] DEBUG NullableType : binding '10' to parameter: 4
09:59:15,921 [main] DEBUG NullableType : binding '10' to parameter: 5
09:59:15,921 [main] DEBUG NullableType : binding '10' to parameter: 6
09:59:15,921 [main] DEBUG NullableType : binding 'linly' to parameter: 7
09:59:16,000 [main] DEBUG NullableType : binding '1' to parameter: 1
09:59:16,000 [main] DEBUG NullableType : binding null to parameter: 2
09:59:16,000 [main] DEBUG NullableType : binding '2008-12-11 09:59:14' to parameter: 3
09:59:16,000 [main] DEBUG NullableType : binding null to parameter: 4
09:59:16,000 [main] DEBUG NullableType : binding '2008-12-11 09:59:15' to parameter: 5
09:59:16,000 [main] DEBUG NullableType : binding '6' to parameter: 6
09:59:16,000 [main] DEBUG NullableType : binding 'true' to parameter: 7
09:59:16,000 [main] DEBUG NullableType : binding 'false' to parameter: 8
09:59:16,000 [main] DEBUG NullableType : binding 'false' to parameter: 9
09:59:16,000 [main] DEBUG NullableType : binding null to parameter: 10
09:59:16,000 [main] DEBUG NullableType : binding '11' to parameter: 11
09:59:16,000 [main] DEBUG NullableType : binding '10' to parameter: 12
09:59:16,000 [main] DEBUG NullableType : binding null to parameter: 13
09:59:16,000 [main] DEBUG NullableType : binding null to parameter: 14
09:59:16,000 [main] DEBUG NullableType : binding '10' to parameter: 15
09:59:16,015 [main] DEBUG NullableType : binding '0' to parameter: 16
09:59:16,015 [main] DEBUG NullableType : binding '1' to parameter: 1
09:59:16,015 [main] DEBUG NullableType : binding null to parameter: 2
09:59:16,015 [main] DEBUG NullableType : binding '2008-12-11 09:59:14' to parameter: 3
09:59:16,015 [main] DEBUG NullableType : binding null to parameter: 4
09:59:16,031 [main] DEBUG NullableType : binding 'false' to parameter: 5
09:59:16,031 [main] DEBUG NullableType : binding '2' to parameter: 6
09:59:16,031 [main] DEBUG NullableType : binding '10' to parameter: 7
09:59:16,031 [main] DEBUG NullableType : binding null to parameter: 8
09:59:16,031 [main] DEBUG NullableType : binding '10' to parameter: 9
09:59:16,031 [main] DEBUG NullableType : binding '0' to parameter: 10
09:59:16,031 [main] DEBUG NullableType : binding '1' to parameter: 1
09:59:16,031 [main] DEBUG NullableType : binding 'Task_AssignToInitiator' to parameter: 2
09:59:16,031 [main] DEBUG NullableType : binding null to parameter: 3
09:59:16,031 [main] DEBUG NullableType : binding null to parameter: 4
Exception in thread "main" org.jbpm.graph.def.DelegationException: com.firstflow.task.NewApplicationAssignmentHandler
at org.jbpm.taskmgmt.exe.TaskMgmtInstance.performAssignment(TaskMgmtInstance.java:259)
at org.jbpm.taskmgmt.exe.TaskInstance.assign(TaskInstance.java:198)
at org.jbpm.taskmgmt.exe.TaskMgmtInstance.createTaskInstance(TaskMgmtInstance.java:197)
at org.jbpm.graph.node.TaskNode.execute(TaskNode.java:168)
at org.jbpm.graph.def.Node.enter(Node.java:318)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:157)
at org.jbpm.graph.def.Node$$EnhancerByCGLIB$$395302c8.enter(&lt;generated&gt;)
at org.jbpm.graph.def.Transition.take(Transition.java:151)
at org.jbpm.graph.def.Node.leave(Node.java:393)
at org.jbpm.graph.node.StartState.leave(StartState.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:157)
at org.jbpm.graph.def.Node$$EnhancerByCGLIB$$395302c8.leave(&lt;generated&gt;)
at org.jbpm.graph.exe.Token.signal(Token.java:192)
at org.jbpm.graph.exe.Token.signal(Token.java:140)
at org.jbpm.graph.exe.ProcessInstance.signal(ProcessInstance.java:270)
at com.sample.FirstFlowProcessDBTest.createProcessInstance(FirstFlowProcessDBTest.java:62)
at com.sample.FirstFlowProcessDBTest.test4000YuanApplication(FirstFlowProcessDBTest.java:25)
at com.sample.FirstFlowProcessDBTest.main(FirstFlowProcessDBTest.java:19)
Caused by: java.lang.ClassCastException: com.firstflow.task.NewApplicationAssignmentHandler
at org.jbpm.taskmgmt.exe.TaskMgmtInstance.performAssignmentDelegation(TaskMgmtInstance.java:266)
at org.jbpm.taskmgmt.exe.TaskMgmtInstance.performAssignment(TaskMgmtInstance.java:244)
... 25 more


从提示上看就是执行你的任务分配handler时出错了。看不出其他的,你应该在NewApplicationAssignmentHandler类中加一些跟踪代码调试一下
54 楼 sevencolor 2008-12-11  
按林兄写的代码测试,已经在数据库部署成功,但第一个4000元测试就过不去了,林兄能麻烦你看下是什么问题造成异常吗?是在'com.firstflow.task.NewApplicationAssignmentHandler' 出现问题的
Thu Dec 11 09:59:15 CST 2008员工linly发起借款流程
09:59:15,671 [main] DEBUG Token : token[10] is unlocked by token[10]
09:59:15,671 [main] DEBUG NullableType : binding '11' to parameter: 1
09:59:15,671 [main] DEBUG NullableType : returning 'K' as column: CLASS2_1_0_
09:59:15,671 [main] DEBUG NullableType : returning '填写借款申请' as column: NAME3_1_0_
09:59:15,671 [main] DEBUG NullableType : returning null as column: DESCRIPT4_1_0_
09:59:15,671 [main] DEBUG NullableType : returning '2' as column: PROCESSD5_1_0_
09:59:15,687 [main] DEBUG NullableType : returning 'false' as column: ISASYNC6_1_0_
09:59:15,687 [main] DEBUG NullableType : returning 'false' as column: ISASYNCE7_1_0_
09:59:15,687 [main] DEBUG NullableType : returning null as column: ACTION8_1_0_
09:59:15,687 [main] DEBUG NullableType : returning null as column: SUPERSTATE9_1_0_
09:59:15,687 [main] DEBUG NullableType : returning '4' as column: SIGNAL15_1_0_
09:59:15,687 [main] DEBUG NullableType : returning 'true' as column: CREATET16_1_0_
09:59:15,687 [main] DEBUG NullableType : returning 'false' as column: ENDTASKS17_1_0_
09:59:15,703 [main] DEBUG GraphElement : event 'node-enter' on 'TaskNode(填写借款申请)' for 'Token(/)'
09:59:15,703 [main] DEBUG NullableType : binding '11' to parameter: 1
09:59:15,703 [main] DEBUG NullableType : binding '11' to parameter: 1
09:59:15,718 [main] DEBUG NullableType : returning '5' as column: ID1_10_0_
09:59:15,718 [main] DEBUG NullableType : returning 'Task_AssignToInitiator' as column: NAME2_10_0_
09:59:15,718 [main] DEBUG NullableType : returning null as column: DESCRIPT3_10_0_
09:59:15,718 [main] DEBUG NullableType : returning '2' as column: PROCESSD4_10_0_
09:59:15,718 [main] DEBUG NullableType : returning 'false' as column: ISBLOCKING5_10_0_
09:59:15,718 [main] DEBUG NullableType : returning 'true' as column: ISSIGNAL6_10_0_
09:59:15,718 [main] DEBUG NullableType : returning null as column: CONDITION7_10_0_
09:59:15,718 [main] DEBUG NullableType : returning null as column: DUEDATE8_10_0_
09:59:15,718 [main] DEBUG NullableType : returning '3' as column: PRIORITY9_10_0_
09:59:15,718 [main] DEBUG NullableType : returning null as column: ACTORID10_10_0_
09:59:15,718 [main] DEBUG NullableType : returning null as column: POOLEDA11_10_0_
09:59:15,718 [main] DEBUG NullableType : returning '6' as column: TASKMGM12_10_0_
09:59:15,718 [main] DEBUG NullableType : returning '11' as column: TASKNODE13_10_0_
09:59:15,718 [main] DEBUG NullableType : returning null as column: STARTSTATE14_10_0_
09:59:15,734 [main] DEBUG NullableType : returning '6' as column: ASSIGNM15_10_0_
09:59:15,734 [main] DEBUG NullableType : returning null as column: SWIMLANE16_10_0_
09:59:15,734 [main] DEBUG NullableType : returning null as column: TASKCON17_10_0_
09:59:15,734 [main] DEBUG NullableType : returning '11' as column: TASKNODE13_1_
09:59:15,734 [main] DEBUG NullableType : returning '5' as column: ID1_1_
09:59:15,734 [main] WARN  StatefulPersistenceContext : Narrowing proxy to class org.jbpm.graph.node.TaskNode - this operation breaks ==
09:59:15,765 [main] DEBUG NullableType : binding '5' to parameter: 1
09:59:15,781 [main] DEBUG NullableType : binding '0' to parameter: 1
09:59:15,781 [main] DEBUG NullableType : binding 'Task_AssignToInitiator' to parameter: 2
09:59:15,796 [main] DEBUG NullableType : binding null to parameter: 3
09:59:15,796 [main] DEBUG NullableType : binding null to parameter: 4
09:59:15,796 [main] DEBUG NullableType : binding null to parameter: 5
09:59:15,796 [main] DEBUG NullableType : binding null to parameter: 6
09:59:15,796 [main] DEBUG NullableType : binding null to parameter: 7
09:59:15,796 [main] DEBUG NullableType : binding null to parameter: 8
09:59:15,796 [main] DEBUG NullableType : binding '3' to parameter: 9
09:59:15,796 [main] DEBUG NullableType : binding 'false' to parameter: 10
09:59:15,796 [main] DEBUG NullableType : binding 'false' to parameter: 11
09:59:15,796 [main] DEBUG NullableType : binding 'true' to parameter: 12
09:59:15,796 [main] DEBUG NullableType : binding 'true' to parameter: 13
09:59:15,796 [main] DEBUG NullableType : binding 'false' to parameter: 14
09:59:15,812 [main] DEBUG NullableType : binding '5' to parameter: 15
09:59:15,812 [main] DEBUG NullableType : binding null to parameter: 16
09:59:15,812 [main] DEBUG NullableType : binding '10' to parameter: 17
09:59:15,812 [main] DEBUG NullableType : binding null to parameter: 18
09:59:15,812 [main] DEBUG NullableType : binding null to parameter: 19
09:59:15,828 [main] DEBUG GraphElement : event 'task-create' on 'Task(Task_AssignToInitiator)' for 'Token(/)'
09:59:15,828 [main] DEBUG NullableType : binding '5' to parameter: 1
09:59:15,828 [main] DEBUG NullableType : binding '6' to parameter: 1
09:59:15,843 [main] DEBUG NullableType : returning 'com.firstflow.task.NewApplicationAssignmentHandler' as column: CLASSNAME2_6_0_
09:59:15,843 [main] DEBUG NullableType : returning null as column: CONFIGUR3_6_0_
09:59:15,843 [main] DEBUG NullableType : returning 'field' as column: CONFIGTYPE4_6_0_
09:59:15,843 [main] DEBUG NullableType : returning '2' as column: PROCESSD5_6_0_
09:59:15,843 [main] DEBUG JbpmContext : closing jbpmContext org.jbpm.JbpmContext@1f26605
09:59:15,843 [main] DEBUG Services : closing service 'persistence': org.jbpm.persistence.db.DbPersistenceService@1ac1e22
09:59:15,859 [main] DEBUG DbPersistenceService : committing hibernate transaction org.hibernate.transaction.JDBCTransaction@cd4544
09:59:15,859 [main] DEBUG NullableType : binding '0' to parameter: 1
09:59:15,859 [main] DEBUG NullableType : binding '10' to parameter: 2
09:59:15,859 [main] DEBUG NullableType : binding '6' to parameter: 3
09:59:15,859 [main] DEBUG NullableType : binding '0' to parameter: 1
09:59:15,875 [main] DEBUG NullableType : binding '10' to parameter: 2
09:59:15,875 [main] DEBUG NullableType : binding '0' to parameter: 1
09:59:15,875 [main] DEBUG NullableType : binding '10' to parameter: 2
09:59:15,875 [main] DEBUG NullableType : binding '20' to parameter: 3
09:59:15,906 [main] DEBUG NullableType : binding '0' to parameter: 1
09:59:15,906 [main] DEBUG NullableType : binding 'initiator' to parameter: 2
09:59:15,906 [main] DEBUG NullableType : binding '10' to parameter: 4
09:59:15,921 [main] DEBUG NullableType : binding '10' to parameter: 5
09:59:15,921 [main] DEBUG NullableType : binding '10' to parameter: 6
09:59:15,921 [main] DEBUG NullableType : binding 'linly' to parameter: 7
09:59:16,000 [main] DEBUG NullableType : binding '1' to parameter: 1
09:59:16,000 [main] DEBUG NullableType : binding null to parameter: 2
09:59:16,000 [main] DEBUG NullableType : binding '2008-12-11 09:59:14' to parameter: 3
09:59:16,000 [main] DEBUG NullableType : binding null to parameter: 4
09:59:16,000 [main] DEBUG NullableType : binding '2008-12-11 09:59:15' to parameter: 5
09:59:16,000 [main] DEBUG NullableType : binding '6' to parameter: 6
09:59:16,000 [main] DEBUG NullableType : binding 'true' to parameter: 7
09:59:16,000 [main] DEBUG NullableType : binding 'false' to parameter: 8
09:59:16,000 [main] DEBUG NullableType : binding 'false' to parameter: 9
09:59:16,000 [main] DEBUG NullableType : binding null to parameter: 10
09:59:16,000 [main] DEBUG NullableType : binding '11' to parameter: 11
09:59:16,000 [main] DEBUG NullableType : binding '10' to parameter: 12
09:59:16,000 [main] DEBUG NullableType : binding null to parameter: 13
09:59:16,000 [main] DEBUG NullableType : binding null to parameter: 14
09:59:16,000 [main] DEBUG NullableType : binding '10' to parameter: 15
09:59:16,015 [main] DEBUG NullableType : binding '0' to parameter: 16
09:59:16,015 [main] DEBUG NullableType : binding '1' to parameter: 1
09:59:16,015 [main] DEBUG NullableType : binding null to parameter: 2
09:59:16,015 [main] DEBUG NullableType : binding '2008-12-11 09:59:14' to parameter: 3
09:59:16,015 [main] DEBUG NullableType : binding null to parameter: 4
09:59:16,031 [main] DEBUG NullableType : binding 'false' to parameter: 5
09:59:16,031 [main] DEBUG NullableType : binding '2' to parameter: 6
09:59:16,031 [main] DEBUG NullableType : binding '10' to parameter: 7
09:59:16,031 [main] DEBUG NullableType : binding null to parameter: 8
09:59:16,031 [main] DEBUG NullableType : binding '10' to parameter: 9
09:59:16,031 [main] DEBUG NullableType : binding '0' to parameter: 10
09:59:16,031 [main] DEBUG NullableType : binding '1' to parameter: 1
09:59:16,031 [main] DEBUG NullableType : binding 'Task_AssignToInitiator' to parameter: 2
09:59:16,031 [main] DEBUG NullableType : binding null to parameter: 3
09:59:16,031 [main] DEBUG NullableType : binding null to parameter: 4
Exception in thread "main" org.jbpm.graph.def.DelegationException: com.firstflow.task.NewApplicationAssignmentHandler
at org.jbpm.taskmgmt.exe.TaskMgmtInstance.performAssignment(TaskMgmtInstance.java:259)
at org.jbpm.taskmgmt.exe.TaskInstance.assign(TaskInstance.java:198)
at org.jbpm.taskmgmt.exe.TaskMgmtInstance.createTaskInstance(TaskMgmtInstance.java:197)
at org.jbpm.graph.node.TaskNode.execute(TaskNode.java:168)
at org.jbpm.graph.def.Node.enter(Node.java:318)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:157)
at org.jbpm.graph.def.Node$$EnhancerByCGLIB$$395302c8.enter(<generated>)
at org.jbpm.graph.def.Transition.take(Transition.java:151)
at org.jbpm.graph.def.Node.leave(Node.java:393)
at org.jbpm.graph.node.StartState.leave(StartState.java:70)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:157)
at org.jbpm.graph.def.Node$$EnhancerByCGLIB$$395302c8.leave(<generated>)
at org.jbpm.graph.exe.Token.signal(Token.java:192)
at org.jbpm.graph.exe.Token.signal(Token.java:140)
at org.jbpm.graph.exe.ProcessInstance.signal(ProcessInstance.java:270)
at com.sample.FirstFlowProcessDBTest.createProcessInstance(FirstFlowProcessDBTest.java:62)
at com.sample.FirstFlowProcessDBTest.test4000YuanApplication(FirstFlowProcessDBTest.java:25)
at com.sample.FirstFlowProcessDBTest.main(FirstFlowProcessDBTest.java:19)
Caused by: java.lang.ClassCastException: com.firstflow.task.NewApplicationAssignmentHandler
at org.jbpm.taskmgmt.exe.TaskMgmtInstance.performAssignmentDelegation(TaskMgmtInstance.java:266)
at org.jbpm.taskmgmt.exe.TaskMgmtInstance.performAssignment(TaskMgmtInstance.java:244)
... 25 more
53 楼 laitaogood 2008-12-04  
原来博主就是林信良啊
汗,我还早就收藏了这个blog呢,居然现在才知道
52 楼 black.angel 2008-11-17  
想问一下 simple.jpdl 这个文件是在哪里的啊,我找不到这个文件,而且我只要 test connection 的时候就报错了,好象是连不上服务器。
51 楼 stta04 2008-11-04  
终于学完你写的jbpm这几篇文章,谢谢!希望能快点看到你下面的文章出来。
50 楼 linliangyi2007 2008-11-03  
yxgyh 写道

写的挺好,容易上手 但是有个地方一直不明白


Java代码

Iterator&lt;TaskInstance&gt;&nbsp;taskinstance&nbsp;=&nbsp;processInstance.getTaskMgmtInstance().getTaskInstances().iterator();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;
for(&nbsp;;taskinstance.hasNext();&nbsp;){&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TaskInstance&nbsp;ti&nbsp;=&nbsp;taskinstance.next();&nbsp; &nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List&lt;Transition&gt;&nbsp;transitions&nbsp;=&nbsp;ti.getToken().getNode().getLeavingTransitions();&nbsp;&nbsp; &nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(Transition&nbsp;t&nbsp;:&nbsp;transitions){&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("----Transition"&nbsp;+&nbsp;t.getName());&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;
&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.err.print("**"+ti.getActorId()); &nbsp;&nbsp;
}&nbsp;&nbsp; Iterator&lt;TaskInstance&gt; taskinstance = processInstance.getTaskMgmtInstance().getTaskInstances().iterator();         
for( ;taskinstance.hasNext(); ){            
            TaskInstance ti = taskinstance.next();
          
            List&lt;Transition&gt; transitions = ti.getToken().getNode().getLeavingTransitions(); 
            for(Transition t : transitions){  
                System.out.println("----Transition" + t.getName());  
            }   

            System.err.print("**"+ti.getActorId());
}运行结果 部门经理审批 ----Transition部门经理审批通过 ----Transition部门经理驳回 ----Transition部门经理审批通过 ----Transition部门经理驳回 **null**zs 改成这样


Java代码

Iterator&lt;TaskInstance&gt;&nbsp;taskinstance&nbsp;=&nbsp;processInstance.getTaskMgmtInstance().getTaskInstances().iterator();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;
for(&nbsp;;taskinstance.hasNext();&nbsp;){&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TaskInstance&nbsp;ti&nbsp;=&nbsp;taskinstance.next();&nbsp; &nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(ti.getActorId().equals("zs")){&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List&lt;Transition&gt;&nbsp;transitions&nbsp;=&nbsp;ti.getToken().getNode().getLeavingTransitions();&nbsp;&nbsp; &nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(Transition&nbsp;t&nbsp;:&nbsp;transitions){&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println("----Transition"&nbsp;+&nbsp;t.getName());&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.err.print("**"+ti.getActorId()); &nbsp;&nbsp;
}&nbsp;&nbsp; Iterator&lt;TaskInstance&gt; taskinstance = processInstance.getTaskMgmtInstance().getTaskInstances().iterator();         
for( ;taskinstance.hasNext(); ){            
            TaskInstance ti = taskinstance.next();
            if(ti.getActorId().equals("zs")){  
            List&lt;Transition&gt; transitions = ti.getToken().getNode().getLeavingTransitions(); 
            for(Transition t : transitions){  
                System.out.println("----Transition" + t.getName());  
            }   
            }
            System.err.print("**"+ti.getActorId());
}
运行结果 部门经理审批 **null**zs ----Transition部门经理审批通过 ----Transition部门经理驳回 为什么不加这个就会出现重复 ,会出现一条空值。



这个我倒没注意jbpm会有这个问题!
从执行结果上分析,只能说明这个TaskInstance被付给了两个actor,一个是正常的zs,一个则是null.

相关推荐

    jBPM-jPDL学习笔记——流程设计与控制(转载)

    在《jBPM-jPDL学习笔记——流程设计与控制》这篇博文中,作者主要介绍了如何使用jPDL进行流程设计和控制,包括以下几个关键知识点: 1. **jPDL介绍**: jPDL是jBPM的核心组成部分,它定义了流程实例的行为。通过...

    JBPM相关文档大全

    jBPM-jPDL学习笔记——流程设计与控制.doc JBPM4.4之HelloWorld示例.doc jbpm4.4会签实例.doc jbpm4.4安装配置step by step.doc jbpm4自带数据库分析.doc jBPM_4教程PPT.pdf JBPM与SSH架构融合.doc 工作流模型...

    jbpm学习资料,jbpm教程

    通过jBPM-jPDL学习笔记,你可以系统地学习如何使用jPDL设计和实施业务流程,掌握jbpm的核心概念和技术。无论是对于开发人员还是业务分析师,这份资料都将极大地提升你在工作流管理领域的专业技能。

    JPBM实战经验以及相关参考文档

    在安装配置方面,"jBPM-jPDL学习笔记—框架设计简介.doc"和"jBPM-JPDL v3.2环境部署——发布到Tomcat + MySQL.doc"将是你的重要参考资料。这两个文档详细介绍了如何搭建jBPM开发环境,包括下载和安装jBPM工具,配置...

Global site tag (gtag.js) - Google Analytics