Flowable實戰(八)BPMN2.0 任務

金楊傑發表於2022-01-15

  任務是流程中最重要的組成部分。Flowable提供了多種任務型別,以滿足實際需求。

  常用任務型別有:

  • 使用者任務

  • Java Service任務

  • 指令碼任務

  • 業務規則任務

  • 執行監聽器

  • 任務監聽器

  • 多例項

  整合擴充套件的任務型別有:

  • 手動任務

  • Java接收任務

  • Shell任務

  • 補償處理器

  • Web Service任務

  • 郵件任務

  • Http任務

  • Camel任務

  • Mule任務

  任務的圖形都是以一個圓角矩形為基礎,在左上角新增具體型別的圖示。

一、常用的任務型別

1.1 使用者任務

1.1.1 描述

  “使用者任務(user task)”指需要人工執行的任務。當流程執行到達使用者任務時,流程例項會停止等待,直到使用者觸發完成任務動作。

1.1.2 圖示

  使用者任務用左上角有一個小使用者圖示的標準任務(圓角矩形)表示。

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 圖示

  服務任務用左上角有一個小齒輪圖示的圓角矩形表示。

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任務(圓角矩形)表示。

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 圖示

  業務規則任務顯示為帶有表格圖示的圓角矩形。

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 圖示

  如果一個活動是多例項,將通過在該活動底部的三條短線表示。三條豎線代表例項會並行執行,而三條橫線代表順序執行。

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任務(圓角矩形)表示。

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任務(圓角矩形)表示。訊息圖示是白色的(對應的黑色訊息圖示代表傳送的含義)。

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(取消酒店預訂)"服務任務的下部中間。

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提供的多種任務型別基本覆蓋企業應用的需求。但還有不少問題需要我們關注,比如指令碼任務中的指令碼安全和多例項中的執行緒安全。

相關文章