jBPM 4.4 入门指南
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
1.工作流与工作流管理系统
1.1. 工作流(Work Flow)
工作流就是工作流程的计算机模型,即将工作流程中的工作如何前后组织在一起的逻辑和规则在计算机中以恰当的模型进行表示并对其实施计算。
工作流要解决的主要问题是:为实现某个业务目标,在多个参与者之间,利用计算机,按某种预定规则自动传递文档、信息或者任务。
通俗的说,流程就是多个人在一起合作完成某件事情的步骤,把步骤变成计算机能理解的方式就是工作流。
公司内采用纸张表单,手工传递的方式,一级一级审批签字,工作效率低下,对于统计报表功能则不能实现。
而采用工作流软件,使用者只需在电脑上填写有关表单,会按照定义好的流程自动往下跑,下一级审批者将会收到相关资料,并可以根据需要修改、跟踪、管理、查询、统计、打印等,大大提高了效率,实现了知识管理,提升了公司的核心竞争力。
作为一个成熟稳定的工作流产品,不仅提供日常办公和关键业务流程智能化管理,而且能根据公司的特殊实际要求轻松方便地随时定制各种流程,并可实现不同角色不同的跟踪、查询、统计、打印等强大功能
1.2.工作流管理系统(Workflow Management System)
工作流管理系统(WfMS)的主要功能是通过计算机技术的支持去定义、执行和管理工作流,协调工作流执行过程中工作之间以及群体成员之间的信息交互。
工作流需要依靠工作流管理系统来实现。
工作流管理系统是定义、创建、执行工作流的系统,应能提供以下三个方面的功能支持:
1.定义工作流:包括具体的活动、规则等
2.运行控制功能:在运行环境中管理工作流过程,对工作流过程中的活动进行调度
3.运行交互功能:指在工作流运行中,WfMS与用户(活动的参与者)及外部应用程
序工具交互的功能。
一、定义工作流
二、执行工作流
1.3.什么地方使用工作流技术
OA中的审批流转的功能,其作用是能按照指定的流程(步骤)流转要审批的表单(就像流水线一样)。
有如下问题:
1,流程有很多。
2,不同的公司,流程的具体步骤是不一样的,有时还需要增加新流程或修改现有流程。
3,流程的管理是由系统的管理员完成的(不是程序员)。
像这样的应用,就需要使用流程管理系统了。
1.4.采用工作流管理系统的优点
1.提高系统的柔性,适应业务流程的变化
2.实现更好的业务过程控制,提高顾客服务质量
3.降低系统开发和维护成本
常见的工作流框架有:Jbpm、OSWorkflow、ActiveBPEL、YAWL等
OA(办公自动化)主要技术之一就是工作流。
2.jBPM4入门与准备
2.1. jBPM介绍
jBPM 即java Business Process Management,是基于java的业务流程管理系统,它是一组J2SE组件,可以作为J2EE应用集群部署。
jBPM是公开源代码项目。
jBPM 是市面上相当流行的一款开源工作流引擎,引擎底层基于Active Diagram模型。
作为jBoss的一个子项目,它使用了hibernate,因此可以很好的支持主流数据库。
官方主页:/jbpm
本文档对应的版本为:jBPM4.4
1,jBPM4.4使用Hibernate3.3.1作为引擎的持久框架。
2,jBPM4.4共有18张表。
2.2. jBPM3与jBPM4的区别说明
2.3. 准备环境1:安装流程设计器插件(Graphical Process Designer)
1,jBPM4.4包含了一个图形化设计流程的工具(GPD),它是eclipse插件,是用来设计jPDL 的图形化流程的,支持的版本为Eclipse3.5。
2,插件所在的路径为:install/src/gpd/jbpm-gpd-site.zip。
3,安装方法:在eclipse3.5中点击help->install new software->work with->add->Archive,选择jbpm4插件的安装包install/src/gpd/jbpm-gpd-site.zip 进行安装。
安装完成后进行重新启动Eclipse。
(在User Guide文档的2.11.2节中有详细说明)。
4,查看是否成功安装了插件:Window Preference中是否有Jboss jBPM项。
(通过实验,发现不可以使用把插件目录直接放到dropins下面的方法)
5,流程定义文件的xsd文件的路径为:JBPM_HOME/src/jpdl-4.4.xsd
2.4. 准备环境2:准备jBPM开发环境
先新建Eclipse工程(Java Project)。
2.4.1.添加jBPM的jar包
1,JBPM_HOME/jbpm.jar(核心包)
2,JBPM_HOME/lib/目录中的所有jar包,以下jar包可以不添加:hsqldb.jar, postgresql.jar, mysql-connector-java.jar, servlet-api.jar, junit.jar。
其中junit.jar一定不要添加,因为是3.8.2版本,与我们使用的junit4有冲突。
3,所使用的数据库对应的驱动的jar包。
2.4.2.添加配置文件并修改配置文件内容
1,配置文件可以从JBPM_HOME/examples/src/中拷贝:jbpm.cfg.xml、
logging.properties、jbpm.hibernate.cfg.xml、jbpm.tx.hibernate.cfg.xml。
2,修改logging.properties中的日志输出级别为W ARNING:修改配置
java.util.logging.ConsoleHandler.level=WARNING
3,修改jbpm.hibernate.cfg.xml中的数据库连接信息。
如果使用MySql,使用的方言一定要是org.hibernate.dialect.MySQL5InnoDBDialect。
(如使用MySQLDialect,就会在流程实例结束时抛异常:com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails
(`jbpm44_20101028/jbpm4_execution`, CONSTRAINT `FK_EXEC_INSTANCE`
FOREIGN KEY (`INSTANCE_`) REFERENCES `jbpm4_execution` (`DBID_`))。
)
4,创建数据库表。
使用Hibernate的自动建表,在jbpm.hibernate.cfg.xml中配置:hibernate.hbm2ddl.auto=update。
说明:如果要改变jbpm.hibernate.cfg.xml的文件名称,需要做:1、从JBPM_HOME/src/中拷贝jbpm.tx.hibernate.cfg.xml放到工程的src/下,然后进行修改。
2、修改jbpm.tx.hibernate.cfg.xml中的hibernate主配置文件的路径配置(指定的是相对于classpath的相对路径)。
3.API与实体与表的说明
3.1. 核心API
Interacting with jBPM occurs through services. The service interfaces can be obtained from the ProcessEngine which is build from a Configuration. A ProcessEngine is thread safe and can be stored in a static member field.
使用默认的配置文件生成Configuration并构建ProcessEngine:
或是使用指定的配置文件(要放到classPath下):
说明:jBPM自己有一个IOC容器。
Process engine objects defined in the configuration can also be retrieved by type (processEngine.get(Class<T>)) or by name (processEngine.get(String))。
各种Service的作用:
3.2. 实体、实体之间的关系、表(了解一下就可以)
4.系统的学习
4.1. 管理流程(定义)
所有的流程定义的操作都是使用RepositoryService实现的。
相关的两个类:Deployment与ProcessDefinition:
1,Deployment表示流程定义文档,可以添加一些文件:.jpdl.xml与.png与.class等。
2,ProcessDefinition表示流程定义对象,流程定义包含多个Activity与多个Transition,流程定义对象是由.jpdl.xml文件解析得到的。
3,jBPM4只存储流程定义文档中的文件,使用时才解析出ProcessDefinition对象。
(jBPM3也存储解析后的ProcessDefinition对象)
4.1.1.部署流程定义
4.1.1.1. 示例代码1:流程定义有关文件在classpath中
String deploymentId = processEngine.getRepositoryService() .createDeployment()
.addResourceFromClasspath("process/test.jpdl.xml")/
.addResourceFromClasspath("process/test.jpdl.xml")
.deploy();
4.1.1.2. 示例代码2:一次添加多个流程定义有关文件(要先打成
zip包)
String deploymentId = processEngine.getRepositoryService() .createDeployment()
.addResourcesFromZipInputStream(zipInputStream)
.deploy();
4.1.1.3. 说明
方法:RepositoryService.createDeployment() : NewDeployment
说明:
1,NewDeployment.addResourceFromClasspath(resource); 可以调用多次以添加多个文件。
文件重复添加也不会报错。
2,再调用.addResourceFromInputStream(resourceName, inputStream)添加一个文件(使用InputStream)
3,.addResourcesFromZipInputStream(zipInputStream)添加一个zip文件,里面有文件夹也没关系。
就相当于一次添加了多个文件
4,以上方法可以在一起调用。
5,一个文件添加多次,则都会存储,但只是一个流程定义。
6,如果name指定为中文,则key就会为一个汉字变为一个"_"。
4.1.2.查询流程定义
RepositoryService().createProcessDefinitionQuery()
说明:
1,调用.list() 查询一个列表,或调用.uniqueResult() 查询一个唯一的结果。
2,排序:.orderAsc(ProcessDefinitionQuery.PROPERTY_NAME),其中属性名为常量
要完成的功能:
1,查询所有
2,查询所有最新的版本(要先查询出所有,再自己处理,只保留最新的版本)
4.1.2.1. 示例代码1:查询所有流程定义
// 1,构建查询
ProcessDefinitionQuery pdQuery = processEngine.getRepositoryService() .createProcessDefinitionQuery()//
.orderAsc(ProcessDefinitionQuery.PROPERTY_NAME)//
.orderDesc(ProcessDefinitionQuery.PROPERTY_VERSION);
// 2,查询出总数量与数据列表
long count = pdQuery.count();
List<ProcessDefinition> list = pdQuery.page(0, 100).list();// 分页:取
出前100条记录
// 3,显示结果
System.out.println(count);
for (ProcessDefinition pd : list) {
System.out.println("id=" + pd.getId()//
+ ",deploymentId=" + pd.getDeploymentId()//
+ ",name=" + pd.getName()//
+ ",version=" + pd.getVersion()//
+ ",key=" + pd.getKey()); //
}
4.1.2.2. 示例代码2:查询所有最新版本的流程定义列表
// 1,查询出所有流程定义
List<ProcessDefinition> all = processEngine.getRepositoryService()// .createProcessDefinitionQuery()//
.list();
// 2,对结果进行过滤,不同的流程定义名称只保留最新的版本
Map<String, ProcessDefinition> map = new HashMap<String, ProcessDefinition>();
for (ProcessDefinition pd : all) {
ProcessDefinition pdInMap = map.get(pd.getName());
if (pdInMap == null) {
map.put(pd.getName(), pd);
} else {
if (pd.getVersion() > pdInMap.getVersion()) {
map.put(pd.getName(), pd);
}
}
}
Collection<ProcessDefinition> result = map.values();
// 3,显示结果
for (ProcessDefinition pd : result) {
System.out.println("id=" + pd.getId()//
+ ",deploymentId=" + pd.getDeploymentId()//
+ ",name=" + pd.getName()//
+ ",version=" + pd.getVersion()//
+ ",key=" + pd.getKey());
}
4.1.3.删除流程定义
4.1.3.1. 示例代码1:删除流程定义
repositoryService.deleteDeployment(deploymentId);
4.1.3.2. 示例代码2:删除流程定义,并删除关联的流程实例与历
史信息
repositoryService.deleteDeploymentCascade(deploymentId);
4.1.3.3. 示例代码3:删除指定名称的所有版本
// 1,查询出指定名称的所有流程定义列表
List<ProcessDefinition> list = processEngine.getRepositoryService()// .createProcessDefinitionQuery()//
.processDefinitionName("test")//
.list();
// 2,循环删除
for (ProcessDefinition pd : list) {
processEngine.getRepositoryService().deleteDeploymentCascade(pd.g etDeploymentId());
}
4.1.4.获取文件内容
4.1.4.1. 示例代码
InputStream in = repositoryService.getResourceAsStream(deploymentId, resourceName);
4.1.4.2. 说明
1,resourceName是相对路径,或是部署时指定的名称。
2,如部署时使用.addResourceFromClasspath("cn/itcast/test.jpdl.xml"),则获取时指定名称为:cn/itcast/test.jpdl.xml。
如果是zip,则是在zip中的一个相对于根的相对路径。
4.1.
5.获取某节点的坐标
repositoryService.getActivityCoordinates(processDefinitionId, activityName);
4.2. 执行流程(实例)
要使用到的Service有:ExecutionService与TaskService。
4.2.1.启动流程实例
4.2.1.1. 示例代码1:使用指定key的最新版本启动流程实例
// 流程定义的key
String processDefinitionKey = "费用报销流程";
// 启动流程实例
ProcessInstance pi = processEngine.getExecutionService()//
.startProcessInstanceByKey(processDefinitionKey);
// 显示信息
System.out.println("id=" + pi.getId()//
+ ",name=" + pi.getName()//
+ ",key=" + pi.getKey()//
+ ",state=" + pi.getState());
4.2.1.2. 示例代码2:使用指定key的最新版本启动流程实例,并
设置一些流程变量
// 流程定义的key
String processDefinitionKey = "费用报销流程";
Map<String, Object> variables = new HashMap<String, Object>(); variables.put("金额", 1000);
variables.put("申请人", "张三");
// 启动流程实例
ProcessInstance pi = processEngine.getExecutionService()//
.startProcessInstanceByKey(processDefinitionKey, variables);
// 显示信息
System.out.println("id=" + pi.getId()//
+ ",name=" + pi.getName()//
+ ",key=" + pi.getKey()//
+ ",state=" + pi.getState());
4.2.1.3. 说明
1,ExecutionService.startProcessInstanceById(processDefinitionId),使用唯一的pdId查询并启动
2,ExecutionService.startProcessInstanceById(processDefinitionId, variables),可设置变量
3,ExecutionService.startProcessInstanceByKey(processDefinitionKey),使用指定key(name)的最新的版本启动
4,ExecutionService.startProcessInstanceByKey(processDefinitionKey, variables),可设置变量
说明:流程实例创建后,直接就到开始节点后的第一个节点,不会在开始节点停留。
4.2.2.获取个人任务列表:
方式1:TaskService().findPersonalTasks(userId);
方式2,TaskService().createTaskQuery()//
.assignee(userId)//
.page(0, 1000)//
.list();
4.2.3.办理任务(完成任务)
TaskService().completeTask(taskId);
4.2.4.获取流程变量
1,TaskService.getVariable(taskId, variableName);
2,ExecutionService.getVariable(executionId, variableName);
4.2.
5.拾取任务
1,TaskService.takeTask(taskId, userId),如果任务有assignee,则会抛异常。
2,processEngine.getTaskService().assignTask(taskId, userId),转交任务给其他人,(如果任务有assignee,则执行这个方法代表重新分配。
也可以把assignee设为null表示组任务没有人办理了)
4.3. 设计流程(画流程图)
4.3.1.Transition
1,一个活动中可以指定一个或多个Transition(Start中只能有一个,End中没有)
2,如果只有一个,则可以不指定名称(名称是null)
3,如果有多个,则要分别指定唯一的名称
4,signal时,如果没有指定signalName/transitionName,则代表要使用没有名称的Transition,如果没有无名称的Transition,就报错。
5,signal时,可以传递一个参数signalName/outcome,代表使用指定的Transition离开,如果没有,就报错。
6,完成任务时,completeTask()将使用默认的Transiton,completeTask(String, String)将使用指定的Transition。
4.3.2.活动(节点)
4.3.2.1. 预定义活动
4.3.2.1.1.Start(开始活动)
用于指明流程的入口。
在一个流程定义中有且只能有一个Start活动。
在Start活动有必须且只能有一个Transition。
流程实例启动后会自动离开开始节点。
4.3.2.1.2.End(结束活动)
End、EndError、EndCancel 结束节点后两个表示不同的状态
4.3.2.1.3.Task(任务活动)
分配任务:
1,actor=#{String型的变量}
2,AssignmentHandler,需要在<task>元素中写<assignment-handler class="AssignmentHandlerImpl"/>子元素。
a)指定的类要实现AssignmentHandler接口
b)在其中可以使用Assignable.setAssignee(String),分配个人任务。
可以不指定assignee,也不会向下执行,而是等待。
4.3.2.1.4.Decision(判断活动)
1,使用expression,如:expr="#{'to state2'}"
2,使用Handler,要实现DecisionHandler接口
3,如果同时配置了expression与Handler,则expression有效,忽略Handler。
4.3.2.1.
5.Fork / Join(分支/ 聚合活动)
测试时要注意:
1,pi.findActiveActivityNames()可以使用,但要保证是最新状态,也就是要先getById一下。
2,流程实例执行完后,用ExecutionService就查不到了
4.3.2.1.6.State(状态活动)
4.3.2.2. 自定义节点:Custom
1,在<custom>元素中指定class属性为指定的类。
2,这个类要实现ExternalActivityBehaviour接口,其中有两个方法:
1,execute(ActivityExecution),节点的功能代码
2,signal(ActivityExecution, String, Map),在当前节点等待时,外部发信号时的行为3,在execute()方法中,可以调用以下方法对流程进行控制
1,ActivityExecution.waitForSignal(),在当前节点等待。
2,ActivityExecution.takeDefaultTransition(),使用默认的Transition离开,当前节点中定义的第一个为默认的。
3,ActivityExecution.take(String transitionName),使用指定的Transition离开
4,ActivityExecution.end(),结束流程实例
4,也可以实现ActivityBehaviour接口,只有一个方法execute(ActivityExecution),这样就不能等待,否则signal时会有类转换异常。
4.3.3.事件
4.3.3.1. 事件种类(有多少种事件)
4.3.3.2. 为事件配置动作(事件触发时做什么事)
1,在根元素中,或在节点元素中,使用<on event="">元素指定事件,其中event属性代表事件的类型。
2,在<on>中用子元素<event-listener class="EventListenerImpl" />,指定处理的类,要求指定的类要实现EventListener接口
3,事件类型:
a)<on>元素放在根元素(<process>)中,可以指定event为start或end,表示流程的
开始与结束。
b)<on>元素放在节点元素中,可以指定event为start或end,表示节点的进入与离开
c)在Start节点中只有end事件,在End节点中只有start事件。
d)在<transition>元素中直接写<event-listener class="">,就是配置事件。
(因为在这里
只有一个事件,所以不用写on与类型)
e)在<task>元素中还可以配置assign事件,是在分配任务时触发的。
5.附录
5.1. 常见问题说明
5.1.1.自行控制事务
1,修改jbpm.tx.hibernate.cfg.xml
f)不自行管理事务:去掉<standard-transaction-interceptor />
g)让Jbpm使用SessionFactory.getCurrentSession():修改为<hibernate-session
current="true" />
2,配置可以使用SessionFactory.getCurrentSession(),在jbpm.hibernate.cfg.xml 中配置:<property name="hibernate.current_session_context_class">thread</property>
3,要使用同一个SessionFactory,且都要使用SessionFactory.getCurrentSession() 获取Session:
h)同一个SessionFactory:SessionFactory sf = processEngine.get(SessionFactory.class)
i)在BaseDaoImpl 中增加:
i.getSession() { return HibernateUtils.getSessionFactory().getCurrentSession(); }
ii.getProcessEngine(){ return org.jbpm.api.Configuration.getProcessEngine(); }
4,统一的打开与提交或回滚事务:使用OpenSessionInViewFilter 控制事务。
5.1.2.启动Tomcat时(使用的是MyEclipse自带的Tomcat,
是 6.0的版本),报错:Caused by: ng.LinkageError: loader constraints violated when linking javax/el/ExpressionFactory class
at org.apache.jsp.WEB_erAction.loginUI_jsp._jspInit(loginUI_jsp.java:39) at org.apache.jasper.runtime.HttpJspBase.init(HttpJspBase.java:52)
at org.apache.jasper.servlet.JspServletWrapper.getServlet(JspServletWrapper.java:159)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:329)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:320)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:266)
... 40 more
说明:原因是Jbpm的juel.jar, juel-engine.jar, juel-impl.jar包和Tomcat6.0中的el-api.jar 包冲突了。
有三个解决办法:
1,方法一:在MyEclipse的Preferences -> MyEclipse -> Application Servers -> Tomcat -> ..
-> Paths 中配置Append to classpath,选中juel.jar, juel-engine.jar, juel-impl.jar 这三个jar包就可以了。
2,方法二:将juel.jar, juel-engine.jar, juel-impl.jar 这三个包复制到tomcat6下lib/ 中,并删除原来的el-api.jar,
切记还要把工程中WEB-INF\lib 下的juel.jar, juel-engine.jar, juel-impl.jar 删除,不然还是要冲突。
3,方法三:换成tomcat5.5,就没有问题了。
5.1.3.完成流程实例中的最后一个任务时(任务实例结束
时),或删除流程定义级联删除流程实例时,报错如下:
com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`itcastoa_20100909/jbpm4_execution`, CONSTRAINT `FK_EXEC_INSTANCE` FOREIGN KEY (`INSTANCE_`) REFERENCES `jbpm4_execution` (`DBID_`))
解决办法:把方言设为MySQL5InnoDBDialect,不能是MySQLDialect。
5.2. 其他说明
1,Jbpm4的所有实体的数据库中用的主键都long型。
2,各种Service中的查询为createXxQuery(),调用list()查询一个列表,或调用uniqueResult()查询一个唯一的结果
3,查询支持分页,方法为:xxQuery.count() 与xxQuery.page(int, int).list()。
4,以下都是String型:
1,deploymentId
2,processDefinitionId
3,executionId
4,taskId
jBPM4
1,可以不画结束节点
2,结束节点可以有多个。