JBPM常用操作

 

 

Jbpm4常用操作

文章分类:Java编程 

一、流程定义 

1.部署流程定义 

ProcessEngine processEngine = new Configuration().buildProcessEngine(); 
RepositoryService repositoryService = processEngine.getRepositoryService(); 
 

 

例:布署文件中的流程 

//布署当前目录下pd.jpdl.xml文件 

String deploymentId = repositoryService.createDeployment() 
.addResourceFromClasspath("pd.jpdl.xml").deploy(); 
 

 

例:布署网页上TextArea中的流程 

<form action="jbpm?family168=deploy" method="post"> 
<textarea name="xml" cols=50 rows=15></textarea> 
<input type="submit" value="http://footman265.javaeye.com/blog/发布"/> 
<input type="reset" value="http://footman265.javaeye.com/blog/取消"/> 
</form> 
 

在后台处理如下: 

String xml = request.getParameter("xml"); 
repositoryService.createDeployment() 
.addResourceFromString("process.jpdl.xml", xml).deploy(); 
 

 

2.删除流程定义 

类ProcessDefinition 包含如下属性: 

ID:流程定义ID号 

DeploymentId:流程定义的布署ID号 

Key:关键字 

name:流程名 

version:版本号 

 

数据如下 

ID Key 名称 版本 

hello-1 hello hello 1 

 

//获取类ProcessDefinition的属性ID 

String id = request.getParameter("id"); 

//根据流程ID获取流程定义实例 

ProcessDefinition pd = repositoryService.createProcessDefinitionQuery() 

.processDefinitionId(id).uniqueResult(); 

//根据流程定义的布署ID号,删除该流程定义 

//注意:jbpm4是不允许直接根据流程定义的ID来直接删除流程定义的,因为还要删除其它配置数据 

repositoryService.deleteDeploymentCascade(pd.getDeploymentId()); 

 

3.获取全部流程定义 

根据流程名获取所有流程定义 

List<ProcessDefinition> processDefinitions = repositoryService 

.createProcessDefinitionQuery().orderAsc( ProcessDefinitionQuery.PROPERTY_NAME).list(); 

 

还可以根据以下顺序进行排序(org.jbpm.api.ProcessDefinitionQuery) 

public static final java.lang.String PROPERTY_DEPLOYMENT_TIMESTAMP "deployment.timestamp" 

public static final java.lang.String PROPERTY_ID "idProperty.stringValue" 

public static final java.lang.String PROPERTY_KEY "keyProperty.stringValue" 

public static final java.lang.String PROPERTY_NAME "idProperty.objectName" 

public static final java.lang.String PROPERTY_VERSION "versionProperty.longValue" 

 

二、流程实例 

 

1.启动一个流程实例 

1.1根据key启动流程实例 

用户必须为新启动的流程实例分配一个key, 这个key是用户执行的时候定义的且唯一。通常在业务流程领域找到这种key。 比如,一个订单id或者一个保险单号。 

ProcessInstance processInstance = executionService.startProcessInstanceByKey("ICL", "CL92837"); 

 

key可以用来创建流程实例的id, 格式为{process-key}.{execution-id}。 所以上面的代码会创建一个id为 ICL.CL92837的流向 (execution)。 

 

1.2根据数据库主键启动流程实例 

如果没有提供用户定义的key,数据库就会把主键作为key。 这样可以使用如下方式获得id: 

ProcessInstance processInstance = executionService.startProcessInstanceByKey("ICL"); 

String pid = processInstance.getId(); 

最好使用一个用户定义的key。提供给一个用户定义的key, 你可以组合流向的id,而不是执行一个基于流程变量的搜索 - 那种方式太消耗资源了。 

 

1.3根据变量启动流程实例 

为一个新的流程实例启动时就提供一组对象参数。 将这些参数放在variables变量里, 然后可以在流程实例创建和启动时使用。 

Map<String,Object> variables = new HashMap<String,Object>(); 

variables.put("customer", "John Doe"); 

variables.put("type", "Accident"); 

variables.put("amount", new Float(763.74)); 

ProcessInstance processInstance = executionService.startProcessInstanceByKey("ICL", variables); 

 

参见流程定义4-1,把order作为一个流程变量分配给它,启动新流程实例代码如下: 

Map<String, Object> variables = new HashMap<String, Object>(); 

variables.put("order", new Order("johndoe")); 

ProcessInstance processInstance = executionService.startProcessInstanceByKey("TaskAssignee", variables); 

 

 

 

 

2.根据流程定义,查看流程实例 

根据类ProcessDefinition的属性ID,查看该流程的所有实例 

String pdId = request.getParameter("id"); 

return executionService.createProcessInstanceQuery() 

.processDefinitionId(pdId).list(); 

 

3.执行实例 

根据类ProcessInstance的属性ID,执行流程实例 

String pid = request.getParameter("pid"); 

executionService.signalExecutionById(pid); 

 

四.任务 

假设流程定义4-1如下: 

<process name="TaskAssignee"> 

<start> 

<transition to="review" /> 

</start> 

<task name="review" assignee="#{order.owner}"> 

<transition to="work" /> 

</task> 

<task name="review" assignee="johndoe"> 

<transition to="wait" /> 

</task> 

<state name="wait"/> 

</process> 

 

assignee="johndoe" 

表示任务会被分配给用户ID为"johndoe"的人。 

assignee="#{order.owner}" 

任务被分配给#{order.owner}。表示通过Order对的getOwner()方法会用来获得用户id,该用户负责完成这个任务。 

public class Order implements Serializable { 

String owner; public Order(String owner) { 

this.owner = owner; 

public String getOwner() { 

return owner; 

public void setOwner(String owner) { 

this.owner = owner; 

 

1.获取任务列表 

1.1根据用户ID获取任务列表 

List<Task> taskList = taskService.findPersonalTasks("johndoe"); 

 

1.2根据任务候选人或候选组获取任务列表 

任务可能被分配给一组用户。 其中的一个用户应该接受这个任务并完成它。 

candidate-groups:一个使用逗号分隔的组id列表。 所有组内的用户将会成为这个任务的候选人。 

candidate-users:一个使用逗号分隔的用户id列表。 所有的用户将会成为这个任务的候选人。 

例如: 

<task name="review" candidate-groups="sales-dept"> 

<transition to="wait" /> 

</task> 

假设:sales-dept有两个成员:johndoe和joesmoe 

 

这个任务被创建时,不显示在任何人的个人任务列表中。 下面的任务列表会是空的。 

taskService.getAssignedTasks("johndoe"); 

taskService.getAssignedTasks("joesmoe"); 

 

分组任务列表中,用户接口必须只接受对这些任务的“接受”操作。 

taskService.takeTask(task.getDbid(), "johndoe"); 

当一个用户接受了一个任务,这个任务的分配人就会变成当前用户。任务会从所有候选人的分组任务列表中消失,它会出现在用户的已分配列表中。 

 

1.3在一个列表中显示该某人的所有任务 

包括他的个人任务,候选任务,这时直接用jbpm4提供的api完成不了该功能要求。于是可以使用以下方式进行扩展: 

/** 

* 取得用户的对应的任务列表 

* @param userId 

* @return 

*/ 

public List<TaskImpl> getTasksByUserId(String userId){ 

AppUser user=(AppUser)getHibernateTemplate().load(AppUser.class, new Long(userId)); 

Iterator<AppRole> rolesIt=user.getRoles().iterator(); 

StringBuffer groupIds=new StringBuffer(); 

int i=0; 

while(rolesIt.hasNext()){ 

if(i>0)groupIds.append(","); 

groupIds.append("'"+rolesIt.next().getRoleId().toString()+"'"); 

/** 

* select * from `jbpm4_task` task 

left join jbpm4_participation pt on task.`DBID_`=pt.`TASK_` 

where task.`ASSIGNEE_`='1' or ( pt.`TYPE_` = 'candidate' and (pt.`USERID_`='1') 

or pt.`GROUPID_`in ('1')) 

*/ 

StringBuffer hqlSb=new StringBuffer(); 

hqlSb.append("select task from org.jbpm.pvm.internal.task.TaskImpl task left join task.participations pt where task.assignee=?"); 

hqlSb.append(" or (pt.type = 'candidate' and ((pt.userId=?)"); 

 

if(user.getRoles().size()>0){ 

hqlSb.append(" or (pt.groupId in ("+groupIds.toString()+"))"); 

hqlSb.append("))"); 

hqlSb.append(" order by task.priority desc"); 

 

return findByHql(hqlSb.toString(), new Object[]{userId,userId}); 

 

 

2分配任务 

如果在流程定义文件中将任务指定到了个人,则jbpm自动分配给个人。否则需要进行指定 

2.1给用户组的用户分配任务 

流程定义文件: 

<task name="review" candidate-groups="sales-dept"> 

<transition to="wait" /> 

</task> 

假设:sales-dept有两个成员:johndoe和joesmoe 

 

分组任务列表中,用户接口必须只接受对这些任务的“接受”操作。 

taskService.takeTask(task.getDbid(), "johndoe"); 

当一个用户接受了一个任务,这个任务的分配人就会变成当前用户。任务会从所有候选人的分组任务列表中消失,它会出现在用户的已分配列表中。 

 

2.2任务分配处理器分配任务 

一个AssignmentHandler可以通过编程方式来计算 一个任务的分配人和候选人。 

public interface AssignmentHandler extends Serializable { 

void assign(Assignable assignable, OpenExecution execution) throws Exception; 

Assignable是任务和泳道的通用接口。 所以任务分配处理器可以使用在任务, 也可以用在泳道中。 

 

包含任务分配处理器的流程定义4-2如下: 

<process name="TaskAssignmentHandler" xmlns="http://jbpm.org/4.2/jpdl"> 

<start g="20,20,48,48"> 

<transition to="review" /> 

</start> 

<task name="review" g="96,16,127,52"> 

<assignment-handler class="org.jbpm.examples.task.assignmenthandler.AssignTask"> 

<field name="assignee"> 

<string value="http://footman265.javaeye.com/blog/johndoe" /> 

</field> 

</assignment-handler> 

<transition to="wait" /> 

</task> 

<state name="wait" g="255,16,88,52" /> 

</process> 

 

类AssignTask代码如下: 

public class AssignTask implements AssignmentHandler { 

String assignee; 

public void assign(Assignable assignable, OpenExecution execution) { 

assignable.setAssignee(assignee); 

请注意,默认AssignmentHandler实现可以使用使用流程变量,任何其他Java API可以访问资源,像你的应用数据库来计算分配人和候选人用户和组。 

启动一个TaskAssignmentHandler的新流程实例,会立即让新流程实例运行到任务节点。 一个新review任务被创建,在这个时候 AssignTask的分配处理器被调用。这将设置johndoe为分配人。 所以John Doe将在他自己的任务列表中找到这个任务。 

 

2.3给泳道分配任务 

任务泳道如下面的流程文件4-3: 

<process name="TaskSwimlane" xmlns="http://jbpm.org/4.2/jpdl"> 

<swimlane name="sales representative" candidate-groups="sales-dept" /> 

<start> 

<transition to="enter order data" /> 

</start> 

<task name="enter order data" swimlane="sales representative"> 

<transition to="calculate quote"/> 

</task> 

<task name="calculate quote" swimlane="sales representative"> 

</task> 

</process> 

 

在这个例子中,我们在身份组件中 创建了下面的信息: 

identityService.createGroup("sales-dept"); 

identityService.createUser("johndoe", "johndoe", "John", "Doe"); 

identityService.createMembership("johndoe", "sales-dept"); 

 

在启动一个新流程实例后,用户johndoe将成为 enter order data的一个候选人。还是像上一个流程候选人例子一样, John Doe可以像这样接收任务: 

taskService.takeTask(task.getDbid(), "johndoe"); 

 

接收这个任务将让johndoe成为任务的负责人。 直到任务与泳道sales representative关联, 分配人johndoe也会关联到泳道中作为负责人。接下来,John Doe可以像下面这样完成任务: 

taskService.completeTask(taskDbid); 

 

完成任务会将流程执行到下一个任务, 下一个任务是calculate quote。 这个任务也关联着泳道。因此, 任务会分配给johndoe。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1.JPDL的流程定义元素 1)第一层:GraphElement 这个容易理解,因为在画流程定义时,每个拖拉的对象都是一个graph的元素。GraphElement有四个属性: (1)processDefine 表示当前元素属于哪个流程定义 (2)events 表示可以接收哪些event (3)name 名字 (4)exceptionHandlers 异常处理类集合(List) 2)第二层:node、processDefinition、Transition、Task 它们都继承自GraphElement (1)processDefinition表示流程定义(implements NodeCollection),它有下面的属性:name、version、nodes、startState。nodes表示流程中所有的node,startState用于启动流程时找到首节点。 (2)Transition表示转移,它有三个属性:from(Node),to(Node),supportedEventTypes表示支持的event类型 (3)node表示节点,它有四个属性:leaving transitions、arriving transitions、action、superState。 (4)Task 定义任务 3)第三层:各种不同的node 它们都继承自node。 Decision、EndState、Fork、Join、Merge、Milestone、 InterleaveEnd、InterleaveStart、ProcessState、State。 2.jBPM的token jbpm中最重要的概念,应该是令牌(Token)和信令(Signal)。在整个流程实例运行过程中,我们可以迅速的利用token得到其当前的current state。在解决“并行”等(比如Fork)问题时,jBpm让Token对象维护了父子关系,这种关系在涉及到Fork的时候会产生。 jBpm让Token这个对象身兼了多种使命: (1)快速定位current state (2)用于fork,join算法 (3)用于告知任务执行者的任务索引。 如下代码: //pd是process definition,pi是process instance ProcessInstance pi = new ProcessInstance( pd ); //得到根令牌 Token token = pi.getRootToken(); //发信令 token.signal(); Token的signal方法也可以传入transition参数,这个方法把信令发送给Token,这样,令牌将被激活,并沿指定的transition离开当前的状态(如果没有指定transition,将沿缺省的transition 离开当前状态)。 jbpm是怎么实现的呢?其实很简单: 1)Token记录了当前的状态(current state),只有当前的状态(或称节点)拥有该令牌 2)向TOKEN发signal后,当前状态收到该signal 3)当前状态把令牌传给signal中指定的transition 4)transition收到令牌后,不强占,马上把令牌传给下个状态. 5)根据令牌的位置,流程的状态已经发生改变. 3.process definition 一个process definition代表了一个正式的业务流程,它以一个流程图为基础。这个流程图由许多node和transition组成。每个node在这个流程图里都有着各自特殊的类型,这些不同的类型决定了node在运行时的不同行为。一个process definition只有一个start state 。 4.token 一个token代表了一条执行路径,它包含了这条执行路径的当前的执行状态(current state)。 5.process instance 一个process instance(流程实例)即一个process definition(流程定义)的流程执行实例。一个process definition可以对应多个process instance。当一个process instance被创建的时候,一个主执行路径token同时被创建,这个token叫做root token,它指向流程定义的start state(processDefinition.getStartState()==token.getNode())。 6.signal 一个signal 发送给token通知token 继续流程的执行。如果signal 没有指定transition,token将沿缺省的transition离开当前状态,如果signal 指定transition,token将沿指定的transition离开当前的状态。看源代码可以看到发给process instance的signal 其实都是发送给了root token。 7.Actions jbpm提供了灵活的action ,当流程执行,token 进入node和transition时,会触发相应的一些event(事件)。在这些event上附上我们自己写的action,就会带动action 的执行。action里是我们自己的相关java操作代码,非常方便。注意的是event(事件)是内置的,无法扩展。另外,action也可以直接挂在node上,而不依赖于event(事件)的触发,这个很重要。 8.node 一个流程图由许多node和transition组成。每个node都有一种类型,这个类型决定了当流程执行到这个node时的不同行为。jbpm有一组node type可以供你选择,当然你可以定制自己node 。 node的作用 node有两个主要的作用: 1)执行java代码,比如说创建task instance(任务实例)、发出通知、更新数据库等等。很典型的就是在node 上挂上我们的action 2) 控制流程的执行: A、等待状态:流程进入到这个node时将处于等待状态,直到一个signal 的发出 B、流程将沿着一个leaving transition越过这个node,这种情况特殊一点,需要有个action挂在这个node上(注意这个action不是event触发的!),action中将会调用到API里 executionContext.leaveNode(String transitionName),transitionName即这里的leaving transition名字。 C、创建新的执行路径: 很典型的就是fork node。流程在这里会分叉,产生新的执行路径。这样就创建了新的token,每个新的token代表一个新的执行路径。注意的是,这些新的token和产生前的token是父子关系! D、结束执行路径:一个node可以结束一条执行路径,这同样意味着相应的token的结束和流程的结束。 9.流程图中的node type 1)task-node 一个task-node可以包含一个或多个task,这些task分配给特定的user。当流程执行到task-node时,task instance将会被创建,一个task对应一个task instance。task instances 创建后,task-node就处于等待状态。当所有的task instances被特定的user执行完毕后,将会发出一个新的signal 到token,即流程继续执行。 2)state state是一个纯粹的wait state(等待状态)。它和task-node的区别就是它不会创建task instances。很典型的用法是,当进入这个节点时(通过绑定一个action到node-enter event),发送一条消息到外部的系统,然后流程就处于等待状态。外部系统完成一些操作后返回一条消息,这个消息触发一个signal 到token,然后流程继续执行。(不常用) 3)decision 当需要在流程中根据不同条件来判断执行不同路径时,就可以用decision节点。两种方法:最简单的是在transitions里增加condition elements(条件),condition是beanshell script写的,它返回一个boolean。当运行的时候,decision节点将会在它的 leaving transitions里循环,同时比较 leaving transitions里的condition,最先返回'true'的condition,那个leaving transitions将会被执行;作为选择,你可以实现DecisionHandler接口,它有一个decide()方法,该方法返回一个String(leaving transition的名字)。 4)fork fork节点把一条执行路径分离成多条同时进行(并发)的执行路径,每条离开fork节点的路径产生一个子token。 5)join 默认情况下,join节点会认为所有到达该节点的token都有着相同的父token。join 节点会结束每一个到达该节点的token,当所有的子token都到达该节点后,父token会激活。当仍然有子token处于活动状态时,join 节点是wait state(等待状态)。 6)node node节点就是让你挂自己的action用的(注意:不是event触发!),当流程到达该节点时,action会被执行。你的action要实现ActionHandler接口。同样,在你的action里要控制流程。 10. Actions的说明 存在两种action,一种是 event触发的action,一种是挂在node 节点的action。要注意它们的区别,event触发的action无法控制流程,也就是说它无法决定流程经过这个节点后下一步将到哪一个leaving transition;而挂在node 节点的action就不同,它可以控制流程。不管是哪一种action都要实现ActionHandler接口。 11. Task(任务) jbpm一个相当重要的功能就是对任务进行管理。Task(任务)是流程定义里的一部分,它决定了task instance的创建和分配。Task(任务)可以在task-node节点下定义,也可以挂在process-definition节点下。最普遍的方式是在task-node节点下定义一个或多个任务。默认情况下,流程在task-node节点会处于等待状态,直到所有的任务被执行完毕。任务的名称在整个流程中必须是唯一的。一个TaskNode对应多个Task。 对于这样的流程定义: xml 代码 1. <task-node name='a'> 2. <task name='laundry' /> 3. <task name='dishes' /> 4. <task name='change nappy' /> 5. <transition to='b' /> 6. </task-node> 只有当节点中的三个任务都完成后,流程才进入后面的节点 对于这样的流程定义: xml 代码 1. <task-node name='a' signal='first'>> 2. <task name='laundry' /> 3. <task name='dishes' /> 4. <task name='change nappy' /> 5. <transition to='b' /> 6. </task-node> 当第一个任务完成后,token就指向后面的节点 对于这样的流程定义: xml 代码 1. <task-node name='a' signal='never'>> 2. <task name='laundry' /> 3. <task name='dishes' /> 4. <task name='change nappy' /> 5. <transition to='b' /> 6. </task-node> 三个任务都完成后,token仍然不会指向后面的节点;需要自己手动调用processInstance.signal()才会驱动流程到下面的节点。 对于这样的流程定义: xml 代码 1. <task-node name='a' signal='unsynchronized'>> 2. <task name='laundry' /> 3. <task name='dishes' /> 4. <task name='change nappy' /> 5. <transition to='b' /> 6. </task-node> token不会在本节点停留,而是直接到后面的节点 12. jbpm的任务管理实现 一个Task instance(任务实例)可以被分配给一个actorId (java.lang.String)。所有的Task instance都被保存在数据库中的表jbpm_taskinstance里。当你想得到特定用户的任务清单时,你就可以通过一个与用户关联的actorId来查询这张表。 一个流程定义有一个TaskMgmtDefinition;一个TaskMgmtDefinition对应多个swimlane,同时对应多个task;一个swimlane有多个task,可以从TaskMgmtDefinition中通过task的名称直接获取相应的task; swimlane对象有四个属性,分别是name(名字)、assignmentDelegation(分配代理类)、taskMgmtDefinition、tasks(Set 对应多个task),可以增加task task对象主要的属性:taskMgmtDefinition、swimlane、assignmentDelegation、taskNode,需要注意的是swimlane和assignmentDelegation中间只是可以一个属性有值,因为它们都和任务的分配有关系。 一个流程实例有一个TaskMgmtInstance;一个TaskMgmtInstance对应多个swimlaneInstance,同时对应多个taskInstance;一个swimlaneInstance有多个taskInstance,可以从TaskMgmtInstance中直接获取相应的taskInstance; swimlaneInstance对象主要有五个属性,分别是name、actorId、pooledActors(Set)、swimlane、taskMgmtInstance。 taskInstance对象的主要属性:name、actorId、task、swimlaneInstance、taskMgmtInstance、pooledActors。 当对任务进行分配时,一般需要实现AssignmentHandler这个接口,这个接口的方法只有一个: void assign( Assignable assignable, ExecutionContext executionContext) throws Exception; 一个典型的实现(把名字是'change nappy'的任务交给NappyAssignmentHandler这个类来分配) xml 代码 1. <task name='change nappy'> 2. <assignment class='org.jbpm.tutorial.taskmgmt.NappyAssignmentHandler' /> 3. task> NappyAssignmentHandler类: java 代码 1. public void assign(Assignable assignable, ExecutionContext executionContext) { 2. assignable.setActorId("papa"); 3. } 同样,Assignable只是一个接口,它有两个方法:setActorId()和setPooledActors(),Assignable的具体实现类也是两个:swimlaneInstancehe和taskInstance。这样就不不难理解整个任务分配流程了: 1、流程进入TaskNode节点,执行TaskNode类的execute()方法,该方法首先获得TaskMgmtInstance实例,然后通过它来创建TaskInstance。taskMgmtInstance.createTaskInstance(task, executionContext); 2、在上面的createTaskInstance(task, executionContext)里,该方法调用了taskInstance.assign(executionContext)对taskInstance进行分配。 3、在assign(executionContext)方法里,首先会判断task属性里是否存在swimlane,如果有的话,这个taskInstance就会分配给swimlane指定的ActorId或 PooledActors;如果不存在,再去找task属性里 assignmentDelegation(分配代理类)通过代理类(即我们自己写的实现AssignmentHandler这个接口的类)指定ActorId或 PooledActors。 13. jbpm的用户角色管理 jbpm在用户角色管理上共设计了四个类:Entity、Membership、Group、User。 Entity类是其他三个类的父类,它包含了两个属性:name(String)、permissions(Set); User类继承Entity类,包含三个属性:password(String)、email(String)、memberships(Set); Group类继承Entity类,包含四个属性: type(String)、parent(Group)、children(Set)、memberships(Set); Membership类继承Entity类,包含三个属性:role(String)、user(User)、group(Group) 很明显,一个user对应一个用户,一个group对应一个用户组,它们之间通过membership关联,并且一个user可以属于多个不同类型(type)的group,user和 group之间是多对多的关系。Membership类的role属性个人感觉用途不大,反倒是name属性代表了user在group里的role(角色)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值