Activiti CamelTask(駱駝任務)
作者:Jesai
人生想講個不成熟的建議
前言:
Camel任務可以從Camel發送和介紹消息,由此強化了activiti的集成功能。 注意camel任務不是BPMN 2.0規范定義的官方任務。 (它也沒有對應的圖標)。 在activiti中,camel任務時由專用的服務任務實現的。
什么是Camel?
Camel能夠在大量的領域語言中讓你定義路由以及中間規則,包括基於Java的Fluent API,Spring或者Blueprint XML配置文件,甚至是Scala(是一種基於JVM,集合了面向對象編程和函數式編程優點的高級程序設計語言)DSL。 您能夠通過你的IDE或者Java、Scala或者XML編輯器里獲得智能化路由規則補全功能。
camel首先是一個規則引擎。其次才是一個開源項目。
Apache Camel是Apache基金會下的一個開源項目,它是一個基於規則路由和中介引擎,提供企業集成模式的Java對象的實現,通過應用程序接口(或稱為陳述式的Java領域特定語言(DSL))來配置路由和中介的規則。領域特定語言意味着Apache Camel支持你在的集成開發工具中使用平常的,類型安全的,可自動補全的Java代碼來編寫路由規則,而不需要大量的XML配置文件。同時,也支持在Spring中使用XML配置定義路由和中介規則。
Camel提供的基於規則的路由(Routing)引擎
from().to().to()
這種表述可以使用Camel定義的DSL語言,xml語言以及scala語言。如下例:
from(“file:path").to("activemq:queue:queuename") 將某文件,讀入並寫入到ActiveMQ的JMS中。
form("file:path").to("ftp://url")將一個文件,讀入並寫入到ftp某目錄中。
開發涉及的jar包
camel-core-2.19.1.jar
camel-spring-2.19.1.jar
activiti-camel-5.22.0.jar
Camel任務圖標
注意:camel任務不是BPMN 2.0標准,它只是Activiti的一個擴展。
流程圖設計以及配置:
這是一個非常簡單的流程圖,只有開始和結束、camel任務節點。實際應用中根據實際情況擴展。
流程圖源碼:
1 <?xml version='1.0' encoding='UTF-8'?> 2 3 <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/processdef"> 4 5 <process id="CamelProcess" isExecutable="true"> 6 7 <startEvent id="sid-80400B65-221F-4DE2-A7AF-1788341E7FAA"> 8 9 <extensionElements> 10 11 <activiti:executionListener event="start" class="light.mvc.workflow.taskListener.CamelListenerImpl" /> 12 13 </extensionElements> 14 15 </startEvent> 16 17 <serviceTask id="MyCamelCall" name="camel任務" activiti:type="camel" /> 18 19 <endEvent id="sid-6920138F-F17F-43A3-8C54-D339AC234DF8" /> 20 21 <sequenceFlow id="sid-23D8E093-77BF-4D8A-8954-3730952ADEF6" sourceRef="MyCamelCall" targetRef="sid-6920138F-F17F-43A3-8C54-D339AC234DF8" /> 22 23 <sequenceFlow id="sid-7B6E8FB0-B1F6-425A-8897-C9CF6A2050CB" sourceRef="sid-80400B65-221F-4DE2-A7AF-1788341E7FAA" targetRef="MyCamelCall" /> 24 25 </process> 26 27 <bpmndi:BPMNDiagram id="BPMNDiagram_CamelProcess"> 28 29 <bpmndi:BPMNPlane bpmnElement="CamelProcess" id="BPMNPlane_CamelProcess"> 30 31 <bpmndi:BPMNShape bpmnElement="sid-80400B65-221F-4DE2-A7AF-1788341E7FAA" id="BPMNShape_sid-80400B65-221F-4DE2-A7AF-1788341E7FAA"> 32 33 <omgdc:Bounds height="30.0" width="30.0" x="223.75" y="86.0" /> 34 35 </bpmndi:BPMNShape> 36 37 <bpmndi:BPMNShape bpmnElement="MyCamelCall" id="BPMNShape_MyCamelCall"> 38 39 <omgdc:Bounds height="80.0" width="100.36219727999998" x="356.56890136" y="61.0" /> 40 41 </bpmndi:BPMNShape> 42 43 <bpmndi:BPMNShape bpmnElement="sid-6920138F-F17F-43A3-8C54-D339AC234DF8" id="BPMNShape_sid-6920138F-F17F-43A3-8C54-D339AC234DF8"> 44 45 <omgdc:Bounds height="28.0" width="28.0" x="600.0" y="87.0" /> 46 47 </bpmndi:BPMNShape> 48 49 <bpmndi:BPMNEdge bpmnElement="sid-23D8E093-77BF-4D8A-8954-3730952ADEF6" id="BPMNEdge_sid-23D8E093-77BF-4D8A-8954-3730952ADEF6"> 50 51 <omgdi:waypoint x="456.93109863999996" y="101.0" /> 52 53 <omgdi:waypoint x="600.0" y="101.0" /> 54 55 </bpmndi:BPMNEdge> 56 57 <bpmndi:BPMNEdge bpmnElement="sid-7B6E8FB0-B1F6-425A-8897-C9CF6A2050CB" id="BPMNEdge_sid-7B6E8FB0-B1F6-425A-8897-C9CF6A2050CB"> 58 59 <omgdi:waypoint x="253.75" y="101.0" /> 60 61 <omgdi:waypoint x="356.56890136" y="101.0" /> 62 63 </bpmndi:BPMNEdge> 64 65 </bpmndi:BPMNPlane> 66 67 </bpmndi:BPMNDiagram> 68 69 </definitions>
這里在開始任務設置了一個監聽類:
監聽類實現代碼:
1 /** 2 3 * 4 5 */ 6 7 package light.mvc.workflow.taskListener; 8 9 10 11 import java.util.HashMap; 12 13 import java.util.Map; 14 15 16 17 import light.mvc.service.workflow.impl.DelegateServiceImpl; 18 19 import light.mvc.workflow.model.DelegateInfo; 20 21 22 23 import org.activiti.engine.delegate.DelegateExecution; 24 25 import org.activiti.engine.delegate.DelegateTask; 26 27 import org.activiti.engine.delegate.JavaDelegate; 28 29 import org.activiti.engine.delegate.TaskListener; 30 31 import org.springframework.beans.factory.annotation.Autowired; 32 33 34 35 /** 36 37 * 38 39 * 項目名稱:lightmvc 40 41 * 類名稱:TaskAsigneeListenerImpl 42 43 * 類描述: 44 45 * 創建人:鄧家海 46 47 * 創建時間:2017年6月1日 下午11:48:55 48 49 * 修改人:deng 50 51 * 修改時間:2017年6月1日 下午11:48:55 52 53 * 修改備注: 54 55 * @version 56 57 * 58 59 */ 60 61 62 63 public class CamelListenerImpl implements TaskListener,JavaDelegate { 64 65 @Override 66 67 public void notify(DelegateTask delegateTask) { 68 69 System.out.println("CamelListenerImpl notify is running"); 70 71 Map<String,Object> map = delegateTask.getVariables(); 72 73 74 75 76 77 Map<String, Object> variables = new HashMap<String, Object>(); 78 79 80 81 variables.put("input", "Hello"); 82 83 Map<String, String> outputMap = new HashMap<String, String>(); 84 85 variables.put("outputMap", outputMap); 86 87 delegateTask.setVariables(variables); 88 89 } 90 91 92 93 @Override 94 95 public void execute(DelegateExecution delegateTask) throws Exception { 96 97 // TODO Auto-generated method stub 98 99 System.out.println("CamelListenerImpl execute is running"); 100 101 Map<String,Object> map = delegateTask.getVariables(); 102 103 104 105 106 107 Map<String, Object> variables = new HashMap<String, Object>(); 108 109 110 111 variables.put("input", "Hello"); 112 113 Map<String, String> outputMap = new HashMap<String, String>(); 114 115 variables.put("outputMap", outputMap); 116 117 delegateTask.setVariables(variables); 118 119 } 120 121 }
Camel路由規則的配置:
路由的設置有兩種方式,第一種是通過java類來配置,另外一種是通過Spring配置文件配置。
(1)Java類配置:
Spring的環境下掃描路由配置
1 <camelContext id="camelContext" xmlns="http://camel.apache.org/schema/spring"> 2 3 <packageScan> 4 5 <package>light.mvc.workflow.camelRoute</package> 6 7 </packageScan> 8 9 </camelContext>
Java配置類
1 /** 2 3 * 4 5 */ 6 7 package light.mvc.workflow.camelRoute; 8 9 10 11 import org.apache.camel.builder.RouteBuilder; 12 13 14 15 /** 16 17 * 18 19 * 項目名稱:lightmvc 20 21 * 類名稱:MyCamelCallRoute 22 23 * 類描述: 24 25 * 創建人:鄧家海 26 27 * 創建時間:2017年6月24日 下午9:00:55 28 29 * 修改人:deng 30 31 * 修改時間:2017年6月24日 下午9:00:55 32 33 * 修改備注: 34 35 * @version 36 37 * 38 39 */ 40 41 42 43 public class MyCamelCallRoute extends RouteBuilder { 44 45 public MyCamelCallRoute(){ 46 47 System.out.println("MyCamelCallRoute is running"); 48 49 } 50 51 @Override 52 53 public void configure() throws Exception { 54 55 // TODO Auto-generated method stub 56 57 // from("activiti:CamelProcess:MyCamelCall").to("log:light.mvc.workflow.camelRoute"); 58 59 System.out.println("MyCamelCallRoute is running"); 60 61 62 63 from("activiti:CamelProcess:MyCamelCall").to("direct:start-activiti"); 64 65 from("direct:start-activiti").to("activiti:CamelStartprocess"); 66 67 //from("activiti:CamelProcess:MyCamelCall?copyCamelBodyToBody=true").transform().simple("${property.input} ,jesai"); 68 69 70 71 72 73 } 74 75 }
(2)Spring配置文件配置:
1 <camelContext id="testCamelContext" xmlns="http://camel.apache.org/schema/spring"> 2 3 <route> 4 5 <from uri="file:d:/temp/inbox?delay=30000"/> 6 7 <process ref="fileConverter"/> 8 9 <to uri="file:d:/temp/outbox"/> 10 11 </route> 12 13 </camelContext>
至此。一個簡單的Activiti整合Camel的例子就已經完成了。運行部署就可以發現,控制台輸出
CamelListenerImpl execute is running
同步和異步
之前的例子都是同步的。流程會等到camel規則返回之后才會停止。 一些情況下,我們需要activiti工作流繼續運行。這時camelServiceTask的異步功能就特別有用。 你可以通過設置camelServiceTask的async屬性來啟用這個功能。
1 <serviceTask id="serviceAsyncPing" activiti:type="camel" activiti:async="true"/>
通過設置這個功能,camel規則會被activiti的jobExecutor異步執行。 當你在camel規則中定義了一個隊列,activiti流程會在camelServiceTask執行時繼續運行。 camel規則會以完全異步的方式執行。 如果你想在什么地方等待camelServiceTask的返回值,你可以使用一個receiveTask。
1 <receiveTask id="receiveAsyncPing" name="Wait State" />
關於Camel路由規則:
例如:
1 public class SimpleCamelCallRoute extends RouteBuilder { 2 3 @Override 4 public void configure() throws Exception { 5 6 from("activiti:SimpleCamelCallProcess:simpleCall").to("log: org.activiti.camel.examples.SimpleCamelCall"); 7 } 8 }
注:部分 說明
終端URL 引用activiti終端
SimpleCamelCallProcess 流程名
simpleCall 流程中的Camel服務
(1)from("activiti:CamelProcess:MyCamelCall").to("log:light.mvc.workflow.camelRoute");
這個路由規則只是打印日志,什么也不做。
(2)
from("activiti:CamelProcess:MyCamelCall").to("direct:start-activiti");
from("direct:start-activiti").to("activiti:CamelStartprocess");
這個規則可以從已有的任務啟動過程中去啟動另外一個已經部署完成等待啟動的任務。比如在這里,我們可以嘗試去啟動一個ServiceTask任務
1)首先我們有一個已經部署好的可以運行的ServiceTask任務。流程名稱叫做CamelStartprocess
2)這個規則任務有一個監聽類:
1 /** 2 3 * 4 5 */ 6 7 package light.mvc.workflow.serviceTask; 8 9 10 11 import org.activiti.engine.delegate.DelegateExecution; 12 13 import org.activiti.engine.delegate.Expression; 14 15 import org.activiti.engine.delegate.JavaDelegate; 16 17 18 19 /** 20 21 * 22 23 * 項目名稱:lightmvc 24 25 * 類名稱:ServiceTask 26 27 * 類描述: 28 29 * 創建人:鄧家海 30 31 * 創建時間:2017年6月4日 下午6:18:11 32 33 * 修改人:deng 34 35 * 修改時間:2017年6月4日 下午6:18:11 36 37 * 修改備注: 38 39 * @version 40 41 * 42 43 */ 44 45 46 47 public class ServiceTask implements JavaDelegate{ 48 49 //流程變量 50 51 private Expression text1; 52 53 54 55 //重寫委托的提交方法 56 57 @Override 58 59 public void execute(DelegateExecution execution) throws Exception { 60 61 System.out.println("serviceTask已經執行已經執行!"); 62 63 String value1 = (String) text1.getValue(execution); 64 65 System.out.println(value1); 66 67 execution.setVariable("var1", new StringBuffer(value1).reverse().toString()); 68 69 } 70 71 72 73 }
3)部署這個流程
4)設置規則
from("activiti:CamelProcess:MyCamelCall").to("direct:start-activiti");
from("direct:start-activiti").to("activiti:CamelStartprocess");
5)啟動我們的Camel任務。可以看到結果
注:這里,我們啟動的是Camel任務,但是Camel任務的路由規則又去啟動一個Server任務。所以這里一共運行了兩個流程實例。
Table 8.5. 已有的camel行為:
行為 |
URL |
描述 |
CamelBehaviorDefaultImpl |
copyVariablesToProperties |
把Activiti變量復制為Camel屬性 |
CamelBehaviorCamelBodyImpl |
copyCamelBodyToBody |
只把名為"camelBody"Activiti變量復制成camel的消息體 |
CamelBehaviorBodyAsMapImpl |
copyVariablesToBodyAsMap |
把activiti的所有變量復制到一個map里,作為Camel的消息體 |
上面的表格解釋和activiti變量如何傳遞給camel。下面的表格解釋和camel的變量如何返回給activiti。 它只能配置在規則URL中。
Table 8.6. 已有的camel行為:
Url |
描述 |
|
默認 |
如果Camel消息體是一個map,把每個元素復制成activiti的變量,否則把整個camel消息體作為activiti的"camelBody"變量。 |
|
copyVariablesFromProperties |
將Camel屬性以相同名稱復制為Activiti變量 |
|
copyCamelBodyToBodyAsString |
和默認一樣,但是如果camel消息體不是map時,先把它轉換成字符串,再設置為"camelBody"。 |
|
copyVariablesFromHeader |
額外把camel頭部以相同名稱復制成Activiti變量 |
|
上面兩個表是關於流程變量的設置。那么我們在規則里面去實驗這幾個變量啟用會有什么效果:
我們設置了兩個變量,一個字符串變量input和一個集合變量OutPutMap
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("input", "Hello");
Map<String, String> outputMap = new HashMap<String, String>();
variables.put("outputMap", outputMap);
from("activiti:CamelProcess:MyCamelCall").transform().simple("${property.input} ,jesai");
Camel變量傳遞給Activiti
1)copyCamelBodyToBody
from("activiti:CamelProcess:MyCamelCall?copyCamelBodyToBody=true").transform().simple("${property.input} ,jesai");
可以看到camelBody只把名為"camelBody"Activiti變量復制成camel的消息體
2)copyVariablesToProperties
from("activiti:CamelProcess:MyCamelCall?copyVariablesToProperties=true").transform().simple("${property.input} ,jesai");
這里,已經把Activiti變量Input的Hello復制作為camelBody的消息
把Activiti變量復制為Camel屬性
3)copyVariablesToBodyAsMap
from("activiti:CamelProcess:MyCamelCall?copyVariablesToBodyAsMap=true").transform().simple("${property.input} ,jesai");
變量放到OutPutMap里面去了。
Activiti變量傳遞給Camel
1)默認
如果Camel消息體是一個map,把每個元素復制成activiti的變量,否則把整個camel消息體作為activiti的"camelBody"變量。
2)copyVariablesFromProperties
from("activiti:CamelProcess:MyCamelCall?copyVariablesFromProperties=true").transform().simple("${property.input} ,jesai");
將Camel屬性以相同名稱復制為Activiti變量
3)copyCamelBodyToBodyAsString
from("activiti:CamelProcess:MyCamelCall?copyCamelBodyToBodyAsString=true").transform().simple("${property.input} ,jesai");
和默認一樣,但是如果camel消息體不是map時,先把它轉換成字符串,再設置為"camelBody"。
4)copyVariablesFromHeader
from("activiti:CamelProcess:MyCamelCall?copyVariablesFromHeader=true").transform().simple("${property.input} ,jesai");
額外把camel頭部以相同名稱復制成Activiti變量
注:路由規則是在啟動部署系統的時候就已經初始化好了的。不支持熱更改。而且系統一單部署完成,運行流程實例的時候,不會執行路由配置。
實驗:我們在路由的配置類里面打印一個控制台:
啟動系統就會看到:
你會發現,它是在系統啟動的時候執行的。並不是在流程執行的時候。
Activiti交流QQ群:634320089