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");
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");
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>
AssignTask类必须实现AssignmentHandler类,代码如下:
//默认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>
泳道中的用户组中的用户在接受任务后成为任务的分配者,同时泳道也会发生变化,接收任务者在流程实例中会被固化为分配者。
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>
子流程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>
流程变量是父子流程用来沟通的纽带。父流程在子流程启动时将自己的“父流程变量”输入子流程,
反之,子流程在结束时可以将自己的“子流程变量”返回父流程,从而实现父子流程间的数据交换。
部分事例代码如下:
// 分别部署子流程跟父流程 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>
子流程SubProcessReview定义:
<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"));
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" />
子流程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>
部分事例代码如下:
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>
自定义活动的类需实现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); } }
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>
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; } }
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; } }
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; } }
测试代码如下:
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());