1、 JBPM4的結構特色
1.嵌入式的工做流引擎,不須要依賴特定的中間件或服務器,減小了硬件和軟件的綁定,徹底支持嵌入式應用的
業務流程開發框架,能夠在事務處理、數據持久化等各個方面與業務應用程序進行靈活的集成。
2.可拔插的體系架構,採用模塊化的架構設計,採用了IOC的設計理念,各模塊之間能夠比較方便地解除耦合或
替換不一樣的實現,例如持久化、事務處理、身份認證、日誌服務等,都由可選模塊實現。
3. 易擴展的流程語言。 html
2、 Jbpm4的安裝配置
1.下載地址:http://sourceforge.net/projects/jbpm/
2.解壓資源包,進入目錄install,在控制檯下運行腳本:ant demo.setup.tomcat。會執行以下操做:
1)下載安裝Tomcat.
2)安裝HSQLDB,並建立數據表結構。
3)啓動Tomcat,建立examples.bar業務流程歸檔,併發布到JBPM數據庫中,初始化相關用戶和組。
4)下載安裝Eclipse,並啓動Eclipse.
5)安裝JBPM Web控制檯。
6)安裝Signavio Web 設計器。
3.在Eclipse中安裝GPD插件,利用eclipse的軟件升級指定GPD安裝文件,文件爲
下載資源包中install/src/gpd/jbpm-gpd-site.zip。
4.添加jbdl4 Schema檢驗,在eclipse中配置schema,指定jbpm4安裝目錄下src文件夾中jpdl.xsd文件。
步驟爲:Window->Preferences->XML->XML CataLog->Add->File System。 java
3、 Jbpm流程API
1.流程相關概念
流程定義:對業務過程步驟的描述,表現爲若干"活動"節點經過"轉移"線條串聯。
流程實例:表示流程定義在運行時特有的執行例程。
流程執行:流程實例在其生命週期中,指向當前執行活動的指針。 sql
2.流程的6個Service API,可經過流程引擎對象的接口方法獲取。
ProcessEngine processEngine = Configuration.getProcessEngine();
1)RepositoryService,流程資源服務的接口。提供對流程定義的部署、查詢、刪除等操做。
2)ExecutionService,流程執行服務的接口。提供啓動流程實例、「執行」推動、設置流程變量等操做。
3)TaskService,人工任務服務的接口。提供對任務的建立、提交、查詢、保存、刪除等操做。
4)HistoryService,流程歷史服務的接口。提供對流程歷史庫中歷史流程實例、歷史活動實例等記錄的查詢操做。
5)IdentityService,身份認證服務的接口。提供對流程用戶、用戶組以及組成員關係的相關服務。
6)ManagementService,流程管理控制服務的接口。提供異步工做(Job)相關的執行和查詢操做。 數據庫
3.流程的佈署和刪除
1)流程的佈署 tomcat
String deploymentId = repositoryService.createDeployment() .addResourceFromClasspath("org/jbpm/examples/task/assignee/process.jpdl.xml") .deploy(); // 可屢次調用addResourceFromClasspath方法部署其它流程定義
2)流程的刪除 安全
repositoryService.deleteDeploymentCascade(deploymentId);
4.發起流程實例
1)流程Key 服務器
ProcessInstance processInstance = executionService.startProcessInstanceByKey("ICL");
2)流程Id session
ProcessInstance processInstance = executionService.startProcessInstanceByKey("ICL-1");
3)根據業務鍵指定流程實例ID 架構
ProcessInstance processInstance = executionService.startProcessInstanceByKey ("ICL",「Order09278」);
4)傳入流程變量 併發
ProcessInstance processInstance = executionService.startProcessInstanceByKey ("ICL", variablesMap);
5.喚醒等待狀態的執行
executionService.signalExecutionById(executionId);
6.得到用戶的任務列表
List<Task> taskList = taskService.findPersonalTasks("johndoe");
7.任務的完成提交
1)將用戶界面的任務表單內容存入任務
taskService.setVariables(taskId,variablesMap);
2)根據任務ID完成任務
taskService.completeTask(taskId);
3)根據任務ID完成任務,同時設入變量
taskService.completeTask(taskId, variablesMap);
4)根據任務ID完成任務,並指定下一步的轉移路徑
taskService.completeTask(taskId, outcome);
8.流程歷史實例獲取
1)得到流程定義的全部歷史流程實例,返回結果按開始時間排序
List<HistoryProcessInstance> historyProcessInstances = historyService .createHistoryProcessInstanceQuery() .processDefinitionId("ICL-1") .orderAsc(HistoryProcessInstanceQuery.PROPERTY_STARTTIME).list();
2)得到流程的歷史活動實例,可指定具體名稱的活動實例
List<HistoryActivityInstance> historyActivityInstances = historyService .createHistoryActivityInstanceQuery().processDefinitionId("ICL-1").list();
9.查詢結果分頁
1)流程實例查詢分頁
List<ProcessInstance> results = executionService.createProcessInstanceQuery() .processDefinitionId("ICL-1").page(0, 50).list();
2)流程任務查詢的分頁
List<Task> myTasks = taskService.createTaskQuery() .processInstanceId("ICL.Order09278") .assignee("Alex") .page(10, 20).list();
4、 流程定義
1.流程控制活動
1)start,開始活動
2)state,狀態活動
3)decision,判斷活動
4)fork,分支活動
5)join,聚合活動
6)end,結束活動
2.State活動
Jpdl定義:
<state name="wait for response"> <transition name="accept" to="submit document" /> <transition name="reject" to="try again"/> </state> <state name="submit document"/> <state name="try again"/>
// 獲取流程實例ID
String executionId = processInstance.findActiveExecutionIn("wait for response").getId(); // 觸發accept流向 processInstance = executionService.signalExecutionById(executionId, "accept");
3.decision活動
1)使用condition元素判斷decision活動
Jpdl:
<!-- decision 中會運行並判斷每個transition 裏的判斷條件。 當遇到一個嵌套條件是true 或者沒有設置判斷條件的轉移, 那麼轉移就會被運行 --> <decision name="evaluate document" > <transition to="submit document" > <condition expr="#{content==’good’}" /> </transition> <transition to="try again"> <condition expr="#{content==’bad’}" /> </transition> <transition to="give up"/> </decision> <state name="submit document"/> <state name="try again"/> <state name="give up"/>
Map<String, Object> variables = new HashMap<String, Object>(); variables.put("content", "good"); // 因爲傳入變量爲good,流向了submit document活動 ProcessInstance processInstance = executionService .startProcessInstanceByKey("DecisionConditions", variables);
2)使用decision的expr屬性判斷decision活動。
<!--可選擇的狀態結點,expr指定將被運行的指定腳本 --> <decision name="evaluate document" expr="#{content}" > <transition name="good" to="submit document"/> <transition name="bad" to="try again"/> <transition name="ugly" to="give up"/> </decision>
流程執行操做同上面condition元素的操做。
3)使用decision活動的handler元素判斷decision活動。
<!-- decision handler決定處理器繼承了DecisionHandler 接口的java 類, 決定處理器負責選擇向外轉移 --> <decision name="evaluate document" g="96,102,48,48"> <handler class="org.jbpm.examples.decision.handler.ContentEvaluation"/> <transition name="good" to="submit document"/> <transition name="bad" to="try again"/> <transition name="ugly" to="give up"/> </decision>
ContentEvaluation類以下:
public class ContentEvaluation implements DecisionHandler { public String decide(OpenExecution execution) { String content = (String) execution.getVariable("content"); return content; } }
流程執行操做同上面condition元素的操做。
Decision活動和state活動均可以實現條件流轉,但兩者的主要區別以下:
若是decision活動定義的流轉條件沒有任何一個獲得知足,那麼流程實例將沒法進行下去,拋出異常。
而state活動在沒有條件知足的條件下將流向state活動定義的第一條流出轉移,從而往下流轉。
所以decision活動具備更加嚴格的條件判斷特性。
4.fork-join活動
Jpdl:
<!-- fork活動在此產生3個並行分支,這些流程分支能夠同步執行。 --> <fork name="fork"> <transition to="send invoice"/> <transition to="load truck"/> <transition to="print shipping documents"/> </fork> <state name="send invoice"> <transition to="final join"/> </state> <state name="load truck"> <transition to="shipping join"/> </state> <state name="print shipping documents"> <transition g="378,213:" to="shipping join"/> </state> <!--join活動爲流程的合併,load truck和print shipping documents在此聚合 --> <join name="shipping join"> <transition to="drive truck to destination"/> </join> <state name="drive truck to destination"> <transition to="final join"/> </state> <!-- drive truck to destination活動和send invoice活動在此完成最終的聚合 --> <join name="final join"> <transition to="end"/> </join>
Fork活動可使流程在一條主幹上出現並行的分支,join活動則可使流程的並行分支聚合成一條主幹。
部分執行測試代碼以下:
5.task人工任務活動
1)、使用task活動的assignee屬性進行任務分配
<task name="review" assignee="#{order.owner}"> <transition to="wait" /> </task>
Assignee屬性默認會被做爲EL表達式來執行,任務被分配給#{order.owner}。
Map<String, Object> variables = new HashMap<String, Object>(); variables.put("order", new Order("johndoe")); //當一個新流程實例會被建立, 把order 做爲一個流程變量分配給它 ProcessInstance processInstance = executionService .startProcessInstanceByKey("TaskAssignee", variables); //獲取johndoe的任務列表 List<Task> taskList = taskService.findPersonalTasks("johndoe");
2)任務侯選者(candidate-groups,candidate-users)
candidate-groups:一個使用逗號分隔的組id 列表,全部組內的用戶將會成爲這個任務的 候選人。
candidate-users: 一個使用逗號分隔的用戶id 列表,全部的用戶將會成爲這個任務的候選人。
<task name="review" candidate-groups="sales-dept> <transition to="wait" /> </task>
部分事例代碼以下:
// 建立sales-dept組 dept = identityService.createGroup("sales-dept"); // 建立用戶johndoe,並加入sales-dept組 identityService.createUser("johndoe", "John", "Doe"); identityService.createMembership("johndoe", dept, "developer"); // 建立用戶joesmoe,並加入sales-dept組 identityService.createUser("joesmoe", "Joe", "Smoe"); identityService.createMembership("joesmoe", dept, "developer"); //在流程建立後, 任務會出如今johndoe 和joesmoe 用戶的分組任務列表中 List<Task> taskList = taskService.findGroupTasks("joesmoe"); List<Task> taskList = taskService.findGroupTasks("johndoe"); //候選人在處理任務以前,必須先接受任務,接受任務後,就會由任務的候選者變成 // 任務的分配者。同時,此任務會從全部候選者的任務列表中消失。 taskService.takeTask(task.getId(), "johndoe");
3)任務分配處理器(AssignmentHandler)
<task name="review"> <!--assignment-handler 是任務元素的一個子元素,它指定用戶代碼對象 --> <assignment-handler class="org.jbpm.examples.task.assignmenthandler.AssignTask"> <field name="assignee"> <string value="johndoe" /> </field> </assignment-handler> <transition to="wait" /> </task>
AssignTask類必須實現AssignmentHandler類,代碼以下:
//默認AssignmentHandler 實現可使用使用流程變量 public class AssignTask implements AssignmentHandler { String assignee; public void assign(Assignable assignable, OpenExecution execution) { assignable.setAssignee(assignee); } }
4)任務泳道(Swimlanes)
泳道能夠理解爲流程定義的」全局用戶組」,也能夠被看成一個流程規則。流程定義中的多個任務須要被分配或候選給
同一個羣用戶,統一將這個「同一羣用戶」定義爲「一個泳道」。
<!—-在這裏定義泳道,屬全局用戶組--> <swimlane name="sales representative" candidate-groups="sales-dept" /> <!-- swimlane 引用一個定義在流程中的泳道 --> <task name="enter order data" swimlane="sales representative"> <transition to="calculate quote"/> </task> <task name="calculate quote" swimlane="sales representative"> </task>
泳道中的用戶組中的用戶在接受任務後成爲任務的分配者,同時泳道也會發生變化,接收任務者在流程實例中會被固化爲分配者。
taskService.takeTask(taskId, "johndoe"); assertEquals(0, taskService.findGroupTasks("johndoe").size()); taskList = taskService.findPersonalTasks("johndoe"); assertEquals(1, taskList.size());
6.子流程活動(sub-process)
1)父子流程間的數據交換(parameter-in,parameter-out)
父流程SubProcessDocument定義JPDL:
<process name="SubProcessDocument" xmlns="http://jbpm.org/4.4/jpdl"> <start> <transition to="review" /> </start> <sub-process name="review" sub-process-key="SubProcessReview"> <parameter-in var="document" subvar="document" /> <parameter-out var="reviewResult" subvar="result" /> <transition to="wait" /> </sub-process> <state name="wait"/> </process>
子流程SubProcessReview定義JPDL:
<process name="SubProcessReview" xmlns="http://jbpm.org/4.4/jpdl"> <start> <transition to="get approval"/> </start> <task name="get approval" assignee="johndoe"> <transition to="end"/> </task> <end name="end"/> </process>
流程變量是父子流程用來溝通的紐帶。父流程在子流程啓動時將本身的「父流程變量」輸入子流程,
反之,子流程在結束時能夠將本身的「子流程變量」返回父流程,從而實現父子流程間的數據交換。
部分事例代碼以下:
// 分別部署子流程跟父流程 String subProcessReviewDeploymentId = repositoryService.createDeployment() .addResourceFromClasspath("org/jbpm/examples/subprocess/variables/SubProcessReview.jpdl.xml") .deploy(); String subProcessDocumentDeploymentId = repositoryService.createDeployment() .addResourceFromClasspath("org/jbpm/examples/subprocess/variables/SubProcessDocument.jpdl.xml") .deploy(); Map<String, Object> variables = new HashMap<String, Object>(); variables.put("document", "This document describes how we can make more money..."); //設置父流程的變量document ProcessInstance processInstance = executionService .startProcessInstanceByKey("SubProcessDocument", variables); List<Task> taskList = taskService.findPersonalTasks("johndoe"); Task task = taskList.get(0); // 父流程的變量document會被傳入子流程,此處爲獲取子流程的變量document String document = (String) taskService .getVariable(task.getId(), "document"); variables = new HashMap<String, Object>(); variables.put("result", "accept"); // 在子流程中設置流程變量result,該流程變量可在父流程中獲取 taskService.setVariables(task.getId(), variables); taskService.completeTask(task.getId()); processInstance = executionService.findProcessInstanceById(processInstance.getId()); // 在父流程中獲取子流程中設置的流程變量result,名稱爲reviewResult String result = (String) executionService .getVariable(processInstance.getId(), "reviewResult"); assertEquals("accept", result);
2)經過outcome屬性影響父流程的流程轉移
父流程SubProcessReview定義:
<!--父流程中的outcome屬性引用名稱爲result的子流程變量 <sub-process name="review" sub-process-key="SubProcessReview" outcome="#{result}"> <!—若是result值等於ok,則流向此轉移--> <transition name="ok" to="next step" /> <transition name="nok" to="update" /> <transition name="reject" to="close" /> </sub-process>
子流程SubProcessReview定義:
<start> <transition to="get approval"/> </start> <task name="get approval" assignee="johndoe"> <transition to="end"/> </task> <end name="end" />
部分事例代碼以下:
ProcessInstance processInstance = executionService .startProcessInstanceByKey("SubProcessDocument"); List<Task> taskList = taskService.findPersonalTasks("johndoe"); Task task = taskList.get(0); Map<String, Object> variables = new HashMap<String, Object>(); variables.put("result", "ok"); // 在子流程中設置流程變量result值爲ok,這個ok值會被傳遞給outcome屬性以決定父流程的走向。 taskService.setVariables(task.getId(), variables); taskService.completeTask(task.getId()); processInstance = executionService.findProcessInstanceById (processInstance.getId()); assertNotNull(processInstance.findActiveExecutionIn("next step"));
3)設置不一樣的子流程end活動名稱自動關聯父流程的流出轉移
父流程SubProcessReview定義:
<sub-process name="review" sub-process-key="SubProcessReview"> <transition name="ok" to="next step" /> <transition name="nok" to="update" /> <transition name="reject" to="close" /> </sub-process> <state name="next step" /> <state name="update" /> <state name="close" />
子流程SubProcessReview定義:
<task assignee="johndoe" name="get approval"> <transition name="ok" to="ok"/> <transition name="nok" to="nok"/> <transition name="reject" to="state1"/> </task> <end name="ok"/> <end name="nok"/> <end name="reject"/> <state name="state1" > <transition name="to reject" to="reject"/> </state>
部分事例代碼以下:
ProcessInstance processInstance = executionService .startProcessInstanceByKey("SubProcessDocument"); List<Task> taskList = taskService.findPersonalTasks("johndoe"); Task task = taskList.get(0); //子流程活動結束於ok活動返回父流程實例,父流程會自動地經過名稱爲ok的轉移, //進入"next step活動" taskService.completeTask(task.getId(), "ok"); processInstance = executionService .findProcessInstanceById(processInstance.getId()); assertNotNull(processInstance.findActiveExecutionIn("next step"));
7.自定義活動
若是有特殊而複雜的業務需求,與其生套JBPM自己提供的流轉控制活動,不如本身實現一個自定義的活動使用。
Jpdl定義:
<!-- custom調用用戶代碼,實現一個自定義的活動行爲 --> <custom name="print dots" class="org.jbpm.examples.custom.PrintDots""> <transition to="end" /> </custom>
自定義活動的類需實現ExternalActivityBehaviour接口
public class PrintDots implements ExternalActivityBehaviour { // 在流程實例進入到此活動時執行此方法 public void execute(ActivityExecution execution) { // 執行自定義的處理邏輯 // 使流程陷入「等待」狀態 execution.waitForSignal(); } // 在流程實例獲得執行信號離開此活動時執行此方法 public void signal(ActivityExecution execution, String signalName, Map<String, ?> parameters) { // 使流程實例進入下一步 execution.take(signalName); } }
8.自動活動
自動活動是在執行過程當中徹底無須人工干預地編排好程序,jbpm在處理和執行這些自動活動時能把人工活動產生的數據
經過流程變量等方式與之完美結合。
1)java程序活動
jpdl定義:
<!-- java 任務,流程處理的流向會執行 這個活動配置的方法 class:徹底類名 method:調用的方法名 var:返回值存儲的 變量名 --> <java name="greet" class="org.jbpm.examples.java.JohnDoe" method="hello" var="answer" g="96,16,83,52"> <!--fileld:在方法調用以前給成員變量注入 配置值 --> <field name="state"> <string value="fine"/> </field> <!--arg:方法參數 --> <arg><string value="Hi, how are you?"/></arg> <transition to="shake hand" /> </java> <!--expr:這個表達式返回方法被調用 產生的目標對象 ,經過對象參數傳入(new Hand()--> <java name="shake hand" expr="#{hand}" method="shake" var="hand" g="215,17,99,52"> <!-- 經過表達式引用流程變量,爲shake方法提供2個參數 --> <arg><object expr="#{joesmoe.handshakes.force}"/></arg> <arg><object expr="#{joesmoe.handshakes.duration}"/></arg> <transition to="wait" /> </java>
JohnDoe事例代碼以下:
public class JohnDoe implements Serializable { String state; public String hello(String msg) { if ( (msg.indexOf("how are you?")!=-1) ) { return "I'm "+state+", thank you."; } return null; } }
Hand事例代碼以下:
public class Hand implements Serializable { private boolean isShaken; public Hand shake(Integer force, Integer duration) { if (force>3 && duration>7) { isShaken = true; } return this; } public boolean isShaken() { return isShaken; } }
JoeSmoe事例代碼以下:
public class JoeSmoe implements Serializable { public Map<String, Integer> getHandshakes() { Map<String, Integer> handshakes = new HashMap<String, Integer>(); handshakes.put("force", 5); handshakes.put("duration", 12); return handshakes; } }
測試代碼以下:
八、JBPM自動活動
2)script腳本活動
jpdl定義:
<!-- script 腳本活動會解析一個script 腳本。 任何一種符合JSR-223 規範 的腳本引擎語言均可以在這裏運行。 expr:執行表達式的文本 var:返回值存儲的 變量名 --> <script name="invoke script" expr="Send packet to #{order.address}" var="text" g="96,16,104,52"> <transition to="wait" /> </script>
測試代碼以下:
Map<String, Object> variables = new HashMap<String, Object>(); variables.put("order", new Order("Berlin")); Execution execution = executionService .startProcessInstanceByKey("ScriptExpression", variables); String executionId = execution.getId(); String text = (String) executionService.getVariable(executionId, "text"); assertTextPresent("Send packet to Berlin", text);
3)hql查詢
jpdl定義:
<!-- 使用hql 活動,咱們能夠在database 中執行HQL query var:存儲結果的變量名 unique:值爲true 是指從uniqueResult()方法中 得到hibernate query 的結果。 默認值是false。 值爲false 的話會使用list()方法獲得結果。 --> <hql name="get task names" var="tasknames with i" g="96,16,115,52"> <!-- query:HQL query parameter:query 的參數 --> <query> select task.name from org.jbpm.pvm.internal.task.TaskImpl as task where task.name like :taskName </query> <parameters> <string name="taskName" value="%i%" /> </parameters> <transition to="count tasks" /> </hql> <hql name="count tasks" var="tasks" unique="true" g="243,16,95,52"> <query> select count(*) from org.jbpm.pvm.internal.task.TaskImpl </query> <transition to="wait" /> </hql>
測試事例代碼以下:
ProcessInstance processInstance = executionService.startProcessInstanceByKey("Hql"); String processInstanceId = processInstance.getId(); // 設定預期結果 Set<String> expectedTaskNames = new HashSet<String>(); expectedTaskNames.add("dishes"); expectedTaskNames.add("iron"); // 獲取第一個hql活動的執行結果,流程變量"tasknames with i" Collection<String> taskNames = (Collection<String>) executionService .getVariable(processInstanceId, "tasknames with i"); taskNames = new HashSet<String>(taskNames); assertEquals(expectedTaskNames, taskNames); // 獲取第二個hql活動的執行結果,流程數據庫中共有3條記錄 Object activities = executionService.getVariable(processInstanceId, "tasks"); assertEquals("3", activities.toString());
4)sql查詢
jpdl定義:
<!--sql 活動和hql 活動十分類似, 惟一不一樣的地方就是 使用session.createSQLQuery(...) --> <sql name="get task names" var="tasknames with i" g="96,16,126,52"> <query> select NAME_ from JBPM4_TASK where NAME_ like :name </query> <parameters> <string name="name" value="%i%" /> </parameters> <transition to="count tasks" /> </sql> <sql name="count tasks" var="tasks" unique="true" g="254,16,92,52"> <query> select count(*) from JBPM4_TASK </query> <transition to="wait" /> </sql>
測試事例代碼同Hql事例代碼。
5) mail(郵件活動)
jpdl定義:
<!-- from:發件者列表 to: 主要收件人列表 cc:抄送收件人列表 bcc: 密送收件人列表 subject:這個元素的文字內容會成爲消息的主題 text:這個元素的文字內容會成爲消息的文字內容 html:這個元素的文字內容會成爲消息的HTML 內容 attachments:每一個附件都會配置在單獨的子元素中 --> <mail g="99,25,115,45" language="juel" name="send rectify note"> <to addresses=" winston@minitrue"/> <cc groups="innerparty" users="bb"/> <bcc groups="thinkpol"/> <subject>rectify ${newspaper}</subject> <text>${newspaper} ${date} reporting bb dayorder doubleplusungood refs unpersons rewrite fullwise upsub antefiling</text> <!-- <html><table><tr><td>${newspaper}</td><td>${date}</td> <td>reporting bb dayorder doubleplusungood refs unpersons rewrite fullwise upsub antefiling</td> </tr></table></html> <attachments> <attachment url='http://www.george-orwell.org/1984/3.html' /> <attachment resource='org/example/pic.jpg' /> <attachment file='${user.home}/.face' /> </attachments> --> <transition to="wait"/> </mail>
九、事件
Jpdl定義:
<state name="wait" g="96,16,104,52"> <!-- event:事件名稱(start或end) event-listener:一個事件監聽器實現對象。 start:活動開始時捕獲 end:活動結束時捕獲 --> <on event="start"> <event-listener class="org.jbpm.examples.eventlistener.LogListener"> <field name="msg"><string value="start on activity wait"/></field> </event-listener> </on> <on event="end"> <event-listener class="org.jbpm.examples.eventlistener.LogListener"> <field name="msg"><string value="end on activity wait"/></field> </event-listener> </on> <transition to="park"> <event-listener class="org.jbpm.examples.eventlistener.LogListener"> <field name="msg"><string value="take transition"/></field> </event-listener> </transition> </state>
監聽器LogListener代碼:
public class LogListener implements EventListener { String msg; public void notify(EventListenerExecution execution) { List<String> logs = (List<String>) execution.getVariable("logs"); if (logs==null) { logs = new ArrayList<String>(); execution.setVariable("logs", logs); } logs.add(msg); execution.setVariable("logs", logs); } }
測試事例代碼以下:
ProcessInstance processInstance = executionService .startProcessInstanceByKey("EventListener"); Execution execution = processInstance.findActiveExecutionIn("wait"); executionService.signalExecutionById(execution.getId()); List<String> expectedLogs = new ArrayList<String>(); expectedLogs.add("start on process definition"); expectedLogs.add("start on activity wait"); expectedLogs.add("end on activity wait"); expectedLogs.add("take transition"); List<String> logs = (List<String>) executionService .getVariable(processInstance.getId(), "logs"); assertEquals(expectedLogs, logs);
默認狀況下,事件監聽器只對當前訂閱的元素所觸發的事件起做用,即propagation=」false」,
但經過指定事件監聽器的傳播屬性propagation=」enabled」或(propagation=」true」),
則該事件監聽器能夠對其監聽元素的全部子元素起做用。
10. 異步執行
1)幾乎全部的活動都支持異步屬性,流程一旦進入異步執行方式,一個異步消息會被做爲當前事務的一部門發送出去,
而後當前事務會當即自動提交。
Jpdl定義:
<!-- continue屬性: sync (默認值) 做爲當前事務的一部分,繼續執行元素。 async 使用一個異步調用(又名安全點)。當前事務被提交,元素在一個新事務中執行。 事務性的異步消息被jBPM 用來 實現這個功能。 --> <java name="generate pdf" continue="async" class="org.jbpm.examples.async.activity.Application" method="generatePdf" g="86,26,87,50"> <transition to="calculate primes"/> </java> <java name="calculate primes" continue="async" class="org.jbpm.examples.async.activity.Application" method="calculatePrimes" g="203,26,98,50"> <transition to="end"/> </java>
Application事例代碼以下:
public class Application implements Serializable { private static final long serialVersionUID = 1L; public void generatePdf() { // 此方法執行須要消耗較長時間 } public void calculatePrimes() { // 此方法執行須要消耗較長時間 } }
測試事例代碼以下:
ProcessInstance processInstance = executionService .startProcessInstanceByKey("AsyncActivity"); String processInstanceId = processInstance.getId(); // 流程實例處於異步執行狀態 assertEquals(Execution.STATE_ASYNC, processInstance.getState()); // 獲取流程實例異步消息隊列中的第1條消息 Job job = managementService.createJobQuery() .processInstanceId(processInstanceId).uniqueResult(); // 手工執行異步消息 managementService.executeJob(job.getId()); processInstance = executionService.findProcessInstanceById(processInstanceId); // 流程實例處於異步執行狀態 assertEquals(Execution.STATE_ASYNC, processInstance.getState()); // 獲取第2條消息(job)並執行之 job = managementService.createJobQuery() .processInstanceId(processInstanceId) .uniqueResult(); managementService.executeJob(job.getId()); assertNull(executionService.findProcessInstanceById(processInstanceId));
2)異步分支/聚合
Jpdl定義:
<!-- exclusive 這個值被用來 將兩個來自分支的異步調用的job 結果進行持久化。 各自的事務會分別執行ship goods 和send bill, 而後這兩個執行都會達到join 節點。 在join 節點中,兩個事務會同步到一個相同的執行上(在數據庫總更新同一個執行), 這可能致使一個潛在的樂觀鎖失敗。--> <fork g="99,68,80,40" name="fork"> <!-- 並行的流程分支以獨佔方式異步執行 --> <on event="end" continue="exclusive" /> <transition g="122,41:" to="ship goods"/> <transition g="123,142:" to="send bill"/> </fork> <java class="org.jbpm.examples.async.fork.Application" g="159,17,98,50" method="shipGoods" name="ship goods"> <transition g="297,42:" to="join"/> </java> <java class="org.jbpm.examples.async.fork.Application" g="159,117,98,50" method="sendBill" name="send bill"> <transition g="297,141:" to="join"/> </java> <join g="274,66,80,40" name="join"> <transition to="end"/> </join>
測試事例代碼以下:
ProcessInstance processInstance = executionService.startProcessInstanceByKey("AsyncFork"); String processInstanceId = processInstance.getId(); // 獲取異步消息列表 List<Job> jobs = managementService.createJobQuery() .processInstanceId(processInstanceId).list(); //有兩個分支,有2條異步消息 assertEquals(2, jobs.size()); Job job = jobs.get(0); //手工執行第1條 managementService.executeJob(job.getId()); job = jobs.get(1); //手工執行第2條 managementService.executeJob(job.getId()); Date endTime = historyService .createHistoryProcessInstanceQuery() .processInstanceId(processInstance.getId()) .uniqueResult() .getEndTime(); // 流程已結束 assertNotNull(endTime);
11. 流程變量
1)流程變量與流程實例綁定, 可經過如下方法來操做流程變量:
例:
ProcessInstance startProcessInstanceById (String processDefinitionId,Map<String,Object>variables); ProcessInstance startProcessInstanceById (String processDefinitionId,Map<String,Object>variables,String processInstanceKey);
2)其它引擎服務中也存在相似的方法,例如TaskService也提供方法操做任務綁定的流程變量。
3)經過流程變量控制流程的流向是正確的作法,可是不要被這種「方便」的機制誘惑而往流程實例裏面放全部的東西,
特別是與流轉控制無關的業務數據。
5、 JBPM數據表
JBPM4_DEPLOYMENT: 流程定義的部署記錄 JBPM4_DEPLOYPROP: 已部署的流程定義的具體屬性 JBPM4_LOB:流程定義的相關資源,包括JPDL XML、圖片、用戶代碼Java類等。 JBPM4_JOB:異步活動或定時執行的Job記錄。 JBPM4_VARIABLE:流程實例的變量。 JBPM4_EXECUTION:流程實例及執行對象。 JBPM4_SWIMLANE:任務泳道。 JBPM4_PARTICIPATION:任務參與者,任務的相關用戶,區別於任務的分配人。 JBPM4_TASK:流程實例的任務記錄。 JBPM4_HIST_PROCINST:保存歷史的流程實例記錄。 JBPM4_HIST_ACTINST:保存歷史的活動實例記錄。 JBPM4_HIST_TASK:保存歷史的任務實例記錄。 JBPM4_HIST_VAR:保存歷史的流程變量數據。 JBPM4_HIST_DETAIL:保存流程實例、活動實例、任務實例運行過程當中歷史明細數據。 JBPM4_ID_USER:保存用戶記錄。 JBPM4_ID_MEMBERSHIP:保存用戶和用戶組之間的關聯關係。 JBPM4_ID_GROUP:保存用戶組記錄。