该帖已经被评为良好帖
|
|
---|---|
作者 | 正文 |
发表时间:2008-03-26
相关资料:
《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=">5000元总经理审批"></transition> <transition to="财务拨款" name="<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(),其本质是要释放数据库的操作连接。 (全文完) 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2008-04-08
我测试时报这个错误:
无法将 NULL 值插入列 'VERSION_',表 'jbpm.dbo.JBPM_TASKINSTANCE';该列不允许空值。INSERT 失败。 数据库也是3.2.2的,但是就是测试不通过 |
|
返回顶楼 | |
发表时间:2008-04-08
讲的真不错,对我这种刚接触JBPM的新手来说很有帮助。不过在执行 标准流程测试案例 的时候出现了以下错误:
Exception in thread "main" java.lang.NullPointerException at com.firstflow.test.FirstFlowProcessDBTest.createProcessInstance(FirstFlowProcessDBTest.java:60) at com.firstflow.test.FirstFlowProcessDBTest.test4000YuanApplication(FirstFlowProcessDBTest.java:28) at com.firstflow.test.FirstFlowProcessDBTest.main(FirstFlowProcessDBTest.java:22) 我觉得是数据库的原因。我执行了附件里面的建库脚本,是不是还需要把流程设计的XML文件也存储到数据库中才行呢?谢谢! |
|
返回顶楼 | |
发表时间:2008-04-08
snowolf819 写道 讲的真不错,对我这种刚接触JBPM的新手来说很有帮助。不过在执行 标准流程测试案例 的时候出现了以下错误:
Exception in thread "main" java.lang.NullPointerException at com.firstflow.test.FirstFlowProcessDBTest.createProcessInstance(FirstFlowProcessDBTest.java:60) at com.firstflow.test.FirstFlowProcessDBTest.test4000YuanApplication(FirstFlowProcessDBTest.java:28) at com.firstflow.test.FirstFlowProcessDBTest.main(FirstFlowProcessDBTest.java:22) 我觉得是数据库的原因。我执行了附件里面的建库脚本,是不是还需要把流程设计的XML文件也存储到数据库中才行呢?谢谢! 肯定是要把流程发布到数据库中的啦,不发表的话只能用非DB的Test实例。可以参考一下本文的流程的部署部分。 |
|
返回顶楼 | |
发表时间:2008-04-09
谢谢林哥的文档和资料!
我初学jbpm,按照文档在发布firstflow流程后,Start一个流程,再点击左下角的Process Image,显示图片,但是jbpm-console的页面报错 Error Error getting diagram info: An exception of type "org.jbpm.util.XmlException" was thrown. The message is: couldn't parse xml 查看myeclipse下的tomcat的控制台 Error] :4:15: Invalid byte 1 of 1-byte UTF-8 sequence. 不知道大家遇到这个问题没有 |
|
返回顶楼 | |
发表时间:2008-04-09
linliangyi2007 18 小时前
snowolf819 写道 讲的真不错,对我这种刚接触JBPM的新手来说很有帮助。不过在执行 标准流程测试案例 的时候出现了以下错误: Exception in thread "main" java.lang.NullPointerException at com.firstflow.test.FirstFlowProcessDBTest.createProcessInstance(FirstFlowProcessDBTest.java:60) at com.firstflow.test.FirstFlowProcessDBTest.test4000YuanApplication(FirstFlowProcessDBTest.java:28) at com.firstflow.test.FirstFlowProcessDBTest.main(FirstFlowProcessDBTest.java:22) 我觉得是数据库的原因。我执行了附件里面的建库脚本,是不是还需要把流程设计的XML文件也存储到数据库中才行呢?谢谢! 肯定是要把流程发布到数据库中的啦,不发表的话只能用非DB的Test实例。可以参考一下本文的流程的部署部分。 我也遇到这个问题 但是不知道怎么解决 还请老大出来解释以下 我也发布成功了呀 |
|
返回顶楼 | |
发表时间:2008-04-10
看了里面的代码,怎么Node要手动执行离开的吗?不是自动的吗?
|
|
返回顶楼 | |
发表时间:2008-04-11
是哒,需要调用leave方法的。所谓手动和自动是我们对TaskNode和Node的业务设计有关,从程序实现上讲,在Node结点中需要调用leave方法。
退一步说,至少我在实验代码中发现,如果你不调用了leave,那么流程就停在那儿了! |
|
返回顶楼 | |
发表时间:2008-04-14
写的非常不错,对于我这样刚开始学习jbpm的新手非常有帮助。
有一个问题请教:jbpm的流程与应用程序如何集成呢?也就是说,流程肯定是嵌入到应用程序中的吧,不可能单独去运行。 |
|
返回顶楼 | |
发表时间:2008-04-17
部门经理是如何确定申请是哪个人提交的呢
|
|
返回顶楼 | |