转载来自:http://blog.csdn.net/ycjunhua/article/details/5980469
5.使用jbpm Service API 控制流程
流程运行模式:定义完流程之后,流程定义在运行时被实例化,因此我们要创建流程定义实例;当流程实例在执行中时,我们要控制和监视流程,以保证业务流程执行在监控中;当流程实例执行完毕,jbpm4会将其归档为历史流程中去,从而提高运行中流程实例的执行效率。
Service API实现的功能:
1.管理流程部署
2.管理流程实例
3.管理流程任务
4.管理流程历史
5.以及管理流程的一切
5.1流程定义.流程实例和执行的概念
流程定义:是对业务过程步骤的描述,在jbpm4中它表现为若干"活动"节点通过“转移”线条串联。
流程实例:表示流程定义在运行时特有的执行例程。
可以把流程定义理解为JavaClass定义,而流程实例可以理解为Java Class定义实例化生成Java Object对象。
信贷流程定义实例
一个流程实例在其生命周期中最典型的特征就是具有指向当前执行活动的指针------"执行"
信贷流程实例执行实例
执行在jbpm3中被称为token,在jbpm4中变为executions
流程实例支持并行执行,所以在同一个流程实例中的执行数量并非绝对为唯一,修改汇款"write money"和存档"archive"被定义并执行,那么主流流程实例就包含了两个用来跟踪状态的子执行。
信贷流程实例并行执行实例
流程实例一般可以理解为一颗执行树,当一个流程实例启动时最初的执行处于这个执行树的根节点位置,之后根据定义的需要产生子执行,即树枝。
Jbpm使用树状执行结构的原因:实际上只有一条执行路径,所以子执行终将归于(join)根执行。
5.2流程引擎API
流程引擎对象org.jbpm.api.ProcessEngine是Jbpm4所有ServiceAPI之源,所有的seviceApi都从ProcessEngine中获得
// processEngine从Configuration获得,是线程安全的,保存在静态变量中
//甚至JNDI命名服务或者其他重要位置
ProcessEngine processEngine = configuration.buildProcessEngine();
//自定义其他位置的jbpm配置文件
ProcessEngine processEngine =new Configuration.setResource("my-jbpm-confguration-file.xml").buildProcessEngine();
// Obtain the services from the process engine
//jbpm4中对外统一服务的6个service api
RepositoryService repositoryService = processEngine.getRepositoryService();
ExecutionService executionService = processEngine.getExecutionService();
TaskService taskService = processEngine.getTaskService();
HistoryService historyService = processEngine.getHistoryService();
ManagementService managementService = processEngine.getManagementService();
IdentityService identityService=processEngine.getIdentityService();
1.RepositoryService:流程资源服务接口。提供对流程定义的部署查询和删除操作。
2. ExecutionService:流程执行服务接口。提供启动流程实例.执行推进和设置流程变量等操作。
3.TaskService :流程任务服务接口。提供对任务的创建.提交.查询.保存和删除操作
4.ManagementService :流程管理控制服务接口。提供对异步工作(Job)相关的执行和查询工作
5.HistoryService :流程历史服务接口。提供对流程历史库(即已完成的流程实例归档)中历史流程实例.历史活动实例等记录的查询工作。还提供诸如某个流程定义中所有活动的平均持续时间.某个流程定义中某转移的经过次数等数据分析服务
6.IdentityService 身份认证服务接口。提供对流程用户.用户组以及组成员关系的相关服务。
public class AbstractServiceImpl {
protected CommandService commandService;
public CommandService getCommandService() {
return commandService;
}
public void setCommandService(CommandService commandService) {
this.commandService = commandService;
}
}
CommandService 是Command模式的服务接口,是将客户端的请求全部封装在一个调用接口中,然后由这个接口去调用org.jbpm.api.cmd.Command接口的众多实现。
*jbpm4 Sevice API的实现广泛地采用了Command设计模式,何谓Command模式?Jbpm4为什么要采用Command模式?
抽象出待执行的动作以参数化某对象,您可用面向过程语言中的回调(callback)函数表达这种参数化机制。所谓回调函数是指函数现在某处注册,而它将在稍后某个需要的时候被调用。可以说Command模式是回调机制的一个面向对象的替代品。
Command模式的目的即在不同的时刻指定.排列和执行请求。一个Command对象可以有一个与初始化请求无关的生存期。如果一个请求的接受者可用一种与地址空间无关的方式表达,那么就可将负责该请求的命令对象传递给另一个不同的进程并在那儿实现该请求。
Command模式的优势在于:
支持取消操作。
支持修改日志。
用构建在原语操作的高层操作构建一个系统。
5.3利用API部署流程
//使用 repositoryService提供的API方法从classpath中部署流程定义
deploymentId = repositoryService.createDeployment()
.addResourceFromClasspath(
"org/jbpm/examples/services/Order.jpdl.xml").deploy();
//当然在这里可以多次调用addResourceFromClasspath方法,将流程定义的其他资源都将部署到数据库中
通过addResourceFromXXX系列方法,流程定义XML内容可以从文件,WebURL.字符串.输入流或Zip流中获取。每次部署的资源的内容都是字节数组的形式。jPDL流程定义文件以扩展名.jpdl.xml被识别。其他资源文件包括任务表单.Java类和脚本等。如果不竟要部署.jpdl.xml流程定义文件,而且要部署一系列流程定义资源,则可以以流程定义归档的方式部署,流程引擎会自动识别归档中扩展名为.jpdl.xml文件为流程定义文件。
<process name="Order" xmlns="http://jbpm.org/4.3/jpdl">
process>
属性名称 |
属性值 |
来源 |
Name |
Order |
流程定义语言 |
Key |
Order(把不是字母或数字替换成下划线) |
系统生成 |
Version |
1(版本号自动递增) |
系统生成 |
Id |
Order -1 |
系统生成 |
5.4通过API删除已部署的流程
repositoryService.deleteDeploymentCascade(deploymentId);
这个方法是物理上的删除会在数据库中彻底销毁这条流程定义的记录
级联删除使用deleteDeploymentCascade方法
5.5使用API发起新的流程实例
5.5.1发起流程的常规方法
ProcessInstance processInstance=executionService.startProcessInstanceByKey("order");
startProcessInstanceByKey方法会去查找key为order的最新版本的流程定义,然后根据最新版本的流程定义启动流程实例。也可以通过id来发起startProcessInstanceById
5.5.2指定业务键发起流程
ProcessInstance processInstance=executionService.startProcessInstanceByKey("order",“Order0098”);
业务键是用户执行流程的时候根据业务定义的。一个业务键必须在流程定义所有的版本的流程实例范围内都是唯一的。提供一个业务键的好处为可以根据业务来执行流程实例搜索
5.5.3根据变量发起流程实例
//创建并填充流程变量
Map
variables.put("customerName", "Alex Miller");
variables.put("type", "Accident");
variables.put("amount", new Float(763.74));
//传入Map,带着流程变量发起例程实例
ProcessInstance processInstance=executionService.startProcessInstanceByKey(
5.6唤醒一个等待的执行
当流程执行进入state活动时,执行会在到达state活动的时候进入等待状态-wait satate,这是jbpm的一个重要概念,task等活动也会陷入等待状态,直到signal(可以理解一个“外部触发信号”)出现,才能进入下一个步骤的活动。ExecutionService的signalExecution方法可以用来发出signal这个方法传入执行对象。
获取正确的执行比较好的实践是给state活动分配一个事件监听器,定义如下:
<state name="receive confirmation" g="96,16,136,52">
....
state>
在监听器StartExternalWork类中,,可以执行那些需要在state活动完成的工作。在这个事件监听器中通过executionId=execution.getId();获得正确的执行id,,在state活动的工作完成之后,可以用它发出signal离开该活动。
executionService.signalExecutionById(executionId);
//以下代码假设我们知道当前活动的名称,用它来开启活动
ProcessInstance processInstance=executionService.startProcessInstanceById(processDefinitionId);
//或
//ProcessInstance processInstance=executionService.signalExecutionById(executionId);
//如上述假设,我们知道当前流程实例为"external work"的活动中等待
Execution execution=processInstance.findActiveExecutionIn("external work");
//获取执行ID
String executionId=execution.getId();
5.7任务服务API
TaskService的主要目的是提供对任务列表的访问操作,这里的任务是指Jbpm task活动产生的人机交互业务。
//获得Id为alexmiller的任务列表
List<Task> taskList=taskService.findPersonalTasks("alexmiller");
//读取任务变量
long taskId=task.getId();
Set
//读取任务变量
HashMap
//或自行创建variableNames=new HashMap
//设置"键-值"形式的任务变量
variables.put("ctegory", "sma11");
variables.put("lires", 9332323);
//将变量存入任务
taskService.setVariables(taskId, variables);
//TaskService完成任务的四种方式:
//根据指定的任务ID完成任务
taskService.completeTask(taskId);
//根据指定的任务ID完成任务,同时设入变量,完成任务
taskService.completeTask(taskId,variableNames);
//指定outcome,即系一部的转移路径,完成任务
taskService.completeTask(taskId,outcome);
//指定下一步的转移路径,同时设入变量,完成任务
taskService.completeTask(taskId,outcome,variableNames);
Outcome这个参数可以用来决定任务完成后流程流向那个流出“转移”。完成任务后,流程将“何去何从”遵循如下的规则:
1>如果任务拥有一个没有名称的流出转移:
A>taskService.getOutcomes(taskId)返回包含一个null值的集合
B>taskService.completeTask(taskId)会经过这个流出转移
C>taskService.completeTask(taskId,null)会通过这个流出转移
D>taskService.completeTask(taskId,"anyvalue")会抛出一个异常
2>如果任务拥有一个已命名为myName的流出转移
A>taskService.getOutcomes(taskId)返回包含这个流出转移的名称集合
B>taskService.completeTask(taskId)会经过这个流出转移
C>taskService.completeTask(taskId,null)会抛出一个异常。因为此任务没有无名称的流出转移。
D>taskService.completeTask(taskId,"anyvalue")会抛出一个异常
E>taskService.completeTask(taskId,"myName")会经过这个流出转移
3>如果任务拥有多个流出转移,而其中一个没有名称,其他都有名称。
A>taskService.getOutcomes(taskId)返回包含一个null值和其他流出转移的名称集合
B>taskService.completeTask(taskId)会经过没有名称的流出转移
C>taskService.completeTask(taskId,null)会经过没有名称的流出转移
D>taskService.completeTask(taskId,"anyvalue")会抛出一个异常
E>taskService.completeTask(taskId,"myName")会经过名称为myName的流出转移(我们假设myName存在有名称的流出转移中)
4>如果任务拥有多个流出转移,且每个流出转移都拥有唯一的名称。
A>taskService.getOutcomes(taskId)包含所有流出转移名称的集合 B>taskService.completeTask(taskId)会抛出一个异常。因为没有无名称的流出转移
C>taskService.completeTask(taskId,null)会抛出一个异常。因为没有无名称的流出转移
D>taskService.completeTask(taskId,"anyvalue")会抛出一个异常
E>taskService.completeTask(taskId,"myName")会经过名称为myName的流出转移(我们假设myName存在有名称的流出转移中)
任务可以拥有多个候选人,候选人可以是单个用户也可以是用户组。用户可以接收候选人是自己的任务,接受任务的意思是用户被流程引擎设置为任务的分配者。接收任务是个”排他“操作,因此在任务被”接收-分配“之后,其他的用户就不能接收并办理此任务。用户接收任务后,一般需要客户端应用程序界面显示任务表单,并引导用户完成任务。对于有候选人,但是还没有被分配的任务,唯一应该暴露给用户的操作是”接受任务“
5.8历史服务API
在流程实例执行的流程中,会不断触发事件,通过这些事件,已完成流程实例的历史信息会被征集到流程历史数据库中。而HistoryServcieAPI提供了对这些历史信息的访问服务。
//查找特定流程定义的所有历史流程实例
List<HistoryProcessInstance> historyProcessInstances=historyService.createHistoryDetailQuery()
//查询Id为“ICL-1”
.processDefinitionId("ICL-1")
//返回的结果集按开始时间正序排列
.oderAsc(HistoryProcessInstanceQuery.PROPERTY_STARTTIME)
.list();
//历史的活动实例被HistoryActivtyInstance查询历史列表
List<HistoryActivityInstance> historyActInsts=historyService
.createHistoryActivityInstanceQuery()
//查询ID为“ICL-1”的流程定义
.processDefinitionId("ICL-1")
.activityName("a")
.list();
avgDurtionPerActivity--获取指定流程定义中每个活动的平均执行时间
choiceDistribution---获取指定活动定义每个转移的经过次数
5.9管理服务API
ManagementService即管理服务,通常用来管理Job,在jbpm4 web控制台等客户端应用上被调用
//提供以下两个方法
//执行指定ID的Job
Void execteJob(String jobId);
//获取Job查询接口
JobQuery createJobQurey();
/**
*JobQuery
*/
public interface JobQuery {
/** duedate property to be used as property in {@link #orderAsc(String)} and {@link #orderDesc(String)} */
public static final String PROPERTY_DUEDATE = "duedate";
/** state property to be used as property in {@link #orderAsc(String)} and {@link #orderDesc(String)} */
public static final String PROPERTY_STATE = "state";
/** 查询所有的消息型Job */
JobQuery messages();
/** 查询所有的定时器Job */
JobQuery timers();
/** 查询属于指定流程实例的Job*/
JobQuery processInstanceId(String processInstanceId);
/** 查询由于异常回滚而产生的Job */
JobQuery exception(boolean hasException);
/** 将查询结果根据指定属性正序排列*/
JobQuery orderAsc(String property);
/** 将查询结果根据指定属性倒序排列 */
JobQuery orderDesc(String property);
/**用于支持查询分页*/
JobQuery page(int firstResult, int maxResults);
/** 执行查询返回Job列表 */
List
/** 执行查询,返回单个Job */
Job uniqueResult();
/**执行查询,放回结果集数量,相当于执行SQL的”count(*)“操作 */
long count();
}
5.10查询服务API
查询服务API是基于主要的jBPM概念实体上创建查询对象来实现的,这个概念的实体包括流程实例.任务.流程历史
//对流程实例的查询
//对流程实例的查询
List
//获取流程实例查询对象
.createProcessInstanceQuery()
//指定流程定义Id
.processDefinitionId("my_process_defintion")
//设定“为挂起”为过渡条件
.notSuspended()
//分页
.page(0, 50)
//查询执行,获得结果列表
.list();
//上面代码放回指定流程定义的所有未挂起的流程,结果集支持分页,获取前50条记录
//对于任务的查询也可以使用类似的查询对象
List
//获取任务查询对象
.createTaskQuery()
//指定流程定义Id
.processInstanceId(piId)
//分配给Alex任务
.assignee("Alex")
//分页
.page(100, 120)
//根据日期逆向排序
.orderDesc(TaskQuery.PROPERTY_DUEDATE)
//查询执行,获得结果列表
.list();
5.11例程:利用jbpm service api完成流程实例
本例程演示如何利用jbpm service api基于一个简单的流程定义,发起,执行.完成整个流程实例并查询该流程实例的历史记录。
首先,流程定义如下
对应的jpdl
xml version="1.0" encoding="UTF-8"?>
<process name="process" xmlns="http://jbpm.org/4.3/jpdl">
<start g="94,16,48,48" name="start">
<transition g="-53,-17" name="to state" to="state"/>
start>
<state g="72,96,92,52" name="state">
<transition g="-47,-17" name="to task" to="task"/>
state>
<task assignee="Alex" g="72,180,92,52" name="task">
<transition g="-41,-17" name="to end" to="end"/>
task>
<end g="97,262,205,117" name="end"/>
process>
单元测试:
单元测试中执行流程定义部署的setUp方法和删除流程定义部署的tearDown方法将不再列出
public class Test extends JbpmTestCase {
String deploymentId;
protected void setUp() throws Exception {
super.setUp();
deploymentId = repositoryService.createDeployment()
.addResourceFromClasspath(
"com/examples/jbpm4/n2_5_11/process.jpdl.xml").deploy();
}
protected void tearDown() throws Exception {
repositoryService.deleteDeploymentCascade(deploymentId);
super.tearDown();
}
public void test() {
//使用执行服务,根据已部署流程定义的名称process,发起流程实例
ProcessInstance processInstance = executionService
.startProcessInstanceByKey("process");
//获取流程id
String pid = processInstance.getId();
//获取当前活动的执行对象
Execution executionInState = processInstance
.findActiveExecutionIn("state");
//assertXXX系列方法是JbpmTestCase为单元测试专门提供的断言类方法
//断言当前活动即为state
assertNotNull(executionInState);
//使用执行服务,发出执行信号结束当前活动,继续流程的执行
executionService.signalExecutionById(executionInState.getId());
//使用执行服务:从持久化层中获取“最新”的流程实例对象
processInstance = executionService.findProcessInstanceById(pid);
//获取当前对象的执行对象
Execution executionInTask = processInstance
.findActiveExecutionIn("task");
//断言当前活动即为task
assertNotNull(executionInTask);
//使用任务服务:获取用户的alex的任务,即task活动产生的任务
Task task = taskService.findPersonalTasks("Alex").get(0);
//使用任务服务:完成任务
taskService.completeTask(task.getId());
//使用任务服务:创建历史任务查询
HistoryTask historyTask = historyService.createHistoryTaskQuery()
.taskId(task.getId()).uniqueResult();
//断言上一步完成的任务已成为历史,即通过历史任务查询获取之
assertNotNull(historyTask);
//断言该流程实例已经结束
assertProcessInstanceEnded(pid);
//使用历史服务:创建历史实例查询
HistoryProcessInstance historyProcInst = historyService
.createHistoryProcessInstanceQuery().processInstanceId(pid)
.uniqueResult();
//断言该流实例已经结束,即可通过历史流程实例查询获取他
assertNotNull(historyProcInst);
//单元测试至此结束
}
}