美文网首页
Flowable实战(八)BPMN2.0 任务

Flowable实战(八)BPMN2.0 任务

作者: 金杨杰 | 来源:发表于2022-01-15 23:57 被阅读0次

      任务是流程中最重要的组成部分。Flowable提供了多种任务类型,以满足实际需求。

      常用任务类型有:

    • 用户任务

    • Java Service任务

    • 脚本任务

    • 业务规则任务

    • 执行监听器

    • 任务监听器

    • 多实例

      集成扩展的任务类型有:

    • 手动任务

    • Java接收任务

    • Shell任务

    • 补偿处理器

    • Web Service任务

    • 邮件任务

    • Http任务

    • Camel任务

    • Mule任务

      任务的图形都是以一个圆角矩形为基础,在左上角添加具体类型的图标。

    一、常用的任务类型

    1.1 用户任务

    1.1.1 描述

      “用户任务(user task)”指需要人工执行的任务。当流程执行到达用户任务时,流程实例会停止等待,直到用户触发完成任务动作。

    1.1.2 图示

      用户任务用左上角有一个小用户图标的标准任务(圆角矩形)表示。

    用户任务.png

    1.1.3 XML表示

      用户任务在XML中如下定义。其中id是必须属性,name是可选属性。

        <userTask id="theTask" name="重要任务" />
    

    1.1.4 到期日期

      每个任务都可以设置到期日期(due date)。

      可以指定固定时间或相对时间,比如,当dueDate为“PT30M”时,表示到达任务30分钟后到期。

      到期日期必须符合java.util.Date或java.util.String(ISO8601格式)。

      实际应用,我们指定为变量值。

        <userTask id="theTask" name="Important task" flowable:dueDate="${dateVariable}"/>
    

      任务的到期日期可以使用TaskService,或者在TaskListener中使用传递的DelegateTask修改。

    1.1.5 任务指派

    • 指派确定的办理人
        <userTask id="theTask" name="重要任务" flowable:assignee="jinyangjie"/>
    
    • 指派潜在办理人
        <userTask id="theTask" name="重要任务" flowable:candidateUsers="jinyangjie, zhangsan" />
    
    • 指派潜在办理组
        <userTask id="theTask" name="重要任务" flowable:candidateGroups="leader, manager" />
    

      更多任务指派的内容,已在“用户和组”的篇章中介绍,这里不再赘述。

    1.2 Java Service任务

    1.2.1 描述

      Java Service任务(Java service task)用于调用Java类。Java Service不属于BPMN2.0规范,而是Flowable的自定义扩展。

    1.2.2 图示

      服务任务用左上角有一个小齿轮图标的圆角矩形表示。

    javaService.png

    1.2.3 XML表示

      有三种方法声明如何调用Java逻辑,下面分别介绍:

    • 调用固定的类

      使用flowable:class属性提供全限定类名(fully qualified classname),指定流程执行时调用的类,该类必须实现JavaDelegate或ActivityBehavior接口。

        <serviceTask id="javaService" flowable:class="com.example.service.MyJavaDelegate" />
    
    • 调用动态类

      使用flowable:delegateExpression属性提供委托对象(delegation object)的表达式。该功能和flowable:class类似,同样需要实现JavaDelegate或ActivityBehavior接口,只不过这里不是指定一个具体的实现类,而是查询指定名称的Bean对象。

        <serviceTask id="javaService" flowable:delegateExpression="${myDelegateExpressionBean}" />
    

      myDelegateExpressionBean是一个实现了JavaDelegate接口的bean,定义在Spring容器中。

    • 调用类的指定方法或属性值

      使用flowable:expression属性指定类的方法或属性值。同样的,该类需要实现JavaDelegate或ActivityBehavior接口。

        <serviceTask id="javaService" flowable:expression="#{printer.printMessage()}" />
    

      将在名为printer的对象上调用printMessage方法(不带参数)。当然也可以为表达式中使用的方法传递变量。

      属性值示例:

        <serviceTask id="javaService" flowable:expression="#{printer.ready}" />
    

      会调用名为printer的bean的ready参数的getter方法,getReady(不带参数)。该值会被解析为执行的流程变量。

    1.2.4 具体实现实例

      下面是一个Java类的示例,用于将流程变量String改为大写。这个类通过实现org.flowable.engine.delegate.JavaDelegate接口,可以在流程执行中被调用。

      同时,需要重写execute(DelegateExecution)方法实现业务逻辑。这个方法就是引擎将调用的方法。另外,通过该方法中的DelegateExecution参数可以访问流程实例的各种信息。

        public class ToUppercase implements JavaDelegate {
          public void execute(DelegateExecution execution) {
            String var = (String) execution.getVariable("input");
            var = var.toUpperCase();
            execution.setVariable("input", var);
          }
        }
    

      如果实现org.flowable.engine.impl.delegate.ActivityBehavior接口,可以访问更强大的引擎功能,例如,可以影响流程的控制流程。但注意这并不是好的实践,需要避免这么使用。

    1.2.5 任务的返回值

      服务执行的返回值(仅对使用表达式的服务任务),可以通过为服务任务定义的'flowable:resultVariable'属性设置为流程变量。可以是已经存在的,或者新的流程变量。 如果指定为已存在的流程变量,则流程变量的值会被服务执行的返回值覆盖。 如果不指定结果变量名,则服务任务的返回值将被忽略。

        <serviceTask id="aMethodExpressionServiceTask"
            flowable:expression="#{myService.doSomething()}"
            flowable:resultVariable="myVar" />
    

      在上例中,服务执行的结果(调用'doSomething()'方法的返回值),在服务执行完成后,会设置为名为'myVar'的流程变量。

    1.2.6 异常处理

      当执行自定义逻辑时,通常需要捕获并在流程中处理特定的业务异常。Flowable提供了多种方式。

    1.2.6.1 抛出BPMN错误

      可以在服务任务或脚本任务的用户代码中抛出BPMN错误。可以在Java委托、脚本、表达式与委托表达式中,抛出特殊的FlowableException:BpmnError。引擎会捕获这个异常,并将其转发至合适的错误处理器,如错误边界事件或错误事件子流程。

        public class ThrowBpmnErrorDelegate implements JavaDelegate {
          public void execute(DelegateExecution execution) throws Exception {
            try {
              executeBusinessLogic();
            } catch (BusinessException e) {
              throw new BpmnError("BusinessExceptionOccurred");
            }
          }
        }
    

      构造函数的参数是错误代码。错误代码决定了处理这个错误的错误处理器。

      这个机制只应该用于业务错误,需要通过流程中定义的错误边界事件或错误事件子流程处理。技术错误应该通过其他异常类型表现,并且通常不在流程内部处理。

    1.2.6.2 异常映射

      可以使用mapException扩展,直接将Java异常映射至业务异常(错误)。单映射是最简单的形式:

        <serviceTask id="servicetask1" flowable:class="...">
          <extensionElements>
            <flowable:mapException
                  errorCode="myErrorCode1">com.example.SomeException</flowable:mapException>
          </extensionElements>
        </serviceTask>
    

      在上面的代码中,如果服务任务抛出org.flowable.SomeException的实例,引擎会捕获该异常,并将其转换为带有给定errorCode的BPMN错误。然后就可以像普通BPMN错误完全一样地处理。其他的异常没有映射,仍将抛出至API调用处。

      也可以在单行中使用includeChildExceptions属性,映射特定异常的所有子异常。

        <serviceTask id="servicetask1" flowable:class="...">
          <extensionElements>
            <flowable:mapException errorCode="myErrorCode1"
                   includeChildExceptions="true">com.example.SomeException</flowable:mapException>
          </extensionElements>
        </serviceTask>
    

      上面的代码中,Flowable会将SomeException的任何直接或间接的子类,转换为带有指定错误代码的BPMN错误。 当未指定includeChildExceptions时,视为“false”。

    1.2.6.3 默认映射

      默认映射最常用。默认映射是一个不指定类的映射,可以匹配任何Java异常:

        <serviceTask id="servicetask1" flowable:class="...">
          <extensionElements>
            <flowable:mapException errorCode="myErrorCode1"/>
          </extensionElements>
        </serviceTask>
    

      除了默认映射,会按照从上至下的顺序检查映射,使用第一个匹配的映射。只在所有映射都不能成功匹配时使用默认映射。 只有第一个不指定类的映射会作为默认映射。默认映射忽略includeChildExceptions

    1.2.6.4 异常顺序流

      还有种推荐用法,在发生异常时,将流程执行路由至另一条路径。下面是一个例子。

        <serviceTask id="servicetask1" flowable:class="com.example.ThrowsExceptionBehavior">
        </serviceTask>
    
        <sequenceFlow id="no-exception" sourceRef="javaService" targetRef="theEnd" />
        <sequenceFlow id="exception" sourceRef="javaService" targetRef="fixException" />
    

      服务任务有两条出口顺序流,命名为exceptionno-exception。在发生异常时,使用顺序流ID控制流程流向:

        public class ThrowsExceptionBehavior implements ActivityBehavior {
    
          public void execute(DelegateExecution execution) {
            String var = (String) execution.getVariable("var");
    
            String sequenceFlowToTake = null;
            try {
              executeLogic(var);
              sequenceFlowToTake = "no-exception";
            } catch (Exception e) {
              sequenceFlowToTake = "exception";
            }
            DelegateHelper.leaveDelegate(execution, sequenceFlowToTake);
          }
    
        }
    

    1.3 脚本任务

    1.3.1 描述

      脚本任务(script task)是自动执行的活动。当流程执行到达脚本任务时,会执行相应的脚本。

    1.3.2 图示

      脚本任务用左上角有一个小“脚本”图标的标准BPMN 2.0任务(圆角矩形)表示。

    脚本任务.png

    1.3.3 XML表示

      脚本任务使用scriptscriptFormat元素定义。

        <scriptTask id="theScriptTask" scriptFormat="groovy">
          <script>
            sum = 0
            for ( i in inputArray ) {
              sum += i
            }
          </script>
        </scriptTask>
    

      默认情况下,JavaScript包含在每一个JDK中,因此不需要添加任何JAR文件。如果想使用其它脚本引擎,则需要在classpath中添加相应的jar,并使用适当的名字。例如,Flowable单元测试经常使用Groovy。Groovy脚本引擎与groovy-all JAR捆绑在一起。添加如下依赖:

        <dependency>
            <groupId>org.codehaus.groovy</groupId>
            <artifactId>groovy-all</artifactId>
            <version>2.x.x<version>
        </dependency>
    

    1.3.4 脚本中的变量

      到达脚本引擎的执行中,所有的流程变量都可以在脚本中使用。在这个例子里,脚本变量'inputArray'实际上就是一个流程变量(一个integer的数组)。

        <script>
            sum = 0
            for ( i in inputArray ) {
              sum += i
            }
        </script>
    

      在脚本中设置变量的例子:

        <script>
            def scriptVar = "test123"
            execution.setVariable("myVar", scriptVar)
        </script>
    

    注意:下列名字是保留字,不能用于变量名:out,out:print,lang:import,context,elcontext

    1.3.5 脚本任务的结果

      脚本任务的返回值,可以通过为脚本任务定义的'flowable:resultVariable'属性设置为流程变量。可以是已经存在的,或者新的流程变量。如果指定为已存在的流程变量,则流程变量的值会被脚本执行的结果值覆盖。如果不指定结果变量名,则脚本结果值将被忽略。

        <scriptTask id="theScriptTask" scriptFormat="juel" flowable:resultVariable="myVar">
          <script>#{echo}</script>
        </scriptTask>
    

      在上面的例子中,脚本执行的结果(解析表达式'#{echo}'的值),将在脚本完成后,设置为名为'myVar'的流程变量。

    1.4 业务规则任务

    1.4.1 描述

      在企业应用中,推荐做法是使用可维护的规则库来管理复杂多变的业务规则,将业务代码和规则分开维护,一旦规则有变动,只需修改预设规则即可,而不会影响到业务代码。

      业务规则任务可以根据流程变量的值处理预设的业务规则。Flowable支持目前最流行的规则引擎——Drools。只需把含有业务规则任务的流程文件和规则引擎文件“.drl”一同打包部署到系统中,同时添加Drools的jar包,即可实现Flowable驱动规则引擎。

    1.4.2 图示

      业务规则任务显示为带有表格图标的圆角矩形。

    业务规则任务.png

    1.4.3 XML表示

      要执行业务规则,需要定义输入与结果变量。输入变量可以用流程变量的列表定义,使用逗号分隔。输出变量只能有一个变量名,如果没有指定结果变量名,默认为org.flowable.engine.rules.OUTPUT。

        <process id="simpleBusinessRuleProcess">
          <startEvent id="theStart" />
          <sequenceFlow sourceRef="theStart" targetRef="businessRuleTask" />
    
          <businessRuleTask id="businessRuleTask" flowable:ruleVariablesInput="${order}"
              flowable:resultVariable="rulesOutput" />
    
          <sequenceFlow sourceRef="businessRuleTask" targetRef="theEnd" />
    
          <endEvent id="theEnd" />
        </process>
    

      也可以将业务规则任务配置为只执行部署的.drl文件中的一组规则。要做到这一点,需要指定规则名字的列表,用逗号分隔。

        <businessRuleTask id="businessRuleTask" flowable:ruleVariablesInput="${order}"
              flowable:rules="rule1, rule2" />
    

      这样只会执行rule1与rule2。

      也可以定义需要从执行中排除的规则列表。

        <businessRuleTask id="businessRuleTask" flowable:ruleVariablesInput="${order}"
              flowable:rules="rule1, rule2" exclude="true" />
    

      这个例子中,除了rule1与rule2之外,其它所有与流程定义一起部署的规则都会被执行。

    注意:集成Drools的业务规则任务,是企业应用中的重要内容,需要重点掌握。

    1.5 执行监听器

    1.5.1 描述

      执行监听器(execution listener)可以在流程执行中发生特定的事件时,执行外部Java代码或计算表达式。可以被捕获的事件有:

    • 流程实例的启动和结束。
    • 流程执行转移。
    • 活动的启动和结束。
    • 网关的启动和结束。
    • 中间事件的启动和结束。
    • 启动事件的结束,和结束事件的启动。

    1.5.2 XML表示

      下面的流程定义包含了三个执行监听器:

    <process id="executionListenersProcess">
    
      <extensionElements>
        <flowable:executionListener
          class="org.flowable.examples.bpmn.executionlistener.ExampleExecutionListenerOne"
          event="start" />
      </extensionElements>
    
      <startEvent id="theStart" />
      <sequenceFlow sourceRef="theStart" targetRef="firstTask" />
    
      <userTask id="firstTask" />
      <sequenceFlow sourceRef="firstTask" targetRef="secondTask">
        <extensionElements>
          <flowable:executionListener
            class="org.flowable.examples.bpmn.executionListener.ExampleExecutionListenerTwo" />
        </extensionElements>
      </sequenceFlow>
    
      <userTask id="secondTask" >
        <extensionElements>
          <flowable:executionListener
            expression="${myPojo.myMethod(execution.event)}"
            event="end" />
        </extensionElements>
      </userTask>
      <sequenceFlow sourceRef="secondTask" targetRef="thirdTask" />
    
      <userTask id="thirdTask" />
      <sequenceFlow sourceRef="thirdTask" targetRef="theEnd" />
    
      <endEvent id="theEnd" />
    
    </process>
    

      第一个执行监听器将在流程启动时收到通知。这个监听器是一个外部Java类(ExampleExecutionListenerOne),并且需要实现org.flowable.engine.delegate.ExecutionListener接口。当该事件发生时(这里是start事件),会调用notify(ExecutionListenerExecution execution)方法。

        public class ExampleExecutionListenerOne implements ExecutionListener {
    
          public void notify(ExecutionListenerExecution execution) throws Exception {
            execution.setVariable("variableSetInExecutionListener", "firstValue");
            execution.setVariable("eventReceived", execution.getEventName());
          }
        }
    

      也可以使用实现了org.flowable.engine.delegate.JavaDelegate接口的委托类。这些委托类也可以用于其他的结构,如服务任务的委托。

      第二个执行监听器在流程执行转移时被调用。请注意listener元素并未定义event,因为在转移上只会触发take事件。当监听器定义在转移上时,event属性的值将被忽略。

    最后一个执行监听器在secondTask活动结束时被调用。监听器声明中没有使用class,而是定义了expression。这个表达式将在事件触发时计算/调用。

        <flowable:executionListener expression="${myPojo.myMethod(execution.eventName)}" event="end" />
    

      与其他表达式一样,可以使用与解析execution变量。

    1.5.3 执行监听器上的字段注入

      使用通过class属性配置的执行监听器时,可以使用字段注入。

      下面的代码片段展示了一个简单的示例流程,带有一个使用了字段注入的执行监听器。

    <process id="executionListenersProcess">
      <extensionElements>
        <flowable:executionListener
            class="org.flowable.examples.bpmn.executionListener.ExampleFieldInjectedExecutionListener"
            event="start">
    
          <flowable:field name="fixedValue" stringValue="Yes, I am " />
          <flowable:field name="dynamicValue" expression="${myVar}" />
    
        </flowable:executionListener>
      </extensionElements>
    
      <startEvent id="theStart" />
      <sequenceFlow sourceRef="theStart" targetRef="firstTask" />
    
      <userTask id="firstTask" />
      <sequenceFlow sourceRef="firstTask" targetRef="theEnd" />
    
      <endEvent id="theEnd" />
    </process>
    

      ExampleFieldInjectedExecutionListener类将连接两个字段(一个是固定值-fixedValue,另一个是动态值-dynamicValue),并将其存储在'var'流程变量中。

        @Deployment(resources = {
          "org/flowable/examples/bpmn/executionListener/ExecutionListenersFieldInjectionProcess.bpmn20.xml"})
        public void testExecutionListenerFieldInjection() {
          Map<String, Object> variables = new HashMap<String, Object>();
          variables.put("myVar", "listening!");
    
          ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(
              "executionListenersProcess", variables);
    
          Object varSetByListener = runtimeService.getVariable(processInstance.getId(), "var");
          assertNotNull(varSetByListener);
          assertTrue(varSetByListener instanceof String);
    
          // 结果为固定注入字段及注入表达式的连接
          assertEquals("Yes, I am listening!", varSetByListener);
        }
    

    1.6 任务监听器

    1.6.1 描述

      任务监听器(task listener)用于在特定的任务相关事件发生时,执行自定义的Java逻辑或表达式。

    1.6.2 XML表示

      任务监听器只能在流程定义中作为用户任务的子元素。请注意,任务监听器是一个Flowable自定义结构,因此也需要作为BPMN 2.0 extensionElements,放在flowable命名空间下。

        <userTask id="myTask" >
          <extensionElements>
            <flowable:taskListener event="create" class="com.example.MyTaskCreateListener" />
          </extensionElements>
        </userTask>
    

    1.6.3 任务监听器属性:

    1.6.3.1 event

      触发任务监听器的任务事件类型,必填项。可用的事件有:

    • create(创建):当任务已经创建,并且所有任务参数都已经设置时触发。
    • assignment(指派):当任务已经指派给某人时触发。请注意:当流程执行到达用户任务时,在触发create事件之前,会首先触发assignment事件。这顺序看起来不太自然,但是有实际原因的:当收到create事件时,我们通常希望能看到任务的所有参数,包括办理人。
    • complete(完成):当任务已经完成,从运行时数据中删除前触发。
    • delete(删除):在任务即将被删除前触发。请注意任务由completeTask正常完成时也会触发。
    1.6.3.2 class

      需要调用的委托类。这个类必须实现org.flowable.engine.delegate.TaskListener接口。

        public class MyTaskCreateListener implements TaskListener {
          public void notify(DelegateTask delegateTask) {
            // 这里是要实现的业务逻辑
          }
        }
    

      也可以使用字段注入,为委托类传递流程变量或执行。请注意委托类的实例在流程部署时创建(与Flowable中其它的委托类一样),这意味着该实例会在所有流程实例执行中共享。

    1.6.3.3 expression

      指定在事件发生时要执行的表达式(不能与class属性一起使用)。可以为被调用的对象传递DelegateTask对象与事件名(使用task.eventName)作为参数。

        <flowable:taskListener event="create" expression="${myObject.callMethod(task, task.eventName)}" />
    
    1.6.3.4 delegateExpression

      指定一个能够解析为TaskListener接口实现类的对象的表达式。

        <flowable:taskListener event="create" delegateExpression="${myTaskListenerBean}" />
    

    1.7 多实例

    1.7.1 描述

      多实例活动(multi-instance activity)是在业务流程中,为特定步骤定义重复的方式。在编程概念中,多实例类似for each结构:可以为给定集合中的每一条目,顺序或并行地,执行特定步骤,甚至是整个子流程。

      网关和事件不能设置为多实例。

      按照BPMN2.0规范的要求,用于为每个实例创建执行的父执行,会提供下列变量:

    • nrOfInstances:实例总数。
    • nrOfActiveInstances:当前活动的(即未完成的)实例数量。对于顺序多实例,这个值总为1。
    • nrOfCompletedInstances:已完成的实例数量。

      可以调用execution.getVariable(x)方法获取这些值。

      另外,每个被创建的执行,都有局部变量(对其他执行不可见,也不存储在流程实例级别):

    • loopCounter:给定实例在for-each循环中的index

    1.7.2 图示

      如果一个活动是多实例,将通过在该活动底部的三条短线表示。三条竖线代表实例会并行执行,而三条横线代表顺序执行。

    多实例.png

    1.7.3 XML表示

      要将活动变成多实例,该活动的XML元素必须有multiInstanceLoopCharacteristics子元素

        <multiInstanceLoopCharacteristics isSequential="false|true">
         ...
        </multiInstanceLoopCharacteristics>
    

      isSequential属性代表了活动的实例为顺序还是并行执行。

      有4种不同方法可以配置数量。

    1.7.3.1 指定数字

      通过loopCardinality子元素,直接指定数字:

        <multiInstanceLoopCharacteristics isSequential="false|true">
          <loopCardinality>5</loopCardinality>
        </multiInstanceLoopCharacteristics>
    
    1.7.3.2 表达式

      使用解析为正整数的表达式:

        <multiInstanceLoopCharacteristics isSequential="false|true">
          <loopCardinality>${nrOfOrders-nrOfCancellations}</loopCardinality>
        </multiInstanceLoopCharacteristics>
    
    1.7.3.3 指定集合

      另一个定义实例数量的方法,是使用loopDataInputRef子元素,指定一个集合型流程变量的名字。对集合中的每一项,都会创建一个实例。可以使用inputDataItem子元素,将该项设置给该实例的局部变量。在下面的XML示例中展示:

        <userTask id="miTasks" name="My Task ${loopCounter}" flowable:assignee="${assignee}">
          <multiInstanceLoopCharacteristics isSequential="false">
            <loopDataInputRef>assigneeList</loopDataInputRef>
            <inputDataItem name="assignee" />
          </multiInstanceLoopCharacteristics>
        </userTask>
    

      假设变量assigneeList包含[kermit, gonzo, fozzie]。上面的代码会创建三个并行的用户任务。每一个执行都有一个名为assignee的(局部)流程变量,含有集合中的一项,并在这个例子中被用于指派用户任务。

      loopDataInputRefinputDataItem的缺点是名字很难记,并且由于BPMN 2.0概要的限制,不能使用表达式。Flowable通过在multiInstanceCharacteristics上提供collectionelementVariable属性解决了这些问题:

        <userTask id="miTasks" name="My Task" flowable:assignee="${assignee}">
          <multiInstanceLoopCharacteristics isSequential="true"
             flowable:collection="${myService.resolveUsersForTask()}" flowable:elementVariable="assignee" >
          </multiInstanceLoopCharacteristics>
        </userTask>
    

      请注意collection属性会作为表达式进行解析。如果表达式解析为字符串而不是一个集合,不论是因为本身配置的就是静态字符串值,还是表达式计算结果为字符串,这个字符串都会被当做变量名,在流程变量中用于获取实际的集合。

      例如,下面的代码片段会让引擎查找存储在assigneeList流程变量中的集合:

        <userTask id="miTasks" name="My Task" flowable:assignee="${assignee}">
          <multiInstanceLoopCharacteristics isSequential="true"
             flowable:collection="assigneeList" flowable:elementVariable="assignee" >
          </multiInstanceLoopCharacteristics>
        </userTask>
    
    1.7.3.4 条件型数量

      多实例活动在所有实例都完成时结束。然而,也可以指定一个表达式,在每个实例结束时进行计算。当表达式计算为true时,将销毁所有剩余的实例,并结束多实例活动,继续执行流程。这个表达式必须通过completionCondition子元素定义。

        <userTask id="miTasks" name="My Task" flowable:assignee="${assignee}">
          <multiInstanceLoopCharacteristics isSequential="false"
             flowable:collection="assigneeList" flowable:elementVariable="assignee" >
            <completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.6 }</completionCondition>
          </multiInstanceLoopCharacteristics>
        </userTask>
    

      在这个例子里,会为assigneeList集合中的每个元素创建并行实例。当60%的任务完成时,其他的任务将被删除,流程继续运行。

    二、集成扩展的任务类型

      Flowable还有很多集成扩展型的任务,这类任务并不常用,初学读者可以略过,在需要时再回头查阅。

    2.1 手动任务

    2.1.1 描述

      手动任务(manual task)用来定义在BPM引擎不能完成的任务。对于引擎来说,手动任务将当做一个空任务来处理,在流程执行到达手动任务时,自动继续执行流程。

    2.1.2 图示

      手动任务用左上角有一个小“手”图标的标准BPMN 2.0任务(圆角矩形)表示。

    手动任务.png

    2.1.3 XML表示

        <manualTask id="myManualTask" name="Call client for more information" />
    

    2.2 Java接收任务

    2.2.1 描述

      接收任务(receive task),是等待特定消息到达的简单任务。当流程执行到达接收任务时,将保持等待状态,直到引擎接收到特定的消息,触发流程穿过接收任务继续执行。

    2.2.2 图示

      接收任务用左上角有一个消息图标的标准BPMN 2.0任务(圆角矩形)表示。消息图标是白色的(对应的黑色消息图标代表发送的含义)。

    java接收任务.png

    2.2.3 XML表示

        <receiveTask id="waitState" name="wait" />
    

    2.2.4 使用方法

      要使流程实例从接收任务的等待状态中继续执行,需要使用到达接收任务的执行id,调用runtimeService.signal(executionId)。下面的代码片段展示了如何操作:

        ProcessInstance pi = runtimeService.startProcessInstanceByKey("receiveTask");
        Execution execution = runtimeService.createExecutionQuery()
          .processInstanceId(pi.getId())
          .activityId("waitState")
          .singleResult();
    
        runtimeService.trigger(execution.getId());
    

    2.3 Shell任务

    2.3.1 描述

      Shell任务(Shell task)可以运行Shell脚本与命令。请注意Shell任务不是BPMN 2.0规范的“官方”任务(因此也没有专用图标)。

    2.3.2 定义Shell任务

      Shell任务实现为特殊的服务任务,将服务任务的type定义为'shell'进行设置。

        <serviceTask id="shellEcho" flowable:type="shell">
    

    2.3.3 Shell任务参数

      Shell任务通过字段注入配置。这些参数的值可以使用EL表达式,将在流程执行运行时解析。可以设置下列参数:

    参数 必填? 类型 描述 默认值
    command String 要执行的Shell命令。
    arg0-5 String 参数0至参数5
    wait true/false 是否等待Shell进程终止。 true
    redirectError true/false 是否将标准错误(standard error)并入标准输出(standard output)。 false
    cleanEnv true/false 是否避免Shell进程继承当前环境。 false
    outputVariable String 保存输出的变量名 不会记录输出。
    errorCodeVariable String 保存结果错误码的变量名 不会记录错误码。
    directory String Shell进程的默认目录 当前目录

    2.3.4 使用示例

      下面的XML代码片段是使用Shell任务的例子。将会运行"cmd /c echo EchoTest" Shell脚本,等待其结束,并将其结果存入resultVar

        <serviceTask id="shellEcho" flowable:type="shell" >
          <extensionElements>
            <flowable:field name="command" stringValue="cmd" />
            <flowable:field name="arg1" stringValue="/c" />
            <flowable:field name="arg2" stringValue="echo" />
            <flowable:field name="arg3" stringValue="EchoTest" />
            <flowable:field name="wait" stringValue="true" />
            <flowable:field name="outputVariable" stringValue="resultVar" />
          </extensionElements>
        </serviceTask>
    

    2.4 补偿处理器

    2.4.1 描述

      如果要使用一个活动补偿另一个活动的影响,可以将其声明为补偿处理器(compensation handler)。补偿处理器不在正常流程中执行,而只在流程抛出补偿事件时才会执行。

      补偿处理器不得有入口或出口顺序流。

      补偿处理器必须通过单向的连接,关联一个补偿边界事件。

    2.4.2 图示

      如果一个活动是补偿处理器,则会在其下部中间显示补偿事件图标。下面摘录的流程图展示了一个带有补偿边界事件的服务任务,并关联至一个补偿处理器。请注意补偿处理器图标显示在"cancel hotel reservation(取消酒店预订)"服务任务的下部中间。

    补偿处理器.png

    2.4.3 XML表示

      要将一个活动声明为补偿处理器,需要将isForCompensation属性设置为true:

        <serviceTask id="undoBookHotel" isForCompensation="true" flowable:class="...">
        </serviceTask>
    

    2.5 集成类任务

    • Web Service任务:调用外部的Web Service资源。

    • 邮件任务:用于发送邮件。

    • Http任务:用于发出Http请求。

    • Camel任务:集成消息路由框架Camel。

    • Mule任务:集成企业系统总线框架Mule。

      上面的集成类任务在后续篇章中会详细介绍集成内容,此处了解即可。

    三、小结

      本篇介绍了BPMN2.0规范下及Flowable自定义扩展的任务类型,Flowable提供的多种任务类型基本覆盖企业应用的需求。但还有不少问题需要我们关注,比如脚本任务中的脚本安全和多实例中的线程安全。

    相关文章

      网友评论

          本文标题:Flowable实战(八)BPMN2.0 任务

          本文链接:https://www.haomeiwen.com/subject/mhoxhrtx.html