jBPM 工作流引擎研究总结
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
JBPM工作流引擎研究
文 档 号:
当前版本: 1.0
作 者: 钟辉
完成日期: 2009-07-16
参考文档:
文档的阅读方法:加了红色标记的标题下的内容请重点阅读,没加的可以先不阅读
?
JBPM工作流引擎研究 1
1 jbpm相关概念 4
1.1什么是jbpm 4
1.2 需要了解的知识点 4
1.2.1 jBPM数据存储 4
1.2.2 工作流的基本构成 4
1.2.3工作流的工作方式 5
1.3 jbpm工作流执行步骤 5
1.4 JBPM中典型节点类型 6
1.5 一个简单的例子 6
1.5.1第一个流程定义文件 7
1.5.2流程调用 7
1.6 JBPM的事件(event)和行为(action) 7
1.7 JBPM的任务 8
1.8 任务分配 8
1.9 JBPM的任务管理 9
1.10 使用jBPM的优势 9
1.11 使用jBPM的问题 9
1.12 结论 10
2 jPDL简介: 11
2.1流程定义描述 11
2.2 流程的程序接口说明 12
2.2.1动作处理接口(ActioinHandler) 12
2.2.2判定处理接口(DecisionHandlder) 13
2.2.3委派处理接口(AssignmentHandler) 13
2.3 创建jBPM的数据库表 13
2.4 jBDL流程元素表 15
3 jBPM开发整理 16
3.1 概述 16
3.2 环境配置 17
3.3 开发步骤 18
3.3.1创建jBPM项目 18
3.3.2配置数据库 18
3.3.3 流程定义 18
3.3.4 JUnit测试 19
3.5 部署 19
4 jBPM APIs整理 19
4.1 ProcessDefinition 19
4.2 Configuration、Context 19
4.3 ContextInstance上下文实例 20
4.4 org.jbpm.context.log变量日志管理 20
4.5 Task任务管理 21
4.6 Swimlane的整理 21
4.7 Token的整理 21
4.8 Node 22
5 jBPM 数据库表整理 23
5.1 jbpm_action表 23
5.2.jbpm_processdefinition表 23
5.3.jbpm_transition表 23
5.4.jbpm_ node表 24
5.5.jbpm_delegation表: 25
5.6.jbpm_event表: 25
6 Boss3.8.2中的集成分析 29
6.1.集成目的 29
6.2.集成思路分析 30
6.3.集成方案分析 31
6.4.集成类UML分析 31
6.5.改进的工作流集成方案研究 32
6.5.1 SpringModules(Spring与jbpm集成的桥梁)与JBPM的集成 32
6.5.2 JbpmTemplate的改写 37
?
1 jbpm相关概念
1.1什么是jbpm
jBPM,全称是Java Business Process Management,是一种基于J2EE的轻量级工作流管理系统。
? jBPM的一个特色是采用了它自己定义的JBoss jBPM Process definition language (jPdl)。jPdl认为一个业务流程可以被看作是一个UML状态图。jPdl就是详细定义了这个状态图的每个部分,如起始、结束状态,状态之间的转换等。
jBPM的另一个特色是它使用Hibernate来管理它的数据库。Hibernate是目前Java领域最好的一种数据持久层解决方案。通过Hibernate,jBPM将数据的管理职能分离出去,自己专注于业务逻辑的处理。
从一个具体的示例,我们是看不出有jBPM的,也就是说jBPM在后台起着作用。从一个固定流程的项目中,我们看不出jBPM的优势。不过,如果在一个流程不确定,经常需要变动的项目中,jBPM的好处将会显然出来。应用jBPM后,改变流程只需改
变流程描述文件。
1.2 需要了解的知识点
1.2.1 jBPM数据存储
jBPM需要把初始化数据和工作流定义存储到数据库中,它定义了一套数据结构来存储这些数据,这也是该容器本身的特点。
JBPM 需要数据库支持,jBPM会把自己的一个初始化数据存储到数据库,同时工作流的数据也是存储到数据库中的。 jBPM 使用 Hibernate 来做为自己的存储层,因此只要是 Hibernate 支持的数据库, jBPM 也就支持。
注:在 JBoss 自带的示例中,并没有设置数据库,那是因为 jBPM 默认使用的是内存数据库 hsqldb 。
1.2.2 工作流的基本构成
jBPM作为一种工作流的实现, 从广义地来看, 工作流又都有哪些基本的构成呢? 一般来说, 一个工作流有如下的三个大构件:
1、流程定义. 通过这个构件来修改现有的流程或定义新的流程.
2、流程执行. 把前面定义好地流程在自己的系统中调用, 从而执行整个业务逻辑, 也就是让前面定义的的流程"流"起来.
3、流程执行监测. 记录流程执行过程中的相关数据, 以便于性能调优等.
一个流程实例是通过一个流程ID与一个业务实体进行绑定。所以每一个业务实体都包含一个流程实例ID。一个业务实体对应一个流程
1.2.3工作流的工作方式
1、我们首先需要定义一个流程(流程定义ProcessDefinition)。
2、该流程可能是多个人发起的,每个人发起的流程称为不同的流程对象(流程实例)(ProcessInstance)。
3、每个定义的流程都有许多的步骤我们称为节点(Node)。
4、每个执行中的流程的执行路径我们称为路径(Token) 。
1.3 jbpm工作流执行步骤
1、加载(发布)流程定义
这个意思是,我们通过jbpm的designer插件,或者是用其他工具,制定出processDefinition,然后将其加载到应用中的过程。这个加载可以是写入内存中,或者是直接写入数据库等。
2、启动流程
创建流程实例的过程。具体创建实例的方法有多种,可根据自己的需要自行选择。
3、处理任务
在流程流转的过程中,JBPM引擎会为我们生成任务的实例,我们就需要针对这些任务实例来进行处理,然后结束这些任务实例,并推动流程的流转。
4、记录流程的相关状态
记录流程状态这点包括且不限于以下内容:
1)流程实例的开启
2)任务实例的创建
3)任务实例的开始执行
4)任务实例的结束
5)流程实例的结束
1.4 JBPM中典型节点类型
1、start-state (开始状态)
2、end-state (结束节点 )
3、state(状态) State节点也叫手工节点,进入到这种节点,整个流程的执行就会中断。直到系统外参与者发起继续执行的命令,即调用signal或end方法,业务程序实例的执行才能够继续下去。
4、node(自动节点)
这种节点
和State相反,也称自动节点。当业务程序实例执行到这个节点,不会停止执行。而是会继续往下执行。如果该节点存在多个离开转向。那么,就会执行其中的第一个离开转向,在Node状态中,不需要外部参与者的参与,业务流程的这个部分是自动的、即时完成的。
5、task-node (任务节点)
其性质和node节点一样,在没有task的时候,也都是自动执行,不等待。task-node被归类为一个等待节点,是指在task-node中的task列表中的task没有全部执行完之前,它会一直等待。Task可以在task-node节点下定义,也可以挂在process-definition节点下。最普遍的方式是在task-node节点下定义一个或多个任务。默认情况下,流程在task-node节点会处于等待状态,直到所有的任务被执行完毕。Task的执行是按顺序执行的,任务都完成后,token仍然不会指向后面的节点;需要自己手动调用processInstance.signal()才会驱动流程到下面的节点。
1.5 一个简单的例子
JBPM的流程定义采用XML的方式(实际绝大多数的流程引擎的流程定义都采用的是这种方式),作为测试XML定义我们既可以写在代码当中,也可以以一个独立的XML文件的形式存在。
1.5.1第一个流程定义文件
1.5.2流程调用
//定义流程
//以文件输入流的形式将流程定义文件读入内存
InputStream is = new FileInputStream("src/hello.xml");
ProcessDefinition pd = ProcessDefinition.parseXmlInputStream(is);
//定义流程实例,流程的执行token定位在了start-state
ProcessInstance ps = new ProcessInstance(pd);
//执行流程
//获取根路径
Token root = ps.getRootToken();
//沿着“mystate”路径流向下一个节点
root.signal("mystate");
System.out.println(root.getNode().getName());
//流程往下走
root.signal();
System.out.println(root.getNode().getName());
}
1.6 JBPM的事件(event)和行为(action)
我们可以在流程执行过程中执行自己的代码, 象触发器一样,在特定的时候触发。
触发点:1.流程转向的时候;
2.流程节点中的某个事件发生的时候.
常用事件:
流程启动(process-start)
流程结束(process-end)
节点进入(node-enter)
节点离开 (node-leave)
任务创建(task-create)
任务分派(task-assign)
任务启动(task-start)等事件。
代码规范:处理触发行为的类必须实现ActionHandler接口
流程中含有事件和Action发布后的数据变化:
1.7 JBPM的任务
Task 是流程定义里的一部分,它决定了task instance的
创建和分配
任务分配: 当流程执行到某个Task的时候,将会引起流程引擎要调用相应的swimlane或assignment将当前的task分配(委派)给某个参与者,外部参与者可以是一个人也可以是某个系统等。
task-node的列子
1.8 任务分配
任务分配有三种方式,我们可以将任务分配给个人或是某个群组
对于个人用户:
1.自己的代码中加入如下语句:TaskInstance.setActorId(String id);
2.在AssignmentHandler中加入assignable.setActorId(String id);
3.在流程定义中的任务定义中加入属性actor-id
取得某角色的任务列表只需要做:
TaskMgmtSession.findTaskInstances(String acotorId);
对于组用户:
1.自己的代码中加入如下语句:TaskInstance. setPooledActorIds(String[])
2.在AssignmentHandler中加入assignable. setPooledActorIds(String[])
3.在流程定义中的任务定义中加入属性pooled-actor-ids
取得某组的任务列表只需要做:
TaskMgmtSession.findPooledTaskInstances(String actorId)
TaskMgmtSession.findPooledTaskInstances(List actorIds)
通过任务管理,我们在开发流程的时候就可以在用户登陆的时候看到需要办理的任务,在你处理完任务后该任务就会从任务列表中删除。
1.9 JBPM的任务管理
我们将带有任务,并且任务有分配给相应的用户或用户组后的流程发布到数据库后,数据库中表数据的变化:
任务表 dbo.JBPM_TASK(任务的静态定义)
任务实例表JBPM_TASKINSTANCE(任务执行实例,动态)
任务参与者dbo.JBPM_POOLEDACTOR
参与者与任务实例之间是多对多的关系中间表:JBPM_TASKACTORPOOL
1.10 使用jBPM的优势
将业务流程复杂的系统结构清晰化,提供系统运行时的灵活性
1、解耦系统业务流程
流程独立,可以使用工具定义和建模,利于跟踪、监控、管理、调度、优化和重整
2、提高系统的灵活性
系统流程定义生产环境的修改和调整,用户和外部工具交互,任务的动态分派
1.11 使用jBPM的问题
1、对当前任务的条件查询
jBPM不提供灵活进行条件查询的api,如果需要,可以自定义hibernate查询,从jbpm相应的数据表中查询任务数据。但需要对jBPM机制比较了解,而且有些复杂条件难以用jBPM本身的信息查到。
2、当前任务的分页
在上一问题的基础上,使用hibernate分页。
3、 统计各个流程实例的状态
可以通过流程实例,在jbpm系统表中查询,也可以在业务表的相应数据上加上状态列来统计。前一个比较麻烦,后一个
比较直观,但不会因使用jBMP而使用工作量减少。
4、 工作流数据与业务数据结合
一般通过在流程实例中添加相应的一笔数据的标识作为变量来关联。也可以有针对性的扩展jbpm的系统表来实现与业务的关联性。
5、 修改流程后的历史数据兼容性问题
Jbpm工作流流程定义有版本的概念,修改流程后要重新发布,与旧的流程不是一个同一个版本。系统可以区别开新旧流程来。
1.12 结论
1、 工作量
初步的结论是:引入工作流技术不会明显减少系统开发工作量。相反,在一般情况下,会增加一部分工作量。
如果项目流程比较少,而且比较固定,则使用工作流技术会明显增加开发工作量。
如果项目流程多,而且比较复杂,则使用工作流技术会使项目结构层次更加清晰、更具有扩展性,根据需求有可能要修改和扩展现有开源工作流类库与数据库结构,也会增加额外的工作量。但权衡之下,利大于弊。
2、 关于业务数据与jBPM本身的数据
理论上说,如果使用jBPM,可以将所有业务数据放到jBPM的context中管理,不再维护业务数据表。但这样的结果是在流程之外的环境(比如在统计报表中)中无法容易的得到业务数据。所以一般会建立业务数据表,我不使用工作流时一样,然后让jBMP从业务数据表中得到业务数据,而不在jBPM中保留业务数据。因此,使用jBPM后,在业务数据方面基本不会减少工作
3、工作流学习成本
工作流本身的概念较复杂,使用jbpm,需要学习其工作流的定义和结构,流程定义工具和语言、了解其数据结构。与其它工作流产品(如Shark)相比,jBPM对Java开发人员来说学习较低成本,在做流程复杂的项目时,学习成本可以接受。
4、 系统用户和角色与工作流整合
流程的流转和任务的分派完成,都是用户在控制,所以需要将用户、角色和权限整合到jbpm工作流中。
5、 系统业务的整合和调整
将流程抽取后,原本连续的业务处理变成一个个的任务节点。需要在每个业务相关处理处添加工作流流程控制、在每个节点处实现相关的业务和流程切入点。
6、适用范围
Jbpm工作流适用于:
项目流程比较多,流程复杂的项目。
系统运行和维护、升级时,流程可能需要修改、调整和跟踪、控制的项目。
2 jPDL简介:
JPDL(JBPM Process Definition Language)是JBPM流程定义语言。JPDL详细定义了这个状态图的每个部分,如: 开始、结束状态,状态之间的转换等。这种语言的定义对于用户来说比较容易理解,也比较容易对其进行扩展。
2.1流程定义描述
除了开始和结束结点外,我们定义了三种类型的结点:
1、任务结点
任务结点是一个需
要人工参与的结点类型。当流程进入结点时,会生成相应的任务实例(TaskInstatnce),并通过委派接口AssignmentHandler或jBPM表达式将任务委派给一个或多个特定的角色或参与者。结点自身进入等待状态,直到任务被参与者完成或者跳过,流程继续。
2、判定结点
判定结点的设计目标是根据上下文环境和程序逻辑,判定流程转向。通过指定一个实现DecisionHandlder接口的Java委派类或jBPM表达式,来返回转向(transition)的字符窜类型的名称(可以是中文哦),来达到决定流程方向的功能。
3、普通结点
普通结点也可以定义相应的处理任务,通过定义相应的ActioinHandler类。同任务结点不同的是,普通结点定义的任务是由流程自动执行的,无须人工干预。
三种结点都可定义结点事件(event):
node-enter,该事件在流程进入结点时触发
node-leave,该事件在流程离开节点是触发
可以在事件上挂接ActioinHandler接口的实现类来完成一些特定的功能。
三种节点都可以定义异步处理方式(async属性):
异步处理意味着每个结点的事务处理是通过消息机制分离的,不再同一线程中统一调用执行。而是由消息监听线程从消息队列中取得消息体来运行相应得程序。
此外我们定义了结点间的转向(transition),用来记录和处理状态的变迁。每个转向中,可以委派一个或多个的ActioinHandler接口实现类,负责处理节点变迁时的上下文状态变更及回调用户定义的处理程序。
我们看看这部分左面的那些东西,什么start啊,end啊,tasknode啊,fork啊,join啊。那我们来解释一下这是个什么东西呢,我们看看我们的需求,员工要写一个报销单,然后交给部门主管来处理,那么部门主管就应该算是一个tasknode,他就是一个任务节点。start和end其实就是一个虚状态,当我们写完报销单的时候我们就提交了,这个时候他就到了第一个tasknode这个节点了。然后他审批完了还要交给总经理审批,那么他又是一个tasknode,然后总经理审批完了结束,ok,是一个end。
start--》tasknode(部门主管审批)--》tasknode(总经理审批)--》end。
如果觉得看的有点模糊可以看看我传上来的那个图。然后你在这个试图框的下面可以看到有个source,点击一下,就会发现他已经自动的给你生成xml代码了。但是这样还是有点不够,我们只是定义了一个tasknode节点,并没有定义tasknode节点的任务由谁来做。那么我们还要定义一个tasknode节点是由谁来做的:这里可以通过
2.2 流程的程序接口说明
2.2.1动作处理接口(ActioinHandler)
接口方法:
void execute( ExecutionContext executionContext ) throws Exception
该接口是jPDL中最常用的一个回调接口。从它的接口方法可以发现,它仅仅暴露了流程执行上下文变量ExecutionContext。用户程序通过ExecutionContext来了解流程的执行状态,并通过改变ExecutionContext中的属性值来影响流程的执行。
ActioinHandler接口可以在所有能包含事件(event)、动作(action)元素的地方被回调。
2.2.2判定处理接口(DecisionHandlder)
接口方法:String decide(ExecutionContext executionContext) throws Exception
判定接口仅适用于判定节点(decision)中。从它的接口方法可以看出,方法要返回一个字符串型的结果,这个结果必须和判定节点拥有的转向(transition)集合中的一条转向名称相匹配。在DecisionHandlder的接口方法中一样能访问到ExecutionContext变量,这为判定提供了执行上下文的根据。当然,如果有必要,用户也可以在该接口中改变ExecutionContext中的变量值。
2.2.3委派处理接口(AssignmentHandler)
接口方法:void assign(Assignable assignable, ExecutionContext executionContext) throws Exception;
委派处理接口被用户任务元素(task)的委派(assignment)子元素中,它的职责很明确,就是将任务分配给指定的人员或角色。
在AssignmentHandler接口的方法中,Assignable变量通常指任务实例(TaskInstance)。通过将ExecutionContext和TaskInstance两个变量都暴露给接口方法,用户就可以根据流程上下文情况,来决定要将指定的任务分配个谁。
2.3 创建jBPM的数据库表
创建jbpm数据库表。他就像hibernate里面的哪个export一样。实际上他就是hibernate里面的哪个export。因为他映射了很多个表,所以我们就能创建那么多个表。
JbpmConfiguration.getInstance().createSchema();
我们并不知道articl表中的Auditstate是怎么变化的啊?
在Struts的action中并没有看见显示的代码调用来修改数据库Auditstate字段啊,难道是JBPM自动做的处理?
当然不是!不过我们可以让JBPM帮助我们来完成。
你注意到了processdefinition.xml配置文件吗?
没错,就是
当一个流程中一个state执行完,需要transition 到下一个State时,JBPM将会自动执行class指定的句柄。
public class PubActionHandler implements ActionHandler {
private static final long serialVersionUID = 1L;
public void execute(ExecutionContext context) throws Exception {
//得到对应实例ID
ProcessInstance processInstance = context.getContextInstance().getProcessInstance();
//得到当前执行转换
Transition transition =
context.getTransition();
Node node = transition.getTo();
//得到对应的文章
ArticleService articleService = (ArticleService)BeanFactory.getBean("articleService");
List list = articleService.getArticlesByPdInstance(processInstance.getId());
//设置文章状态为发布中
if(list != null){
for(int i=0; i
if(article.getState() != null && article.getState().intValue() == Article.EDITED){
article.setState(new Integer(Article.PUBLISH));
article.setAuditState(node.getName());
articleService.modArticle(article);
}
}
}
}
}
由此,可以得知,JBPM中句柄是怎么在流程运作的过程中对业务数据做出处理的吧!这也正是JBPM句柄的作用之所在!
下面来说说项目中是怎么巧妙的将业务和JBPM流程结合使用的吧。
我们来看看业务表article的结构,
article CREATE TABLE `article` (
`ArticleNo` int(11) NOT NULL auto_increment COMMENT '文章号',
`UserNo` int(11) default NULL COMMENT '用户号',
`TypeNo` int(11) default NULL COMMENT '文章类型号',
`ArticleName` varchar(128) default NULL COMMENT '文章名称',
`Content` text COMMENT '文章内容',
`PiId` bigint(20) default NULL COMMENT '对应流程实例号',
`AuditState` varchar(64) default NULL COMMENT '审批状态',
`AuditComment` varchar(255) default NULL COMMENT '审批说明',
`State` int(11) default NULL COMMENT '文章状态',
PRIMARY KEY (`ArticleNo`),
KEY `FK_Relationship_1` (`TypeNo`),
KEY `FK_Relationship_2` (`UserNo`),
CONSTRAINT `FK_Relationship_1` FOREIGN KEY (`TypeNo`) REFERENCES `articletype` (`TypeNo`),
CONSTRAINT `FK_Relationship_2` FOREIGN KEY (`UserNo`) REFERENCES `user` (`UserNo`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=gb2312 COMMENT='文章表
不然看出,恰恰是表中的 `PiId` bigint(20) default NULL COMMENT '对应流程实例号字段,完美的和流程的ID映射起来,使得一篇文章绑定到一个具体的流程实例。并且文章的状态在流程的运作当中利用JBPM句柄ActionHandler动态变化。最终实现业务的运作。
2.4 jBDL流程元素表
名称 类型 数量 描述
name 属性 可选的 流程的名称。
swimlane 元素 [0..*] 流程中使用的泳道。泳道表示流程角色,它们被用于任务分配。
start-state 元素 [0..1] 流程起始状态。注意,没有起始状态的流程是合法的,但是不能被执行。
end-state|state|node|task-node|process-state|super-state|fork|join|decision 元素 [0..*] 流程定义的节点。注意,没有节点的流程是合法的,但是不能被执行。
event 元素 [0..*] 作为一个容器服务于动作的流程事件。
action|script|create-timer|cancel-timer 元素 [0..*] 全局定义的的动作,可以在事件和转换中引用。注意,为了被引用,这些动作必须指定名称。
task 元素 [0..*] 全局定义的任务,可以
在动作中使用。
exception-handler 元素 [0..*] 一个异常处理器列表,用于这个流程定义中的委托类所抛出的所有异常。
注:更加具体的内容请参考《JBoss jBPM jPDL用户开发手册》和《jbpm开发指南》
3 jBPM开发整理
3.1 概述
jBPM是一个工作流平台,它实现了一系列的基础类,提供了面向图形编程的方式来定义流程,方便用户在此基础上专注业务流程逻辑的实现;他依赖很少的库文件,能很好地嵌入到已有的Java Project当中。
一般的开发流程为:
1.选定数据库,对数据库进行初始化;jBPM平台本身需要数据库的支持,所以在使用jBPM之前要先初始化相应的数据库,包括创建表和插入初始化数据等。
2.使用JBPM定义流程,生成ProcessDefinition.xml;并加入自定义的处理代码,如Action等。
3.利用JUnit Framework写单元测试。
4.部署jBPM。
3.2 环境配置
3.2.1 Eclipse
为了支持图形化的流程定义,安装jBPM图形化插件,位置为:
\jbpm-starters-kit-3.1.1\jbpm-designer\jbpm-gpd-feature\eclipse
3.2.2 Library
1.JBPM 3 要求 J2SE 1.4.2 +
2.JBPM库:
jBPM核心功能库:jbpm-[version].jar (位于jbpm/build下)
jBPM可选的身份验证库:jbpm-[version].jar(位于jbpm/build下)
3.必须的第三方库:(位于jbpm/lib下)
XML分析库:dom4j-1.6.1.jar
Logging库:commons-logging.jar
4.可选的第三方库:
见User Guide Chapter 5
3.2.3 Configuration Files
Table 2-1 Configuration Files
路径 作用描述
src/config.files/jbpm.cfg.xml 配置jBPM参数
src/config.files/hibernate.cfg.xml 配置Hibernate连接参数等
org/jbpm/db/hibernate.queries.hbm.xml 定义了Hibernate的常用查询Query
org/jbpm/graph/node/node.types.xml Node类型和对应实现类的映射
org/jbpm/graph/action/action.types.xml Action类型和对应实现类的映射
org/jbpm/calendar/jbpm.business.calendar.properties 定义了Business Hour和Free Time
org/jbpm/context/exe/jbpm.varmapping.xml 定义了使用何种类完成某种Java对象转换为jBPM中可存储的数据库实例
org/jbpm/db/hibernate/jbpm.converter.properties 定义了ID和Class Name的映射
org/jbpm/graph/def/jbpm.default.modules.properties 定义了创建ProcessDefinition时,缺省加载的模块
org/jbpm/jpdl/par/jbpm.parsers.xml 定义了分析器
3.3 开发步骤
3.3.1创建jBPM项目
1.在Eclipse中创建一个Process Project项目
3.3.2配置数据库
1.下载PostgreSQL、pgAdmin和dbVisualizer
Account:qlisguo/leesen;SuperUser:postgres/leesen127
2.创建JbpmDB数据库;
3.运行jbpm-db/build/postgresql/scripts/postgresql.create.sql,为jBPM创建初始化的表、索引等;
4.可选:为了让jBPMStarterKit自带的WebApp能运行,必须对jbpm_id_user初始化,加入一些帐号;
5.可选:为了让jBPMStarterKit自带的WebApp运行,必须修改已部署的JBoss
的配置文件,配置数据源;
6.修改jbpm 项目中Hibernate的数据库连接配置文件Hibernate.cfg.xml;
3.3.3 流程定义
1.在Processes目录下创建一个ProcessDefinition Project;
2.利用图形化界面对ProcessDefinition.xml进行业务逻辑流程的定义;
3.3.4 JUnit测试
1.src/java.jbpm.test 是StarterKit 自带的测试用例
2.继承junit.framework.TestCase类
3.jBPM工作流的测试流程
3.5 部署
4 jBPM APIs整理
4.1 ProcessDefinition
一个ProcessDefinition即是一张有向图,由Node和Transition完成流程的执行。ProcessDefinition中里面包含Node,Tasks,Swimlane,Transition等的定义,可以利用getXXX方法获得其中的元素。
ProcessDefinition processDefinition = ProcessDefinition.parseXmlString(
"
"
"
"
"
"
"
);
4.2 Configuration、Context
jbpmConfiguration jbpmConfiguration = JbpmConfiguration.parseResource("org/jbpm/jbpm.test.cfg.xml"); //获取jbpm配置,JbpmConfiguration是jbpm的核心
//jbpm上下文,用来处理与jbpm数据库的交互
jbpmContext = getJbpmConfiguration().createJbpmContext();从jbpmContext中可以获得所有的session,例如getGraphSession()…一个JbpmConfiguration能够用于系统中的所有线程。
Jbpm-context上下文机制把JBPM核心引擎和jbpm使用的服务从环境中分离开来。有五个常用的jbpm服务:
4.3 ContextInstance上下文实例
org.jbpm.module.exe.ModuleInstance.ContextInstance类负责对上下文变量进行操作。
获取一个ContextInstance:
ContextInstance contextInstance = (ContextInstance) processInstance.getInstance(ContextInstance.class);
上下文变量跨越整个流程的生命周期
Variables的 持久化是伴随JbpmContext的save(ProcessInstance)方法的,因为Variables是ProcessInstance的一 部分;如果有不想持久化的Variable的话就利用ContextInstance.setTransientVariable(),创建临时变量。从流程实例中取得同流程变量一起工作的上下文实例。其中的流程变量可以通过显式的API被用户代码来访问。流程变量也可以作为流程实例的一部分存储早数据库中。
4.4 org.jbpm.context.log变量日志管理
记录的Variable的更新,删除,创
建等日志
4.5 Task任务管理
TaskMgmtDefinition管理Task所和Swimlane
taskMgmtDefinition = processDefinition.getTaskMgmtDefinition();
processDefinition = new ProcessDefinition();
taskMgmtDefinition = new TaskMgmtDefinition();
processDefinition.addDefinition(taskMgmtDefinition);
buyer = new Swimlane("buyer");
laundry = new Task("laundry");
dishes = new Task("dishes");
TaskNode Task TaskController VariableAccess 层次关系。
TaskController.submitParameters()方法,可以把TaskInstance的Task Variable新值更新到对应的Process Variable;
TaskMgmtInstance运行时的任务管理
TaskMgmtInstance tmi = (TaskMgmtInstance) processInstance.getInstance(TaskMgmtInstance.class);
TaskInstance可以调用set/getVariable()方法,对Task的Local变量进行存取。(任务上下文变量)
TaskMgmtInstance.createStartTaskInstance(),创建start state 中的任务,并把该任务分派给当前的Actor。
4.6 Swimlane的整理
Swimlane是task的role。Tasks可以分配给swimlane,同时users、group也可以被指派到swimlane,通过 swimlane这作桥梁,tasks就被分配给了特定的users。 需要注意的是,swimlane不包含授权控制,即如果user A 没有通过swimlane分配到task A,user A仍然可以通过 task.end()来结束任务。
4.7 Token的整理
1.当创建一ProcessInstance时,自动创建了一rootToken
2.当rootToken 进入Fork Node 时,就会自动根据Transition 名创建子Token(/TransitionName),而rootToken则在Fork Node处等待,通过ProcessInstance.findToken()找到子Token继续分支流程的完成,待所有的分支流程完成,则 rootToken被自动激发到 Join Node 的下一个节点。分支流程的token起始指向的Node就是ForkNode对应的 Transition to的节点。
3.Decision Node 根据指定的Decision Handle ,来决定选择哪一条transition
Token:
是一个链表结构,具有唯一的父Token和若干子Token;每个Token都包含一个Node节点指针;这意味着,Token链表表示了一条流程的执行路径。
当一个ProcessInstance被创建时,一个rootToken同时被创建。
Token.signal()方法调用 Node.leave(),Node.leave() 设置Token的当前Node为新的Node,从而让流程继续下去。其执行路进如下所示:
Token.signal(),Node.leave(),Transition.take(),Node.enter(), Node.execute()
当Token进 入一个Node时,该Node的execute方法即被调用,每个Node都可以在execute()方法中定义自己的流程执行方式。而 State 类型的Node是不会传播执行的,即当Token进入State Node时,只能通过Token.signal()方法,显式地去让流程继续。
4.8 Node
Node有 两个责任:1)执行纯java代码,比如创建Task,更新数据库等;2)负责传播执行流程:不传播(内置的Node Type中只有State Node不传播);自动选择一条Leave Transition,继续流程;创建新的执行路径即新的Token(例如Fork No
de);结束流程;更改流程执行时的结构,即Token Tree。
每种Node Type 都重载了Node 类的execute方法,其中只有State Node的execute()方法为空,即意味着当Token进入State Node时,流程即停止在此。
Node Type Description
Task Node 当流程进入该Node时,该Node会创建一系列的Tasks;然后等待这些Tasks完成;当Tasks完成之后,则会调用token.signal()方法,使流程自动执行下去。
State 要显示调用token.signal()方法,才能使流程继续
5 jBPM 数据库表整理
5.1 jbpm_action表
字段 含义
ID_ 标识 主键
class 流程动作分类
A(动作): org.jbpm.graph.def.Action
B(脚本):org.jbpm.graph.action.Script
C(创建定时器): org.jbpm.scheduler.def.CreateTimerAction
I(取消定时器): org.jbpm.scheduler.def.CancelTimerAction
NAME_ 流程动作名称
ISPROPAGATIONALLOWED_ boolean类型通常值为true
ACTIONEXPRESSION_ 动作类行为I时,表示动作执行的脚本
ISASYNC_ 该动作是否支持异步机制
REFERENCEDACTION_ 动作中引用的动作,对应JBPM_ACTION
ACTIONDELEGATION_ 动作类型为A时使用,表示动作执行的代理类,对JBPM_DELEFGATION
EVENT_ 动作中指定的事件,对应JBPM_EVENT
PROCESSDEFINITION_ 动作在流程模板id
TIMERNAME_ 定时器名称
DUEDATE_ 定时器间隔时间
REPEAT_ 定时器动作执行次数
TRANSITIONNAME 动作指定后的transition的name
TIMERACTION 定时器动作代理类,对应JBPM_ACTION
EXPRESSION_ 定时器执行表达式
EVENTINDEX_ 事件索引
EXCEPTIONHANDLER_ 异常处理类,对应JBPM_EXCEPTIONHANDLER
EXCEPTIONHANDLERINDEX 异常处理类索引
5.2.jbpm_processdefinition表
字段 含义
ID_ 标识 主键
CLASS_
NAME_ 流程定义的名字
DESCRIPTION_ 流程定义表述
VERSION_ 流程的版本
ISTERMINATIONIMPLICIT_ 是否支持强制终止流程
STARTSTATE_ 起始节点ID,在JBPM_NODE表中
5.3.jbpm_transition表
字段 含义
ID_ 流程迁移标识 主键
NAME_ 流程迁移的名字
DESCRIPTION_ 流程迁移描述
PROCESSDEFINITION_ 流程定义的ID_ 外键
FROM_ 迁移的来源,与node节点的id对应
TO_ 迁移的目的,与node节点的id对应
CONDITION_ 还不了解
FROMINDEX_ 还不了解
5.4.jbpm_ node表
字段 含义
ID_ 节点的标识 主键
CLASS_ 节点类型:
C: org.jbpm.graph.node.ProcessState
D: org.jbpm.graph.node.Decision
E: org.jbpm.graph.node.EndState
F: org.jbpm.graph.node.Fork
J: org.jbpm.graph.node.Join
K: org.jbpm.graph.node.TaskNode
N: org.jbpm.graph.def.Node
R: org.jbpm.graph.node.StartState
S: org.jbpm.graph.node.State
U: org.jbpm.graph.def.SuperState
NAME_ 节点名字
DESCRIPTION_ 节点描述
PROCESSDEFINITION_ 节点所在流程定义的ID 外键
ISASYNC_ 节点是否支持异步机制
ISAYYNCEXCL_ ???
ACTION_ 节点上的动作,对应与JBPM_ACTION表
SUPERSTATE_ 节点对应的superState的id,表明该节点属于某个SuperState
SUBPROCNAME_ 节点类型为ProcessState时使用,代表子流程定义的Name
SUBPROCESSDEFINITION_ 节点类型为ProcessState时使用,代表子流程定义的ID外键
DECISIONEXPRESSION_ 节点类型为Decision时使用,该属性表示Decision中使用的判断表达式
DECISIONDELEGATION 节点类型为Decision时使用,表明Decision对应的代理类,对应JBPM_DELEGATION
SCRIPT_ 脚本
SIGNAL_ 节点类型为Task
CREATETASKS_ 节点类型为Task
ENDTASKS_ 节点类型为Task
NODECOLLECTIONINDEX_ 节点类型为SuperState时使用 `
5.5.jbpm_delegation表:
字段 含义
ID_ 流程代理标识
CLASSNAME_ 流程代理类名称
CONFIGURATION_ 流程代理类配置信息
CONFIGTYPE_ 流程代理类配置类型
PROCESSDEFINITION_ 流程代理类所属流程定义,对应JBPM_PROCESSDEFINITION 外键
5.6.jbpm_event表:
字段 含义
ID_ 流程事件标识
EVENTTYPE_ 流程事件类型名称
TYPE_ 流程事件所在的图形节点类型
"A" :Task
"C" :ProcessState
"D" :Decision"
"E" :EndState"
"F" :Fork"
"J" :Join"
"K" :TaskNode"
"N" :Node"
"P" :ProcessDefinition"
"R" :StartState"
"S" :State"
"T" :Transition"
"U" :SuperState"
GRAPHELEMENT_ 流程事件所在的图形节点的ID
PROCESSDEFINITION_ 流程事件所属流程定义,对应JBPM_PROCESSDEFINITION
外键
NODE_ 流程定义所属的节点,对应于JBPM_NODE
外键
TRANSITION_ 流程事件所属迁移,对应于JBPM_TRANSITION,外键
TASK_ 流程事件所属任务,对应JBPM_TASK,外键
看一个例子:
Processdefinition.xml
name="simple">
Test1Action.java
public class Test1Action implements ActionHandler{
private static final long serialVersionUID = 1L;
public void execute(ExecutionContext executionContext) throws Exception {
System.out.println("你好,我是Test1Action动作!");
}
}
Test2Action.
java
public class Test2Action implements ActionHandler{
private static final long serialVersionUID = 1L;
public void execute(ExecutionContext executionContext) throws Exception {
System.out.println("你好,我是Test2Action动作!");
}
}
SimpleProcessTest.java
public class SimpleProcessTest extends TestCase {
// 1、创建jbpm依赖的数据库
public void testCreateSchema() {
JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();
jbpmConfiguration.createSchema();
System.out.println("the database is created successfully!");
}
// 2、定义流程并将流程部署到数据库中
public void testDeployProcessDefinition() {
JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
ProcessDefinition pdf = ProcessDefinition
.parseXmlResource("simple/processdefinition.xml");
jbpmContext.deployProcessDefinition(pdf);
} finally {
if (jbpmContext != null)
jbpmContext.close();
}
System.out.println("the deploy is successful!");
}
}
我用的是mysql数据库,Hibernate配置
SimpleProcessTest运行后,我们来看一下数据库里的结果:
jbpm_processdefinition如下:
jbpm_action如下:
jbpm_transition如下:
jbpm_node如下:
Jbpm_delegation如下:
jbpm_event如下:
6 Boss3.8.2中的集成分析
6.1.集成目的
将业务逻辑和过程逻辑分开。
6.2.集成思路分析
在基础业务组件和服务层之间提供一个工作流引擎服务。工作流在业务层的上层,作用就是串接起若干基础业务,把SMS系统中原子的业务逻辑基础业务组件用工作流管理的流程逻辑衔接起来,实现整体业务过程调用。
图1
图2
上图2展示的是工单示例应用中,JBPM集成在SMS中所处的层次结构。
其中,service层就是SMS应用提供给客户端的服务,business层系统是基础业务组件。JBPM(工作流)在这两层中间,作用就是串接起若干基础业务,把SMS系统中原子的业务逻辑(基础业务组件)用工作流管理的流程逻辑衔接起来。
实际上工作流管理系统在企业级的系统中,可以应用在更高的层面上,充当信息总路线的角色
,把整个企业应用中的服务串接起来。也就是说把上图中的jBpm这一层,移到service的上面去。因为目前对工作流(JBPM)的积累还不是很多,为了尽量减少引入工作流引擎所影响的面,减少风险,这里提出的集成方案,只把JBPM局限在一个很小的范围内。不像图中画的,整个把service层和business层分开。
6.3.集成方案分析
1, 将jbpm的核心类JbpmConfiguration纳入Spring容器中管理
//这里是我们自己写的一个JbpmConfigurationFactoryBean,用于管理JbpmConfiguration同时为JbpmContext注入SessionFactory
2, 创建一个模板类(JbpmTemplate)封装Jbpm的底层操作。这个模版类的编写参照了开源工程Spring-modules-jbpm31.jar包中JbpmTemplate类。
3, 提供一个服务接口(IWorkflowDriver)供上层业务调用。
4, WorkflowDriverJbpmImp注入模版类JbpmTemplate实现接口IWorkflowDriver。
5, 上层业务接口在实现时通过注入WorkflowDriverJbpmImp来调用jbpm工作流的服务。
6.4.集成类UML分析
关键的类与接口:
包com.star.sms.workflow.jbpm.core:封装底层jbpm应用的核心包
1、 JbpmTemplate(jbpm模板类)(JbpmExecutor类似JbpmTemplate)
IJbpmCallback(jbpm回调接口,为JbpmTemplate服务的,在jbpm未调用回调接口之前为JbpmContext注入hibernate会话)
JbpmTemplate依赖于HibernateTemplate(hibernate持久化支持)和JbpmConfigurationFactoryBean (JbpmConfiguration,Jbpm配置文件)。
2、 JbpmConfigurationFactoryBean jbpm配置工厂Bean 用来生产JbpmConfiguration 依赖JbpmConfiguration、SessionFactory(jbpm的会话工厂),ObjectFactory(jbpm的对象工厂)
3、 JbpmObjectFactory
4、 JbpmFactoryLocator 历史遗留类
5、 AbstractIntegrateSupport 历史遗留类
6、 JbpmHelper jbpm帮助类
7、 StarActionHandler、StarAssignmentHandler、StarDecisionHandler 动作代理类
8、 StarActionDelegate、StarAssignmentDelegate、StarDecisionDelegate历史遗留的动作代理类
包com.star.sms.business.order.workflow 为上层提供jbpm服务的应
用程序包
1、IWorkflowDriver 工作流服务接口 (改进点:服务接口不直接依赖Jbpm的API,将对jbpm API的直接依赖转移到JbpmTemplate中)
2、WorkflowDriverJbpmImp 工作流服务接口实现
3、包..jbpm是工jbpm工作流引擎调用的应用程序。
6.5.改进的工作流集成方案研究
6.5.1 SpringModules(Spring与jbpm集成的桥梁)与JBPM的集成
xmlns:jee="/schema/jee"
xsi:schemaLocation="/schema/beans /schema/beans/spring-beans-2.0.xsd
/schema/jee /schema/jee/spring-jee-2.0.xsd">
destroy-method="close">
r-arg index="0" ref="resource.JbpmConfiguration" />
LocalJbpmConfigurationFactoryBean
最重要的就是LocalJbpmConfigurationFactoryBean了,相信Spring的用户对这样的Bean都很熟悉。根据jbpmConfiguration配置文件和提供的sessionFactory,它会创建一个可以与提供的流程定义协同工作的jBPM Configuration。sessionFactory属性可有可无,如果提供了,将会使用sessionFactory提供的session进行数据库操作。但是如果注入了sessionFactory,就可以重用Spring提供的事务管理架构,且不需要对jBPM做任何代码修改,还可以结合使用OpenSessionInView模式。
JbpmTemplate and JbpmCallback
像Spring与Hibernate,JDBC的集成有相应的Template, Callback类一样,Spring与Jbpm的集成也有JbpmTemplate和JbpmCallback,且默认提供了一些便利方法供用户使用,例如findProcessInstance,findPooledTaskInstances,saveProcessInstance,signal等。
因为JbpmTemplate继承了JbpmAccessor,父类JbpmAccessor实现了InitializingBean,所以Spring在初始化这个JbpmTemplate时会调用afterPropertiesSet方法来配置JbpmTemplate所使用到的HibernateTemplate。JbpmAccessor 还做了一些访问异常的转换操作。
这两个类协同起来要完成的事情是,如果提供了SessionFactory或者
HibernateTemplate, 在JbpmCallback里的doInJbpm方法之前注入SessionFactory提供的hibernate session。如果没有提供SessionFactory,那么持久化的操作还是交回给Jbpm。如果存在SessionFactory, session的打开,关闭就由SessionFactory管理。否则由JBPM自身的持久化逻辑来维护。
这部分逻辑可以查看JbpmTemplate的execute方法:
public Object execute(final JbpmCallback callback) {
final JbpmContext context = getContext();
try {
// use the hibernateTemplate is present and if needed
if (hibernateTemplate != null && hasPersistenceService) {
// use hibernate template
return hibernateTemplate.execute(new HibernateCallback() {
/**
*@see org.springframework.orm.hibernate3.HibernateCallback#doInHibernate(org.hibernate.Session)
*/
public Object doInHibernate(Session session) throws HibernateException, SQLException {
// inject the session in the context
context.setSession(session);
return callback.doInJbpm(context);
}
});
}
// plain callback invocation (no template w/ persistence)
return callback.doInJbpm(context);
}
catch (JbpmException ex) {
throw convertJbpmException(ex);
}
finally {
releaseContext(context);
}
}
6.5.2 JbpmTemplate的改写
// TODO