??任務(wù)是流程中最重要的組成部分塑悼。Flowable提供了多種任務(wù)類(lèi)型,以滿(mǎn)足實(shí)際需求价脾。
??常用任務(wù)類(lèi)型有:
用戶(hù)任務(wù)
Java Service任務(wù)
腳本任務(wù)
業(yè)務(wù)規(guī)則任務(wù)
執(zhí)行監(jiān)聽(tīng)器
任務(wù)監(jiān)聽(tīng)器
多實(shí)例
??集成擴(kuò)展的任務(wù)類(lèi)型有:
手動(dòng)任務(wù)
Java接收任務(wù)
Shell任務(wù)
補(bǔ)償處理器
Web Service任務(wù)
郵件任務(wù)
Http任務(wù)
Camel任務(wù)
Mule任務(wù)
??任務(wù)的圖形都是以一個(gè)圓角矩形為基礎(chǔ)推汽,在左上角添加具體類(lèi)型的圖標(biāo)。
一瘦馍、常用的任務(wù)類(lèi)型
1.1 用戶(hù)任務(wù)
1.1.1 描述
??“用戶(hù)任務(wù)(user task)”指需要人工執(zhí)行的任務(wù)。當(dāng)流程執(zhí)行到達(dá)用戶(hù)任務(wù)時(shí)应役,流程實(shí)例會(huì)停止等待情组,直到用戶(hù)觸發(fā)完成任務(wù)動(dòng)作。
1.1.2 圖示
??用戶(hù)任務(wù)用左上角有一個(gè)小用戶(hù)圖標(biāo)的標(biāo)準(zhǔn)任務(wù)(圓角矩形)表示箩祥。
1.1.3 XML表示
??用戶(hù)任務(wù)在XML中如下定義院崇。其中id是必須屬性,name是可選屬性袍祖。
<userTask id="theTask" name="重要任務(wù)" />
1.1.4 到期日期
??每個(gè)任務(wù)都可以設(shè)置到期日期(due date)底瓣。
??可以指定固定時(shí)間或相對(duì)時(shí)間,比如蕉陋,當(dāng)dueDate為“PT30M”時(shí)捐凭,表示到達(dá)任務(wù)30分鐘后到期。
??到期日期必須符合java.util.Date或java.util.String(ISO8601格式)凳鬓。
??實(shí)際應(yīng)用茁肠,我們指定為變量值。
<userTask id="theTask" name="Important task" flowable:dueDate="${dateVariable}"/>
??任務(wù)的到期日期可以使用TaskService缩举,或者在TaskListener中使用傳遞的DelegateTask修改垦梆。
1.1.5 任務(wù)指派
- 指派確定的辦理人
<userTask id="theTask" name="重要任務(wù)" flowable:assignee="jinyangjie"/>
- 指派潛在辦理人
<userTask id="theTask" name="重要任務(wù)" flowable:candidateUsers="jinyangjie, zhangsan" />
- 指派潛在辦理組
<userTask id="theTask" name="重要任務(wù)" flowable:candidateGroups="leader, manager" />
??更多任務(wù)指派的內(nèi)容匹颤,已在“用戶(hù)和組”的篇章中介紹,這里不再贅述托猩。
1.2 Java Service任務(wù)
1.2.1 描述
??Java Service任務(wù)(Java service task)用于調(diào)用Java類(lèi)印蓖。Java Service不屬于BPMN2.0規(guī)范,而是Flowable的自定義擴(kuò)展京腥。
1.2.2 圖示
??服務(wù)任務(wù)用左上角有一個(gè)小齒輪圖標(biāo)的圓角矩形表示另伍。
1.2.3 XML表示
??有三種方法聲明如何調(diào)用Java邏輯,下面分別介紹:
- 調(diào)用固定的類(lèi)
??使用flowable:class屬性提供全限定類(lèi)名(fully qualified classname)绞旅,指定流程執(zhí)行時(shí)調(diào)用的類(lèi),該類(lèi)必須實(shí)現(xiàn)JavaDelegate或ActivityBehavior接口温艇。
<serviceTask id="javaService" flowable:class="com.example.service.MyJavaDelegate" />
- 調(diào)用動(dòng)態(tài)類(lèi)
??使用flowable:delegateExpression屬性提供委托對(duì)象(delegation object)的表達(dá)式因悲。該功能和flowable:class類(lèi)似,同樣需要實(shí)現(xiàn)JavaDelegate或ActivityBehavior接口勺爱,只不過(guò)這里不是指定一個(gè)具體的實(shí)現(xiàn)類(lèi)晃琳,而是查詢(xún)指定名稱(chēng)的Bean對(duì)象。
<serviceTask id="javaService" flowable:delegateExpression="${myDelegateExpressionBean}" />
??myDelegateExpressionBean
是一個(gè)實(shí)現(xiàn)了JavaDelegate
接口的bean琐鲁,定義在Spring容器中卫旱。
- 調(diào)用類(lèi)的指定方法或?qū)傩灾?/strong>
??使用flowable:expression屬性指定類(lèi)的方法或?qū)傩灾怠M瑯拥奈Ф危擃?lèi)需要實(shí)現(xiàn)JavaDelegate或ActivityBehavior接口顾翼。
<serviceTask id="javaService" flowable:expression="#{printer.printMessage()}" />
??將在名為printer
的對(duì)象上調(diào)用printMessage
方法(不帶參數(shù))。當(dāng)然也可以為表達(dá)式中使用的方法傳遞變量奈泪。
??屬性值示例:
<serviceTask id="javaService" flowable:expression="#{printer.ready}" />
??會(huì)調(diào)用名為printer
的bean的ready
參數(shù)的getter方法适贸,getReady
(不帶參數(shù))。該值會(huì)被解析為執(zhí)行的流程變量涝桅。
1.2.4 具體實(shí)現(xiàn)實(shí)例
??下面是一個(gè)Java類(lèi)的示例拜姿,用于將流程變量String改為大寫(xiě)。這個(gè)類(lèi)通過(guò)實(shí)現(xiàn)org.flowable.engine.delegate.JavaDelegate接口冯遂,可以在流程執(zhí)行中被調(diào)用蕊肥。
??同時(shí),需要重寫(xiě)execute(DelegateExecution)方法實(shí)現(xiàn)業(yè)務(wù)邏輯蛤肌。這個(gè)方法就是引擎將調(diào)用的方法壁却。另外,通過(guò)該方法中的DelegateExecution參數(shù)可以訪問(wèn)流程實(shí)例的各種信息寻定。
public class ToUppercase implements JavaDelegate {
public void execute(DelegateExecution execution) {
String var = (String) execution.getVariable("input");
var = var.toUpperCase();
execution.setVariable("input", var);
}
}
??如果實(shí)現(xiàn)org.flowable.engine.impl.delegate.ActivityBehavior接口儒洛,可以訪問(wèn)更強(qiáng)大的引擎功能,例如狼速,可以影響流程的控制流程琅锻。但注意這并不是好的實(shí)踐,需要避免這么使用。
1.2.5 任務(wù)的返回值
??服務(wù)執(zhí)行的返回值(僅對(duì)使用表達(dá)式的服務(wù)任務(wù))恼蓬,可以通過(guò)為服務(wù)任務(wù)定義的'flowable:resultVariable'屬性設(shè)置為流程變量惊完。可以是已經(jīng)存在的处硬,或者新的流程變量小槐。 如果指定為已存在的流程變量,則流程變量的值會(huì)被服務(wù)執(zhí)行的返回值覆蓋荷辕。 如果不指定結(jié)果變量名凿跳,則服務(wù)任務(wù)的返回值將被忽略。
<serviceTask id="aMethodExpressionServiceTask"
flowable:expression="#{myService.doSomething()}"
flowable:resultVariable="myVar" />
??在上例中疮方,服務(wù)執(zhí)行的結(jié)果(調(diào)用'doSomething()'方法的返回值)控嗜,在服務(wù)執(zhí)行完成后,會(huì)設(shè)置為名為'myVar'的流程變量骡显。
1.2.6 異常處理
??當(dāng)執(zhí)行自定義邏輯時(shí)疆栏,通常需要捕獲并在流程中處理特定的業(yè)務(wù)異常。Flowable提供了多種方式惫谤。
1.2.6.1 拋出BPMN錯(cuò)誤
??可以在服務(wù)任務(wù)或腳本任務(wù)的用戶(hù)代碼中拋出BPMN錯(cuò)誤壁顶。可以在Java委托溜歪、腳本若专、表達(dá)式與委托表達(dá)式中,拋出特殊的FlowableException:BpmnError痹愚。引擎會(huì)捕獲這個(gè)異常富岳,并將其轉(zhuǎn)發(fā)至合適的錯(cuò)誤處理器,如錯(cuò)誤邊界事件或錯(cuò)誤事件子流程拯腮。
public class ThrowBpmnErrorDelegate implements JavaDelegate {
public void execute(DelegateExecution execution) throws Exception {
try {
executeBusinessLogic();
} catch (BusinessException e) {
throw new BpmnError("BusinessExceptionOccurred");
}
}
}
??構(gòu)造函數(shù)的參數(shù)是錯(cuò)誤代碼窖式。錯(cuò)誤代碼決定了處理這個(gè)錯(cuò)誤的錯(cuò)誤處理器。
??這個(gè)機(jī)制只應(yīng)該用于業(yè)務(wù)錯(cuò)誤动壤,需要通過(guò)流程中定義的錯(cuò)誤邊界事件或錯(cuò)誤事件子流程處理萝喘。技術(shù)錯(cuò)誤應(yīng)該通過(guò)其他異常類(lèi)型表現(xiàn),并且通常不在流程內(nèi)部處理琼懊。
1.2.6.2 異常映射
??可以使用mapException
擴(kuò)展阁簸,直接將Java異常映射至業(yè)務(wù)異常(錯(cuò)誤)。單映射是最簡(jiǎn)單的形式:
<serviceTask id="servicetask1" flowable:class="...">
<extensionElements>
<flowable:mapException
errorCode="myErrorCode1">com.example.SomeException</flowable:mapException>
</extensionElements>
</serviceTask>
??在上面的代碼中哼丈,如果服務(wù)任務(wù)拋出org.flowable.SomeException
的實(shí)例启妹,引擎會(huì)捕獲該異常,并將其轉(zhuǎn)換為帶有給定errorCode的BPMN錯(cuò)誤醉旦。然后就可以像普通BPMN錯(cuò)誤完全一樣地處理拄轻。其他的異常沒(méi)有映射,仍將拋出至API調(diào)用處不瓶。
??也可以在單行中使用includeChildExceptions
屬性,映射特定異常的所有子異常照瘾。
<serviceTask id="servicetask1" flowable:class="...">
<extensionElements>
<flowable:mapException errorCode="myErrorCode1"
includeChildExceptions="true">com.example.SomeException</flowable:mapException>
</extensionElements>
</serviceTask>
??上面的代碼中,F(xiàn)lowable會(huì)將SomeException
的任何直接或間接的子類(lèi)丧慈,轉(zhuǎn)換為帶有指定錯(cuò)誤代碼的BPMN錯(cuò)誤析命。 當(dāng)未指定includeChildExceptions
時(shí),視為“false”逃默。
1.2.6.3 默認(rèn)映射
??默認(rèn)映射最常用鹃愤。默認(rèn)映射是一個(gè)不指定類(lèi)的映射,可以匹配任何Java異常:
<serviceTask id="servicetask1" flowable:class="...">
<extensionElements>
<flowable:mapException errorCode="myErrorCode1"/>
</extensionElements>
</serviceTask>
??除了默認(rèn)映射完域,會(huì)按照從上至下的順序檢查映射昼浦,使用第一個(gè)匹配的映射。只在所有映射都不能成功匹配時(shí)使用默認(rèn)映射筒主。 只有第一個(gè)不指定類(lèi)的映射會(huì)作為默認(rèn)映射。默認(rèn)映射忽略includeChildExceptions
鸟蟹。
1.2.6.4 異常順序流
??還有種推薦用法乌妙,在發(fā)生異常時(shí),將流程執(zhí)行路由至另一條路徑建钥。下面是一個(gè)例子藤韵。
<serviceTask id="servicetask1" flowable:class="com.example.ThrowsExceptionBehavior">
</serviceTask>
<sequenceFlow id="no-exception" sourceRef="javaService" targetRef="theEnd" />
<sequenceFlow id="exception" sourceRef="javaService" targetRef="fixException" />
??服務(wù)任務(wù)有兩條出口順序流,命名為exception
與no-exception
熊经。在發(fā)生異常時(shí)泽艘,使用順序流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 腳本任務(wù)
1.3.1 描述
??腳本任務(wù)(script task)是自動(dòng)執(zhí)行的活動(dòng)。當(dāng)流程執(zhí)行到達(dá)腳本任務(wù)時(shí)镐依,會(huì)執(zhí)行相應(yīng)的腳本匹涮。
1.3.2 圖示
??腳本任務(wù)用左上角有一個(gè)小“腳本”圖標(biāo)的標(biāo)準(zhǔn)BPMN 2.0任務(wù)(圓角矩形)表示。
1.3.3 XML表示
??腳本任務(wù)使用script與scriptFormat元素定義槐壳。
<scriptTask id="theScriptTask" scriptFormat="groovy">
<script>
sum = 0
for ( i in inputArray ) {
sum += i
}
</script>
</scriptTask>
??默認(rèn)情況下然低,JavaScript包含在每一個(gè)JDK中,因此不需要添加任何JAR文件务唐。如果想使用其它腳本引擎雳攘,則需要在classpath中添加相應(yīng)的jar,并使用適當(dāng)?shù)拿址愕选@缍置穑現(xiàn)lowable單元測(cè)試經(jīng)常使用Groovy。Groovy腳本引擎與groovy-all JAR捆綁在一起刑巧。添加如下依賴(lài):
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.x.x<version>
</dependency>
1.3.4 腳本中的變量
??到達(dá)腳本引擎的執(zhí)行中喧兄,所有的流程變量都可以在腳本中使用无畔。在這個(gè)例子里,腳本變量'inputArray'實(shí)際上就是一個(gè)流程變量(一個(gè)integer的數(shù)組)繁莹。
<script>
sum = 0
for ( i in inputArray ) {
sum += i
}
</script>
??在腳本中設(shè)置變量的例子:
<script>
def scriptVar = "test123"
execution.setVariable("myVar", scriptVar)
</script>
注意:下列名字是保留字檩互,不能用于變量名:out,out:print咨演,lang:import闸昨,context,elcontext薄风。
1.3.5 腳本任務(wù)的結(jié)果
??腳本任務(wù)的返回值饵较,可以通過(guò)為腳本任務(wù)定義的'flowable:resultVariable'屬性設(shè)置為流程變量≡饴福可以是已經(jīng)存在的循诉,或者新的流程變量。如果指定為已存在的流程變量撇他,則流程變量的值會(huì)被腳本執(zhí)行的結(jié)果值覆蓋茄猫。如果不指定結(jié)果變量名,則腳本結(jié)果值將被忽略困肩。
<scriptTask id="theScriptTask" scriptFormat="juel" flowable:resultVariable="myVar">
<script>#{echo}</script>
</scriptTask>
??在上面的例子中划纽,腳本執(zhí)行的結(jié)果(解析表達(dá)式'#{echo}'的值),將在腳本完成后锌畸,設(shè)置為名為'myVar'的流程變量勇劣。
1.4 業(yè)務(wù)規(guī)則任務(wù)
1.4.1 描述
??在企業(yè)應(yīng)用中,推薦做法是使用可維護(hù)的規(guī)則庫(kù)來(lái)管理復(fù)雜多變的業(yè)務(wù)規(guī)則潭枣,將業(yè)務(wù)代碼和規(guī)則分開(kāi)維護(hù)比默,一旦規(guī)則有變動(dòng),只需修改預(yù)設(shè)規(guī)則即可盆犁,而不會(huì)影響到業(yè)務(wù)代碼命咐。
??業(yè)務(wù)規(guī)則任務(wù)可以根據(jù)流程變量的值處理預(yù)設(shè)的業(yè)務(wù)規(guī)則。Flowable支持目前最流行的規(guī)則引擎——Drools谐岁。只需把含有業(yè)務(wù)規(guī)則任務(wù)的流程文件和規(guī)則引擎文件“.drl”一同打包部署到系統(tǒng)中侈百,同時(shí)添加Drools的jar包,即可實(shí)現(xiàn)Flowable驅(qū)動(dòng)規(guī)則引擎翰铡。
1.4.2 圖示
??業(yè)務(wù)規(guī)則任務(wù)顯示為帶有表格圖標(biāo)的圓角矩形钝域。
1.4.3 XML表示
??要執(zhí)行業(yè)務(wù)規(guī)則,需要定義輸入與結(jié)果變量锭魔。輸入變量可以用流程變量的列表定義例证,使用逗號(hào)分隔。輸出變量只能有一個(gè)變量名迷捧,如果沒(méi)有指定結(jié)果變量名织咧,默認(rèn)為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>
??也可以將業(yè)務(wù)規(guī)則任務(wù)配置為只執(zhí)行部署的.drl文件中的一組規(guī)則。要做到這一點(diǎn)笙蒙,需要指定規(guī)則名字的列表抵屿,用逗號(hào)分隔。
<businessRuleTask id="businessRuleTask" flowable:ruleVariablesInput="${order}"
flowable:rules="rule1, rule2" />
??這樣只會(huì)執(zhí)行rule1與rule2捅位。
??也可以定義需要從執(zhí)行中排除的規(guī)則列表轧葛。
<businessRuleTask id="businessRuleTask" flowable:ruleVariablesInput="${order}"
flowable:rules="rule1, rule2" exclude="true" />
??這個(gè)例子中,除了rule1與rule2之外艇搀,其它所有與流程定義一起部署的規(guī)則都會(huì)被執(zhí)行尿扯。
注意:集成Drools的業(yè)務(wù)規(guī)則任務(wù),是企業(yè)應(yīng)用中的重要內(nèi)容焰雕,需要重點(diǎn)掌握衷笋。
1.5 執(zhí)行監(jiān)聽(tīng)器
1.5.1 描述
??執(zhí)行監(jiān)聽(tīng)器(execution listener)可以在流程執(zhí)行中發(fā)生特定的事件時(shí),執(zhí)行外部Java代碼或計(jì)算表達(dá)式矩屁”僮冢可以被捕獲的事件有:
- 流程實(shí)例的啟動(dòng)和結(jié)束。
- 流程執(zhí)行轉(zhuǎn)移吝秕。
- 活動(dòng)的啟動(dòng)和結(jié)束慢蜓。
- 網(wǎng)關(guān)的啟動(dòng)和結(jié)束。
- 中間事件的啟動(dòng)和結(jié)束郭膛。
- 啟動(dòng)事件的結(jié)束,和結(jié)束事件的啟動(dòng)氛悬。
1.5.2 XML表示
??下面的流程定義包含了三個(gè)執(zhí)行監(jiān)聽(tīng)器:
<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>
??第一個(gè)執(zhí)行監(jiān)聽(tīng)器將在流程啟動(dòng)時(shí)收到通知则剃。這個(gè)監(jiān)聽(tīng)器是一個(gè)外部Java類(lèi)(ExampleExecutionListenerOne
),并且需要實(shí)現(xiàn)org.flowable.engine.delegate.ExecutionListener
接口如捅。當(dāng)該事件發(fā)生時(shí)(這里是start
事件)棍现,會(huì)調(diào)用notify(ExecutionListenerExecution execution)
方法。
public class ExampleExecutionListenerOne implements ExecutionListener {
public void notify(ExecutionListenerExecution execution) throws Exception {
execution.setVariable("variableSetInExecutionListener", "firstValue");
execution.setVariable("eventReceived", execution.getEventName());
}
}
??也可以使用實(shí)現(xiàn)了org.flowable.engine.delegate.JavaDelegate
接口的委托類(lèi)镜遣。這些委托類(lèi)也可以用于其他的結(jié)構(gòu)己肮,如服務(wù)任務(wù)的委托。
??第二個(gè)執(zhí)行監(jiān)聽(tīng)器在流程執(zhí)行轉(zhuǎn)移時(shí)被調(diào)用悲关。請(qǐng)注意listener
元素并未定義event
谎僻,因?yàn)樵谵D(zhuǎn)移上只會(huì)觸發(fā)take
事件。當(dāng)監(jiān)聽(tīng)器定義在轉(zhuǎn)移上時(shí)寓辱,event
屬性的值將被忽略艘绍。
最后一個(gè)執(zhí)行監(jiān)聽(tīng)器在secondTask
活動(dòng)結(jié)束時(shí)被調(diào)用。監(jiān)聽(tīng)器聲明中沒(méi)有使用class
秫筏,而是定義了expression
诱鞠。這個(gè)表達(dá)式將在事件觸發(fā)時(shí)計(jì)算/調(diào)用挎挖。
<flowable:executionListener expression="${myPojo.myMethod(execution.eventName)}" event="end" />
??與其他表達(dá)式一樣,可以使用與解析execution變量航夺。
1.5.3 執(zhí)行監(jiān)聽(tīng)器上的字段注入
??使用通過(guò)class
屬性配置的執(zhí)行監(jiān)聽(tīng)器時(shí)蕉朵,可以使用字段注入。
??下面的代碼片段展示了一個(gè)簡(jiǎn)單的示例流程阳掐,帶有一個(gè)使用了字段注入的執(zhí)行監(jiān)聽(tīng)器始衅。
<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
類(lèi)將連接兩個(gè)字段(一個(gè)是固定值-fixedValue,另一個(gè)是動(dòng)態(tài)值-dynamicValue)锚烦,并將其存儲(chǔ)在'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);
// 結(jié)果為固定注入字段及注入表達(dá)式的連接
assertEquals("Yes, I am listening!", varSetByListener);
}
1.6 任務(wù)監(jiān)聽(tīng)器
1.6.1 描述
??任務(wù)監(jiān)聽(tīng)器(task listener)用于在特定的任務(wù)相關(guān)事件發(fā)生時(shí),執(zhí)行自定義的Java邏輯或表達(dá)式涮俄。
1.6.2 XML表示
??任務(wù)監(jiān)聽(tīng)器只能在流程定義中作為用戶(hù)任務(wù)的子元素蛉拙。請(qǐng)注意,任務(wù)監(jiān)聽(tīng)器是一個(gè)Flowable自定義結(jié)構(gòu)彻亲,因此也需要作為BPMN 2.0 extensionElements孕锄,放在flowable命名空間下。
<userTask id="myTask" >
<extensionElements>
<flowable:taskListener event="create" class="com.example.MyTaskCreateListener" />
</extensionElements>
</userTask>
1.6.3 任務(wù)監(jiān)聽(tīng)器屬性:
1.6.3.1 event
??觸發(fā)任務(wù)監(jiān)聽(tīng)器的任務(wù)事件類(lèi)型苞尝,必填項(xiàng)畸肆。可用的事件有:
- create(創(chuàng)建):當(dāng)任務(wù)已經(jīng)創(chuàng)建宙址,并且所有任務(wù)參數(shù)都已經(jīng)設(shè)置時(shí)觸發(fā)轴脐。
- assignment(指派):當(dāng)任務(wù)已經(jīng)指派給某人時(shí)觸發(fā)。請(qǐng)注意:當(dāng)流程執(zhí)行到達(dá)用戶(hù)任務(wù)時(shí)抡砂,在觸發(fā)create事件之前大咱,會(huì)首先觸發(fā)assignment事件。這順序看起來(lái)不太自然注益,但是有實(shí)際原因的:當(dāng)收到create事件時(shí)碴巾,我們通常希望能看到任務(wù)的所有參數(shù),包括辦理人丑搔。
- complete(完成):當(dāng)任務(wù)已經(jīng)完成厦瓢,從運(yùn)行時(shí)數(shù)據(jù)中刪除前觸發(fā)。
- delete(刪除):在任務(wù)即將被刪除前觸發(fā)啤月。請(qǐng)注意任務(wù)由completeTask正常完成時(shí)也會(huì)觸發(fā)煮仇。
1.6.3.2 class
??需要調(diào)用的委托類(lèi)。這個(gè)類(lèi)必須實(shí)現(xiàn)org.flowable.engine.delegate.TaskListener
接口谎仲。
public class MyTaskCreateListener implements TaskListener {
public void notify(DelegateTask delegateTask) {
// 這里是要實(shí)現(xiàn)的業(yè)務(wù)邏輯
}
}
??也可以使用字段注入欺抗,為委托類(lèi)傳遞流程變量或執(zhí)行。請(qǐng)注意委托類(lèi)的實(shí)例在流程部署時(shí)創(chuàng)建(與Flowable中其它的委托類(lèi)一樣)强重,這意味著該實(shí)例會(huì)在所有流程實(shí)例執(zhí)行中共享绞呈。
1.6.3.3 expression
??指定在事件發(fā)生時(shí)要執(zhí)行的表達(dá)式(不能與class屬性一起使用)贸人。可以為被調(diào)用的對(duì)象傳遞DelegateTask
對(duì)象與事件名(使用task.eventName
)作為參數(shù)佃声。
<flowable:taskListener event="create" expression="${myObject.callMethod(task, task.eventName)}" />
1.6.3.4 delegateExpression
??指定一個(gè)能夠解析為TaskListener
接口實(shí)現(xiàn)類(lèi)的對(duì)象的表達(dá)式艺智。
<flowable:taskListener event="create" delegateExpression="${myTaskListenerBean}" />
1.7 多實(shí)例
1.7.1 描述
??多實(shí)例活動(dòng)(multi-instance activity)是在業(yè)務(wù)流程中,為特定步驟定義重復(fù)的方式圾亏。在編程概念中十拣,多實(shí)例類(lèi)似for each結(jié)構(gòu):可以為給定集合中的每一條目,順序或并行地志鹃,執(zhí)行特定步驟夭问,甚至是整個(gè)子流程。
??網(wǎng)關(guān)和事件不能設(shè)置為多實(shí)例曹铃。
??按照BPMN2.0規(guī)范的要求缰趋,用于為每個(gè)實(shí)例創(chuàng)建執(zhí)行的父執(zhí)行,會(huì)提供下列變量:
- nrOfInstances:實(shí)例總數(shù)陕见。
- nrOfActiveInstances:當(dāng)前活動(dòng)的(即未完成的)實(shí)例數(shù)量秘血。對(duì)于順序多實(shí)例,這個(gè)值總為1评甜。
- nrOfCompletedInstances:已完成的實(shí)例數(shù)量灰粮。
??可以調(diào)用execution.getVariable(x)
方法獲取這些值。
??另外忍坷,每個(gè)被創(chuàng)建的執(zhí)行粘舟,都有局部變量(對(duì)其他執(zhí)行不可見(jiàn),也不存儲(chǔ)在流程實(shí)例級(jí)別):
- loopCounter:給定實(shí)例在for-each循環(huán)中的index佩研。
1.7.2 圖示
??如果一個(gè)活動(dòng)是多實(shí)例柑肴,將通過(guò)在該活動(dòng)底部的三條短線(xiàn)表示。三條豎線(xiàn)代表實(shí)例會(huì)并行執(zhí)行韧骗,而三條橫線(xiàn)代表順序執(zhí)行。
1.7.3 XML表示
??要將活動(dòng)變成多實(shí)例零聚,該活動(dòng)的XML元素必須有multiInstanceLoopCharacteristics
子元素
<multiInstanceLoopCharacteristics isSequential="false|true">
...
</multiInstanceLoopCharacteristics>
??isSequential屬性代表了活動(dòng)的實(shí)例為順序還是并行執(zhí)行袍暴。
??有4種不同方法可以配置數(shù)量。
1.7.3.1 指定數(shù)字
??通過(guò)loopCardinality子元素隶症,直接指定數(shù)字:
<multiInstanceLoopCharacteristics isSequential="false|true">
<loopCardinality>5</loopCardinality>
</multiInstanceLoopCharacteristics>
1.7.3.2 表達(dá)式
??使用解析為正整數(shù)的表達(dá)式:
<multiInstanceLoopCharacteristics isSequential="false|true">
<loopCardinality>${nrOfOrders-nrOfCancellations}</loopCardinality>
</multiInstanceLoopCharacteristics>
1.7.3.3 指定集合
??另一個(gè)定義實(shí)例數(shù)量的方法政模,是使用loopDataInputRef
子元素,指定一個(gè)集合型流程變量的名字蚂会。對(duì)集合中的每一項(xiàng)淋样,都會(huì)創(chuàng)建一個(gè)實(shí)例⌒沧。可以使用inputDataItem
子元素趁猴,將該項(xiàng)設(shè)置給該實(shí)例的局部變量刊咳。在下面的XML示例中展示:
<userTask id="miTasks" name="My Task ${loopCounter}" flowable:assignee="${assignee}">
<multiInstanceLoopCharacteristics isSequential="false">
<loopDataInputRef>assigneeList</loopDataInputRef>
<inputDataItem name="assignee" />
</multiInstanceLoopCharacteristics>
</userTask>
??假設(shè)變量assigneeList
包含[kermit, gonzo, fozzie]
。上面的代碼會(huì)創(chuàng)建三個(gè)并行的用戶(hù)任務(wù)儡司。每一個(gè)執(zhí)行都有一個(gè)名為assignee
的(局部)流程變量娱挨,含有集合中的一項(xiàng),并在這個(gè)例子中被用于指派用戶(hù)任務(wù)捕犬。
??loopDataInputRef
與inputDataItem
的缺點(diǎn)是名字很難記跷坝,并且由于BPMN 2.0概要的限制,不能使用表達(dá)式碉碉。Flowable通過(guò)在multiInstanceCharacteristics
上提供collection與elementVariable屬性解決了這些問(wèn)題:
<userTask id="miTasks" name="My Task" flowable:assignee="${assignee}">
<multiInstanceLoopCharacteristics isSequential="true"
flowable:collection="${myService.resolveUsersForTask()}" flowable:elementVariable="assignee" >
</multiInstanceLoopCharacteristics>
</userTask>
??請(qǐng)注意collection
屬性會(huì)作為表達(dá)式進(jìn)行解析柴钻。如果表達(dá)式解析為字符串而不是一個(gè)集合,不論是因?yàn)楸旧砼渲玫木褪庆o態(tài)字符串值垢粮,還是表達(dá)式計(jì)算結(jié)果為字符串贴届,這個(gè)字符串都會(huì)被當(dāng)做變量名,在流程變量中用于獲取實(shí)際的集合足丢。
??例如粱腻,下面的代碼片段會(huì)讓引擎查找存儲(chǔ)在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 條件型數(shù)量
??多實(shí)例活動(dòng)在所有實(shí)例都完成時(shí)結(jié)束。然而斩跌,也可以指定一個(gè)表達(dá)式绍些,在每個(gè)實(shí)例結(jié)束時(shí)進(jìn)行計(jì)算。當(dāng)表達(dá)式計(jì)算為true時(shí)耀鸦,將銷(xiāo)毀所有剩余的實(shí)例柬批,并結(jié)束多實(shí)例活動(dòng),繼續(xù)執(zhí)行流程袖订。這個(gè)表達(dá)式必須通過(guò)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>
??在這個(gè)例子里,會(huì)為assigneeList
集合中的每個(gè)元素創(chuàng)建并行實(shí)例洛姑。當(dāng)60%的任務(wù)完成時(shí)上沐,其他的任務(wù)將被刪除,流程繼續(xù)運(yùn)行楞艾。
二参咙、集成擴(kuò)展的任務(wù)類(lèi)型
??Flowable還有很多集成擴(kuò)展型的任務(wù),這類(lèi)任務(wù)并不常用硫眯,初學(xué)讀者可以略過(guò)蕴侧,在需要時(shí)再回頭查閱。
2.1 手動(dòng)任務(wù)
2.1.1 描述
??手動(dòng)任務(wù)(manual task)用來(lái)定義在BPM引擎不能完成的任務(wù)两入。對(duì)于引擎來(lái)說(shuō)净宵,手動(dòng)任務(wù)將當(dāng)做一個(gè)空任務(wù)來(lái)處理,在流程執(zhí)行到達(dá)手動(dòng)任務(wù)時(shí),自動(dòng)繼續(xù)執(zhí)行流程择葡。
2.1.2 圖示
??手動(dòng)任務(wù)用左上角有一個(gè)小“手”圖標(biāo)的標(biāo)準(zhǔn)BPMN 2.0任務(wù)(圓角矩形)表示紧武。
2.1.3 XML表示
<manualTask id="myManualTask" name="Call client for more information" />
2.2 Java接收任務(wù)
2.2.1 描述
??接收任務(wù)(receive task),是等待特定消息到達(dá)的簡(jiǎn)單任務(wù)刁岸。當(dāng)流程執(zhí)行到達(dá)接收任務(wù)時(shí)脏里,將保持等待狀態(tài),直到引擎接收到特定的消息虹曙,觸發(fā)流程穿過(guò)接收任務(wù)繼續(xù)執(zhí)行迫横。
2.2.2 圖示
??接收任務(wù)用左上角有一個(gè)消息圖標(biāo)的標(biāo)準(zhǔn)BPMN 2.0任務(wù)(圓角矩形)表示。消息圖標(biāo)是白色的(對(duì)應(yīng)的黑色消息圖標(biāo)代表發(fā)送的含義)酝碳。
2.2.3 XML表示
<receiveTask id="waitState" name="wait" />
2.2.4 使用方法
??要使流程實(shí)例從接收任務(wù)的等待狀態(tài)中繼續(xù)執(zhí)行矾踱,需要使用到達(dá)接收任務(wù)的執(zhí)行id,調(diào)用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任務(wù)
2.3.1 描述
??Shell任務(wù)(Shell task)可以運(yùn)行Shell腳本與命令呛讲。請(qǐng)注意Shell任務(wù)不是BPMN 2.0規(guī)范的“官方”任務(wù)(因此也沒(méi)有專(zhuān)用圖標(biāo))。
2.3.2 定義Shell任務(wù)
??Shell任務(wù)實(shí)現(xiàn)為特殊的服務(wù)任務(wù)返奉,將服務(wù)任務(wù)的type定義為'shell'進(jìn)行設(shè)置贝搁。
<serviceTask id="shellEcho" flowable:type="shell">
2.3.3 Shell任務(wù)參數(shù)
??Shell任務(wù)通過(guò)字段注入配置。這些參數(shù)的值可以使用EL表達(dá)式芽偏,將在流程執(zhí)行運(yùn)行時(shí)解析雷逆。可以設(shè)置下列參數(shù):
參數(shù) | 必填? | 類(lèi)型 | 描述 | 默認(rèn)值 |
---|---|---|---|---|
command | 是 | String | 要執(zhí)行的Shell命令污尉。 | |
arg0-5 | 否 | String | 參數(shù)0至參數(shù)5 | |
wait | 否 | true/false | 是否等待Shell進(jìn)程終止膀哲。 | true |
redirectError | 否 | true/false | 是否將標(biāo)準(zhǔn)錯(cuò)誤(standard error)并入標(biāo)準(zhǔn)輸出(standard output)。 | false |
cleanEnv | 否 | true/false | 是否避免Shell進(jìn)程繼承當(dāng)前環(huán)境被碗。 | false |
outputVariable | 否 | String | 保存輸出的變量名 | 不會(huì)記錄輸出某宪。 |
errorCodeVariable | 否 | String | 保存結(jié)果錯(cuò)誤碼的變量名 | 不會(huì)記錄錯(cuò)誤碼。 |
directory | 否 | String | Shell進(jìn)程的默認(rèn)目錄 | 當(dāng)前目錄 |
2.3.4 使用示例
??下面的XML代碼片段是使用Shell任務(wù)的例子锐朴。將會(huì)運(yùn)行"cmd /c echo EchoTest" Shell腳本兴喂,等待其結(jié)束,并將其結(jié)果存入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 補(bǔ)償處理器
2.4.1 描述
??如果要使用一個(gè)活動(dòng)補(bǔ)償另一個(gè)活動(dòng)的影響衣迷,可以將其聲明為補(bǔ)償處理器(compensation handler)。補(bǔ)償處理器不在正常流程中執(zhí)行娩嚼,而只在流程拋出補(bǔ)償事件時(shí)才會(huì)執(zhí)行蘑险。
??補(bǔ)償處理器不得有入口或出口順序流滴肿。
??補(bǔ)償處理器必須通過(guò)單向的連接岳悟,關(guān)聯(lián)一個(gè)補(bǔ)償邊界事件。
2.4.2 圖示
??如果一個(gè)活動(dòng)是補(bǔ)償處理器,則會(huì)在其下部中間顯示補(bǔ)償事件圖標(biāo)贵少。下面摘錄的流程圖展示了一個(gè)帶有補(bǔ)償邊界事件的服務(wù)任務(wù)呵俏,并關(guān)聯(lián)至一個(gè)補(bǔ)償處理器。請(qǐng)注意補(bǔ)償處理器圖標(biāo)顯示在"cancel hotel reservation(取消酒店預(yù)訂)"服務(wù)任務(wù)的下部中間滔灶。
2.4.3 XML表示
??要將一個(gè)活動(dòng)聲明為補(bǔ)償處理器普碎,需要將isForCompensation
屬性設(shè)置為true:
<serviceTask id="undoBookHotel" isForCompensation="true" flowable:class="...">
</serviceTask>
2.5 集成類(lèi)任務(wù)
Web Service任務(wù):調(diào)用外部的Web Service資源。
郵件任務(wù):用于發(fā)送郵件录平。
Http任務(wù):用于發(fā)出Http請(qǐng)求麻车。
Camel任務(wù):集成消息路由框架Camel。
Mule任務(wù):集成企業(yè)系統(tǒng)總線(xiàn)框架Mule斗这。
??上面的集成類(lèi)任務(wù)在后續(xù)篇章中會(huì)詳細(xì)介紹集成內(nèi)容动猬,此處了解即可。
三表箭、小結(jié)
??本篇介紹了BPMN2.0規(guī)范下及Flowable自定義擴(kuò)展的任務(wù)類(lèi)型赁咙,F(xiàn)lowable提供的多種任務(wù)類(lèi)型基本覆蓋企業(yè)應(yīng)用的需求。但還有不少問(wèn)題需要我們關(guān)注免钻,比如腳本任務(wù)中的腳本安全和多實(shí)例中的線(xiàn)程安全彼水。