JBPM4.4应用开发

来源:互联网 发布:顶级域名服务器 权威 编辑:程序博客网 时间:2024/06/10 01:15

 

JBPM简介

转载:http://blog.sina.com.cn/s/blog_48b9354d0100q4na.html

JBPM是基于JAVA的工作流引擎。工作流引擎,给我们提供流程逻辑的定义方法,给我们提供根据流程逻辑来调度业务对象的功能。能使我们避免在代码中硬编码流程逻辑,因为硬编码的逻辑难于理解和复用,并且非常容易受到变化的影响,维护起来极度困难。

 

所谓业务对象,从现实生活的角度理解,就是那些需要在不同的人之间流动的信息。比如一个请假单据、一个报销单据、上级下发的一份文件、一次用车申请。任何一个业务流程,都会涉及到多个环节,在这些环节中,不同的人有不同的任务需要处理。

 

JBPM就负责在不同的环节中传递各种信息,并在信息到达某个环节之后,给某个相关责任人分配任务(比如“审批”任务)。

 

应用JBPM的基本步骤

定义流程

使用JPDL流程定义语言定义流程规则,得到流程定义(ProcessDefinition)文件(比如,把请假单的流转过程定义出来)

部署流程

将流程定义文件部署到数据库中相应的表中存储起来

执行流程

根据流程定义文件的规则,针对某一个具体的业务对象进行调度。比如:孙三创建了一个请假单,现在就把这个请假单对象交给JBPM来进行调度(在不同的审批者之间进行调度)。JBPM将会启动一个流程实例(ProcessInstance)来调度某一个具体的业务对象!

快速开始的实例

我们将基于JBPM4.4来开始我们的JBPM之旅!将模拟(利用JUnit测试单元)实现一个请假审批的基本流程。

 

Eclipse插件的安装

我们在Eclipse(不是MyEclipse)中安装JBPM的插件(这个插件的主要功能是提供了一个图形化的流程定义界面,方便进行流程定义)。

 

在安装插件之前,请自行下载JBPM4.4。

 

目前JBPM4.4版本对应的这个基于Eclipse的编辑器插件,功能不是很完整,也有很多BUG,比如:在handler中定义的<field>和<arg>参数,在你修改了图形之后,会自动去除!

 

下面分步介绍安装过程。

 

第一步 打开插件安装界面

选择相应菜单,如下图所示:

 

打开下面的界面

 

 

第二步 打开添加site的界面

 

点击Add…按钮,打开下面的界面

 

 

 

第三步 选择插件包并按照

输入Name: jbpm4.4

点击Archive…按钮,导航到:jbpm4.4/install/src/gpd/jbpm-gpd-site.zip

点击OK

 

【请取消“Contact all update site during install to find required software”前面的复选框!】

 

一直点击Next,直到Finish即可完成安装

 

第四步 配置JBPM运行环境

配置JBPM Runtimes

 

如上图所示,点击Window->Preferences打开上述界面,并选中:JBoss jBPM下面的Runtime Locations,点击Add…按钮,输入名称,并选中JBPM4.4的解压目录即可。

 

第五步 配置流程定义文件自动提示功能

点击Window->Preferences打开下面的界面,并进入XML->XML catelog,在User Specified Entries下面增加一个entry。点击旁边的Add…打开界面,选中jpdl-4.4.xsd文件,Key Type: Namespace Name;Key:http://jbpm.org/4.4/jpdl,如下所示:

 

 

第六步 修改eclipse.ini文件

关闭Eclipse,在Eclipse安装根目录下,修改eclipse.ini文件,在最末尾加上一行:

-Dfile.encoding=UTF-8

这是为了避免在设计流程的时候,出现中文乱码问题。

然后重启Eclipse即可。

依赖包介绍

 

 

activation.jar

发送EMAIL需要的包

antlr-2.7.6.jar

hibernate依赖包

commons-collections-3.1.jar

hibernate依赖包

dom4j-1.6.1.jar

hibernate依赖包

drools-api.jar

JBoss组织下面的一个规则引擎

drools-compiler.jar

JBoss组织下面的一个规则引擎

drools-core.jar

JBoss组织下面的一个规则引擎

freemarker.jar

模板引擎

hibernate3.jar

Hibernate

janino.jar

一个开源的JAVA编译器,能够把JAVA源代码编译为字节码

javassist-3.9.0.GA.jar

hibernate依赖包

jbpm.jar

JBPM核心包

joda-time.jar

对日期和时间进行处理的工具类库,可替代java.util.Calendar中的相关功能

jta-1.1.jar

Hibernate依赖包

juel-api.jar

EL表达式的一个开源实现包(EL表达式不仅仅可以用于JSP中)

juel-engine.jar

EL表达式的一个开源实现包(EL表达式不仅仅可以用于JSP中)

juel-impl.jar

EL表达式的一个开源实现包(EL表达式不仅仅可以用于JSP中)

livetribe-jsr223.jar

jsr223是把其它脚本语言嵌入JAVA的一个规范,这个JAR包是对这个规范的实现

log4j-1.2.16.jar

日志记录

mail.jar

发送EMAIL的依赖包

mvel2.jar

MVEL,是一个类似于OGNL的表达式语言解释工具,性能优于OGNL

mysql-connector-java-3.1.13-bin.jar

MySql驱动

slf4j-api-1.5.8.jar

Hibernate依赖包

slf4j-log4j12-1.5.8.jar

Hibernate依赖包

 

 

缺省情况下,JBPM使用Hibernate来对各种信息进行持久化,所以,上述列表中也有Hibernate相关的依赖包。

第一个项目的创建

·       首先创建普通的JAVA项目

·       引入上述依赖包

·       在类路径根目录下添加如下文件,命名为jbpm.cfg.xml:

 

 

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

 

  <jbpm-configuration>

 

    <import resource="jbpm.default.cfg.xml"/>

    <import resource="jbpm.businesscalendar.cfg.xml"/>

    <import resource="jbpm.tx.hibernate.cfg.xml"/>

    <import resource="jbpm.jpdl.cfg.xml"/>

    <import resource="jbpm.identity.cfg.xml"/>

    <import resource="jbpm.jobexecutor.cfg.xml"/>

 

  </jbpm-configuration>

 

 

 

·       在类路径根目录下添加如下文件,命名为:jbpm.hibernate.cfg.xml:

 

 

<!DOCTYPE hibernate-configuration PUBLIC

    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"

    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

    <session-factory>

       <!-- 数据库链接的相关配置-->

       <property name="hibernate.connection.url">jdbc:mysql://localhost/jbpm</property>

       <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>

       <property name="hibernate.connection.username">root</property>

       <property name="hibernate.connection.password">mysql</property>

      

       <!-- 数据库方言-->

        <property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>

       

        <!-- 自动打印出SQL语句-->

       <property name="show_sql">true</property>

      

       <!-- 自动创建数据库表-->

       <property name="hibernate.hbm2ddl.auto">update</property>

      

       <!-- 映射文件列表-->

       <mapping resource="jbpm.execution.hbm.xml"/>

       <mapping resource="jbpm.history.hbm.xml"/>

       <mapping resource="jbpm.identity.hbm.xml"/>

       <mapping resource="jbpm.repository.hbm.xml"/>

       <mapping resource="jbpm.task.hbm.xml"/>

    </session-factory>

</hibernate-configuration>

 

 

 

这个文件是hibernate配置文件,JBPM底层使用hibernate来存取数据。此配置的目的是希望JBPM能够把有关信息存储到MYSQL数据库中,便于我们查看。

流程定义

流程定义,是一个XML文件,JBPM4中常用的流程定义语言是:JPDL(JBPM Process Definition Language)。

我们可以选择新建一个流程定义:

 

 

在流程定义设计器上,设计流程:

 

 

注意中间要使用Task节点,最终定义好的流程定义文件如下所示:

 

 

 

<?xmlversion="1.0" encoding="UTF-8"?>

 

<processkey="LEAVE" name="请假单"xmlns="http://jbpm.org/4.4/jpdl">

   <startform="baoxiao" g="234,7,48,48" name="start1">

      <transitiong="-71,-17" name="提交给张三审批"to="张三审批"/>

   </start>

   <taskassignee="张三"g="215,113,92,52" name="张三审批">

      <transitiong="-71,-17" name="提交给李四审批"to="李四审批"/>

   </task>

   <taskassignee="李四"g="218,216,92,52" name="李四审批">

      <transitiong="-47,-17" name="to end1" to="end1"/>

   </task>

   <endg="247,326,48,48" name="end1"/>

</process>

 

 

 

在完成流程的设计之后,我们可以编写代码,把这个流程定义文件部署到数据库中:

 

 

publicclassJbpm_01_DeployProcessDefinition extendsTestCase{

   

    //把流程定义文件的相关信息保存到数据库中!

    publicvoidtestDeployProcessDefinition(){

      

       //流程引擎

       ProcessEngine engine = newConfiguration()

           .setResource("jbpm.cfg.xml").buildProcessEngine();

      

       //从流程引擎中获得跟数据库操作有关的服务

       engine.getRepositoryService()

           .createDeployment() //现在要部署流程定义

           .addResourceFromClasspath("process01.jpdl.xml") //添加流程定义文件

           .deploy(); //部署,将流程定义的相关信息插入数据库\

    }

}

 

 

 

在运行完上述代码之后,我们可以到数据库中查看相关的信息:

 

 

上面的数据库表,描述了有关ProcessDefinition对象的信息。ProcessDefinition对象有:name,id,key,version等属性。

 

Name就是给人类看的名称,比如:“请假单”

Key就是一个键值,比如:LEAVE

Version是版本号,对于相同的流程定义(名称和KEY相同),如果重复部署(比如上述代码多次运行),其version将一直递增。

Id就是KEY和VERSION组合的值。比如:LEAVE-1

 

上面数据库表中呈现的pdid,pdkey,pdversion和OBJNAME_字段的值,就是这些属性。

流程执行

流程定义完成之后,就可以创建流程实例(ProcessInstance)来按照这些流程定义进行流转。流程实例的概念很容易理解。

比如,公司的请假制度是:先给张三审批,然后还得给李四审批,最后还得王五去审批,这三个人审批都结束之后,请假单才能生效。这些描述就是流程定义(ProcessDefintion)。

现在假设员工“小赵”生病了要请假,在现实中,他需要拿到请假条,并且按照请假条上固定的格式填写好请假的内容(比如:谁请假,请假的时间,请假单的天数等等)之后,按照公司的规定,把请假条首先拿给张三去审批。张三审批完成之后,再拿给李四去审批……

“小赵”拿到请假条,填写内容这样的事情,我们可以称之为“创建流程实例(ProcessInstance)”。小赵将请假条交给张三去审批这样的事情,我们可以称之为“提交”;张三审批完成之后再拿给李四去审批这样的事情,也可以称之为“提交”。

       所以,流程定义(ProcessDefinition)我们可以理解为流转规则的定义;而流程实例(ProcessInstance)则可以理解为按照特定规则对某些数据执行流转。比如上述例子中,“小赵的请假条”,我们就可以理解为一个流程实例(ProcessInstance)对象。每个流程实例都会包含有各种数据,比如“小赵的请假条”这个流程实例,可能就包含了:请假者是小赵,请假时间是某年某月某日,请假天数是3天,请假事项是“有病要去医院”等等这些数据。这些数据可以称为“流程实例变量”。

       在JBPM4中,创建流程实例并把流程实例提交到第一个环节(比如张三审批这个环节是第一个环节)称为“启动流程实例(startProcessInstance)”。

 

 

publicclassJbpm_02_StartProcessInstance extendsTestCase{

   

    //根据流程定义,启动一个新的流程实例

    publicvoidtestStartProcessInstance(){

      

       //流程引擎

       ProcessEngine engine = newConfiguration()

           .setResource("jbpm.cfg.xml").buildProcessEngine();

      

       Map instanceVariables =newHashMap();

       instanceVariables.put("leaver", "小赵"); //请假者

       instanceVariables.put("leaveDate",newDate()); //请假时间

       instanceVariables.put("leaveDays", 5); //请假天数

       instanceVariables.put("reason", "有病要去医院"); //请假事项/原因

      

       //启动流程实例

       ProcessInstance processInstance =

              engine.getExecutionService()

                  .startProcessInstanceByKey("LEAVE",instanceVariables);

      

       System.out.println("流程实例【"+processInstance.getId()+"】已经被创建了!");

    }

}

 

 

在上面的例子中,我们调用startProcessInstanceByKey方法来启动流程实例,传了两个参数:

第一个参数是流程定义的KEY

第二个参数是流程实例变量

 

我们调用这个方法,JBPM将自动查询对应流程定义KEY的最新版本的流程定义对象来创建流程实例对象,并把流程实例对象提交到第一个环节。

 

当然,实际上更加常见的用法是:像请假单的这些数据,可以存放在我们自己的业务表中,然后在启动流程实例的时候,直接用业务表中的主键值作为流程实例的KEY即可(即无需把所有的业务数据都传递给JBPM),如下所示:

 

 

    publicvoidtestStartProcessInstance02(){

      

       //流程引擎

       ProcessEngine engine = newConfiguration()

           .setResource("jbpm.cfg.xml").buildProcessEngine();

      

       //启动流程实例

       ProcessInstance processInstance =

              engine.getExecutionService()

                  .startProcessInstanceByKey("LEAVE","101");

      

       System.out.println("流程实例【"+processInstance.getId()+"】已经被创建了!");

    }

 

 

在这个例子中,我们用的是另外的一个方法来启动流程实例,第一个参数的意义与前面例子的一样,第二个参数则是流程实例的KEY。

 

流程实例也是有KEY这种概念的,流程实例的KEY,刚才说了,一般是将业务对象的键值作为流程实例的KEY。比如,上面我们给了一个“101”,这是我们假设某个请假单的ID是101,现在,我们要给这个请假单创建一个流程实例对象(以便于这个请假单能在JBPM中进行流转)。

 

当然,还有更多的方法,也可以用于启动流程实例,具体请参考JBPM4的API,我们在课堂上对此将做详细的介绍。

 

在启动流程实例之后,数据库中JBPM4_EXECUTION表将被添加相应的记录:

 

ProcessInstance与Execution

上面我们了解了所谓流程实例(ProcessInstance),实质就是指得一个业务对象(比如:“小赵的请假单”、“小李的报销单”、“某某单位的某次发文”等等)。而Execution是一个执行控制对象,你必须要理解的一点就是每个Execution对象必然会指向一个节点(Activity)。Execution指向哪里,就代表它执行到了哪里。每个Activity都有自己的行为。比如TaskActivity的行为就是创建任务实例(TaskImpl),并把任务实例与参与者(assignee)相关联。这样,通过assignee就可以查找到其任务实例列表。

Execution有name、key、id、state等属性,Execution是树型结构,在fork和join流程中,将会创建子Execution对象。

子Execution对象的name属性是进入这个子Execution对象时的transition的名称。

ProcessInstance也是一种Execution类型的对象。实际上,对于ProcessInstance对象来说,它对应的ProcessInstance(因为ProcessInstance也是Execution,所以它也有对应的ProcessInstance对象)就是它自己。而对于ProcessInstance的那些子Execution对象(孙Execution对象等等等等)而言,也肯定会有指向ProcessInstance对象的一个引用!

对于根Execution对象(即ProcessInstance对象),它的ID是:流程定义的KEY.流程实例的KEY,比如上面例子中创建的流程实例的ID就是:”LEAVE.101”。

我们可以通过这个ID(或其它方法)来查询流程实例对象或Execution对象,从而得知这些Execution指向哪里。

 

 

       //流程引擎

       ProcessEngine engine = newConfiguration()

           .setResource("jbpm.cfg.xml").buildProcessEngine();

      

       ProcessInstance instance = engine.getExecutionService().findProcessInstanceById("LEAVE.101");

      

       Set<String> currentActivityNames = instance.findActiveActivityNames();

      

       //流程实例现在流转到哪个Activity了

       System.out.println(currentActivityNames.toString());

 

 

上述代码将输出:[张三审批],表明现在执行到了张三审批这个环节。

Task

在我们上述流程定义中,张三审批这个环节,用的是一个Task类型的Activity来定义的。这种类型的Activity的行为是:创建任务实例(TaskImpl),并把任务实例与参与者(assignee)相关联。Task是一个接口,而TaskImpl是具体的实现类。

 

任务实例(Task)这种概念,其实质是用来定义Execution与assignee之间的关联的。从上面我们知道流程已经执行到了张三审批这个环节了,因此,我们下一步的目标就是:通过张三,找出流转到他手上的所有的业务对象信息。下面是一个示例:

 

 

       ProcessEngine engine = newConfiguration()

           .setResource("jbpm.cfg.xml").buildProcessEngine();

      

       //根据用户,查询它的任务列表

       List<Task> tasks = engine.getTaskService().findPersonalTasks("张三");

       for(Task t:tasks){

           System.out.println("任务ID:"+t.getId()+","+t.getAssignee()+"手上有【"+t.getExecutionId()+"】等待:"+t.getActivityName());

       }

 

 

 

在数据库中,亦可查询到相关信息:

 

关于Task及其与Execution等之间的关系,我们在课堂上将做详细解析。

 

提交

张三如果审批完成,他需要继续向下提交,下面是示例代码:

 

 

       //根据任务ID,完成此任务

       //任务被完成之后,将自动流转到下一个环节

       engine.getTaskService().completeTask("10002");

      

       //任务被完成之后,下面的查询将无数据

       List<Task> tasks = engine.getTaskService().findPersonalTasks("张三");

       for(Task t:tasks){

           System.out.println(t.getAssignee()+"手上有【"+t.getExecutionId()+"】等待:"+t.getActivityName());

       }

 

 

completeTask就是完成某个任务实例,我们在查询出某人有哪些任务实例之后,他就可以选择其中一个任务实例来完成(complete)。任务实例被完成之后,任务实例对象将被删除,并被转移到历史库中。

 

在张三提交完成之后,JBPM4_TASK表将变为:

 

 

你也可以到JBPM4_HIST_TASK表中查询有关刚才那个Task的历史信息(比如它的完成时间等信息)。

 

后续工作

张三审批完成之后,我们可以继续用李四来查询其手上的任务列表,并结束任务对象;然后继续用王五来查询其手上的任务列表,并结束任务对象。

 

当所有环节都完成之后,流程实例对象及各种任务实例对象都会被删除,并被转移到历史库中。

大家可继续执行,直到执行结束!

0 0
原创粉丝点击