一、 JBPM4的结构特点
1.嵌入式的工作流引擎,不需要依赖特定的中间件或服务器,减少了硬件和软件的绑定,完全支持嵌入式应用的
业务流程开发框架,可以在事务处理、数据持久化等各个方面与业务应用程序进行灵活的集成。
2.可拔插的体系架构,采用模块化的架构设计,采用了IOC的设计理念,各模块之间可以比较方便地解除耦合或
替换不同的实现,例如持久化、事务处理、身份认证、日志服务等,都由可选模块实现。
3. 易扩展的流程语言。
二、 Jbpm4的安装配置
1.下载地址:http://sourceforge.net/projects/jbpm/
2.解压资源包,进入目录install,在控制台下运行脚本:ant demo.setup.tomcat。会执行如下操作:
1)下载安装Tomcat.
2)安装HSQLDB,并创建数据表结构。
3)启动Tomcat,创建examples.bar业务流程归档,并发布到JBPM数据库中,初始化相关用户和组。
4)下载安装Eclipse,并启动Eclipse.
5)安装JBPM Web控制台。
6)安装Signavio Web 设计器。
3.在Eclipse中安装GPD插件,利用eclipse的软件升级指定GPD安装文件,文件为
下载资源包中install/src/gpd/jbpm-gpd-site.zip。
4.添加jbdl4 Schema检验,在eclipse中配置schema,指定jbpm4安装目录下src文件夹中jpdl.xsd文件。
步骤为:Window->Preferences->XML->XML CataLog->Add->File System。
三、 Jbpm流程API
1.流程相关概念
流程定义:对业务过程步骤的描述,表现为若干"活动"节点通过"转移"线条串联。
流程实例:表示流程定义在运行时特有的执行例程。
流程执行:流程实例在其生命周期中,指向当前执行活动的指针。
2.流程的6个Service API,可通过流程引擎对象的接口方法获取。
ProcessEngine processEngine = Configuration.getProcessEngine();
1)RepositoryService,流程资源服务的接口。提供对流程定义的部署、查询、删除等操作。
2)ExecutionService,流程执行服务的接口。提供启动流程实例、“执行”推进、设置流程变量等操作。
3)TaskService,人工任务服务的接口。提供对任务的创建、提交、查询、保存、删除等操作。
4)HistoryService,流程历史服务的接口。提供对流程历史库中历史流程实例、历史活动实例等记录的查询操作。
5)IdentityService,身份认证服务的接口。提供对流程用户、用户组以及组成员关系的相关服务。
6)ManagementService,流程管理控制服务的接口。提供异步工作(Job)相关的执行和查询操作。
3.流程的布署和删除
1)流程的布署
- String deploymentId = repositoryService.createDeployment()
- .addResourceFromClasspath("org/jbpm/examples/task/assignee/process.jpdl.xml") .deploy();
- // 可多次调用addResourceFromClasspath方法部署其它流程定义
String deploymentId = repositoryService.createDeployment()
.addResourceFromClasspath("org/jbpm/examples/task/assignee/process.jpdl.xml") .deploy();
// 可多次调用addResourceFromClasspath方法部署其它流程定义
2)流程的删除
repositoryService.deleteDeploymentCascade(deploymentId);
4.发起流程实例
1)流程Key
ProcessInstance processInstance = executionService.startProcessInstanceByKey("ICL");
2)流程Id
ProcessInstance processInstance = executionService.startProcessInstanceByKey("ICL-1");
3)根据业务键指定流程实例ID
- ProcessInstance processInstance = executionService.startProcessInstanceByKey ("ICL",“Order09278”);
ProcessInstance processInstance = executionService.startProcessInstanceByKey ("ICL",“Order09278”);
4)传入流程变量
- ProcessInstance processInstance = executionService.startProcessInstanceByKey ("ICL", variablesMap);
ProcessInstance processInstance = executionService.startProcessInstanceByKey ("ICL", variablesMap);
5.唤醒等待状态的执行
executionService.signalExecutionById(executionId);
6.获得用户的任务列表
List<Task> taskList = taskService.findPersonalTasks("johndoe");
7.任务的完成提交
1)将用户界面的任务表单内容存入任务
taskService.setVariables(taskId,variablesMap);
2)根据任务ID完成任务
taskService.completeTask(taskId);
3)根据任务ID完成任务,同时设入变量
taskService.completeTask(taskId, variablesMap);
4)根据任务ID完成任务,并指定下一步的转移路径
taskService.completeTask(taskId, outcome);
8.流程历史实例获取
1)获得流程定义的所有历史流程实例,返回结果按开始时间排序
- List<HistoryProcessInstance> historyProcessInstances = historyService
- .createHistoryProcessInstanceQuery() .processDefinitionId("ICL-1")
- .orderAsc(HistoryProcessInstanceQuery.PROPERTY_STARTTIME).list();
List<HistoryProcessInstance> historyProcessInstances = historyService
.createHistoryProcessInstanceQuery() .processDefinitionId("ICL-1")
.orderAsc(HistoryProcessInstanceQuery.PROPERTY_STARTTIME).list();
2)获得流程的历史活动实例,可指定具体名称的活动实例
- List<HistoryActivityInstance> historyActivityInstances = historyService
- .createHistoryActivityInstanceQuery().processDefinitionId("ICL-1").list();
List<HistoryActivityInstance> historyActivityInstances = historyService
.createHistoryActivityInstanceQuery().processDefinitionId("ICL-1").list();
9.查询结果分页
1)流程实例查询分页
- List<ProcessInstance> results = executionService.createProcessInstanceQuery()
- .processDefinitionId("ICL-1").page(0, 50).list();
List<ProcessInstance> results = executionService.createProcessInstanceQuery()
.processDefinitionId("ICL-1").page(0, 50).list();
2)流程任务查询的分页
- List<Task> myTasks = taskService.createTaskQuery()
- .processInstanceId("ICL.Order09278") .assignee("Alex")
- .page(10, 20).list();
List<Task> myTasks = taskService.createTaskQuery()
.processInstanceId("ICL.Order09278") .assignee("Alex")
.page(10, 20).list();
四、 流程定义
1.流程控制活动
1)start,开始活动
2)state,状态活动
3)decision,判断活动
4)fork,分支活动
5)join,聚合活动
6)end,结束活动
2.State活动
Jpdl定义:
- <state name="wait for response">
- <transition name="accept" to="submit document" />
- <transition name="reject" to="try again"/>
- </state>
- <state name="submit document"/>
- <state name="try again"/>
<state name="wait for response">
<transition name="accept" to="submit document" />
<transition name="reject" to="try again"/>
</state>
<state name="submit document"/>
<state name="try again"/>
// 获取流程实例ID
- String executionId = processInstance.findActiveExecutionIn("wait for response").getId();
- // 触发accept流向
- processInstance = executionService.signalExecutionById(executionId, "accept");
String executionId = processInstance.findActiveExecutionIn("wait for response").getId();
// 触发accept流向
processInstance = executionService.signalExecutionById(executionId, "accept");
3.decision活动
1)使用condition元素判断decision活动
Jpdl:
- <!-- decision 中会运行并判断每一个transition 里的判断条件。
- 当遇到一个嵌套条件是true 或者没有设置判断条件的转移,
- 那么转移就会被运行 -->
- <decision name="evaluate document" >
- <transition to="submit document" >
- <condition expr="#{content==’good’}" />
- </transition>
- <transition to="try again">
- <condition expr="#{content==’bad’}" />
- </transition>
- <transition to="give up"/>
- </decision>
- <state name="submit document"/>
- <state name="try again"/>
- <state name="give up"/>
<!-- decision 中会运行并判断每一个transition 里的判断条件。
当遇到一个嵌套条件是true 或者没有设置判断条件的转移,
那么转移就会被运行 -->
<decision name="evaluate document" >
<transition to="submit document" >
<condition expr="#{content==’good’}" />
</transition>
<transition to="try again">
<condition expr="#{content==’bad’}" />
</transition>
<transition to="give up"/>
</decision>
<state name="submit document"/>
<state name="try again"/>
<state name="give up"/>
- Map<String, Object> variables = new HashMap<String, Object>(); variables.put("content", "good");
- // 由于传入变量为good,流向了submit document活动
- ProcessInstance processInstance = executionService
- .startProcessInstanceByKey("DecisionConditions", variables);
Map<String, Object> variables = new HashMap<String, Object>(); variables.put("content", "good");
// 由于传入变量为good,流向了submit document活动
ProcessInstance processInstance = executionService
.startProcessInstanceByKey("DecisionConditions", variables);
2)使用decision的expr属性判断decision活动。
- <!--可选择的状态结点,expr指定将被运行的指定脚本 -->
- <decision name="evaluate document" expr="#{content}" >
- <transition name="good" to="submit document"/>
- <transition name="bad" to="try again"/>
- <transition name="ugly" to="give up"/>
- </decision>
<!--可选择的状态结点,expr指定将被运行的指定脚本 -->
<decision name="evaluate document" expr="#{content}" >
<transition name="good" to="submit document"/>
<transition name="bad" to="try again"/>
<transition name="ugly" to="give up"/>
</decision>
流程执行操作同上面condition元素的操作。
3)使用decision活动的handler元素判断decision活动。
- <!-- decision handler决定处理器继承了DecisionHandler 接口的java 类,
- 决定处理器负责选择向外转移 -->
- <decision name="evaluate document" g="96,102,48,48">
- <handler class="org.jbpm.examples.decision.handler.ContentEvaluation"/>
- <transition name="good" to="submit document"/>
- <transition name="bad" to="try again"/>
- <transition name="ugly" to="give up"/>
- </decision>
<!-- decision handler决定处理器继承了DecisionHandler 接口的java 类,
决定处理器负责选择向外转移 -->
<decision name="evaluate document" g="96,102,48,48">
<handler class="org.jbpm.examples.decision.handler.ContentEvaluation"/>
<transition name="good" to="submit document"/>
<transition name="bad" to="try again"/>
<transition name="ugly" to="give up"/>
</decision>
ContentEvaluation类如下:
- public class ContentEvaluation implements DecisionHandler {
- public String decide(OpenExecution execution) {
- String content = (String) execution.getVariable("content");
- return content; }
- }
public class ContentEvaluation implements DecisionHandler {
public String decide(OpenExecution execution) {
String content = (String) execution.getVariable("content");
return content; }
}
流程执行操作同上面condition元素的操作。
Decision活动和state活动都可以实现条件流转,但二者的主要区别如下:
如果decision活动定义的流转条件没有任何一个得到满足,那么流程实例将无法进行下去,抛出异常。
而state活动在没有条件满足的条件下将流向state活动定义的第一条流出转移,从而往下流转。
因此decision活动具有更加严格的条件判断特性。
4.fork-join活动
Jpdl:
- <!-- fork活动在此产生3个并行分支,这些流程分支可以同步执行。 -->
- <fork name="fork">
- <transition to="send invoice"/>
- <transition to="load truck"/>
- <transition to="print shipping documents"/>
- </fork>
- <state name="send invoice">
- <transition to="final join"/>
- </state>
- <state name="load truck">
- <transition to="shipping join"/>
- </state>
- <state name="print shipping documents">
- <transition g="378,213:" to="shipping join"/>
- </state>
- <!--join活动为流程的合并,load truck和print shipping documents在此聚合 -->
- <join name="shipping join">
- <transition to="drive truck to destination"/>
- </join>
- <state name="drive truck to destination">
- <transition to="final join"/>
- </state>
- <!-- drive truck to destination活动和send invoice活动在此完成最终的聚合 -->
- <join name="final join">
- <transition to="end"/>
- </join>
<!-- fork活动在此产生3个并行分支,这些流程分支可以同步执行。 -->
<fork name="fork">
<transition to="send invoice"/>
<transition to="load truck"/>
<transition to="print shipping documents"/>
</fork>
<state name="send invoice">
<transition to="final join"/>
</state>
<state name="load truck">
<transition to="shipping join"/>
</state>
<state name="print shipping documents">
<transition g="378,213:" to="shipping join"/>
</state>
<!--join活动为流程的合并,load truck和print shipping documents在此聚合 -->
<join name="shipping join">
<transition to="drive truck to destination"/>
</join>
<state name="drive truck to destination">
<transition to="final join"/>
</state>
<!-- drive truck to destination活动和send invoice活动在此完成最终的聚合 -->
<join name="final join">
<transition to="end"/>
</join>
Fork活动可以使流程在一条主干上出现并行的分支,join活动则可以使流程的并行分支聚合成一条主干。
部分执行测试代码如下:
- ProcessInstance processInstance = executionService
- .startProcessInstanceByKey("ConcurrencyGraphBased");
- // 当前活动为产生的3个分支活动
- assertNotNull(processInstance.findActiveExecutionIn("send invoice"));
- assertNotNull(processInstance.findActiveExecutionIn("load truck"));
- assertNotNull(processInstance.findActiveExecutionIn("print shipping documents"));
- // 执行send invoice活动,流程会在聚合活动上等待其它分支的到来
- String sendInvoiceExecutionId = processInstance
- .findActiveExecutionIn("send invoice").getId();
- processInstance = executionService.signalExecutionById(sendInvoiceExecutionId);
5.task人工任务活动
1)、使用task活动的assignee属性进行任务分配
<task name="review" assignee="#{order.owner}">
<transition to="wait" />
</task>
Assignee属性默认会被作为EL表达式来执行,任务被分配给#{order.owner}。
- Map<String, Object> variables = new HashMap<String, Object>();
- variables.put("order", new Order("johndoe"));
- //当一个新流程实例会被创建, 把order 作为一个流程变量分配给它
- ProcessInstance processInstance = executionService
- .startProcessInstanceByKey("TaskAssignee", variables);
- //获取johndoe的任务列表
- List<Task> taskList = taskService.findPersonalTasks("johndoe");
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("order", new Order("johndoe"));
//当一个新流程实例会被创建, 把order 作为一个流程变量分配给它
ProcessInstance processInstance = executionService
.startProcessInstanceByKey("TaskAssignee", variables);
//获取johndoe的任务列表
List<Task> taskList = taskService.findPersonalTasks("johndoe");
2)任务侯选者(candidate-groups,candidate-users)
candidate-groups:一个使用逗号分隔的组id 列表,所有组内的用户将会成为这个任务的 候选人。
candidate-users: 一个使用逗号分隔的用户id 列表,所有的用户将会成为这个任务的候选人。
<task name="review" candidate-groups="sales-dept>
<transition to="wait" />
</task>
部分事例代码如下:
- // 创建sales-dept组
- dept = identityService.createGroup("sales-dept");
- // 创建用户johndoe,并加入sales-dept组
- identityService.createUser("johndoe", "John", "Doe");
- identityService.createMembership("johndoe", dept, "developer");
- // 创建用户joesmoe,并加入sales-dept组
- identityService.createUser("joesmoe", "Joe", "Smoe");
- identityService.createMembership("joesmoe", dept, "developer");
- //在流程创建后, 任务会出现在johndoe 和joesmoe 用户的分组任务列表中
- List<Task> taskList = taskService.findGroupTasks("joesmoe");
- List<Task> taskList = taskService.findGroupTasks("johndoe");
- //候选人在处理任务之前,必须先接受任务,接受任务后,就会由任务的候选者变成
- // 任务的分配者。同时,此任务会从所有候选者的任务列表中消失。
- taskService.takeTask(task.getId(), "johndoe");
// 创建sales-dept组
dept = identityService.createGroup("sales-dept");
// 创建用户johndoe,并加入sales-dept组
identityService.createUser("johndoe", "John", "Doe");
identityService.createMembership("johndoe", dept, "developer");
// 创建用户joesmoe,并加入sales-dept组
identityService.createUser("joesmoe", "Joe", "Smoe");
identityService.createMembership("joesmoe", dept, "developer");
//在流程创建后, 任务会出现在johndoe 和joesmoe 用户的分组任务列表中
List<Task> taskList = taskService.findGroupTasks("joesmoe");
List<Task> taskList = taskService.findGroupTasks("johndoe");
//候选人在处理任务之前,必须先接受任务,接受任务后,就会由任务的候选者变成
// 任务的分配者。同时,此任务会从所有候选者的任务列表中消失。
taskService.takeTask(task.getId(), "johndoe");
3)任务分配处理器(AssignmentHandler)
- <task name="review">
- <!--assignment-handler 是任务元素的一个子元素,它指定用户代码对象 -->
- <assignment-handler
- class="org.jbpm.examples.task.assignmenthandler.AssignTask">
- <field name="assignee">
- <string value="johndoe" />
- </field>
- </assignment-handler>
- <transition to="wait" />
- </task>
<task name="review">
<!--assignment-handler 是任务元素的一个子元素,它指定用户代码对象 -->
<assignment-handler
class="org.jbpm.examples.task.assignmenthandler.AssignTask">
<field name="assignee">
<string value="johndoe" />
</field>
</assignment-handler>
<transition to="wait" />
</task>
AssignTask类必须实现AssignmentHandler类,代码如下:
- //默认AssignmentHandler 实现可以使用使用流程变量
- public class AssignTask implements AssignmentHandler {
- String assignee;
- public void assign(Assignable assignable, OpenExecution execution) {
- assignable.setAssignee(assignee);
- }
- }
//默认AssignmentHandler 实现可以使用使用流程变量
public class AssignTask implements AssignmentHandler {
String assignee;
public void assign(Assignable assignable, OpenExecution execution) {
assignable.setAssignee(assignee);
}
}
4)任务泳道(Swimlanes)
泳道可以理解为流程定义的”全局用户组”,也可以被当作一个流程规则。流程定义中的多个任务需要被分配或候选给
同一个群用户,统一将这个“同一群用户”定义为“一个泳道”。
- <!—-在这里定义泳道,属全局用户组-->
- <swimlane name="sales representative" candidate-groups="sales-dept" />
- <!-- swimlane 引用一个定义在流程中的泳道 -->
- <task name="enter order data" swimlane="sales representative">
- <transition to="calculate quote"/>
- </task>
- <task name="calculate quote" swimlane="sales representative">
- </task>
<!—-在这里定义泳道,属全局用户组-->
<swimlane name="sales representative" candidate-groups="sales-dept" />
<!-- swimlane 引用一个定义在流程中的泳道 -->
<task name="enter order data" swimlane="sales representative">
<transition to="calculate quote"/>
</task>
<task name="calculate quote" swimlane="sales representative">
</task>
泳道中的用户组中的用户在接受任务后成为任务的分配者,同时泳道也会发生变化,接收任务者在流程实例中会被固化为分配者。
- taskService.takeTask(taskId, "johndoe");
- assertEquals(0, taskService.findGroupTasks("johndoe").size());
- taskList = taskService.findPersonalTasks("johndoe");
- assertEquals(1, taskList.size());
taskService.takeTask(taskId, "johndoe");
assertEquals(0, taskService.findGroupTasks("johndoe").size());
taskList = taskService.findPersonalTasks("johndoe");
assertEquals(1, taskList.size());
6.子流程活动(sub-process)
1)父子流程间的数据交换(parameter-in,parameter-out)
父流程SubProcessDocument定义JPDL:
- <process name="SubProcessDocument" xmlns="http://jbpm.org/4.4/jpdl">
- <start>
- <transition to="review" />
- </start>
- <sub-process name="review" sub-process-key="SubProcessReview">
- <parameter-in var="document" subvar="document" />
- <parameter-out var="reviewResult" subvar="result" />
- <transition to="wait" />
- </sub-process>
- <state name="wait"/>
- </process>
<process name="SubProcessDocument" xmlns="http://jbpm.org/4.4/jpdl">
<start>
<transition to="review" />
</start>
<sub-process name="review" sub-process-key="SubProcessReview">
<parameter-in var="document" subvar="document" />
<parameter-out var="reviewResult" subvar="result" />
<transition to="wait" />
</sub-process>
<state name="wait"/>
</process>
子流程SubProcessReview定义JPDL:
- <process name="SubProcessReview" xmlns="http://jbpm.org/4.4/jpdl">
- <start>
- <transition to="get approval"/>
- </start>
- <task name="get approval" assignee="johndoe">
- <transition to="end"/>
- </task>
- <end name="end"/>
- </process>
<process name="SubProcessReview" xmlns="http://jbpm.org/4.4/jpdl">
<start>
<transition to="get approval"/>
</start>
<task name="get approval" assignee="johndoe">
<transition to="end"/>
</task>
<end name="end"/>
</process>
流程变量是父子流程用来沟通的纽带。父流程在子流程启动时将自己的“父流程变量”输入子流程,
反之,子流程在结束时可以将自己的“子流程变量”返回父流程,从而实现父子流程间的数据交换。
部分事例代码如下:
- // 分别部署子流程跟父流程
- String subProcessReviewDeploymentId = repositoryService.createDeployment()
- .addResourceFromClasspath("org/jbpm/examples/subprocess/variables/SubProcessReview.jpdl.xml")
- .deploy();
- String subProcessDocumentDeploymentId = repositoryService.createDeployment()
- .addResourceFromClasspath("org/jbpm/examples/subprocess/variables/SubProcessDocument.jpdl.xml")
- .deploy();
- Map<String, Object> variables = new HashMap<String, Object>();
- variables.put("document", "This document describes how we can make more money...");
- //设置父流程的变量document ProcessInstance processInstance = executionService
- .startProcessInstanceByKey("SubProcessDocument", variables);
- List<Task> taskList = taskService.findPersonalTasks("johndoe"); Task task = taskList.get(0);
- // 父流程的变量document会被传入子流程,此处为获取子流程的变量document
- String document = (String) taskService
- .getVariable(task.getId(), "document");
- variables = new HashMap<String, Object>();
- variables.put("result", "accept");
- // 在子流程中设置流程变量result,该流程变量可在父流程中获取 taskService.setVariables(task.getId(), variables);
- taskService.completeTask(task.getId());
- processInstance = executionService.findProcessInstanceById(processInstance.getId());
- // 在父流程中获取子流程中设置的流程变量result,名称为reviewResult
- String result = (String) executionService
- .getVariable(processInstance.getId(), "reviewResult");
- assertEquals("accept", result);
// 分别部署子流程跟父流程
String subProcessReviewDeploymentId = repositoryService.createDeployment()
.addResourceFromClasspath("org/jbpm/examples/subprocess/variables/SubProcessReview.jpdl.xml")
.deploy();
String subProcessDocumentDeploymentId = repositoryService.createDeployment()
.addResourceFromClasspath("org/jbpm/examples/subprocess/variables/SubProcessDocument.jpdl.xml")
.deploy();
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("document", "This document describes how we can make more money...");
//设置父流程的变量document ProcessInstance processInstance = executionService
.startProcessInstanceByKey("SubProcessDocument", variables);
List<Task> taskList = taskService.findPersonalTasks("johndoe"); Task task = taskList.get(0);
// 父流程的变量document会被传入子流程,此处为获取子流程的变量document
String document = (String) taskService
.getVariable(task.getId(), "document");
variables = new HashMap<String, Object>();
variables.put("result", "accept");
// 在子流程中设置流程变量result,该流程变量可在父流程中获取 taskService.setVariables(task.getId(), variables);
taskService.completeTask(task.getId());
processInstance = executionService.findProcessInstanceById(processInstance.getId());
// 在父流程中获取子流程中设置的流程变量result,名称为reviewResult
String result = (String) executionService
.getVariable(processInstance.getId(), "reviewResult");
assertEquals("accept", result);
2)通过outcome属性影响父流程的流程转移
父流程SubProcessReview定义:
- <!--父流程中的outcome属性引用名称为result的子流程变量
- <sub-process name="review" sub-process-key="SubProcessReview" outcome="#{result}">
- <!—如果result值等于ok,则流向此转移-->
- <transition name="ok" to="next step" />
- <transition name="nok" to="update" />
- <transition name="reject" to="close" />
- </sub-process>
<!--父流程中的outcome属性引用名称为result的子流程变量
<sub-process name="review" sub-process-key="SubProcessReview" outcome="#{result}">
<!—如果result值等于ok,则流向此转移-->
<transition name="ok" to="next step" />
<transition name="nok" to="update" />
<transition name="reject" to="close" />
</sub-process>
子流程SubProcessReview定义:
- <start>
- <transition to="get approval"/>
- </start>
- <task name="get approval" assignee="johndoe">
- <transition to="end"/>
- </task>
- <end name="end" />
<start>
<transition to="get approval"/>
</start>
<task name="get approval" assignee="johndoe">
<transition to="end"/>
</task>
<end name="end" />
部分事例代码如下:
- ProcessInstance processInstance = executionService
- .startProcessInstanceByKey("SubProcessDocument");
- List<Task> taskList = taskService.findPersonalTasks("johndoe");
- Task task = taskList.get(0);
- Map<String, Object> variables = new HashMap<String, Object>();
- variables.put("result", "ok");
- // 在子流程中设置流程变量result值为ok,这个ok值会被传递给outcome属性以决定父流程的走向。
- taskService.setVariables(task.getId(), variables);
- taskService.completeTask(task.getId());
- processInstance = executionService.findProcessInstanceById
- (processInstance.getId());
- assertNotNull(processInstance.findActiveExecutionIn("next step"));
ProcessInstance processInstance = executionService
.startProcessInstanceByKey("SubProcessDocument");
List<Task> taskList = taskService.findPersonalTasks("johndoe");
Task task = taskList.get(0);
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("result", "ok");
// 在子流程中设置流程变量result值为ok,这个ok值会被传递给outcome属性以决定父流程的走向。
taskService.setVariables(task.getId(), variables);
taskService.completeTask(task.getId());
processInstance = executionService.findProcessInstanceById
(processInstance.getId());
assertNotNull(processInstance.findActiveExecutionIn("next step"));
3)设置不同的子流程end活动名称自动关联父流程的流出转移
父流程SubProcessReview定义:
- <sub-process name="review" sub-process-key="SubProcessReview">
- <transition name="ok" to="next step" />
- <transition name="nok" to="update" />
- <transition name="reject" to="close" />
- </sub-process> <state name="next step" />
- <state name="update" />
- <state name="close" />
<sub-process name="review" sub-process-key="SubProcessReview">
<transition name="ok" to="next step" />
<transition name="nok" to="update" />
<transition name="reject" to="close" />
</sub-process> <state name="next step" />
<state name="update" />
<state name="close" />
子流程SubProcessReview定义:
- <task assignee="johndoe" name="get approval">
- <transition name="ok" to="ok"/>
- <transition name="nok" to="nok"/>
- <transition name="reject" to="state1"/>
- </task>
- <end name="ok"/>
- <end name="nok"/>
- <end name="reject"/>
- <state name="state1" >
- <transition name="to reject" to="reject"/>
- </state>
<task assignee="johndoe" name="get approval">
<transition name="ok" to="ok"/>
<transition name="nok" to="nok"/>
<transition name="reject" to="state1"/>
</task>
<end name="ok"/>
<end name="nok"/>
<end name="reject"/>
<state name="state1" >
<transition name="to reject" to="reject"/>
</state>
部分事例代码如下:
- ProcessInstance processInstance = executionService
- .startProcessInstanceByKey("SubProcessDocument");
- List<Task> taskList = taskService.findPersonalTasks("johndoe");
- Task task = taskList.get(0);
- //子流程活动结束于ok活动返回父流程实例,父流程会自动地通过名称为ok的转移,
- //进入"next step活动" taskService.completeTask(task.getId(), "ok");
- processInstance = executionService
- .findProcessInstanceById(processInstance.getId());
- assertNotNull(processInstance.findActiveExecutionIn("next step"));
ProcessInstance processInstance = executionService
.startProcessInstanceByKey("SubProcessDocument");
List<Task> taskList = taskService.findPersonalTasks("johndoe");
Task task = taskList.get(0);
//子流程活动结束于ok活动返回父流程实例,父流程会自动地通过名称为ok的转移,
//进入"next step活动" taskService.completeTask(task.getId(), "ok");
processInstance = executionService
.findProcessInstanceById(processInstance.getId());
assertNotNull(processInstance.findActiveExecutionIn("next step"));
7.自定义活动
如果有特殊而复杂的业务需求,与其生套JBPM本身提供的流转控制活动,不如自己实现一个自定义的活动使用。
Jpdl定义:
- <!-- custom调用用户代码,实现一个自定义的活动行为 -->
- <custom name="print dots" class="org.jbpm.examples.custom.PrintDots"">
- <transition to="end" />
- </custom>
<!-- custom调用用户代码,实现一个自定义的活动行为 -->
<custom name="print dots" class="org.jbpm.examples.custom.PrintDots"">
<transition to="end" />
</custom>
自定义活动的类需实现ExternalActivityBehaviour接口
- public class PrintDots implements ExternalActivityBehaviour {
- // 在流程实例进入到此活动时执行此方法
- public void execute(ActivityExecution execution) {
- // 执行自定义的处理逻辑
- // 使流程陷入“等待”状态
- execution.waitForSignal();
- }
- // 在流程实例得到执行信号离开此活动时执行此方法
- public void signal(ActivityExecution execution, String signalName, Map<String, ?> parameters)
- { // 使流程实例进入下一步
- execution.take(signalName);
- }
- }
public class PrintDots implements ExternalActivityBehaviour {
// 在流程实例进入到此活动时执行此方法
public void execute(ActivityExecution execution) {
// 执行自定义的处理逻辑
// 使流程陷入“等待”状态
execution.waitForSignal();
}
// 在流程实例得到执行信号离开此活动时执行此方法
public void signal(ActivityExecution execution, String signalName, Map<String, ?> parameters)
{ // 使流程实例进入下一步
execution.take(signalName);
}
}
8.自动活动
自动活动是在执行过程中完全无须人工干预地编排好程序,jbpm在处理和执行这些自动活动时能把人工活动产生的数据
通过流程变量等方式与之完美结合。
1)java程序活动
jpdl定义:
- <!-- java 任务,流程处理的流向会执行 这个活动配置的方法
- class:完全类名
- method:调用的方法名
- var:返回值存储的 变量名 -->
- <java name="greet" class="org.jbpm.examples.java.JohnDoe"
- method="hello" var="answer" g="96,16,83,52">
- <!--fileld:在方法调用之前给成员变量注入 配置值 -->
- <field name="state">
- <string value="fine"/>
- </field>
- <!--arg:方法参数 -->
- <arg><string value="Hi, how are you?"/></arg>
- <transition to="shake hand" />
- </java>
- <!--expr:这个表达式返回方法被调用 产生的目标对象 ,通过对象参数传入(new Hand()-->
- <java name="shake hand" expr="#{hand}"
- method="shake" var="hand" g="215,17,99,52">
- <!-- 通过表达式引用流程变量,为shake方法提供2个参数 -->
- <arg><object expr="#{joesmoe.handshakes.force}"/></arg>
- <arg><object expr="#{joesmoe.handshakes.duration}"/></arg>
- <transition to="wait" /> </java>
<!-- java 任务,流程处理的流向会执行 这个活动配置的方法
class:完全类名
method:调用的方法名
var:返回值存储的 变量名 -->
<java name="greet" class="org.jbpm.examples.java.JohnDoe"
method="hello" var="answer" g="96,16,83,52">
<!--fileld:在方法调用之前给成员变量注入 配置值 -->
<field name="state">
<string value="fine"/>
</field>
<!--arg:方法参数 -->
<arg><string value="Hi, how are you?"/></arg>
<transition to="shake hand" />
</java>
<!--expr:这个表达式返回方法被调用 产生的目标对象 ,通过对象参数传入(new Hand()-->
<java name="shake hand" expr="#{hand}"
method="shake" var="hand" g="215,17,99,52">
<!-- 通过表达式引用流程变量,为shake方法提供2个参数 -->
<arg><object expr="#{joesmoe.handshakes.force}"/></arg>
<arg><object expr="#{joesmoe.handshakes.duration}"/></arg>
<transition to="wait" /> </java>
JohnDoe事例代码如下:
- public class JohnDoe implements Serializable {
- String state;
- public String hello(String msg) {
- if ( (msg.indexOf("how are you?")!=-1)
- ) {
- return "I'm "+state+", thank you.";
- }
- return null;
- }
- }
public class JohnDoe implements Serializable {
String state;
public String hello(String msg) {
if ( (msg.indexOf("how are you?")!=-1)
) {
return "I'm "+state+", thank you.";
}
return null;
}
}
Hand事例代码如下:
- public class Hand implements Serializable {
- private boolean isShaken;
- public Hand shake(Integer force, Integer duration) {
- if (force>3 && duration>7) {
- isShaken = true;
- }
- return this;
- }
- public boolean isShaken() {
- return isShaken;
- }
- }
public class Hand implements Serializable {
private boolean isShaken;
public Hand shake(Integer force, Integer duration) {
if (force>3 && duration>7) {
isShaken = true;
}
return this;
}
public boolean isShaken() {
return isShaken;
}
}
JoeSmoe事例代码如下:
- public class JoeSmoe implements Serializable {
- public Map<String, Integer> getHandshakes() {
- Map<String, Integer> handshakes = new HashMap<String, Integer>();
- handshakes.put("force", 5);
- handshakes.put("duration", 12);
- return handshakes;
- }
- }
public class JoeSmoe implements Serializable {
public Map<String, Integer> getHandshakes() {
Map<String, Integer> handshakes = new HashMap<String, Integer>();
handshakes.put("force", 5);
handshakes.put("duration", 12);
return handshakes;
}
}
测试代码如下:
- Map<String, Object> variables = new HashMap<String, Object>();
- variables.put("hand", new Hand());
- variables.put("joesmoe", new JoeSmoe());
- ProcessInstance processInstance = executionService.startProcessInstanceByKey("Java", variables);
- String pid = processInstance.getId();
- // 获取流程变量answer
- String answer = (String) executionService.getVariable(pid, "answer");
- assertEquals("I'm fine, thank you.", answer);
- // 获取流程变量hand
- Hand hand = (Hand) executionService.getVariable(pid, "hand");
- assertTrue(hand.isShaken());
8、JBPM自动活动
2)script脚本活动
jpdl定义:
- <!-- script 脚本活动会解析一个script 脚本。
- 任何一种符合JSR-223 规范 的脚本引擎语言都可以在这里运行。
- expr:执行表达式的文本
- var:返回值存储的 变量名
- -->
- <script name="invoke script"
- expr="Send packet to #{order.address}"
- var="text"
- g="96,16,104,52">
- <transition to="wait" />
- </script>
<!-- script 脚本活动会解析一个script 脚本。
任何一种符合JSR-223 规范 的脚本引擎语言都可以在这里运行。
expr:执行表达式的文本
var:返回值存储的 变量名
-->
<script name="invoke script"
expr="Send packet to #{order.address}"
var="text"
g="96,16,104,52">
<transition to="wait" />
</script>
测试代码如下:
- Map<String, Object> variables = new HashMap<String, Object>();
- variables.put("order", new Order("Berlin"));
- Execution execution = executionService
- .startProcessInstanceByKey("ScriptExpression", variables);
- String executionId = execution.getId();
- String text = (String) executionService.getVariable(executionId, "text");
- assertTextPresent("Send packet to Berlin", text);
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("order", new Order("Berlin"));
Execution execution = executionService
.startProcessInstanceByKey("ScriptExpression", variables);
String executionId = execution.getId();
String text = (String) executionService.getVariable(executionId, "text");
assertTextPresent("Send packet to Berlin", text);
3)hql查询
jpdl定义:
- <!-- 使用hql 活动,我们可以在database 中执行HQL query
- var:存储结果的变量名
- unique:值为true 是指从uniqueResult()方法中 获得hibernate query 的结果。
- 默认值是false。 值为false 的话会使用list()方法得到结果。
- -->
- <hql name="get task names"
- var="tasknames with i"
- g="96,16,115,52">
- <!--
- query:HQL query
- parameter:query 的参数
- -->
- <query>
- select task.name
- from org.jbpm.pvm.internal.task.TaskImpl as task
- where task.name like :taskName
- </query>
- <parameters>
- <string name="taskName" value="%i%" />
- </parameters>
- <transition to="count tasks" />
- </hql>
- <hql name="count tasks"
- var="tasks"
- unique="true"
- g="243,16,95,52">
- <query>
- select count(*)
- from org.jbpm.pvm.internal.task.TaskImpl
- </query>
- <transition to="wait" />
- </hql>
<!-- 使用hql 活动,我们可以在database 中执行HQL query
var:存储结果的变量名
unique:值为true 是指从uniqueResult()方法中 获得hibernate query 的结果。
默认值是false。 值为false 的话会使用list()方法得到结果。
-->
<hql name="get task names"
var="tasknames with i"
g="96,16,115,52">
<!--
query:HQL query
parameter:query 的参数
-->
<query>
select task.name
from org.jbpm.pvm.internal.task.TaskImpl as task
where task.name like :taskName
</query>
<parameters>
<string name="taskName" value="%i%" />
</parameters>
<transition to="count tasks" />
</hql>
<hql name="count tasks"
var="tasks"
unique="true"
g="243,16,95,52">
<query>
select count(*)
from org.jbpm.pvm.internal.task.TaskImpl
</query>
<transition to="wait" />
</hql>
测试事例代码如下:
- ProcessInstance processInstance = executionService.startProcessInstanceByKey("Hql");
- String processInstanceId = processInstance.getId();
- // 设定预期结果
- Set<String> expectedTaskNames = new HashSet<String>();
- expectedTaskNames.add("dishes");
- expectedTaskNames.add("iron");
- // 获取第一个hql活动的执行结果,流程变量"tasknames with i"
- Collection<String> taskNames = (Collection<String>) executionService
- .getVariable(processInstanceId, "tasknames with i");
- taskNames = new HashSet<String>(taskNames);
- assertEquals(expectedTaskNames, taskNames);
- // 获取第二个hql活动的执行结果,流程数据库中共有3条记录
- Object activities = executionService.getVariable(processInstanceId, "tasks");
- assertEquals("3", activities.toString());
ProcessInstance processInstance = executionService.startProcessInstanceByKey("Hql");
String processInstanceId = processInstance.getId();
// 设定预期结果
Set<String> expectedTaskNames = new HashSet<String>();
expectedTaskNames.add("dishes");
expectedTaskNames.add("iron");
// 获取第一个hql活动的执行结果,流程变量"tasknames with i"
Collection<String> taskNames = (Collection<String>) executionService
.getVariable(processInstanceId, "tasknames with i");
taskNames = new HashSet<String>(taskNames);
assertEquals(expectedTaskNames, taskNames);
// 获取第二个hql活动的执行结果,流程数据库中共有3条记录
Object activities = executionService.getVariable(processInstanceId, "tasks");
assertEquals("3", activities.toString());
4)sql查询
jpdl定义:
- <!--sql 活动和hql 活动十分相似, 唯一不同的地方就是 使用session.createSQLQuery(...) -->
- <sql name="get task names"
- var="tasknames with i"
- g="96,16,126,52">
- <query>
- select NAME_
- from JBPM4_TASK
- where NAME_ like :name
- </query>
- <parameters>
- <string name="name" value="%i%" />
- </parameters>
- <transition to="count tasks" />
- </sql>
- <sql name="count tasks"
- var="tasks"
- unique="true"
- g="254,16,92,52">
- <query>
- select count(*)
- from JBPM4_TASK
- </query>
- <transition to="wait" />
- </sql>
<!--sql 活动和hql 活动十分相似, 唯一不同的地方就是 使用session.createSQLQuery(...) -->
<sql name="get task names"
var="tasknames with i"
g="96,16,126,52">
<query>
select NAME_
from JBPM4_TASK
where NAME_ like :name
</query>
<parameters>
<string name="name" value="%i%" />
</parameters>
<transition to="count tasks" />
</sql>
<sql name="count tasks"
var="tasks"
unique="true"
g="254,16,92,52">
<query>
select count(*)
from JBPM4_TASK
</query>
<transition to="wait" />
</sql>
测试事例代码同Hql事例代码。
5) mail(邮件活动)
jpdl定义:
- <!-- from:发件者列表
- to: 主要收件人列表
- cc:抄送收件人列表
- bcc: 密送收件人列表
- subject:这个元素的文字内容会成为消息的主题
- text:这个元素的文字内容会成为消息的文字内容
- html:这个元素的文字内容会成为消息的HTML 内容
- attachments:每个附件都会配置在单独的子元素中
- -->
- <mail g="99,25,115,45" language="juel" name="send rectify note">
- <to addresses=" winston@minitrue"/>
- <cc groups="innerparty" users="bb"/>
- <bcc groups="thinkpol"/>
- <subject>rectify ${newspaper}</subject>
- <text>${newspaper} ${date} reporting bb dayorder doubleplusungood
- refs unpersons rewrite fullwise upsub antefiling</text>
- <!--
- <html><table><tr><td>${newspaper}</td><td>${date}</td>
- <td>reporting bb dayorder doubleplusungood
- refs unpersons rewrite fullwise upsub antefiling</td>
- </tr></table></html>
- <attachments>
- <attachment url='http://www.george-orwell.org/1984/3.html' />
- <attachment resource='org/example/pic.jpg' />
- <attachment file='${user.home}/.face' />
- </attachments>
- -->
- <transition to="wait"/>
- </mail>
<!-- from:发件者列表
to: 主要收件人列表
cc:抄送收件人列表
bcc: 密送收件人列表
subject:这个元素的文字内容会成为消息的主题
text:这个元素的文字内容会成为消息的文字内容
html:这个元素的文字内容会成为消息的HTML 内容
attachments:每个附件都会配置在单独的子元素中
-->
<mail g="99,25,115,45" language="juel" name="send rectify note">
<to addresses=" winston@minitrue"/>
<cc groups="innerparty" users="bb"/>
<bcc groups="thinkpol"/>
<subject>rectify ${newspaper}</subject>
<text>${newspaper} ${date} reporting bb dayorder doubleplusungood
refs unpersons rewrite fullwise upsub antefiling</text>
<!--
<html><table><tr><td>${newspaper}</td><td>${date}</td>
<td>reporting bb dayorder doubleplusungood
refs unpersons rewrite fullwise upsub antefiling</td>
</tr></table></html>
<attachments>
<attachment url='http://www.george-orwell.org/1984/3.html' />
<attachment resource='org/example/pic.jpg' />
<attachment file='${user.home}/.face' />
</attachments>
-->
<transition to="wait"/>
</mail>
9、事件
Jpdl定义:
- <state name="wait" g="96,16,104,52">
- <!-- event:事件名称(start或end)
- event-listener:一个事件监听器实现对象。
- start:活动开始时捕获
- end:活动结束时捕获
- -->
- <on event="start">
- <event-listener class="org.jbpm.examples.eventlistener.LogListener">
- <field name="msg"><string value="start on activity wait"/></field>
- </event-listener>
- </on>
- <on event="end">
- <event-listener class="org.jbpm.examples.eventlistener.LogListener">
- <field name="msg"><string value="end on activity wait"/></field>
- </event-listener>
- </on>
- <transition to="park">
- <event-listener class="org.jbpm.examples.eventlistener.LogListener">
- <field name="msg"><string value="take transition"/></field>
- </event-listener>
- </transition>
- </state>
<state name="wait" g="96,16,104,52">
<!-- event:事件名称(start或end)
event-listener:一个事件监听器实现对象。
start:活动开始时捕获
end:活动结束时捕获
-->
<on event="start">
<event-listener class="org.jbpm.examples.eventlistener.LogListener">
<field name="msg"><string value="start on activity wait"/></field>
</event-listener>
</on>
<on event="end">
<event-listener class="org.jbpm.examples.eventlistener.LogListener">
<field name="msg"><string value="end on activity wait"/></field>
</event-listener>
</on>
<transition to="park">
<event-listener class="org.jbpm.examples.eventlistener.LogListener">
<field name="msg"><string value="take transition"/></field>
</event-listener>
</transition>
</state>
监听器LogListener代码:
- public class LogListener implements EventListener {
- String msg;
- public void notify(EventListenerExecution execution) {
- List<String> logs = (List<String>) execution.getVariable("logs");
- if (logs==null) {
- logs = new ArrayList<String>();
- execution.setVariable("logs", logs);
- }
- logs.add(msg);
- execution.setVariable("logs", logs);
- }
- }
public class LogListener implements EventListener {
String msg;
public void notify(EventListenerExecution execution) {
List<String> logs = (List<String>) execution.getVariable("logs");
if (logs==null) {
logs = new ArrayList<String>();
execution.setVariable("logs", logs);
}
logs.add(msg);
execution.setVariable("logs", logs);
}
}
测试事例代码如下:
- ProcessInstance processInstance = executionService
- .startProcessInstanceByKey("EventListener");
- Execution execution = processInstance.findActiveExecutionIn("wait");
- executionService.signalExecutionById(execution.getId());
- List<String> expectedLogs = new ArrayList<String>();
- expectedLogs.add("start on process definition");
- expectedLogs.add("start on activity wait");
- expectedLogs.add("end on activity wait");
- expectedLogs.add("take transition");
- List<String> logs = (List<String>) executionService
- .getVariable(processInstance.getId(), "logs");
- assertEquals(expectedLogs, logs);
ProcessInstance processInstance = executionService
.startProcessInstanceByKey("EventListener");
Execution execution = processInstance.findActiveExecutionIn("wait");
executionService.signalExecutionById(execution.getId());
List<String> expectedLogs = new ArrayList<String>();
expectedLogs.add("start on process definition");
expectedLogs.add("start on activity wait");
expectedLogs.add("end on activity wait");
expectedLogs.add("take transition");
List<String> logs = (List<String>) executionService
.getVariable(processInstance.getId(), "logs");
assertEquals(expectedLogs, logs);
默认情况下,事件监听器只对当前订阅的元素所触发的事件起作用,即propagation=”false”,
但通过指定事件监听器的传播属性propagation=”enabled”或(propagation=”true”),
则该事件监听器可以对其监听元素的所有子元素起作用。
10. 异步执行
1)几乎所有的活动都支持异步属性,流程一旦进入异步执行方式,一个异步消息会被作为当前事务的一部门发送出去,
然后当前事务会立即自动提交。
Jpdl定义:
- <!--
- continue属性: sync (默认值) 作为当前事务的一部分,继续执行元素。
- async 使用一个异步调用(又名安全点)。当前事务被提交,元素在一个新事务中执行。
- 事务性的异步消息被jBPM 用来 实现这个功能。
- -->
- <java name="generate pdf"
- continue="async"
- class="org.jbpm.examples.async.activity.Application"
- method="generatePdf"
- g="86,26,87,50">
- <transition to="calculate primes"/>
- </java>
- <java name="calculate primes"
- continue="async"
- class="org.jbpm.examples.async.activity.Application"
- method="calculatePrimes"
- g="203,26,98,50">
- <transition to="end"/>
- </java>
<!--
continue属性: sync (默认值) 作为当前事务的一部分,继续执行元素。
async 使用一个异步调用(又名安全点)。当前事务被提交,元素在一个新事务中执行。
事务性的异步消息被jBPM 用来 实现这个功能。
-->
<java name="generate pdf"
continue="async"
class="org.jbpm.examples.async.activity.Application"
method="generatePdf"
g="86,26,87,50">
<transition to="calculate primes"/>
</java>
<java name="calculate primes"
continue="async"
class="org.jbpm.examples.async.activity.Application"
method="calculatePrimes"
g="203,26,98,50">
<transition to="end"/>
</java>
Application事例代码如下:
- public class Application implements Serializable {
- private static final long serialVersionUID = 1L;
- public void generatePdf() {
- // 此方法执行需要消耗较长时间
- }
- public void calculatePrimes() {
- // 此方法执行需要消耗较长时间
- }
- }
public class Application implements Serializable {
private static final long serialVersionUID = 1L;
public void generatePdf() {
// 此方法执行需要消耗较长时间
}
public void calculatePrimes() {
// 此方法执行需要消耗较长时间
}
}
测试事例代码如下:
- ProcessInstance processInstance = executionService
- .startProcessInstanceByKey("AsyncActivity");
- String processInstanceId = processInstance.getId();
- // 流程实例处于异步执行状态
- assertEquals(Execution.STATE_ASYNC, processInstance.getState());
- // 获取流程实例异步消息队列中的第1条消息
- Job job = managementService.createJobQuery()
- .processInstanceId(processInstanceId).uniqueResult();
- // 手工执行异步消息
- managementService.executeJob(job.getId());
- processInstance = executionService.findProcessInstanceById(processInstanceId);
- // 流程实例处于异步执行状态
- assertEquals(Execution.STATE_ASYNC, processInstance.getState());
- // 获取第2条消息(job)并执行之
- job = managementService.createJobQuery()
- .processInstanceId(processInstanceId)
- .uniqueResult();
- managementService.executeJob(job.getId());
- assertNull(executionService.findProcessInstanceById(processInstanceId));
ProcessInstance processInstance = executionService
.startProcessInstanceByKey("AsyncActivity");
String processInstanceId = processInstance.getId();
// 流程实例处于异步执行状态
assertEquals(Execution.STATE_ASYNC, processInstance.getState());
// 获取流程实例异步消息队列中的第1条消息
Job job = managementService.createJobQuery()
.processInstanceId(processInstanceId).uniqueResult();
// 手工执行异步消息
managementService.executeJob(job.getId());
processInstance = executionService.findProcessInstanceById(processInstanceId);
// 流程实例处于异步执行状态
assertEquals(Execution.STATE_ASYNC, processInstance.getState());
// 获取第2条消息(job)并执行之
job = managementService.createJobQuery()
.processInstanceId(processInstanceId)
.uniqueResult();
managementService.executeJob(job.getId());
assertNull(executionService.findProcessInstanceById(processInstanceId));
2)异步分支/聚合
Jpdl定义:
- <!--
- exclusive 这个值被用来 将两个来自分支的异步调用的job 结果进行持久化。
- 各自的事务会分别执行ship goods 和send bill, 然后这两个执行都会达到join 节点。
- 在join 节点中,两个事务会同步到一个相同的执行上(在数据库总更新同一个执行),
- 这可能导致一个潜在的乐观锁失败。-->
- <fork g="99,68,80,40" name="fork">
- <!-- 并行的流程分支以独占方式异步执行 -->
- <on event="end" continue="exclusive" />
- <transition g="122,41:" to="ship goods"/>
- <transition g="123,142:" to="send bill"/>
- </fork>
- <java class="org.jbpm.examples.async.fork.Application" g="159,17,98,50"
- method="shipGoods" name="ship goods">
- <transition g="297,42:" to="join"/>
- </java>
- <java class="org.jbpm.examples.async.fork.Application" g="159,117,98,50"
- method="sendBill" name="send bill">
- <transition g="297,141:" to="join"/>
- </java>
- <join g="274,66,80,40" name="join">
- <transition to="end"/>
- </join>
<!--
exclusive 这个值被用来 将两个来自分支的异步调用的job 结果进行持久化。
各自的事务会分别执行ship goods 和send bill, 然后这两个执行都会达到join 节点。
在join 节点中,两个事务会同步到一个相同的执行上(在数据库总更新同一个执行),
这可能导致一个潜在的乐观锁失败。-->
<fork g="99,68,80,40" name="fork">
<!-- 并行的流程分支以独占方式异步执行 -->
<on event="end" continue="exclusive" />
<transition g="122,41:" to="ship goods"/>
<transition g="123,142:" to="send bill"/>
</fork>
<java class="org.jbpm.examples.async.fork.Application" g="159,17,98,50"
method="shipGoods" name="ship goods">
<transition g="297,42:" to="join"/>
</java>
<java class="org.jbpm.examples.async.fork.Application" g="159,117,98,50"
method="sendBill" name="send bill">
<transition g="297,141:" to="join"/>
</java>
<join g="274,66,80,40" name="join">
<transition to="end"/>
</join>
测试事例代码如下:
- ProcessInstance processInstance = executionService.startProcessInstanceByKey("AsyncFork");
- String processInstanceId = processInstance.getId();
- // 获取异步消息列表
- List<Job> jobs = managementService.createJobQuery()
- .processInstanceId(processInstanceId).list();
- //有两个分支,有2条异步消息
- assertEquals(2, jobs.size());
- Job job = jobs.get(0);
- //手工执行第1条
- managementService.executeJob(job.getId());
- job = jobs.get(1);
- //手工执行第2条
- managementService.executeJob(job.getId());
- Date endTime = historyService
- .createHistoryProcessInstanceQuery()
- .processInstanceId(processInstance.getId())
- .uniqueResult()
- .getEndTime();
- // 流程已结束
- assertNotNull(endTime);
ProcessInstance processInstance = executionService.startProcessInstanceByKey("AsyncFork");
String processInstanceId = processInstance.getId();
// 获取异步消息列表
List<Job> jobs = managementService.createJobQuery()
.processInstanceId(processInstanceId).list();
//有两个分支,有2条异步消息
assertEquals(2, jobs.size());
Job job = jobs.get(0);
//手工执行第1条
managementService.executeJob(job.getId());
job = jobs.get(1);
//手工执行第2条
managementService.executeJob(job.getId());
Date endTime = historyService
.createHistoryProcessInstanceQuery()
.processInstanceId(processInstance.getId())
.uniqueResult()
.getEndTime();
// 流程已结束
assertNotNull(endTime);
11. 流程变量
1)流程变量与流程实例绑定, 可通过以下方法来操作流程变量:
例:
- ProcessInstance startProcessInstanceById
- (String processDefinitionId,Map<String,Object>variables);
- ProcessInstance startProcessInstanceById
- (String processDefinitionId,Map<String,Object>variables,String processInstanceKey);
ProcessInstance startProcessInstanceById
(String processDefinitionId,Map<String,Object>variables);
ProcessInstance startProcessInstanceById
(String processDefinitionId,Map<String,Object>variables,String processInstanceKey);
2)其它引擎服务中也存在类似的方法,例如TaskService也提供方法操作任务绑定的流程变量。
3)通过流程变量控制流程的流向是正确的做法,但是不要被这种“方便”的机制诱惑而往流程实例里面放所有的东西,
特别是与流转控制无关的业务数据。
五、 JBPM数据表
JBPM4_DEPLOYMENT: 流程定义的部署记录
JBPM4_DEPLOYPROP: 已部署的流程定义的具体属性
JBPM4_LOB:流程定义的相关资源,包括JPDL XML、图片、用户代码Java类等。
JBPM4_JOB:异步活动或定时执行的Job记录。
JBPM4_VARIABLE:流程实例的变量。
JBPM4_EXECUTION:流程实例及执行对象。
JBPM4_SWIMLANE:任务泳道。
JBPM4_PARTICIPATION:任务参与者,任务的相关用户,区别于任务的分配人。
JBPM4_TASK:流程实例的任务记录。
JBPM4_HIST_PROCINST:保存历史的流程实例记录。
JBPM4_HIST_ACTINST:保存历史的活动实例记录。
JBPM4_HIST_TASK:保存历史的任务实例记录。
JBPM4_HIST_VAR:保存历史的流程变量数据。
JBPM4_HIST_DETAIL:保存流程实例、活动实例、任务实例运行过程中历史明细数据。
JBPM4_ID_USER:保存用户记录。
JBPM4_ID_MEMBERSHIP:保存用户和用户组之间的关联关系。
JBPM4_ID_GROUP:保存用户组记录。