jBPM 用户指南(3)

作者:jBPM组

版本:5.0.0

原文版权归作者所有,该翻译稿仅供学习交流使用,引用或转载请保证该部分的完整

 

 

章节3流程

 

 

3.1创建流程

3.1.1BPMN2可视编辑器

3.1.2使用XML定义流程

3.1.3使用(Process)API定义流程

3.2在应用程序中使用流程

3.3节点类型

3.4数据(Data)

3.5约束(Constraints)

3.6动作(Actions)

3.7事件(Events)

3.8计时器(Timers)

3.9更新流程

3.9.1流程实例迁移

 

 

图3.1流程

 

业务流程用流图表展示时就是由一系列需要调度执行的有序步骤组成的图形。流程有一些相互连接的节点组成,每个节点代表一个步骤,节点之间的连接则表示节点之间如何迁移。本章将介绍如何在应用程序中定义流程。

 

3.1创建流程

 

可以用以下三种方式创建流程:

  1. 使用Eclipse插件中的图形化流程编辑器
  2. 使用XML文件,根据BPMN2.0规范中XML Schema Definition声明的XML流程格式定义
  3. 直接使用API创建流程

3.1.1BPMN2可视编辑器

 

图形化BPMN2编辑器允许在画布上用拖拽各种类型节点的方式创建流程,并且支持对节点属性的编辑。这个编辑器是jBPM/Drools Eclipse插件的一部分。在建立一个jBPM项目(参考前面安装部分Eclipse工作环境的创建)之后就可以开始添加流程了。选择目标项目,执行"New"(Ctrl+N)向导或者在需要存放流程的目录上点右键,选择"New->File",填写名称及扩展名,添加新的流程。这样就会打开流程编辑器(此时文件还不存在,可以忽略文件不能读取的警告)。

 

图3.2新建流程

 

 编辑器由面板、画布及一个大纲视图组成。从面板上选择想要的元素,然后在画布的指定位置上点击添加该元素。譬如在面板上选择"End Event"图标就可以添加结束事件。点击流程中的元素可以编辑该元素的属性。在面板上选择"Sequence Flow"可以在节点(允许不同类型)间建立连接。

 

你可以一直添加直到满足业务逻辑的需求。

 

3.1.2使用XML定义流程

 

也允许直接使用下面样式的XML来定义流程。XML流程语法由相关的XML Schema Definition给出。下面的XML片段显示了一个由开始事件,在控制台打印"Hello World"字样的脚本任务,结束事件这样的顺序组成的简单流程。

 

<?xml version="1.0" encoding="UTF-8"?> 

<definitions id="Definition"

             targetNamespace="http://www.jboss.org/drools"

             typeLanguage="http://www.java.com/javaTypes"

             expressionLanguage="http://www.mvel.org/2.0"

             xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"Rule Task

             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

             xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd"

             xmlns:g="http://www.jboss.org/drools/flow/gpd"

             xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"

             xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"

             xmlns:di="http://www.omg.org/spec/DD/20100524/DI"

             xmlns:tns="http://www.jboss.org/drools">



  <process processType="Private" isExecutable="true" id="com.sample.hello" name="Hello Process" >



    <!-- nodes -->

    <startEvent id="_1" name="Start" />

    <scriptTask id="_2" name="Hello" >

      <script>System.out.println("Hello World");</script>

    </scriptTask>

    <endEvent id="_3" name="End" >

        <terminateEventDefinition/>

    </endEvent>



    <!-- connections -->

    <sequenceFlow id="_1-_2" sourceRef="_1" targetRef="_2" />

    <sequenceFlow id="_2-_3" sourceRef="_2" targetRef="_3" />



  </process>



  <bpmndi:BPMNDiagram>

    <bpmndi:BPMNPlane bpmnElement="com.sample.hello" >

      <bpmndi:BPMNShape bpmnElement="_1" >

        <dc:Bounds x="16" y="16" width="48" height="48" />

      </bpmndi:BPMNShape>

      <bpmndi:BPMNShape bpmnElement="_2" >

        <dc:Bounds x="96" y="16" width="80" height="48" />

      </bpmndi:BPMNShape>

      <bpmndi:BPMNShape bpmnElement="_3" >

        <dc:Bounds x="208" y="16" width="48" height="48" />

      </bpmndi:BPMNShape>

      <bpmndi:BPMNEdge bpmnElement="_1-_2" >

        <di:waypoint x="40" y="40" />

        <di:waypoint x="136" y="40" />

      </bpmndi:BPMNEdge>

      <bpmndi:BPMNEdge bpmnElement="_2-_3" >

        <di:waypoint x="136" y="40" />

        <di:waypoint x="232" y="40" />

      </bpmndi:BPMNEdge>

    </bpmndi:BPMNPlane>

  </bpmndi:BPMNDiagram>



</definitions>

这个XML文件有两部分组成,第一部分("process"元素)包含了各种节点及其属性的定义,第二部分("BPMNDiagram"元素)包含了相关的图形化信息,比如节点位置等。流程XML有且只有一个<process>元素,该元素包含了一些与该流程相关的参数(类型、名称、id及包名称等),该元素包含三个子部分,第一个是头(包含流程级别信息,如定义variables,globals,imports及lanes),第二个是节点集合,第三个是连接集合。在节点部分,流程中的每一个节点都对应一个特定的元素,定义各种参数,也可以为该节点类型定义子元素。

 

3.1.3使用(Process)API定义流程

 

尽管推荐使用图形化编辑器或者用XML(可以从内部API中摆脱出来),当然也可以直接使用API来定义流程。大部分重要的流程元素都放在org.jbpm.workflow.core和org.jbpm.workflow.core.node两个包中。使用快捷API("fluent API")可以比较轻松地用工厂构建一个可读的流程。构建完成之后还可以校验该流程是否合法。可以参考测试单元中的一些使用快捷API构建流程的样例。

 

3.2在应用程序中使用流程

 

在应用程序中调用引擎之前需要处理两个事情:(1)创建一个包含流程定义的Knowledge Base,(2)需要与引擎建立会话。

 

1.创建Knowledge Base:在定义好一个有效的流程之后需要把它加入到Knowledge Base中:

 KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();

kbuilder.add( ResourceFactory.newClassPathResource("MyProcess.rf"),  ResourceType.BPMN2 );

 

在把全部流程加入到构造器这后,可以按下面的代码创建一个Knowledge Base:


KnowledgeBase kbase = kbuilder.newKnowledgeBase();

 

注意如果Knowledge base存在错误的话(比如解析不了你的流程)会抛出一个异常。

 

2.启动流程:因为Knowledge base可以添加多个流程定义,你得告诉引擎需要启动哪一个。要激活特定的流程可以在会话对象上调用startProcess方法,例如:

 
StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();

ksession.startProcess("com.sample.hello");

 

startProcess方法的参数是需要启动的流程的标识,该标识是流程的一个属性,在图形化编辑器上点击画布会显示在属性视图中。

 

如果需要传递额外的参数给流程的话,可以使用startProcess(String processId, Map parameters)方法。这些以名-值对存放的参数会复制到实例的顶层变量中。

 

 

3.3节点类型

 

BPMN2流程是一些相互连接的不同类型节点组成的流图表。流程本身拥有以下属性:

  • Id:唯一标识
  • Name:显示名称
  • Version:版本号
  • Package:所在包
  • Variables:变量可以在流程执行时存储数据,参考"数据(Data)"一节
  • Swimlanes:泳道,在执行人工任务时可以指定响应的角色,参考"人工任务(Human Task)"一节
  • Connection Layout:指定连接在画布上以何种方式可视化:
    • "Manual" 在开始与结束点之间画直线(也可能有中间的折点)
    • "Shortest path" 与前一种相似,但是这种方式在碰到障碍物时会试着绕过避免产生交叉线
    • "Manhattan" 使用水平或者垂直线

BPMN2支持不同类型的节点:

 

 

图3.3节点类型

 

 

  1.  开始事件(Start Event):流程的开始。一个流程只能有一个开始节点,开始节点不能有入连接,且只能有一个出连接,流程开始时将自动从该节点转到与之相连的第一个节点。该节点有以下属性:
    • Id:节点的标识(在节点容器内唯一)
    • Name:显示名称
  2. 结束事件(End Event):流程的结束。流程可以有一个或者多个结束节点,结束节点有一个入连接,不能有出连接。包含以下属性:
    • Id:节点的标识(在节点容器内唯一)
    • Name:显示名称
    • Terminate:结束事件可以终止整个流程的执行或者只结束当前路径。如果流程被终止,其他并行路径上的还在活动的节点将被取消。非终止结束事件只结束当前路径,其他并行的活动将继续执行。
  3. 规则任务(Rule Task):表示一套需要评估的规则。当流程运行到该节点时,这些规则将被评估。规则任务有一个入连接及一个出连接。规则可以用Drools规则格式在一个独立的文件中定义。使用ruleflow-group属性,规则可以组成规则流组。当流程到达规则任务时,如果存在ruleflow-group,引擎会执行规则,直到规则流组中不再有活动规则时,调度将自动转到下一个节点。也就是说属于当前活动规则流组的规则可能会改变其他规则的结果。If the ruleflow group was already active, the ruleflow group will remain active and execution will only continue if all active rules of the ruleflow group has been completed。规则任务包括以下属性:
    • Id:节点的标识(在节点容器内唯一)
    • Name:显示名称
    • RuleFlowGroup:规则流组的名称
  4. 发散网关(Diverging Gateway):用来创建流程分支。发散网关可以有一个入连接及二个或者以上的出连接。目前支持三种类型的网关节点:
    • AND或者并行,控制流在各个出连接上同时执行。
    • XOR或者独占为,只有一个出连接会被选择。决策是根据与各个出连接相关的约束的评估结果作出的。必须保证有一个出连接会被选择,否则规则流会抛了异常。
    • OR或者包容,所有的出连接只要条件评估返回真"true"就会被选择。条件部分与独占网关相似除了没有优先级。同样必须保证至少有一个出连接会被选择,否则会产生异常。
    • 发散网关包含以下属性:
    • Id:节点的标识(在节点容器内唯一)
    • Name:显示名称
    • Type:节点分离类型,AND,XOR或者OR
    • Constraints:与各个出连接相关的约束条件
  5. 聚合网关(Converging Gateway):可用来同步分支。聚合网关可以有二个或以上的入连接及一个出连接,目前支持二种类型:
    • AND或者并行,等待全部的分支完成后继续。
    • XOR或者独占,有一个分支完成的情况下就继续向后执行。如果被多个入连接触发,那这些触发也将传递给下一下节点。
    • 包含以下属性:
    • Id:节点的标识(在节点容器内唯一)
    • Name:显示名称
    • Type:节点分离类型,AND,XOR
  6. 可复用子流程(Reusable Sub-Process):表示从当前流程用调用其他流程。子流程节点需要有一个入连接及一个出连接。当流程执行到子流程节点时,引擎会根据给定的标识启动对以的流程。该节点包含以下属性:
    • Id:节点的标识(在节点容器内唯一)
    • Name:显示名称
    • ProcessId:对应调用的流程标识
    • Wait for completion:等待子流程完成,如果该属性为"true",子流程节点只有当对应的子流程终止(完成或者取消)时才会继续向后执行,否则将在启动子流程之后立即向后执行。
    • Independent:独立流程。如果该属性为"true",子流程将作为一个独立流程启动,也就是说在当前流程结束时子流程将不会终止。否则在当前流程结束时所有还在活动的子流程将被取消。
    • On-entry及on-exit动作:在进入或者离开节点时执行的动作。
    • Parameter in/out mapping:子流程节点可以定义变量的输入/输出映射。如果当前流程中的变量名称与输入映射中的名称相同则将作为参数在启动子流程时传入。如果子流程在结束时流程中变量名称与输出映射中的名称相同则这些变量值将被复制到当前流程中。需要注意的是如果使用输出映射那等待完成("Wait for completion")的属性必须为"true"。
  7. 脚本任务(Script Task):表示一个要执行的脚本。有一个入连接及一个出连接。一般用于可编程行为(如Java MVEL)或者特定的行为代码。在代码中可以访问globals,包含ProcessContext对象(可以访问ProcessInstance,NodeInstance,访问或者设置变量,调用kcontext.getKnowledgeRuntime()访问ksession)的预定义变量kcontext。流程运行到该节点时执行相应的动作然后继续后面节点。该类型包含以下的属性:
    • Id:节点的标识(在节点容器内唯一)
    • Name:显示名称
    • Action:定义动作
  8. 计时器事件(Timer Event):按给定周期触发一次或多次的计时器。有一个入连接,一个出连接。计时器在触发事件之前会延迟指定时长(毫秒为单位)。流程运行到该类型节点时启动相应的计时器。如果节点被取消(例如流程完成或者退出)那计时器也将被取消。可参考计时器(Timer)部分。该类型包含以下属性:
    • Id:节点的标识(在节点容器内唯一)
    • Name:显示名称
    • Timer delay:延迟时长(毫秒为单位)。
  9. 错误事件(Error Event):错误事件在流程中作为异常状态的一个信号。有一个入连接,没有出连接。当流程到达错误事件时将按给定的名称抛出一个异常,此时流程将查找与之匹配的异常处理程序,如果找不到那么该流程实例将会退回。错误事件包含以下属性:
    • Id:节点的标识(在节点容器内唯一)
    • Name:显示名称
    • FaultName:错误名称,用来搜索匹配的异常处理程序。
    • FaultVariable:变量名称,存储与错误相关的数据。如果异常处理程序找到,这些数据会传递给它。
  10. 消息事件(Message Event):在流程执行期,消息事件用来响应内部或者外部事件。没有入连接,有一个出连接。消息事件指定期待的事件类型,如果该类型的事件发生,则与该类型消息事件节点相连的节点会被触发。该类型包含以下属性:
    • Id:节点的标识(在节点容器内唯一)
    • Name:显示名称
    • EventType:期待的事件类型
    • VariableName:变量名称,存放与事件相关的数据。
    • Scope:事件范围。可以只监听流程内部的事件,譬如用processInstance.signalEvent(String type,Object data)触发的。如果定义成外部的,则将在流程引擎级别监听,譬如用ksession.signalEvent(String type,Object data)触发的事件。
  11. 用户任务(User Task):流程可以包含需要由人工角色执行的任务。该任务是一个需要由人工角色来执行的不可分割的原子任务。用户任务可以与泳道配合使用把多个任务分配给一类人员。更多的细节可以参考人工任务章节。实际上用户任务并不比服务节点类型多什么。该类型包含以下属性:
    • Id:节点的标识(在节点容器内唯一)
    • Name:显示名称
    • TaskName:人工任务(human task)名称
    • Priority:优先级
    • Comment:注释
    • ActorId:处理该任务的角色标识,多个标识可以用逗号(",")分隔
    • GroupId:处理该任务的组标识,多个标识可以用逗号(",")分隔
    • Skippable:是否可以跳过该任务,任务角色可以决定是否执行该任务。
    • Content:与任务相关的数据
    • Swimlane:该任务所属泳道的名称。使用泳道可以方便地将多个任务指派给相同的角色。参考人工服务章节了解泳道更多的细节。
    • On-entry及on-exit动作:在进入或者离开节点时执行的动作
    • Parameter mapping:从流程中复制变量作为参数传递给任务。
    • Result mapping:结果映射。任务完成之后复制结果到流程变量中。变量"Result"包含返回的数据,变量"ActorId"包含执行该任务的角色标识。
  12. 子流程(Sub-Process):子流程节点是一个容器节点,可以包含其他节点。子流程节点不仅可以作为流程一部分嵌入,还可以定义附加的变量供容器内部的节点访问。有一个入连接,一个出连接。像一般流程一样包含一个用于启动的开始节点。包含一个或多个结束节点。当子流程内部不再存在活动的节点时子流程结束。拥有以下的属性:
    • Id:节点的标识(在节点容器内唯一)
    • Name:显示名称
    • Variables:定义附加的变量存放执行期间的数据。参考数据("Data")章节了解更多的细节。
  13. 多实例(Multiple Instances):多实例是一种特殊类型的子流程,允许多次执行被包含的流程段。多实例有一个入连接及一个出连接。在继续执行前将等待每个子流程的结束。它包含以下属性:
    • Id:节点的标识(在节点容器内唯一)
    • Name:显示名称
    • CollectionExpression:变量名称,表示一个可迭代的元素集合。该集合的类型是java.util.Collection。
    • VariableName:变量名称包含集合中的当前元素。可以让复合节点内的节点集访问被选择的元素。
  14. 服务任务(Service Task)或者工作事项节点(Work Item node):表示流程中一个需要执行的抽象的工作单元。所有在流程引擎外部需要执行的工作都需要用服务任务来表示。已经定义好了一些不同类型的服务,如发送邮件,消息日志等。用户可以用一个唯一名称为工作项定义领域服务(domain-specific services),定义与该项工作相关的输入参数及输出结果。参考领域(domain-specific)流程章节查看详细信息及示例,了解如何在流程中定义、使用工作事项。服务任务带一个入连接及一个出连接。
    • Id:节点的标识(在节点容器内唯一)
    • Name:显示名称
    • Parameter mapping:从流程中复制变量作为参数传递给工作事项。
    • Result mapping:结果映射。任务完成之后复制结果到流程变量中。比如,名为"FileFiinder"的工作事项返回了一个文件列表与结果参数Files的搜索条件匹配,则这个列表就会绑定到流程变量中。
    • On-entry及on-exit:在进入或者离开节点时执行的动作
    • Additional parameters:每个类型的工作事项都可以定义与之相关的附加参数,比如"Email"会定义From,To,Subject,Body,用户可以直接给这些参数赋值,也可以用映射的方式从流程变量中复制值。如果两者同时指定,那映射方式将优先。字符串(String)类型的参数可以用#{expression}方式嵌入值。创建工作事项时将取得该值,包含的表达式则用toString()的结果替换。表达式可以是一个简单的变量名称(解析为变量值),也可以是像MVEL之类的高级表达式,如#{person.name.firstname}。

3.4数据(Data)

 

在以流程图的方式从控制流的角度来看流程的同时,也有必要从数据的角度来看流程。流程的执行伴随着数据的获取、存储、传递与使用。

 

一般用变量来存放流程执行时的运行数据。变量由名称及类型来定义,可以是布尔量、整形、字符串之类的基本数据类型,也可以是从Object派生的子类。变量定义在一个范围之内,最高级是流程,子流程可以定义子范围。子范围内定义的变量只能被其内的节点访问。

 

访问变量时流程将在合适的范围内查找,允许变量范围的嵌套。节点总是在其父容器的范围内开始查找,找不到则向上递推,直到流程实例一级。如果变量找不到,对于读操作则返回null,如果是写操作则生成一个错误消息,流程继续执行。

 

变量有多种使用方式:

  • 流程级别的变量在启动流程时从startProcess方法的参数中进行映射。
  • 执行动作时可以用变量名作为参数直接访问该变量:  

//  call method on the process variable  "person"
person.setAge(10);

 

通过Knownledge Context改变变量的值

kcontext . setVariable ( variableName ,  value );

 

  • 服务任务及可复用子流程可以用映射的方式将变量值传递出去,或者用#{expression}注入字符串的方式传递。
  • 其他类型的节点也可以访问数据。

最后,流程与规则可以访问globals,例如在Knowledge Session中定义的变量与数据。可以在动作中像访问变量一样访问globals。如:

 

kcontext . getKnowledgeRuntime (). insert (   new   Person (...)   );

 

3.5约束(Constraints)

 

流程中的许多地方都要用到约束,比如发散网关。jBPM支持两种类型的约束:

  • 代码约束(Code constraints)是一个布尔表达式,在使用时进行求值。当前支持两种表达方式:Java 及 MVEL。这两者都可以直接访问globals及流程中的变量。比如下面这个代码片段中person是一个流程变量:

return  person . getAge ()   >   20 ;

 

类似的MVEL代码:

 

return person.age > 20;

 

  • 规则约束(Rule constraints)等效于正常的Drools规则条件。它们使用Drools规则语言的语法来表达复杂的约束。这些规则可以直接访问内存中的数据,如globals。

Person( age > 20 )

 

此处测试内存中年龄大于20的人员。

 

规则约束不能直接访问流程实例中的变量。当然,在把实例添加的工作内存中,并且与约束进行匹配的情况下也可以引用该流程实例。我们已经添加了逻辑确保工作内存中类型为WorkflowInstance的变量processIntance只会与当前流程实例匹配而不是其他的。你自己还得保证把实例添加到会话中并且同步的更新它,如用代码或者on-entry或者on-exit或者其他明确的动作中。下面这段规则约束的例示例代码用名为"name"的流程变量的值搜索与之匹配的人员:

 

processInstance : WorkflowProcessInstance()
Person( name == ( processInstance.getVariable("name") ) )
# add more constraints here ...

 

3.6动作(Actions)

 

动作可以有不同的使用方式:

  • 在脚本任务(Script Task)中
  • 某些类型节点的on-entry及on-exit动作中。

动作可以访问globals,流程变量及预定义的kcontext变量。那些类型为org.drools.runtimes.process.ProcessContext可以在下面这些任务中使用:

  • 获取当前节点实例(如果有效)。节点实例上可以查询名称或者类型之类的数据,也可以取消该实例:

NodeInstance  node  =  kcontext . getNodeInstance ();

String name = node.getNodeName();

  • 获取当前流程实例。可以查询名称,标识,processId等数据,退出实例或者触发一个内部事件。

NodeInstance  node  =  kcontext . getNodeInstance ();

String name = node.getNodeName();

  • 获取或设置变量值
  • 用Knowledge运行时可以启动流程,触发外部事件,插入数据等。

jBPM支持两种语言:Java 及 MVEL。Java动作可以是一段有效的Java代码,MVEL动作则用MVEL表示。MVEL可以用Java代码,同时支持对参数嵌套访问(比如用person.name代替person.getName()),以及其他脚本增强。因此,MVEL对于业务用户来说更方便。下面这段例子是在打印流程变量"requester"中的存储的人员对象姓名的一个动作:

 

// Java dialect
System.out.println( person.getName() );

//  MVEL dialect
System.out.println( person.name
);

 

3.7事件(Events)

 

 

图3.4使用事件的流程

 

 流程引擎在流程执行过程中,通过请求工作事项的执行,等待结果返回保证所有相关任务按照既定计划调度。然而,流程也会需要对一些不是引擎发出的请求事件作出响应。明确这些事件可以让流程作者决定在碰到这些事件时流程如何反应。

 

事件拥有类型及相关的数据,这些用户可以自由定义。

 

流程可以用消息事件决定如何响应事件。事件节点需要指定感兴趣的事件类型。可以通过定义变量名称来获取事件相关的数据,这样后续节点就可以访问该数据并执行适当的动作。

 

有多种方式向正在运行的流程实例发送事件信号:

  • 内部事件:流程内部的任何动作(动作节点,某些节点上的on-entry及on-exit动作)都可以向流程实例发送事件信号:

kcontext . getProcessInstance (). signalEvent ( type ,  eventData );

  • 外部事件:下面的代码从外部向流程实例发送事件消息:

processInstance . signalEvent ( type ,  eventData );

  • 使用事件关连(event correlation)的外部事件:与直接向流程实例发送事件信号不同,允许引擎根据流程实例感兴趣的基于事件类型的关连来决定。当某一类型的事件发生时,哪些包含正在监听此类事件的节点的流程实例就会被通知到。下面的代码向引擎发送事件信号:

ksession . signalEvent ( type ,  eventData );

 

事件也被用来启动流程。如果有一个定义了特定类型的消息开始事件,那么引擎每次得到此类事件信号时都会启动一个新的实例。

 

3.8计时器(Timer)

 

计时器在单次或者重复触发之前会等待一个预设的时间量。可用于定时监控、周期性的触发某个逻辑或者按固定时间片执行某些动作。

 

计时器事件定义了一个延迟,是以毫秒为单位的一个时长,表示在节点激活后到事件触发之前等待的时间段。

 

计时器服务负责保证适时地触发计时事件。可以取消计时器,之后不再触发事件。

 

流程中有两种方式使用计时器:

  • 在流程中添加计时器事件。在激活时将一次性或者重复启动计时器、触发器,以激活该节点的后续节点。也就是说如果是一个周期性的计时器的话将多次被触发。取消计时器节点将同时取消计时器,之后不再触发事件。
  • 可以作为边界事件与子流程关联。目前只能直接以XML方式定义使用,后面将在新的BPMN2编辑器中提供支持。

     

     

    随着时间的变化,流程或许会因为自身原因、需求变化等因素而演化。实际上并不能更新流程,只能发布一个新的版本,旧的依然存在。这是因已经存在的流程实例有可能还需要这些定义。所以新的流程可以用一个相同的名称,但是必须用不同的id,流程更新之后可以用版本来区分(版本参数只是一个字符串,流程框架对此没有要求也不校验,你可以按照更新的主次版本等选择一个自己的格式)。

     

    当流程更新之后,首先要考虑对于已经在运行的实例会发生什么。下面列举了几种可考虑的策略:

    • Proceed:按照流程实例启动时的定义继续运行。结果是已经运行的实例就像流程没更新过一样执行,而新的实例则按照新的定义启动。
    • Abort(重新启动):退出实例,如果需要的话按照新的定义重新启动新的实例。
    • Transfer:流程实例迁移到新的定义上,一旦迁移成功,将按照更新过的流程逻辑继续运行。

    默认情况下,jBPM使用继续执行(proceed)的策略,允许部署多个版本的流程,已经运行的实例按其启动时的流程定义运行。实例的迁移是很困难的,下面的这一段将给出解释。 

3.9更新流程

 

 

3.9.1流程实例迁移

 

流程实例包含了要持续执行的全部运行时信息,包括相关的数据与流程图上的状态。对于每个激活的节点也有节点实例与之对应,某些特定节点还包含附加数据。

 

流程实例只包含运行时状态,并且与特定的流程关联(使用id引用间接地关联),该流程表示实例执行时所依赖的流转逻辑,所以更新实例到的版本使其按新的逻辑运行只需要简单地将旧的流程id更新到新的id。

 

然而,不能就这么认定流程实例(包括变量、节点实例)的状态能够恰当的迁移。如果旧的流程中的各个状态都被保留,新的流程只是做为一些扩展的话,那流程实例中的状态都不需要作任何变动。如果某些状态被删除了,或者被分割成多个状态,那处于这些状态中的已经运行的实例就不能再简单的更新了。另外一种情况是新的流程中引入了新的变量,这些变量需要正确初始化以保证那些被更新的实例在流程剩下部分中能正常使用。

可以用WorkflowProcessInstanceUpgrader来更新流程实例。默认情况下,jBPM采用id同名匹配的映射规则。当然也可以自己提供新旧节点之间的映射关系。存在复合节点时,节点的标识符可以用与父节点标识组合(冒号分隔)的方式生成。

 

//  create the session and start the process  "com.sample.process"

KnowledgeBuilder kbuilder = ...

StatefulKnowledgeSession ksession = ...

ProcessInstance processInstance = ksession.startProcess("com.sample.process");


// add a new version of the process "com.sample.process2"

kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();

kbuilder.add(..., ResourceType.BPMN2);

kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());


// migrate process instance to new version

Map<String, Long> mapping = new HashMap<String, Long>();

// top level node 2 is mapped to a new node with id 3

mapping.put("2", 3L); 

// node 2, which is part of composite node 5, is mapped to a new node with id 4

mapping.put("5.2", 4L); 

WorkflowProcessInstanceUpgrader.upgradeProcessInstance(

   ksession, processInstance.getId(),

   "com.sample.process2", mapping);

 

如果这种映射的效率不高,你可以根据实际情况提供自己的映射机制。更新时需要先同实例断开,再更改,再重新连接,就像WorkflowProcessInstanceUpgrader一样。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值