@see org.jbpm.context.exe.ContextInstance
参考 org.jbpm.context.exe.VariableInstance
参考 org.jbpm.context.exe.VariableInstance
一
工作流虽然还在不成熟的发展阶段,甚至还没有一个公认的规范标准。但其应用却已经在快速展开,这说明市场对工作流框架的需求是急迫而巨大的。
我们公司的后台短信管理系统涉及短信编发、领导层层审核等操作,这是一个比较典型的工作流系统。过去我们用的工作流引擎是 shark,然后在使用后发现其过于庞大,后台数据库操作频繁而未进行优化,直接导致的后果就是前台操作缓慢。于是经研究决定,将工作流引擎由 shark换成 jBPM 。
jBPM之前是一个开源软件,后加入 JBoss组织。正好我们公司也是用 JBoss的。不过 jBPM 并没有绑定在 JBOSS 上, Tomcat 等也可以使用它。
jBPM的正处在不断发展中,做为开源软件的特点,其设计变化往往很大。所以一些过去的资料可能已经不适用了。于是作者根据自己的使用经验,重新整理出这份文档,以备学习参考。
注:本文使用的 jBPM版本为 3.1.1
环境准备
1、安装JDK
所有 JAVA开发第一个需要安装的,没什么好说的。记得把系统变量 JAVA_HOME设上。
2、安装Ant
Ant 是使用 jBPM必须的一个工具。 jBPM中的很多操作都要用到 Ant 。
安装方法:
( 1)先下载:http://archive.apache.org/dist/ant/binaries/,选一个如: apache-ant-1.6.5-bin.zip。
( 2)解压到 D:/ant(当然其他目录也可以)。
( 3)设置如下系统变量: ANT_HOME=d:/ant。
( 4)把 %ANT_HOME%/bin加入到系统变量 PATH中。
3、安装Eclipse
Eclipse不是开发 jBPM必须的工具,但它是对 jBPM开发很有帮助的工具,特别是 jBPM提供了一个 Eclipse插件用来辅助开发 jBPM 。关于 Eclipse 的安装不赘述了,本文用的版本是: Eclipse3.2
安装 jBPM
jBPM 的下载地址:http://nchc.dl.sourceforge.net/sourceforge/jbpm/jbpm-starters-kit-3.1.2.zip
l JBoss jBPM是 jBPM的软件包
l JBoss jBPM Starters Kit 是一个综合包,它包括了 jBPM软件包、开发插件、一个配置好了的基于 JBoss的 jBPM示例、一些数据库配置文件示例。
l JBoss jBPM Process Designer Plugin 是辅助开发 jBPM的 Eclipse插件。
l JBoss jBPM BPEL Extension jBPM关于 BPEL的扩展包
本指南选择下载: JBoss jBPM Starters Kit。下载后解压到 D:/jbpm-starters-kit-3.1,目录下含有五个子目录:
l jbpm jBPM的软件包
l jbpm-bpel只含有一个网页
l jbpm-db各种数据库 hibernate配置文件示例,有些还包含了相应的 jdbc驱动程序。
l jbpm-designer 辅助开发 jBPM的 Eclipse插件,具体在 jbpm-gpd-feature子目录中
l jbpm-server 一个已经配置好了的基于 JBoss的 jBPM示例 .
感觉下工作流
前面我们说了,在 JBoss jBPM Starters Kit的 jbpm-server目录是一个已经配置好的了 jBPM示例,那么让我们来感觉一下 jBPM做出的东西吧。
双击 jbpm-server目录下的 start.bat文件,启动 JBoss 服务。这时会打开一个 DOS 窗口,启动完成后,日志会不断输出,其中最后一句是“ 13:55:39,937 DEBUG [StaticNotifier] going to wait for (CMD_EXECUTOR, java.lang.Object@1df59bd)”,这表示 jBPM在开始工作了,它不断进行轮询。
打开网页: http://localhost:8080/jbpm/ 得到如下画面
这是一个已经用 jBPM开发好的用户定单流程,具有下单、审核、估价等流程。它所用的数据库是一个内置的数据库。
以 cookie monster用户登录,选择“ create new web sale order”可以创建一个定单。如下图所示,在图左边是填写的定单情况,右边一整个定货流程的示意图,红色框表示流程进行到哪一步了。填写好定单好,选择“ Save and Close Task”,完成定单提交。
选择右上角的“ Login as another user”以另外一个用户名 ernie登录。这时可以看到 ernie用户的任务列表中多了一项。
点进去后,显示如下画面。这个示例对中文的支持不好,全都显示成了 unicode码了。不管这什么多,反正知道是这么回事就行了。在 comment项填写意见,选 OK按钮,进入到下一步。如果选择 more info needed按钮,则打回给 cookie monster用户修改定单。
下面的流程,这里就不再赘述了。在这个很标准的工作流示例中,我们基本可以看到 jBPM的应用范围还是比较广的。而且从这个示例,我们是看不出有 jBPM的,也就是说 jBPM 在后台起着作用。
从这个例子,还看不出 jBPM的优势。不过,如果在一个流程不确定,经常需要变动的项目中, jBPM的好处将会显然出来。应用 jBPM后,改变流程只需改变流程描述文件,这将在后面的内容提到。
这是一个已做好的示例,接下来我们将仿造这个实例来开发一个请假流程。
4 数据库初始化
jBPM 需要数据库支持, jBPM会把自己的一个初始化数据存储到数据库,同时工作流的数据也是存储到数据库中的。 jBPM使用 Hibernate来做为自己的存储层,因此只要是 Hibernate支持的数据库, jBPM也就支持。
本文先以 MySQL为例,然后再以 Oracle为例,来谈谈 jBPM的数据库初始化操作。
注:在上面的 JBoss自带的示例中,并没有设置数据库,那是因为 jBPM默认使用的是内存数据库 hsqldb。
4.1 MySQL
1 、首先安装 MySQL。
MySQL的安装比较简单,网上也有很多文章,本文不再赘述。本指南所用 MySQL版本为 MySQL 4.1( for windows)。再找一个 MySQL客户端,目的是方便查看数据库中的数据,本文推荐使用 MySQL网站上免费提供的“ MySQL Query Brower”,当然你用其他的客户端也行,比如 MySQL-Front。
2 、建库
MySQL中创建一个库,库名: jbpm
3 、生成建表的 SQL语句并建表
将 jbpm-starters-kit-3.1.1下的子目录 jbpm改名为 jbpm.3,否则在执行下面的 ant命令时会报如 jbpm.3目录不存在的错误:
D:/jbpm-starters-kit-3.1.1/jbpm-db/build.xml:361: The following error occurred while executing this line:
D:/jbpm-starters-kit-3.1.1/jbpm-db/build.xml:68: Basedir D:/jbpm-starters-kit-3.1.1/jbpm.3 does not exist
在 DOS窗下,进入 D:/jbpm-starters-kit-3.1.1/jbpm-db目录,执行如下命令:
ant mysql.scripts
执行成功后,在 D:/jbpm-starters-kit-3.1.1/jbpm-db/build/mysql/scripts目录里生成了四个 sql文件,它们做什么用的一看名字就知道了。在 MySQL客户端中执行“ mysql.create.sql”脚本,这样将在 jbpm库中创建一个个的数据表。
4.2 Oracle
先安装好 Oracle服务器。我们公司有现存的 Oracle服务器,也提供给了我一个属于我自己的用户名,一登录就可以任意在我的库之下创建表了。所以这一步就省了,没有的自个先装好吧。
访问 Oracle推荐用“ PLSQL Developer”。不过要连接 Oracle还要在本机上装上 Oracle自己的客户端程序,里面提供了 JDBC包和一些配置。要连接服务器还得配置一下,我一般都是不用 GUI而直接改 tnsnames.ora文件,在我的电脑里此文件的目录地址是: D:/oracle/ora92/network/ADMIN/tnsnames.ora,内容如下 (两面有两个配置了 ):
# TNSNAMES.ORA Network Configuration File: E:/oracle/ora92/network/admin/tnsnames.ora
# Generated by Oracle configuration tools.
WXXRDB_192.168.3.2 =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.3.2)(PORT = 1521))
)
(CONNECT_DATA =
(SID = wxxrDB)
(SERVER = DEDICATED)
)
)
WXXRDB_192.168.4.2 =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.4.2)(PORT = 1521))
)
(CONNECT_DATA =
(SID = wxxrDB)
(SERVER = DEDICATED)
)
)
前面都是 Oracle的一些知识,不会的 Google一下吧。最后配置好后,用 PLSQL Developer输入你的用户名和密码联接到 Oracle,就算 OK了。
参考上面 MySQL的步骤,基本一样:
(1) 将 jbpm改名为 jbpm.3 (或者修改jbpm-db目录下的build.properties文件)
(2) 再执行ant oracle.scripts
(3) 用 jbpm-db/build/oracle/scripts目录的 oracle.create.sql脚本,在 Oracle中生成 jBPM的所有表。在“ PLSQL Developer”中可以新建一个 Command Windows窗口然后输入命令: @D:/jbpm-starters-kit-3.1.1/jbpm-db/build/oracle/scripts/oracle.create.sql
(如果报错,就将报错的地方注释掉)
5 安装 jBPM的 Eclipse开发插件
有个辅助工具开发起来方便一点,只不过现在 jBPM的开发工具插件功能还不算太强,也就一个“项目创建向导”的功能,让你:
(1)不用再去配置 classpath 库的引用了
(2)直接得到了一个 jBPM 的项目初始结构
其实吧,开发 jBPM也不需要什么插件工具,在熟练了以后,库引用了项目初始结构都可以手工创建。
插件不用再去下载了, jbpm-starters-kit-3.1.1包里就有,目录地址如下:D:/jbpm-starters-kit-3.1.1/jbpm-designer/jbpm-gpd-feature/eclipse,插件的安装方式是链接式还是直接复制式,任选吧。不懂的就去看看《 Eclipse从入门精通》这本书,在前面章节都有讲到。另外,注明一下 Eclipse的版本我是用 3.2,插件和 Eclispe版本相关的,要注意了。
如果安装成功,则 Eclipse首选项里多了一个 JBoss jBPM,另外我们也需要到这个 jBPM的首选项里做一些配置工作――指定 jBPM的安装路径(如下图所示)。这个配置主要是为了找到 jbpm下的各种 jar包,好让 Eclipse设置项目的库引用。本文指向路径是 d:/jbpm-starters-kit-3.1.1/jbpm.3
6 jBPM的 Hello World
6.1 新建jBPM项目
主菜单“文件->新建->项目”,在弹出的对话框里,有“ 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程序,不仔佃看还真摸不着头脑。
6.2 修改hibernate.cfg.xml
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
6.3 完善库引用
虽然 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加入到项目的库引用中。
(1) 找到缺少的 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里。
(2) 在项目里创建一个 lib目录,将这三个 jar复制到 lib目录。
(3) 如下图设置三 jar包的库引用
6.4 开始HellorWorld
这里是一个很简单的请假流程,请假人提交假单给经理审批,经理审批后结束。要说明的是,这个流程并不严谨,比如经理不通过流程应该到哪?不过这并不防碍拿它来做示例,螃蟹还得一个一个的吃。我们先拿这一杆子捅到底的流程做一个最简单的示例,从整体上对 jBPM 工作流开发有概念先。然后我们再慢慢丰富。
1 、定义流程
流程的定义文件是 processdefinition.xml,这个是一个关键文件, jBPM的很大一部份内容都是关于它的。在这里我们把原来自动生成的内容,稍做改动:
<?xmlversion="1.0"encoding="GBK"?>
<process-definitionxmlns="urn:jbpm.org:jpdl-3.1"name="helloworld">
<!--申请-->
<start-statename="request">
<task>
<controller>
<variablename="name"/>
<variablename="day"/>
<variablename="note"/>
</controller>
</task>
<!--流程转向-->
<transitionname="to_confirm"to="confirm">
<actionname="requestAction"
class="cn.com.chengang.jbpm.RequestAction">
<reason>我要请假</reason>
</action>
</transition>
</start-state>
<!--审批-->
<statename="confirm">
<transitionname="to_end"to="end">
<actionname="finishAction"
class="cn.com.chengang.jbpm.ConfirmAction" />
</transition>
</state>
<!--结束-->
<end-statename="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。其代码如下:
packagecn.com.chengang.jbpm;
importorg.jbpm.graph.def.ActionHandler;
importorg.jbpm.graph.exe.ExecutionContext;
publicclassRequestActionimplements ActionHandler {
private static final longserialVersionUID= 1L;
privateString reason;
publicString getReason() {
returnreason;
}
publicvoid setReason(String reason) {
this.reason = reason;
}
publicvoid execute(ExecutionContext context)throws Exception {
context.getContextInstance().setVariable("note",reason);
}
}
说明: ExecutionContext是一个贯通流程的容器。它是个大宝箱,里面啥玩意都有,后面将更深入的提到。这里的 reasion就是 processdefinition.xml中的 ”我要请假 ”
packagecn.com.chengang.jbpm;
importorg.jbpm.graph.def.ActionHandler;
importorg.jbpm.graph.exe.ExecutionContext;
publicclassConfirmActionimplements ActionHandler {
private static final longserialVersionUID= 1L;
publicvoid execute(ExecutionContext context)throws Exception {
context.getContextInstance().setVariable("note","准假" );
}
}
OK ,后台的程序就算写完了(前台客户端的程序还没写),下面开始部署。
6.5 部署processdefinition.xml
我们要把 processdefinition.xml的流程定义的数据部署到数据库中,因为 jBPM在正式运行的时候不是去读 processdefinition.xml文件,而是去读数据库中的流程定义。 这里写了一个个 JUnit程序来部署 processdefinition.xml,当然你用普通的 Java Main也可以。
packagecom.sample;
importjava.io.FileInputStream;
importjava.io.FileNotFoundException;
importjava.io.InputStream;
importjunit.framework.TestCase;
importorg.jbpm.JbpmConfiguration;
importorg.jbpm.JbpmContext;
importorg.jbpm.graph.def.ProcessDefinition;
/**
*部署processdefinition.xml
*
*@author chengang
*
*/
publicclassDeployProcessTestextends TestCase {
/**
*在本方法执行完毕后,检查jbpm_processdefinition表会多了一条记录
*
*@throws FileNotFoundException
*/
publicvoid testDeployProcessDefinition() throws FileNotFoundException {
//从jbpm.cfg.xml取得jbpm的配置
JbpmConfiguration config = JbpmConfiguration.getInstance();
//创建一个jbpm容器
JbpmContextjbpmContext = 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”和“完善库引用”的内容做好了设置。
如果报Named query not known: GraphSession.findLatestProcessDefinitionQuery,则需要将jbpm的hibernate映射文件引入hibernate.cfg.xml
<mapping resource="org/jbpm/bytes/ByteArray.hbm.xml"/>
<mapping resource="org/jbpm/command/ExecuteActionCommand.hbm.xml"/>
<mapping resource="org/jbpm/command/ExecuteNodeCommand.hbm.xml"/>
<mapping resource="org/jbpm/command/SignalCommand.hbm.xml"/>
<mapping resource="org/jbpm/command/TaskInstanceEndCommand.hbm.xml"/>
<mapping resource="org/jbpm/context/def/ContextDefinition.hbm.xml"/>
<mapping resource="org/jbpm/context/def/VariableAccess.hbm.xml"/>
<mapping resource="org/jbpm/context/exe/ContextInstance.hbm.xml"/>
<mapping resource="org/jbpm/context/exe/TokenVariableMap.hbm.xml"/>
<mapping resource="org/jbpm/context/exe/variableinstance/ByteArrayInstance.hbm.xml"/>
<mapping resource="org/jbpm/context/exe/variableinstance/DateInstance.hbm.xml"/>
<mapping resource="org/jbpm/context/exe/variableinstance/DoubleInstance.hbm.xml"/>
<mapping resource="org/jbpm/context/exe/variableinstance/HibernateLongInstance.hbm.xml"/>
<mapping resource="org/jbpm/context/exe/variableinstance/HibernateStringInstance.hbm.xml"/>
<mapping resource="org/jbpm/context/exe/variableinstance/LongInstance.hbm.xml"/>
<mapping resource="org/jbpm/context/exe/variableinstance/NullInstance.hbm.xml"/>
<mapping resource="org/jbpm/context/exe/variableinstance/StringInstance.hbm.xml"/>
<mapping resource="org/jbpm/context/exe/VariableInstance.hbm.xml"/>
<mapping resource="org/jbpm/context/log/VariableCreateLog.hbm.xml"/>
<mapping resource="org/jbpm/context/log/VariableDeleteLog.hbm.xml"/>
<mapping resource="org/jbpm/context/log/variableinstance/ByteArrayUpdateLog.hbm.xml"/>
<mapping resource="org/jbpm/context/log/variableinstance/DateUpdateLog.hbm.xml"/>
<mapping resource="org/jbpm/context/log/variableinstance/DoubleUpdateLog.hbm.xml"/>
<mapping resource="org/jbpm/context/log/variableinstance/HibernateLongUpdateLog.hbm.xml"/>
<mapping resource="org/jbpm/context/log/variableinstance/HibernateStringUpdateLog.hbm.xml"/>
<mapping resource="org/jbpm/context/log/variableinstance/LongUpdateLog.hbm.xml"/>
<mapping resource="org/jbpm/context/log/variableinstance/StringUpdateLog.hbm.xml"/>
<mapping resource="org/jbpm/context/log/VariableLog.hbm.xml"/>
<mapping resource="org/jbpm/context/log/VariableUpdateLog.hbm.xml"/>
<mapping resource="org/jbpm/db/hibernate.queries.hbm.xml"/>
<mapping resource="org/jbpm/file/def/FileDefinition.hbm.xml"/>
<mapping resource="org/jbpm/graph/action/Script.hbm.xml"/>
<mapping resource="org/jbpm/graph/def/Action.hbm.xml"/>
<mapping resource="org/jbpm/graph/def/Event.hbm.xml"/>
<mapping resource="org/jbpm/graph/def/ExceptionHandler.hbm.xml"/>
<mapping resource="org/jbpm/graph/def/Node.hbm.xml"/>
<mapping resource="org/jbpm/graph/def/ProcessDefinition.hbm.xml"/>
<mapping resource="org/jbpm/graph/def/SuperState.hbm.xml"/>
<mapping resource="org/jbpm/graph/def/Transition.hbm.xml"/>
<mapping resource="org/jbpm/graph/exe/Comment.hbm.xml"/>
<mapping resource="org/jbpm/graph/exe/ProcessInstance.hbm.xml"/>
<mapping resource="org/jbpm/graph/exe/RuntimeAction.hbm.xml"/>
<mapping resource="org/jbpm/graph/exe/Token.hbm.xml"/>
<mapping resource="org/jbpm/graph/log/ActionLog.hbm.xml"/>
<mapping resource="org/jbpm/graph/log/NodeLog.hbm.xml"/>
<mapping resource="org/jbpm/graph/log/ProcessInstanceCreateLog.hbm.xml"/>
<mapping resource="org/jbpm/graph/log/ProcessInstanceEndLog.hbm.xml"/>
<mapping resource="org/jbpm/graph/log/ProcessStateLog.hbm.xml"/>
<mapping resource="org/jbpm/graph/log/SignalLog.hbm.xml"/>
<mapping resource="org/jbpm/graph/log/TokenCreateLog.hbm.xml"/>
<mapping resource="org/jbpm/graph/log/TokenEndLog.hbm.xml"/>
<mapping resource="org/jbpm/graph/log/TransitionLog.hbm.xml"/>
<mapping resource="org/jbpm/graph/node/Decision.hbm.xml"/>
<mapping resource="org/jbpm/graph/node/EndState.hbm.xml"/>
<mapping resource="org/jbpm/graph/node/Fork.hbm.xml"/>
<mapping resource="org/jbpm/graph/node/Join.hbm.xml"/>
<mapping resource="org/jbpm/graph/node/ProcessState.hbm.xml"/>
<mapping resource="org/jbpm/graph/node/StartState.hbm.xml"/>
<mapping resource="org/jbpm/graph/node/State.hbm.xml"/>
<mapping resource="org/jbpm/graph/node/TaskNode.hbm.xml"/>
<mapping resource="org/jbpm/instantiation/Delegation.hbm.xml"/>
<mapping resource="org/jbpm/logging/log/CompositeLog.hbm.xml"/>
<mapping resource="org/jbpm/logging/log/MessageLog.hbm.xml"/>
<mapping resource="org/jbpm/logging/log/ProcessLog.hbm.xml"/>
<mapping resource="org/jbpm/module/def/ModuleDefinition.hbm.xml"/>
<mapping resource="org/jbpm/module/exe/ModuleInstance.hbm.xml"/>
<mapping resource="org/jbpm/msg/db/TextMessage.hbm.xml"/>
<mapping resource="org/jbpm/msg/Message.hbm.xml"/>
<mapping resource="org/jbpm/scheduler/def/CancelTimerAction.hbm.xml"/>
<mapping resource="org/jbpm/scheduler/def/CreateTimerAction.hbm.xml"/>
<mapping resource="org/jbpm/scheduler/exe/Timer.hbm.xml"/>
<mapping resource="org/jbpm/taskmgmt/def/Swimlane.hbm.xml"/>
<mapping resource="org/jbpm/taskmgmt/def/Task.hbm.xml"/>
<mapping resource="org/jbpm/taskmgmt/def/TaskController.hbm.xml"/>
<mapping resource="org/jbpm/taskmgmt/def/TaskMgmtDefinition.hbm.xml"/>
<mapping resource="org/jbpm/taskmgmt/exe/PooledActor.hbm.xml"/>
<mapping resource="org/jbpm/taskmgmt/exe/SwimlaneInstance.hbm.xml"/>
<mapping resource="org/jbpm/taskmgmt/exe/TaskInstance.hbm.xml"/>
<mapping resource="org/jbpm/taskmgmt/exe/TaskMgmtInstance.hbm.xml"/>
<mapping resource="org/jbpm/taskmgmt/log/SwimlaneAssignLog.hbm.xml"/>
<mapping resource="org/jbpm/taskmgmt/log/SwimlaneCreateLog.hbm.xml"/>
<mapping resource="org/jbpm/taskmgmt/log/SwimlaneLog.hbm.xml"/>
<mapping resource="org/jbpm/taskmgmt/log/TaskAssignLog.hbm.xml"/>
<mapping resource="org/jbpm/taskmgmt/log/TaskCreateLog.hbm.xml"/>
<mapping resource="org/jbpm/taskmgmt/log/TaskEndLog.hbm.xml"/>
<mapping resource="org/jbpm/taskmgmt/log/TaskLog.hbm.xml"/>
(如果抛出异常09:32:43,791 [main] ERROR ErrorCounter : *** ERROR: line 3:45: expecting "set", found 't'
09:32:43,822 [main] ERROR ErrorCounter : *** ERROR: line 3:45: expecting "set", found 't'
09:32:43,869 [main] ERROR ErrorCounter : *** ERROR: line 3:50: unexpected token: t
09:32:43,931 [main] ERROR SessionFactoryImpl : Error in named query: SchedulerSession.deleteTimersForProcessInstance
org.hibernate.hql.ast.QuerySyntaxError: unexpected token: t near line 3, column 50 [
delete from org.jbpm.scheduler.exe.Timer t
where t.processInstance = :processInstance
]
at org.hibernate.hql.ast.ErrorCounter.throwQueryException(ErrorCounter.java:63)
at org.hibernate.hql.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:215)
at org.hibernate.hql.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:127)
at org.hibernate.hql.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:83)
at org.hibernate.impl.SessionFactoryImpl.getQuery(SessionFactoryImpl.java:427)
at org.hibernate.impl.SessionFactoryImpl.checkNamedQueries(SessionFactoryImpl.java:388)
at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:291)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1005)
at org.jbpm.persistence.db.DbPersistenceServiceFactory.getSessionFactory(DbPersistenceServiceFactory.java:93)
at org.jbpm.persistence.db.DbPersistenceService.getSessionFactory(DbPersistenceService.java:74)
at org.jbpm.persistence.db.DbPersistenceService.getSession(DbPersistenceService.java:78)
at org.jbpm.persistence.db.DbPersistenceService.getGraphSession(DbPersistenceService.java:201)
at org.jbpm.JbpmContext.getGraphSession(JbpmContext.java:427)
at org.jbpm.JbpmContext.deployProcessDefinition(JbpmContext.java:166)
at com.hanxr.jbpm.action.DeployProcessTest.testDeployProcessDefinition(DeployProcessTest.java:35)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at junit.framework.TestCase.runTest(TestCase.java:154)
at junit.framework.TestCase.runBare(TestCase.java:127)
at junit.framework.TestResult$1.protect(TestResult.java:106)
at junit.framework.TestResult.runProtected(TestResult.java:124)
at junit.framework.TestResult.run(TestResult.java:109)
at junit.framework.TestCase.run(TestCase.java:118)
at junit.framework.TestSuite.runTest(TestSuite.java:208)
at junit.framework.TestSuite.run(TestSuite.java:203)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:478)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:344)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: line 3:50: unexpected token: t
at org.hibernate.hql.antlr.HqlBaseParser.deleteStatement(HqlBaseParser.java:242)
at org.hibernate.hql.antlr.HqlBaseParser.statement(HqlBaseParser.java:139)
at org.hibernate.hql.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:209)
... 28 more
09:32:43,947 [main] ERROR SessionFactoryImpl : Error in named query: SchedulerSession.suspendTimersForToken
org.hibernate.hql.ast.QuerySyntaxError: expecting "set", found 't' near line 3, column 45 [
update org.jbpm.scheduler.exe.Timer t
set t.isSuspended = true
where t.token = :token
]
at org.hibernate.hql.ast.ErrorCounter.throwQueryException(ErrorCounter.java:63)
at org.hibernate.hql.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:215)
at org.hibernate.hql.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:127)
at org.hibernate.hql.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:83)
at org.hibernate.impl.SessionFactoryImpl.getQuery(SessionFactoryImpl.java:427)
at org.hibernate.impl.SessionFactoryImpl.checkNamedQueries(SessionFactoryImpl.java:388)
at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:291)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1005)
at org.jbpm.persistence.db.DbPersistenceServiceFactory.getSessionFactory(DbPersistenceServiceFactory.java:93)
at org.jbpm.persistence.db.DbPersistenceService.getSessionFactory(DbPersistenceService.java:74)
at org.jbpm.persistence.db.DbPersistenceService.getSession(DbPersistenceService.java:78)
at org.jbpm.persistence.db.DbPersistenceService.getGraphSession(DbPersistenceService.java:201)
at org.jbpm.JbpmContext.getGraphSession(JbpmContext.java:427)
at org.jbpm.JbpmContext.deployProcessDefinition(JbpmContext.java:166)
at com.hanxr.jbpm.action.DeployProcessTest.testDeployProcessDefinition(DeployProcessTest.java:35)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at junit.framework.TestCase.runTest(TestCase.java:154)
at junit.framework.TestCase.runBare(TestCase.java:127)
at junit.framework.TestResult$1.protect(TestResult.java:106)
at junit.framework.TestResult.runProtected(TestResult.java:124)
at junit.framework.TestResult.run(TestResult.java:109)
at junit.framework.TestCase.run(TestCase.java:118)
at junit.framework.TestSuite.runTest(TestSuite.java:208)
at junit.framework.TestSuite.run(TestSuite.java:203)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:478)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:344)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: line 3:45: expecting "set", found 't'
at antlr.Parser.match(Parser.java:211)
at org.hibernate.hql.antlr.HqlBaseParser.setClause(HqlBaseParser.java:337)
at org.hibernate.hql.antlr.HqlBaseParser.updateStatement(HqlBaseParser.java:183)
at org.hibernate.hql.antlr.HqlBaseParser.statement(HqlBaseParser.java:133)
at org.hibernate.hql.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:209)
... 28 more
09:32:43,947 [main] ERROR SessionFactoryImpl : Error in named query: SchedulerSession.resumeTimersForToken
org.hibernate.hql.ast.QuerySyntaxError: expecting "set", found 't' near line 3, column 45 [
update org.jbpm.scheduler.exe.Timer t
set t.isSuspended = false
where t.token = :token
]
at org.hibernate.hql.ast.ErrorCounter.throwQueryException(ErrorCounter.java:63)
at org.hibernate.hql.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:215)
at org.hibernate.hql.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:127)
at org.hibernate.hql.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:83)
at org.hibernate.impl.SessionFactoryImpl.getQuery(SessionFactoryImpl.java:427)
at org.hibernate.impl.SessionFactoryImpl.checkNamedQueries(SessionFactoryImpl.java:388)
at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:291)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1005)
at org.jbpm.persistence.db.DbPersistenceServiceFactory.getSessionFactory(DbPersistenceServiceFactory.java:93)
at org.jbpm.persistence.db.DbPersistenceService.getSessionFactory(DbPersistenceService.java:74)
at org.jbpm.persistence.db.DbPersistenceService.getSession(DbPersistenceService.java:78)
at org.jbpm.persistence.db.DbPersistenceService.getGraphSession(DbPersistenceService.java:201)
at org.jbpm.JbpmContext.getGraphSession(JbpmContext.java:427)
at org.jbpm.JbpmContext.deployProcessDefinition(JbpmContext.java:166)
at com.hanxr.jbpm.action.DeployProcessTest.testDeployProcessDefinition(DeployProcessTest.java:35)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at junit.framework.TestCase.runTest(TestCase.java:154)
at junit.framework.TestCase.runBare(TestCase.java:127)
at junit.framework.TestResult$1.protect(TestResult.java:106)
at junit.framework.TestResult.runProtected(TestResult.java:124)
at junit.framework.TestResult.run(TestResult.java:109)
at junit.framework.TestCase.run(TestCase.java:118)
at junit.framework.TestSuite.runTest(TestSuite.java:208)
at junit.framework.TestSuite.run(TestSuite.java:203)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:478)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:344)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: line 3:45: expecting "set", found 't'
at antlr.Parser.match(Parser.java:211)
at org.hibernate.hql.antlr.HqlBaseParser.setClause(HqlBaseParser.java:337)
at org.hibernate.hql.antlr.HqlBaseParser.updateStatement(HqlBaseParser.java:183)
at org.hibernate.hql.antlr.HqlBaseParser.statement(HqlBaseParser.java:133)
at org.hibernate.hql.ast.QueryTranslatorImpl.parse(QueryTranslatorImpl.java:209)
... 28 more
则修改JBossIDE-1.5.1.GA-ALL/eclipse/plugins/org.jbpm.core_3.0.8.1/build/jbpm-3.1-beta3.jar/org/jbpm/db/hibernate.queries.hbm.xml文件
将
<query name="SchedulerSession.deleteTimersForProcessInstance">
<![CDATA[
delete from org.jbpm.scheduler.exe.Timer t
where t.processInstance = :processInstance
]]>
</query>
<query name="SchedulerSession.suspendTimersForToken">
<![CDATA[
update org.jbpm.scheduler.exe.Timer t
set t.isSuspended = true
where t.token = :token
]]>
</query>
<query name="SchedulerSession.resumeTimersForToken">
<![CDATA[
update org.jbpm.scheduler.exe.Timer t
set t.isSuspended = false
where t.token = :token
]]>
</query>
改为:
<query name="SchedulerSession.deleteTimersForProcessInstance">
<![CDATA[
delete from org.jbpm.scheduler.exe.Timer
where processInstance = :processInstance
]]>
</query>
<query name="SchedulerSession.suspendTimersForToken">
<![CDATA[
update org.jbpm.scheduler.exe.Timer
set isSuspended = true
where token = :token
]]>
</query>
<query name="SchedulerSession.resumeTimersForToken">
<![CDATA[
update org.jbpm.scheduler.exe.Timer
set isSuspended = false
where token = :token
]]>
</query>
)
6.6 从数据库中的查看部署效果
无论是 MySQL还是 Oracle,查询 jbpm_processdefinition表,你会发现多了一条记录,如下图 (以 PLSQL Developer的显示为例 )
依次检查各表我们可以发现有如下变化:
并由此简单判断出各表的作用,表中各字段的作用由字段名也能知晓一二。
jbpm_processdefinition | 一个流程定义文件对应一条记录,可记录多个流程定义文件,可记录一个流程定义文件的对个版本。 |
jbpm_action | 记录 ActionHandler的对象实例(以名称为标识) |
jbpm_delegation | 记录了 ActionHandler全类名,以便于用反射方式来加载 |
jbpm_event | 它的 transition引用了 Jbpm_transition表的 id,再看其它字段,估计此表是表示流程转向事件的一个实例,或者是一个各表之间的联接表。 |
jbpm_node | 流程结点 |
jbpm_transition | 流程的转向定义 |
jbpm_variableaccess | 流程中携带的变量。 ACCESS字段是这些变量的读写权限 |
jBPM 的客户端开发
有了前面的 HelloWorld后台流程,我们就要开始客户端程序了。正如前面提到的,本文不写 JSP,而改采用 JUnit的形式,输出则用 System.out.println。举一反三,知道在方法中输入及用 println输出,在 JSP和 SWING等 GUI界面还不是一样嘛。
这个 JUnit客户端,我们就借用创建项目时自动生成的 SimpleProcessTest.java了,改写后如下:
packagecom.sample;
importjunit.framework.TestCase;
importorg.jbpm.JbpmConfiguration;
importorg.jbpm.JbpmContext;
importorg.jbpm.context.exe.ContextInstance;
importorg.jbpm.graph.def.ProcessDefinition;
importorg.jbpm.graph.exe.ProcessInstance;
public class SimpleProcessTest extends TestCase {
private JbpmConfiguration config = JbpmConfiguration.getInstance();
private JbpmContext ctx = config.createJbpmContext();
// helloworld对应于jbpm_processdefinition表的name字段值,也即processdefinition.xml的name
//这个值得取比较耗时,实际项目里最好和“数据库的JDBC连接”一样,让它共享,不要频繁打开关闭。
private ProcessDefinition processDefinition = ctx.getGraphSession().findLatestProcessDefinition("helloworld");
publicvoid testNewRequest() {
longid = newRequest();
System.out.println("id=" + id);
checkNewRequest(id);
confirmRequest(id);
checkconfirmRequest(id);
ctx.close();//关闭jbpm容器
}
/**
* 创建一个请假单
*
* @return
*/
privatelong newRequest() {
//创建一个新流程
ProcessInstance pi =processDefinition.createProcessInstance();
//取得流程的数据环境
ContextInstance ci = pi.getContextInstance();
//创建一张请假单
ci.setVariable("name","陈刚www.chengang.com.cn" );
ci.setVariable("day", 2);
assertEquals(null, ci.getVariable("note"));
//请假申请结束,转到下一个流程结点
pi.signal();
returnpi.getId();
}
/**
* 检查请假单的数据
*
* @paramid
*/
privatevoid checkNewRequest(long id) {
//从数据库提取原流程
ProcessInstance pi =ctx.loadProcessInstance(id);
//取得流程的数据环境
ContextInstance ci = pi.getContextInstance();
//创建一张请假单
assertEquals("陈刚www.chengang.com.cn" , ci.getVariable("name"));
assertEquals(Integer.valueOf(2), ci.getVariable("day"));
assertEquals("我要请假" , ci.getVariable("note"));
//当前是结点为confirm
assertEquals(pi.getRootToken().getNode().getName(),"confirm");
//流程还没结束
assertFalse(pi.hasEnded());
}
/**
* 审批陈刚的请假申请
*
* @paramid
*/
privatevoid confirmRequest(long id) {
ProcessInstance pi =ctx.loadProcessInstance(id);
ContextInstance ci = pi.getContextInstance();
//不通过
ci.setVariable("note","不准请假,继续加班" );
//审批结束,到下一个流程结点
pi.signal();
}
privatevoid checkConfirmRequest(long id) {
ProcessInstance pi =ctx.loadProcessInstance(id);
ContextInstance ci = pi.getContextInstance();
// ConfirmAction类在signal后执行,所以覆盖了经理的审批意见
assertEquals("准假" , ci.getVariable("note"));
//当前是结点为end
assertEquals(pi.getRootToken().getNode().getName(),"end");
//流程结束了
assertTrue(pi.hasEnded());
}
}