jBPM 开发入门指南(三)
学习笔记,转自http://www.blogjava.net/chengang/archive/2006/08/23/65346.html
有个辅助工具开发起来方便一点,只不过现在?jBPM?的开发工具插件功能还不算太强,也就一个“项目创建向导”的功能,让你:
(1)不用再去配置?classpath?库的引用了
(2)直接得到了一个?jBPM?的项目初始结构
其实吧,开发?jBPM?也不需要什么插件工具,在熟练了以后,库引用了项目初始结构都可以手工创建。
?
插件不用再去下载了,?jbpm-starters-kit-3.1.1?包里就有,目录地址如下:??
如果安装成功,则?Eclipse?首选项里多了一个?JBoss jBPM?,另外我们也需要到这个?jBPM?的首选项里做一些配置工作――指定?jBPM?的安装路径(如下图所示)。这个配置主要是为了找到?jbpm?下的各种?jar?包,好让?Eclipse?设置项目的库引用。本文指向路径是?d:\jbpm-starters-kit-3.1.1\jbpm.3
?
?
?
主菜单“文件->新建->项目”,在弹出的对话框里,有“?Process Project?”项,如下图所示:
?
??
?
选上好,单击“下一步”,起个名“?myjbpm?”,然后就可以单击“完成”了。然后就生成了如下图所示的一个项目结构:?
?
这个项目和通常?Eclipse?的项目结构有点不同,不过这是一个现在非常流行的项目结构,?src/java?存放源文件,?test/java?存放相应的?JUnit?单元测试代码。如果你用?Maven?来编译构建项目,对这种目录结构一定不陌生。
?
项目创建起了,介绍一下里面的文件吧:
l???????????MessageActionHandler?,自动生成的一个?ActionHandler?。不想要可以删掉。
l???????????ehcache.xml??cache?的配置文件,里面有很详解的英文说明。没有必要可以不用改它。
l???????????hibernate.cfg.xml? ?jBPM?是用?Hibernate?进行工作流的数据存储的,这个就是?Hibernate?的配置文件。后面我们将讲到如何配置这个文件。
l???????????jbpm.cfg.xml? ?jbpm?本身的配置文件。现在是空的,它用的是缺省配置,你想知道有哪些配置就去看这个文件?D:\jbpm-starters-kit-3.1.1\jbpm.3\src\java.jbpm\org\jbpm\default.jbpm.cfg.xml
l???????????log4j.properties? 这个是日志?API?包?log4j?的配置文件,用过?log4j?的都知道。
l???????????SimpleProcessTest.java? 这个是对最重要的流程配置文件的?processdefinition.xml?单元测试代码。这里表扬一点,?jBPM?的优良设计使得它的可测试性非常之高,喜欢写?t?单元测试的人有福了。
l???????????gpd.xml? 用于生成流程图的定义文件。都是一些方框的坐标和长宽
l???????????processdefinition.xml? 这个是对最重要的流程配置文件,以后写流程要经常和它打交道。
l???????????processimage.jpg? 一个流程图
?
从项目结构来看,我们没有看到?JSP?网页程序,也没有看到?GUI?客户端程序,这些代码都是要我们以后开发中来写的。但本文不准备用?JSP?、?GUI?(Swing?、?SWT?)来做示例,而是用?JUnit?代码来做使用?jBPM?客户端来演示。因为?jBPM?实际上是一个后台框架,至于前台是?JSP?还是?Swing?还是无界面的?java.class?都是无关紧要的。在教程里用无界面的?java.class?来做客户端则更方便一些,如果进一步采用?JUnit?,则这样的?java.class?同时还具备了单元测试的功能。以后就是用?JSP?写了?WEB?页面,我们还是可以用这些?JUnit?程序来做单元测试,避免了频繁的鼠标点按?WEB?页面这样的力气活。所以在?jBPM?自带的英文教程里都是一个?JUnit?程序,不仔佃看还真摸不着头脑。
?
???????hibernate.cfg.xml?的默认设置是用?HSQL?,这是一个内存数据库,这种内存数据库用来代替项目实际所用的数据库来做单元测试挺不错的。不过我们这里是要试试用?MySQL?、?Oracle?,那就改一下设置吧。
注:配置值可参考?D:\jbpm-starters-kit-3.1.1\jbpm-db?对应子目录下的?hibernate.properties?文件。
1?、?MySQL?的更改如下:
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/jbpm</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">123456</property>
?
2?、?Oracle?的更改如下:
<property name="hibernate.dialect">org.hibernate.dialect.OracleDialect</property>
<property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="hibernate.connection.url">jdbc:oracle:thin:@192.168.123.10:1521:wxxrDB</property>
<property name="hibernate.connection.username">chengang</property>
<property name="hibernate.connection.password">chengang</property>
?
如果你装了?Oracle?的客户端,并且?D:\oracle\ora92\network\ADMIN\tnsnames.ora?里做了如下的设置
WXXRDB_192.168.123.10 =
??(DESCRIPTION =
????(ADDRESS_LIST =
??????(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.123.10)(PORT = 1521))
????)
????(CONNECT_DATA =
??????(SID = wxxrDB)
??????(SERVER = DEDICATED)
????)
??)
则?Oracle?的?hibernate.connection.url?项也可以设为:?jdbc:oracle:oci:@WXXRDB_192.168.123.10
?
???????虽然?jBPM?在创建项目之初给我们设置好了库引用,如下图
?
?
?
但后面运行时还是报一些?NoClassDefFoundError?异常,如没有对?hibernate3.jar?的引用导致下面的错误
java.lang.NoClassDefFoundError: org/hibernate/Session
????at org.jbpm.persistence.db.DbPersistenceServiceFactory.openService(DbPersistenceServiceFactory.java:55)
????at org.jbpm.svc.Services.getService(Services.java:136)
????.......
?
所以我们要为本文的实例完善库引用。主要是把?MySQL?和?Oracle?的?JDBC?库、以及?Hibernate?的?hibernate3.jar?加入到项目的库引用中。
l???????????mysql?的?jdbc?包,在?D:\jbpm-starters-kit-3.1.1\jbpm-db\mysql\lib?目录里
l???????????oracle?的?jdbc?包,?jbmp?中没有包含(可能是没拿到?oracle?授权),我们可以自已去?oracle?网站上下载,或者去?oracle?安装目录D:\oracle\ora92\jdbc\lib?找?ojdbc14.jar?(我们公司用的是?Oracle9i?)
l???????????Hibernate3.jar?在目录?D:\jbpm-starters-kit-3.1.1\jbpm.3\lib\hibernate?里。
?
?
?
这里是一个很简单的请假流程,请假人提交假单给经理审批,经理审批后结束。要说明的是,这个流程并不严谨,比如经理不通过流程应该到哪?不过这并不防碍拿它来做示例,螃蟹还得一个一个的吃。我们先拿这一杆子捅到底的流程做一个最简单的示例,从整体上对?jBPM?工作流开发有概念先。然后我们再慢慢丰富。
?
1?、定义流程
流程的定义文件是?processdefinition.xml?,这个是一个关键文件,?jBPM?的很大一部份内容都是关于它的。在这里我们把原来自动生成的内容,稍做改动:
<?xml?version="1.0"?encoding="GBK"?>
?
<process-definition?xmlns="urn:jbpm.org:jpdl-3.1"?name="helloworld">
????<!--?申请?-->
????<start-state?name="request">
????????<task>
????????????<controller>
????????????????<variable?name="name"?/>
????????????????<variable?name="day"?/>
????????????????<variable?name="note"?/>
????????????</controller>
????????</task>
????????<!--?流程转向?-->
????????<transition?name="to_confirm"?to="confirm">
????????????<action?name="requestAction"
????????????????class?="cn.com.chengang.jbpm.RequestAction">
????????????????<reason>?我要请假?</reason>
????????????</action>
????????</transition>
????</start-state>
????<!--?审批?-->
????<state?name="confirm">
????????<transition?name="to_end"?to="end">
????????????<action?name="finishAction"
????????????????class?="cn.com.chengang.jbpm.ConfirmAction" />
????????</transition>
????</state>
????<!--?结束?-->
????<end-state?name="end"?/>
</process-definition>
说明:
流程的名称改成了?helloworld?。(呵呵,也就是这里和?helloworld?有关了)
<controller>?标签定义了三个数据:姓名、请假天数、说明。
<transition>?标签定了?request?节点的一个流程转向,这里是转到?confirm?节点。
<action>?标签定义了流程由一个节点转到另一个节点时,所要执行的动作,动作封装在一个?ActionHandler?类中。比如这里当?request?到?confirm?结点时将执行?RequestAction?类的?execute?方法。
FinishAction?下面还有一个?<reason>?(请假理由),它对应于?FinshAction?的属性?String reason?。
?
2?、 编写?ActionHandler
???????在上面?processdefinition.xml?里我们定义了两个?ActionHandler?:?RequestAction?、?ConfirmAction?。其代码如下:
package?cn.com.chengang.jbpm;
?
import?org.jbpm.graph.def.ActionHandler;
import?org.jbpm.graph.exe.ExecutionContext;
?
public?class?RequestAction?implements?ActionHandler {
?
????private?static?final?long?serialVersionUID?=?1L;
?
????private?String?reason;
?
????public?String?getReason() {
????????return?reason;
????}
?
????public?void?setReason(String reason) {
????????this?.reason?= reason;
????}
?
????public?void?execute(ExecutionContext context)?throws?Exception {
????????context.getContextInstance().setVariable("note",?reason);
????}
?
}
?
说明:?ExecutionContext?是一个贯通流程的容器。它是个大宝箱,里面啥玩意都有,后面将更深入的提到。这里的?reasion?就是?processdefinition.xml中的?”?我要请假?”
?
?
package?cn.com.chengang.jbpm;
?
import?org.jbpm.graph.def.ActionHandler;
import?org.jbpm.graph.exe.ExecutionContext;
?
public?class?ConfirmAction?implements?ActionHandler {
?
????private?static?final?long?serialVersionUID?=?1L;
?
????public?void?execute(ExecutionContext context)?throws?Exception {
????????context.getContextInstance().setVariable("note",?"?准假?"?);
????}
?
}
?
OK?,后台的程序就算写完了(前台客户端的程序还没写),下面开始部署。
?
???????我们要把?processdefinition.xml?的流程定义的数据部署到数据库中,因为?jBPM?在正式运行的时候不是去读?processdefinition.xml?文件,而是去读数据库中的流程定义。?这里写了一个个?JUnit?程序来部署?processdefinition.xml?,当然你用普通的?Java Main?也可以。
package?com.sample;
?
import?java.io.FileInputStream;
import?java.io.FileNotFoundException;
import?java.io.InputStream;
?
import?junit.framework.TestCase;
?
import?org.jbpm.JbpmConfiguration;
import?org.jbpm.JbpmContext;
import?org.jbpm.graph.def.ProcessDefinition;
?
/**
?*?部署?processdefinition.xml
??*
?*?@author?chengang
??*
??*/
public?class?DeployProcessTest?extends?TestCase {
????/**
?????*?在本方法执行完毕后,检查?jbpm_processdefinition?表会多了一条记录
??????*
?????*?@throws?FileNotFoundException
??????*/
????public?void?testDeployProcessDefinition()?throws?FileNotFoundException {
????????//?从?jbpm.cfg.xml?取得?jbpm?的配置
????????JbpmConfiguration config = JbpmConfiguration.getInstance();
????????//?创建一个?jbpm?容器
????????JbpmContext?jbpmContext?= config.createJbpmContext();
????????//?由?processdefinition.xml?生成相对应的流程定义类?ProcessDefinition
????????InputStream is =?new?FileInputStream("processes/simple/processdefinition.xml");
????????ProcessDefinition processDefinition = ProcessDefinition.parseXmlInputStream(is);
????????//?利用容器的方法将流程定义数据部署到数据库上
????????jbpmContext.deployProcessDefinition(processDefinition);
????????//?关闭?jbpmContext
????????jbpmContext.close();
????}
?
}
?
运行此程序,在控制台打印了一些日志,通过。如果出错,仔佃阅读出错信息以判断错误原因,并确定你按照前面两节:“修改?hibernate.cfg.xml”和“完善库引用”的内容做好了设置。
?
无论是?MySQL?还是?Oracle?,查询?jbpm_processdefinition?表,你会发现多了一条记录,如下图?(?以?PLSQL Developer?的显示为例?)
?
?
?
依次检查各表我们可以发现有如下变化:
?
?
?
?
?
?
?
?
?
?
?
?
?
并由此简单判断出各表的作用,表中各字段的作用由字段名也能知晓一二。
?
jbpm_processdefinition
一个流程定义文件对应一条记录,可记录多个流程定义文件,可记录一个流程定义文件的对个版本。
jbpm_action
记录?ActionHandler?的对象实例(以名称为标识)
jbpm_delegation
记录了?ActionHandler?全类名,以便于用反射方式来加载
jbpm_envent
它的?transition?引用了?Jbpm_transition?表的?id?,再看其它字段,估计此表是表示流程转向事件的一个实例,或者是一个各表之间的联接表。
jbpm_node
流程结点
jbpm_transition
流程的转向定义
jbpm_variableaccess
流程中携带的变量。?ACCESS?字段是这些变量的读写权限
?