Start(启动节点):表示流程的开始从哪里开始,每个流程都有一个start节点。
Start有一个name属性给start节点取一个名字
State:此节点表示一个等待状态,进入此节点,整个流程都会中断,直到系统外的参与者发起继续执行的命令。
State 只有一个name属性
例子:
1.序列状态节点
2.可选择的状态节点
Task:(任务节点) 进入这个节点,业务会继续下去。当task下所有的节点下的所以任务都完成,流程才会继续下去。
1一个任务分配给指定的用户,我们可以使用assignee属性实现:
这个流程演示了任务分配的两个方面。
第一,assignee 用来指派用户;
第二, 任务被分配给#{users.owner},这说明程序首先调用users这个名字查找一个对象, 并且调用getOwner()方法来获得用户变量。
当一个流程实例创建时,需要把users 作为一个流程变量分配给它。
Map<String, Object> variables = newHashMap<String, Object>();
variables.put("users", newUsers("Marker"));
executionService.startProcessInstanceByKey("testTask",variables);
然后Marker 的任务列表可以这样获得:
List<Task> taskList = taskService.findPersonalTasks("Marker");
2 任务候选人:
候选人顾名思义就是有多个人可以接收并完成这个任务。
我们可以通过两个属性定义候选人,candidate-groups和candidate-users。
当这个流程实例启动后,一个任务就会被创建,而这个任务在这时是不显示在任何人的任务
列表中的,因此下面的任务列表会是空的。
taskService.findPersonalTasks("Marker");
taskService.findPersonalTasks("King");
如果想得到这个任务,我们需要将用户添加到组别中,接下来我们为user-dept增加两个成员:
Marker 和King
//创建名为user-dept的组
identityService.createGroup("user-dept");
//创建用户Marker
identityService.createUser("Marker", "first name","family name",
"majk@vip.qq.com");
//将Marker归入user-dept
identityService.createMembership("Marker","user-dept");
//创建用户King
identityService.createUser("King","first name", "family name", "majk@9.cn");
//将King归入user-dept
identityService.createMembership("King","user-dept");
至此,用户Marker和King 就属于user-dept组,这时再查询任务列表,就会得到这个任务了。
但是候选人必须在他处理这个任务之前,接收这个任务。
taskService.takeTask(task.getId(),"Marker");
当一个候选人接收了这个任务,那么这个任务就会分配(assignee)给他,并且任务会从所
有候选人的分组任务列表中消失,它会出现在接收人的任务列表中。
candidate-users 属性则是用来处理以逗号分隔的一系列用户ID。candidate-users属性可以
和其他分配选项结合使用。
3任务分配处理器
一个AssignmentHandler 可以通过编程方式来计算一个任务的分配人和候选人。
public interfaceAssignmentHandler extends Serializable {
/** 设置分配人和候选人*/
void assign(Assignable assignable,OpenExecution execution) throws Exception;
}
Assignment-handler 是task节点的子节点。它的子节点都来自用户代码。
下面是任务分配处理器的使用示例:
<processname="testTaskAssignmentHandler"xmlns="http://jbpm.org/4.4/jpdl">
<start>
<transition to="review"/>
</start>
<task name="task1">
<assignment-handlerclass="test.AssignTask">
<field name="assignee">
<string value="Marker"/>
</field>
</assignment-handler>
<transition to="wait1"/>
</task>
<state name="wait1"/>
</process>
该流程启动后进入task1 节点,AssignTask 被调用,将Marker设置为改任务的assignee。
我们需要注意的是,默认的AssignmentHandler实现类可以使用流程实例中的变量,或使
用数据库加载其他业务对象。
Fork和Jion:fork节点把一条执行路径分离成多条同时进行(并发)的执行路径,每条路径产生一个单独的执 行,该节点适用于需要并发流程的场景,比如多部门的审批等。fork后一般会有join节点来合并流
程,join节点可以设置在执行该节点之前需要到达该节点的执行的数目。
默认情况下join会等待所有的子执行全部结束才会执行,如果设置了multiplicity,那么只要
满足multiplicity 的数量就会执行该join节点,比如在上面流程定义文件中的join 节点中加入
multiplicity= “1”,那么只要有一个执行到达这里,join就会继续往下执行。而不会等待。
Set<String> set = null;
//启动流程,获得流程实例
processInstance =executionService.startProcessInstanceByKey("fork");
//查看当前的活动节点[state1,state2]
set =processInstance.findActiveActivityNames();
//得到活动节点
String id1 =processInstance.findActiveExecutionIn("state1").getId();
processInstance =executionService.signalExecutionById(id1);
//查看当前的活动节点[state2]
set =processInstance.findActiveActivityNames();
//得到活动节点
String id3 =processInstance.findActiveExecutionIn("state2").getId();
processInstance =executionService.signalExecutionById(id2);
//查看当前的活动节点[state3]
set =processInstance.findActiveActivityNames();
//得到活动节点
String id3 =processInstance.findActiveExecutionIn("state3").getId();
processInstance =executionService.signalExecutionById(id3);
//查看当前的活动节点[无]
set =processInstance.findActiveActivityNames();
executionService每次调用signalExecutionById之后都会返回一个新的流程实例,我们在上下
文中要使用这个返回的流程实例,而且每次调用完signalExecutionById都要为这个对象重新赋值,
这样才会得到最新的流程执行结果。
Decision:决策节点,用于觉得在多个执行路径中哪个才可以被执行。
有transition和handler class元素
Transition转向时可以是一个条件或者一个表达式
1条件:
2表达式
当我们使用“工作结束”,启动一个新的流程实例。
Map<String,Object> variables=newHsahMap< String,Object>();
Variables.put(“content”,”工作结束”);
executionService.startProcessInstanceByKey("testDecision",variables);
3决策处理器: 使用决策处理器需要实现DecisionHandler接口,将决策权交给决策处理器
下面是自定义的handler类
package test;
import org.jbpm.api.jpdl.DecisionHandler;
import org.jbpm.api.model.OpenExecution;
public class MyDecisionHandlerimplements DecisionHandler {
@Override
public String decide(OpenExecution execution) {
String content=(String)execution.getVariable("content");
if (content.equals("工作结束")) {
return "睡觉";
}
else {
return "学习";
}
}
}
Transition:转换节点,转换节点是流程中最多的节点,它在设计器中用一条有向线段表示,表示从一个即节点到达另一个节点,它的to属性就是标明它的目的节点
End:结束节点,用来表示流程的结束。
有name属性
上面的流程一开始就结束
Sub-process:子流程节点,创建一个子节点实例并等待直到它完成。当子实例完成,父流程的执行就会继续前进。
1:子流程变量
如何向子流程实例传递信息,我们需要运用到变量,与流程变量不同的是,子流程变量有
parameter-in和parameter-out,其属性var 和subvar分别对应父流程变量和子流程变量。
在parameter-in 中var 代表将父流程变量传递到subvar子流程变量中;
在parameter-out 中var 代表父流程变量读取子流程变量subvar。
定义一个子流程:
定义一个父任务:
我们启动父流程,并赋予一个文档变量:
Map<String, Object> variables = newHashMap<String, Object>();
variables.put("document","sometext");
ProcessInstance processInstance =
executionService.startProcessInstanceByKey("SubProcessDocument",variables);
然后父流程达到sub-process 节点,触发子流程启动,并且子流程达到get approval任务节
点,将subvar 设置的subDocument作为分配人赋值给该任务,因此我们可以使用sometext
用户来获得其任务列表。
List<Task> taskList =taskService.findPersonalTasks("sometext");
Task task = taskList.get(0);
而后我们将变量subResult设置到子流程的实例中
Map<String, Object> variables = newHashMap<String, Object>();
variables.put("subResult","accept");
taskService.setVariables(task.getId(),variables);
完成这个子流程的任务,整个子流程结束
taskService.completeTask(task.getId());
当子流程结束时,父流程会被signal标记(不是notify 提醒)。首先subResult变量会从子
流程赋值到父流程的result 变量中,然后上级流程继续并离开review 节点。
Customs:自定义节点,调用实现了的自定义的用户代码。
以下是自定义的代码 Custom类需要实现ExternalActivityBehaviour接口
Java:java任务节点,流程的执行会调用在java活动中配置的类的方法。
当流程执行到java节点的时候,会执行在该节点上配置的相应的方法。如果有返回值则通过var
属性设置,我们可以通过variables拿到该返回值。
Java节点不在界面体现
下面是JavaTest
接下来我们设定好变量,启动新的流程实例:
Map<String,Object> variables = newHashMap<String,Object>();
List<String> list = newArrayList<String>();
//动态为java类设值添加测试数据
list.add("jbpm");
variables.put("list", list);
variables.put("listName","jbpmList");
//通过表达式实例化添加测试数据
variables.put("hand", new Handler());
variables.put("msg", "oneworld!!!");
//启动流程,获得流程实
executionService.startProcessInstanceByKey("javaTask",variables);
Script:脚本节点,此节点会解析一个脚本
Hql:使用hql活动,我们可以在数据库中执行hql查询,并将返回的结果保存到流程变量中。
有var属性 用来存放查询的结果集
Unique属性 用来判断是从uniqueResult还是list中取得结果
子节点 :
Query 查询语句
Parameters 查询语句的参数
Sql:使用的是sql语句 和hql节点差不多。
Mail:邮件节点,使用mail活动,流程作者可以指定一封email的内容,一次发送给多个收件人。
有template属性 是个字符串 引用配置文件中的一个mail-template 元素。
如果没找到, 必须使用子元素在内部指定。
子节点:
attachment节点
例子:
End-concel:事件结束节点。可以触发一个事件,可以是流程取消<end-cancel>等价于<end state=”cancel”>;。。
End-error:表示结束发生错误的事件。<end-error>等价于<endstate=”error”>
JBPM有五大服务接口 这些服务接口都可以由 流程引擎ProcessEngine获得,该引擎则由Configuration构建的 有以下几种方式
1: ProcessEngine pe =Configuration.getProcessEngine();
过静态方法直接按照默认的配置文件名称构建流程引擎,该方法会直接加载classpath 下
名为jbpm.cfg.xml的配置文件,通过该配置文件中的生命去构建引擎
2: ProcessEngine pe = newConfiguration().buildProcessEngine();
通过实例化Cofiguration后的buildProcessEngine 方法构建引擎,该方法与上述方法过程类似
3: ProcessEngine pe=newConfiguration().setResource("").buildProcessEngine();
自定义JBPM 配置文件名称后,构建流程引擎.
五大服务接口:
1流程资源服务接口
RepositoryService;
2执行服务接口
ExecutionService
3任务服务接口
TaskService
4 历史服务接口
HistoryService
5管理服务接口
ManagementService
1流程资源服务接口
//由流程引擎得到
RepositoryServicerepositoryService = processEngine.getRepositoryService();
RepositoryService 包含了管理流程资源的所有方法。
1) 部署流程
//通过classpath中的文件名加载
repositoryService.createDeployment().addResourceFromClasspath(path).deploy();
//通过File类加载
repositoryService.createDeployment().addResourceFromFile(file).deploy();
//通过url地址加载
repositoryService.createDeployment().addResourceFromUrl(url).deploy();
//通过Zip文件流加载 (流程图实例化可以采用此方式)
repositoryService.createDeployment().addResourcesFromZipInputStream(zip).deploy();
//通过字符串加载
repositoryService.createDeployment().addResourceFromString(resourceName,string);
流程部署后,会生成{key}-{version}格式的流程定义ID,如果流程定义文件未指明key 和
version,程序将使用流程名称作为key,根据当前数据库存储情况生成version (version =key
的数量+1)。
生成的key将不是数字和字母的字符,替换为下划线。
2) 删除流程
//删除流程定义,如果该定义还存在活动的流程实例,将抛出异常
repositoryService.deleteDeployment(deploymentId);
//级联删除所有该流程相关的内容
repositoryService.deleteDeploymentCascade(deploymentId);
3) 挂起/恢复流程
//当流程定义挂起后,其相关记录不会被删除,此时发起新流程实例或更新其实例的数据,都会抛出异常
repositoryService.suspendDeployment(deploymentId);
//恢复流程定义后,可以继续操作流程及其实例
repositoryService.resumeDeployment(deploymentId);
4) 查询流程示例
ProcessDefinitionQuery pdq =repositoryService.createProcessDefinitionQuery();
List<ProcessDefinition> pdList =pdq.processDefinitionKey("xxx").list();
for (ProcessDefinition processDefinition :pdList) {
//TODO
}
5) 获得流程定义图片
//该方法可以将流程设计时生成的图片文件以流格式返回,前提条件是图片也发布到了数据库中
//具体使用方法后面将会介绍到
repositoryService.getResourceAsStream(deploymentId,"xxx.png");
2执行服务接口
由流程引擎得到
ExecutionServiceexecutionService = processEngine.getExecutionService();
ExecutionService 主要用于维护流程实例,执行等待状态的流程,并且包含了流程实例的查询和
流程变量的操作。
1) 启动新的流程实例
a) 使用Key 启动最新的流程实例
executionService.startProcessInstanceByKey("VWIN");
上述方法会以Key 为VWIN 流程定义的最新版本启动一个新的流程实例,是最简单且最常
用的方法。
b) 指定流程版本
executionService.startProcessInstanceById("VWIN-2");
上述方法会以流程为VWIN-2的定义启动一个新的流程实例,VWIN 是流程定义的Key,2
是流程定义的版本。
c) 指定流程实例的ID
executionService.startProcessInstanceByKey("VWIN","CGD0008");
上述方法与第a种方法类似,唯一区别是手动指定了流程实例的ID,这个ID可以用我们的业务对象ID,例如采购单。
d) 使用变量
Map<String,Object> variables =new HashMap<String,Object>();
variables.put("customer","John Doe");
variables.put("type","Accident");
variables.put("amount", newFloat(763.74));
executionService.startProcessInstanceByKey("VWIN",variables);
上述方法在启动流程实例时,就会将设置的变量保存,启动后可以使用这些变量。
2) 执行等待的流程
当使用一个state活动时,流程会在达到state 的时候等待,直到一个Signal 出现。
其实state 的执行就是流程实例本身,我们可以通过:
executionService.signalExecutionById(executionId);
来执行。
获得正确执行的方法是给state分配一个监听器,如:
<state name="wait">
<on event="start">
<event-listenerclass="org.jbpm.examples.StartExternalWork" />
</on>
...
</state>
在事件监听器StartExternalWord 中,我们可以执行额外需要完成的工作,通过
execution.getId()获得确切的流程。
3任务服务TaskService
//由流程引擎得到
TaskService taskService =processEngine.getTaskService();
1) 任务列表的访问
TaskService 主要是提供了对任务列表的访问及操作,如:
//查找指定用户的任务列表
List<Task> taskList = taskService.findPersonalTasks("marker");
//查找指定组的任务列表
List<Task> groupTaskList =taskService.findGroupTasks("hr-group");
for (Task task : taskList) {
//TODO
taskService.completeTask(task.getId());
}
2) 读写相关数据
一般情况下,我们在执行任务时都会对应一个表单,表单可以读写任务的相关数据。
taskService.getVariable(taskId, variableName);
taskService.getVariables(taskId, variableNames);
taskService.getVariableNames(taskId);
taskService.setVariables(taskId, variables);
3) 完成任务
我们可以通过以下几种方式完成任务:
taskService.completeTask(taskId);
taskService.completeTask(taskId, variables);
taskService.completeTask(taskId, outcome);
taskService.completeTask(taskId, outcome, variables);
我们可以提供一个 map,在任务完成之前作为流程变量添加到流程实例中,也可以提供一个
outcome (出口),以决定任务完成后的流向。但是我们需要注意任务的不同配置将会带来的问题:
a) 如果一个任务拥有一个没有name 的transition:
<task name="myTask">
<transition to="end"/>
</task>
taskService.getOutcomes(taskId);//返回一个包含null值的集合
taskService.completeTask(taskId);//使用这个transition
taskService.completeTask(taskId, variables);//使用这个transition
By Marker(majk@9.cn)
----------------------- Page10-----------------------
taskService.completeTask(taskId, outcome);//抛出异常
taskService.completeTask(taskId, outcome, variables);//抛出异常
b) 如果一个任务拥有一个有name 的transition:
<task name="myTask">
<transition name="to end" to="end"/>
</task>
taskService.getOutcomes(taskId);//返回包含这个transition名称的集合
taskService.completeTask(taskId);//使用这个transition
taskService.completeTask(taskId, variables); //抛出异常(因为没有无名的transition)
taskService.completeTask(taskId, outcome);//根据指定的名称执行转移 (如果没有该名称,
则抛出异常)
taskService.completeTask(taskId, outcome, variables);//抛出异常 (因为没有无名的
transition)
c) 如果一个任务拥有多个transition,而其中一个没有name,其他都有name:
<task name="myTask">
<transition to="next"/>
<transition name="to other task"to="otherTask"/>
<transition name="to end" to="end"/>
</task>
taskService.getOutcomes(taskId);//返回一个包含null值和其他transition名称的集合
taskService.completeTask(taskId);//使用没有名称的transition
taskService.completeTask(taskId, variables);//使用没有名称的transition
taskService.completeTask(taskId, outcome);//使用指定名称的transition
taskService.completeTask(taskId,outcome, variables);//使用指定名称的transition
d) 如果一个任务拥有多个transition,并且都有唯一的name:
<task name="myTask">
<transition name="to next" to="next"/>
<transition name="to other task"to="otherTask"/>
<transition name="to end" to="end"/>
</task>
taskService.getOutcomes(taskId);//返回一个包含null值和其他transition名称的集合
taskService.completeTask(taskId);//抛出异常(因为没有无名的transition)
taskService.completeTask(taskId, variables); //抛出异常(因为没有无名的transition)
taskService.completeTask(taskId, outcome);//使用指定名称的transition
taskService.completeTask(taskId, outcome, variables);//使用指定名称的transition
4历史服务HistoryService
//由流程引擎得到
HistoryService historyService =processEngine.getHistoryService();
历史服务顾名思义就是对流程实例的运行时历史进行访问,如果想查询某流程定义的所有历史流 程实例,可以使用如下方法:
historyService.createHistoryProcessInstanceQuery()
.processDefinitionId(processDefinitionId)
.orderAsc(HistoryProcessInstanceQuery.PROPERTY_STARTTIME)
.list();
5管理服务ManagementService
管理服务通常用来管理job,下文中我们用Timer进行测试,因为任务需要时间触发,因此在JUnit
测试中,达到时间要求是不可能的,因此我们可以显式获得Job 手动触发,例如:
Job job = managementService.createJobQuery()
.processInstanceId(processInstance.getId())
.uniqueResult();
managementService.executeJob(job.getId());