国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
jbpm - jPDL
jPDL的schema文件包含了比這個文檔中更多的屬性和元素。 這個文檔解釋了jPDL中穩(wěn)定的被支持的部分。 試驗性的、不支持的jPDL特性可以在開發(fā)者指南中找到。
下面是一個jPDL流程文件的例子:
<?xml version="1.0" encoding="UTF-8"?>
<process name="Purchase order" xmlns="  <start>
    <transition to="Verify supplier" />
  </start>
  <state name="Verify supplier">
    <transition name="Supplier ok" to="Check supplier data" />
    <transition name="Supplier not ok" to="Error" />
  </state>
  <decision name="Check supplier data">
    <transition name="nok" to="Error" />
    <transition name="ok" to="Completed" />
  </decision>
  <end name="Completed" />
  <end name="Error" />
</process>6.1. process流程處理
頂級元素(element)是流程處理定義。
表 6.1. process流程處理的屬性
屬性 類型 默認(rèn)值 是否必須 描述
name名稱 文本   必須 在與用戶交互時, 作為流程名字顯示的一個名字或是標(biāo)簽。
key鍵 字母或數(shù)字,下劃線 如果省略,key中的非字母和非數(shù)字的字符會被替換為 下劃線。 可選(optional) 用來辨別不同的流程定義。 擁有同一個key的流程會有多個版本。 對于所有已發(fā)布的流
程版本,key-name這種組合都必須是 完全一樣的。
version版本 整型 比已部署的key相同的流程版本號高1, 如果還沒有與之相同的key的流程被部署,那么版本就從1開始。 可選 流程的版本號
表 6.2. process流程的元素
元素 個數(shù) 描述
description描述 0個或1個 描述文本
activities活動 至少1個 流程中會有很多活動, 至少要有1個是啟動的活動。
6.2. 控制流程Activities活動
6.2.1. start啟動
說明一個流程的實例從哪里開始。 在一個流程里必須有一個開始節(jié)點。 一個流程必須至少擁有一個開始節(jié)點。 開始節(jié)點必須有一個向外的流向,這個流向會在流程啟動的時候執(zhí)行。
已知的限制:直到現(xiàn)在, 一個流程處理只能有一個啟動節(jié)點(start)。
表 6.3. start啟動的屬性
屬性 類型 默認(rèn)值 是否必須 描述
name名稱 文本   可選 活動的名字,在啟動活動沒有內(nèi)部的轉(zhuǎn)移(transition)時, name名稱是可選的。
表 6.4. start啟動的元素
元素 個數(shù) 描述
transition轉(zhuǎn)移 1 向外的轉(zhuǎn)移
6.2.2. State狀態(tài)節(jié)點
一個等待狀態(tài)節(jié)點。 流程處理的流向會在外部觸發(fā)器調(diào)用提供的API之前一直等待。 狀態(tài)節(jié)點和其他的活動不一樣, 它沒有其他任何屬性或元素。
6.2.2.1. 序列狀態(tài)節(jié)點
讓我們看一個用序列連接狀態(tài) 和轉(zhuǎn)移的例子
圖 6.1. 序列狀態(tài)節(jié)點
<process name="StateSequence" xmlns="
  <start>
    <transition to="a" />
  </start>
  <state name="a">
    <transition to="b" />
  </state>
  <state name="b">
    <transition to="c" />
  </state>
  <state name="c" />
</process>下列代碼將啟動一個流向:
ProcessInstance processInstance =
    executionService.startProcessInstanceByKey("StateSequence");創(chuàng)建的流程處理實例會停留在狀態(tài)節(jié)點a的位置, 使用signalExecution方法就會觸發(fā) 一個外部觸發(fā)器。
Execution executionInA = processInstance.findActiveExecutionIn("a");
assertNotNull(executionInA);
processInstance = executionService.signalExecutionById(executionInA.getId());
Execution executionInB = processInstance.findActiveExecutionIn("b");
assertNotNull(executionInB);
processInstance = executionService.signalExecutionById(executionInB.getId());
Execution executionInC = processInstance.findActiveExecutionIn("c");
assertNotNull(executionInC);6.2.2.2. 可選擇的狀態(tài)節(jié)點
在第2個狀態(tài)節(jié)點的例子里, 我們將演示如何使用狀態(tài)節(jié)點實現(xiàn) 路徑的選擇。
圖 6.2. 狀態(tài)節(jié)點中的選擇
<process name="StateChoice" xmlns="
  <start>
    <transition to="wait for response" />
  </start>
  <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" />
</process>讓我們在這個流程處理定義里啟動一個新的流程實例。
ProcessInstance processInstance = executionService
    .startProcessInstanceByKey("StateChoice");現(xiàn)在,流向到達(dá)wait for response狀態(tài)節(jié)點了。 流向會一直等待到外部觸發(fā)器的出現(xiàn)。 這里的狀態(tài)節(jié)點擁有多個向外的轉(zhuǎn)移, 外部觸發(fā)器將為向
reject的向外的轉(zhuǎn)移 繼續(xù)進(jìn)行。
6.2.3. decision決定節(jié)點
在多個選擇中選擇一條路徑。也可以當(dāng)做是一個決定。 一個決定活動擁有很多個向外的轉(zhuǎn)移。當(dāng)一個流向到達(dá)一個決定活動時, 會自動執(zhí)行并決定交給哪個向外的轉(zhuǎn)移。
一個決定節(jié)點應(yīng)該配置成下面三個方式之一。
6.2.3.1. decision決定條件
decision中會運行并判斷每一個transition里的判斷條件。 當(dāng)遇到一個嵌套條件是true或者沒有設(shè)置判斷條件的轉(zhuǎn)移, 那么轉(zhuǎn)移就會被運行。
表 6.5. exclusive.transition.condition 屬性
屬性 類型 默認(rèn)值 是否必須? 描述
expr expression   required必須 將被運行的 指定腳本
lang expression language 從腳本引擎配置里得到的默認(rèn)代表性語言(default-expression-language) 可選 指定expr中執(zhí)行的 腳本語言的種類
例子:
圖 6.3. 流程處理的決定條件例子
<process name="DecisionConditions" >
  <start>
    <transition to="evaluate document" />
  </start>
  <decision name="evaluate document">
    <transition to="submit document">
      <condition expr="#{content=="good"}" />
    </transition>
    <transition to="try again">
      <condition expr="#{content=="not so good"}" />
    </transition>
    <transition to="give up" />
  </decision>
  <state name="submit document" />
  <state name="try again" />
  <state name="give up" />
</process>在使用good content啟動一個流程之后
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("content", "good");
ProcessInstance processInstance =
    executionService.startProcessInstanceByKey("DecisionConditions", variables);submit document活動會變成活動的
assertTrue(processInstance.isActive("submit document"));參考實例中的單元測試,了解更多的場景。
6.2.3.2. decision expression唯一性表達(dá)式
decision表達(dá)式返回類型為字符串的 向外轉(zhuǎn)移的名字。
表 6.6. 決定屬性
屬性 類型 默認(rèn)值 是否必須? 描述
expr expression   required必須 將被運行的指定 腳本
lang expression language 從腳本引擎配置里得到的默認(rèn)指定的腳本語言(default-expression-language) 可選 指定expr中執(zhí)行的腳本語言的 種類。
例子:
圖 6.4. 流程處理的決定表達(dá)式例子
<process name="DecisionExpression" xmlns="
  <start >
    <transition to="evaluate document"/>
  </start>
  <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>
  <state name="submit document"  />
  <state name="try again"  />
  <state name="give up"  />
</process>當(dāng)你使用good content啟動一個新的流程實例,代碼如下:
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("content", "good");
ProcessInstance processInstance =
    executionService.startProcessInstanceByKey("DecisionExpression", variables);然后新流程會到達(dá)submit document活動。
參考實例中的單元測試,獲得其他場景。
6.2.3.3. Decision handler決定處理器
唯一性管理是繼承了DecisionHandler接口的java類。 決定處理器負(fù)責(zé)選擇 向外轉(zhuǎn)移。
public interface DecisionHandler {
   String decide(OpenExecution execution);
}這個handler被列為decision的子元素。 配置屬性和decision的handler的內(nèi)容元素 可以在第 6.7 節(jié) “用戶代碼”中找到。
下面是一個決定使用DecisionHandler的流程處理例子:
圖 6.5. 流程處理的exclusive管理例子
<process name="DecisionHandler">
  <start>
    <transition to="evaluate document" />
  </start>
  <decision name="evaluate document">
    <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>
  <state name="submit document" />
  <state name="try again" />
  <state name="give up" />
</process>下面是ContentEvalation類:
public class ContentEvaluation implements DecisionHandler {
  public String decide(OpenExecution execution) {
    String content = (String) execution.getVariable("content");
    if (content.equals("you're great")) {
      return "good";
    }
    if (content.equals("you gotta improve")) {
      return "bad";
    }
    return "ugly";
  }
}
當(dāng)你啟動流程處理實例, 并為變量content提供值you're great時, ContentEvalation就會返回字符串good, 流程處理實例便會到達(dá)Submit document活動。
6.2.4. concurrency并發(fā)
流程的并發(fā)路徑可以使用 fork 和 join 活動來建模。下面的表格描述了 join 的屬性;fork沒有特別的屬性。
表 6.7. join屬性:
屬性 類型 默認(rèn)值 是否必須? 描述
multiplicity 整數(shù)或表達(dá)式 傳入轉(zhuǎn)移的數(shù)目 可選 在這個join活動之前需要到達(dá)的執(zhí)行的數(shù)目, 然后一個執(zhí)行 會沿著join的單獨的外向轉(zhuǎn)移向外執(zhí)行。
lockmode {none, read, upgrade, upgrade_nowait, write} upgrade 可選 hibernate的鎖定模式,應(yīng)用在上級執(zhí)行, 來防止兩個還沒到達(dá)join的同步事務(wù)看到對方, 這會導(dǎo)致死鎖。
6.2.4.1. 使用 fork實現(xiàn)并行分支
fork 活動允許將一個單獨的流程路徑分成 兩個或多個分支,這些流程分支可以同步執(zhí)行。
圖 6.6. 流程處理的并發(fā)例子
<process name="ConcurrencyGraphBased" xmlns="
   <start>
      <transition to="fork"/>
   </start>
   <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 to="shipping join" />
   </state>
   <join name="shipping join" >
      <transition to="drive truck to destination" />
   </join>
   <state name="drive truck to destination" >
      <transition to="final join" />
   </state>
   <join name="final join" >
      <transition to="end"/>
   </join>
   <end name="end" />
</process>6.2.5. end結(jié)束
結(jié)束流向
6.2.5.1. end process instance結(jié)束流程處理實例
默認(rèn)情況下,結(jié)束活動會終結(jié)已完成流程處理實例。 因此在流程處理實例中, 仍然在活動的多個并發(fā)(concurrent)流向(concurrent) 也會結(jié)束。
圖 6.7. 結(jié)束活動
<process name="EndProcessInstance" xmlns="
  <start>
    <transition to="end" />
  </start>
  <end name="end" />
</process>新的流程處理實例一創(chuàng)建便會直接結(jié)束。
6.2.5.2. end execution結(jié)束流向
只有流向到達(dá)結(jié)束(end)活動時會結(jié)束流程處理實例, 并且其他并發(fā)流向會放棄活動。 我們可以設(shè)置屬性ends="execution" 來達(dá)到這種狀況。
表 6.8. end execution屬性
屬性 類型 默認(rèn)值 是否必須 描述
ends {processinstance|execution} processinstance optional可選 流向路徑到達(dá)end活動 整個流程處理實例就會結(jié)束。
  <start>
    <transition to="get return code" />
  </start>
  <state name="get return code">
    <transition name="200" to="ok"/>
    <transition name="400" to="bad request"/>
    <transition name="500" to="internal server error"/>
  </state>
  <end name="ok"/>
  <end name="bad request"/>
  <end name="internal server error"/>
</process>
        如果你啟動一個流向并使用下面的代碼將它執(zhí)行到get return code等待狀態(tài), 流向便會以bad request的end 活動(event)結(jié)束
ProcessInstance processInstance = executionService.startProcessInstanceByKey("EndMultiple");
String pid = processInstance.getId();
processInstance = executionService.signalExecutionById(pid, "400");同樣地,使用值為200或者500就會讓流向(execution) 分別以ok或者internal server error的end events結(jié)束。
6.2.5.4. end State結(jié)束狀態(tài)
流向(execution)可以以不同的狀態(tài)結(jié)束??梢杂闷渌姆绞搅谐隽鞒烫幚韺嵗慕Y(jié)果。 可以用end event的狀態(tài)屬性或者end-cancel 和end-error表示。
表 6.9. end execution 屬性
屬性 類型 默認(rèn)值 是否必須 描述
state String   可選 狀態(tài)分配給流向
 
參考下面流程的例子。

圖 6.9. 不同的結(jié)束狀態(tài)
 
<process name="EndState" xmlns="
  <start>
     <transition to="get return code"/>
  </start>
  <state name="get return code">
    <transition name="200" to="ok"/>
    <transition name="400" to="bad request" />
    <transition name="500" to="internal server error"/>
  </state>
  <end name="ok" state="completed"/>
  <end-cancel name="bad request"/>
  <end-error name="internal server error"/>
</process>
        這時,如果我們啟動一個流向并使用下面的代碼將流向執(zhí)行到get return code等待狀態(tài), 流向會以取消狀態(tài)(cancel state)結(jié)束。
和上面一樣,使用值為200或500會讓流向 分別以comleted或者error states結(jié)束。
6.2.6. task
在任務(wù)組件中,為一個人創(chuàng)建一個任務(wù)。
6.2.6.1. 任務(wù)分配者
一個簡單的任務(wù)會被分配給一個指定的用戶
表 6.10. 任務(wù)屬性:
屬性 類型 默認(rèn)值 是否必填 描述
assignee 表達(dá)式   可選 用戶id引用的用戶 負(fù)責(zé)完成任務(wù)。
 

圖 6.10. 任務(wù)分配者示例流程
 
<process name="TaskAssignee">
  <start>
    <transition to="review" />
  </start>
  <task name="review"
        assignee="#{order.owner}">
     <transition to="wait" />
  </task>
  <state name="wait" />
</process>這個流程演示了任務(wù)分配的兩個方面。第一, assignee用來指示用戶, 負(fù)責(zé)完成任務(wù)的人。分配人是一個任務(wù)中的字符串屬性 引用一個用戶。
第二,這個屬性默認(rèn)會當(dāng)做表達(dá)式來執(zhí)行。 在這里任務(wù)被分配給#{order.owner}。 這意味著首先使用order這個名字查找一個對象。 其中一個查找對象的地方是這個任務(wù)對應(yīng)的流程變量。 然后
getOwner()方法會用來 獲得用戶id, 引用的用戶負(fù)責(zé)完成這個任務(wù)。
這就是我們例子中使用到得Order類:
public class Order implements Serializable {
  String owner;
  public Order(String owner) {
    this.owner = owner;
  }
  public String getOwner() {
    return owner;
  }
  public void setOwner(String owner) {
    this.owner = owner;
  }
}當(dāng)一個新流程實例會被創(chuàng)建, 把order作為一個流程變量分配給它。
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("order", new Order("johndoe"));
ProcessInstance processInstance = executionService
    .startProcessInstanceByKey("TaskAssignee", variables);然后johndoe的任務(wù)列表可以像下面這樣獲得。
List<Task> taskList = taskService.findPersonalTasks("johndoe");注意也可以使用純文本, assignee="johndoe"。 在這里,任務(wù)會被分配給johndoe。
6.2.6.2. task候選人
任務(wù)可能被分配給一組用戶。 其中的一個用戶應(yīng)該接受這個任務(wù)并完成它。
表 6.11. 任務(wù)屬性:
屬性 類型 默認(rèn)值 是否必填 描述
candidate-groups 表達(dá)式   可選 一個使用逗號分隔的組id列表。 所有組內(nèi)的用戶將會成為這個任務(wù)的 候選人。
candidate-users 表達(dá)式   可選 一個使用逗號分隔的用戶id列表。 所有的用戶將會成為這個任務(wù)的候選人。
 

圖 6.11. 任務(wù)候選人示例流程
 
這是一個使用任務(wù)候選人的示例流程:
<process name="TaskCandidates">
  <start>
    <transition to="review" />
  </start>
  <task name="review"
        candidate-groups="sales-dept">
     <transition to="wait" />
  </task>
  <state name="wait"/>
</process>
        在啟動之后,一個任務(wù)會被創(chuàng)建。這個任務(wù)不顯示在任何人的個人任務(wù)列表中。 下面的任務(wù)列表會是空的。
taskService.findPersonalTasks("johndoe");
taskService.findPersonalTasks("joesmoe");但是任務(wù)會顯示在所有sales-dept組成員的 分組任務(wù)列表中。
在我們的例子中,sales-dept有兩個成員:johndoe和joesmoe
identityService.createGroup("sales-dept");
identityService.createUser("johndoe", "johndoe", "John", "Doe");
identityService.createMembership("johndoe", "sales-dept");
identityService.createUser("joesmoe", "joesmoe", "Joe", "Smoe");
identityService.createMembership("joesmoe", "sales-dept"); 所以在流程創(chuàng)建后, 任務(wù)會出現(xiàn)在johndoe和joesmoe用戶的分組任務(wù)列表中。
taskService.findGroupTasks("johndoe");
taskService.findGroupTasks("joesmoe");候選人必須接受一個任務(wù),在他們處理它之前。 這會表現(xiàn)為兩個候選人在同一個任務(wù)上開始工作。 分組任務(wù)列表中,用戶接口必須只接受對這些任務(wù)的“接
受”操作。
taskService.takeTask(task.getDbid(), "johndoe");當(dāng)一個用戶接受了一個任務(wù),這個任務(wù)的分配人就會變成當(dāng)前用戶。 任務(wù)會從所有候選人的分組任務(wù)列表中消失, 它會出現(xiàn)在用戶的已分配列表
中。
用戶只允許工作在他們的個人任務(wù)列表上。 這應(yīng)該由用戶接口控制。
簡單的,candidate-users屬性 可以用來處理用逗號分隔的一系列用戶id。 candidate-users屬性可以和其他分配選項結(jié)合使用。
6.2.6.3. 任務(wù)分配處理器
一個AssignmentHandler可以通過編程方式來計算 一個任務(wù)的分配人和候選人。
public interface AssignmentHandler extends Serializable {
  /** sets the actorId and candidates for the given assignable. */
  void assign(Assignable assignable, OpenExecution execution) throws Exception;
}Assignable是任務(wù)和泳道的通用接口。 所以任務(wù)分配處理器可以使用在任務(wù), 也可以用在泳道中(參考后面的內(nèi)容)。
assignment-handler是任務(wù)元素的一個子元素。 它指定用戶代碼對象。所以assignment-handler的屬性和元素 都來自第 6.7 節(jié) “用戶代碼”
讓我們看一下任務(wù)分配的例子流程。

圖 6.12. 任務(wù)分配處理器的示例流程
 
<process name="TaskAssignmentHandler" xmlns="
  <start g="20,20,48,48">
    <transition to="review" />
  </start>
  <task name="review" g="96,16,127,52">
    <assignment-handler class="org.jbpm.examples.task.assignmenthandler.AssignTask">
      <field name="assignee">
        <string value="johndoe" />
      </field>
    </assignment-handler>
    <transition to="wait" />
  </task>
  <state name="wait" g="255,16,88,52" />
</process>引用的類AssignTask看起來像這樣:
public class AssignTask implements AssignmentHandler {
  String assignee;
  public void assign(Assignable assignable, OpenExecution execution) {
    assignable.setAssignee(assignee);
  }
}請注意,默認(rèn)AssignmentHandler實現(xiàn)可以使用使用流程變量 任何其他Java API可以訪問資源,像你的應(yīng)用數(shù)據(jù)庫來計算 分配人和候選人用戶和組。
啟動一個TaskAssignmentHandler的新流程實例 會立即讓新流程實例運行到任務(wù)節(jié)點。 一個新review任務(wù)被創(chuàng)建,在這個時候 AssignTask的分配處理器被調(diào)用。這將設(shè)置johndoe為分配人。 所以John
Doe將在他自己的任務(wù)列表中找到這個任務(wù)。
6.2.6.4. 任務(wù)泳道
一個流程中的多任務(wù)應(yīng)該被分配給同一個用戶或換選人。 一個流程中的多任務(wù)可以分配給一個單獨的泳道。 流程實例將記得換選人和用戶,在泳道中執(zhí)行的第一個任務(wù)。 任務(wù)序列在同一個泳道中將被
分配給 這些用戶和候選人。
一個泳道也可以當(dāng)做一個流程規(guī)則。 在一些情況下, 這可能與身份組件中的權(quán)限角色相同。 但是實際上它們并不是同一個東西。
表 6.12. 任務(wù)屬性:
屬性 類型 默認(rèn)值 是否必填 描述
swimlane 泳道(字符串)   可選 引用一個定義在流程中的泳道
 
泳道可以被聲明在流程元素中:
表 6.13. 泳道屬性:
屬性 類型 默認(rèn)值 是否必填 描述
name 泳道(字符串)   必填 泳道名稱。 這個名稱將被任務(wù)泳道屬性中引用。
assignee 表達(dá)式   可選 用戶id引用的用戶 負(fù)責(zé)完成這個任務(wù)。
candidate-groups 表達(dá)式   可選 一個使用逗號分隔的組id列表。 所有組中的人將作為這個任務(wù)的這個泳道中的 候選人。
candidate-users 表達(dá)式   可選 一個使用逗號分隔的用戶id列表。 所有的用戶將作為這個任務(wù)的這個泳道中的 候選人。
 

圖 6.13. 任務(wù)泳道示例流程
 
任務(wù)泳道示例是下面這個流程文件:
<process name="TaskSwimlane" xmlns="
  <swimlane name="sales representative"
            candidate-groups="sales-dept" />
  <start>
    <transition to="enter order data" />
  </start>
  <task name="enter order data"
        swimlane="sales representative">
    <transition to="calculate quote"/>
  </task>
  <task
      name="calculate quote"
      swimlane="sales representative">
  </task>
</process>在這個例子中,我們在身份組件中 創(chuàng)建了下面的信息:
identityService.createGroup("sales-dept");
identityService.createUser("johndoe", "johndoe", "John", "Doe");
identityService.createMembership("johndoe", "sales-dept");在啟動一個新流程實例后,用戶johndoe將成為 enter order data的一個候選人。還是像上一個流程候選人例子一樣, John Doe可以像
這樣接收任務(wù):
taskService.takeTask(taskDbid, "johndoe");接收這個任務(wù)將讓johndoe成為任務(wù)的分配人。 直到任務(wù)與泳道sales representative關(guān)聯(lián), 分配人johndoe也會關(guān)聯(lián)到泳道中 作為分配人。
接下來,John Doe可以像下面這樣完成任務(wù):
taskService.completeTask(taskDbid);完成任務(wù)會將流程執(zhí)行到下一個任務(wù), 下一個任務(wù)是calculate quote。 這個任務(wù)也關(guān)聯(lián)著泳道。因此, 任務(wù)會分配給johndoe。 初始化分配的候選人用戶和候
選人組也會從泳道復(fù)制給任務(wù)。 這里所指的用戶johndoe 會釋放任務(wù),返回它給其他候選人。
6.2.6.5. 任務(wù)變量
任務(wù)可以讀取,更新流程變量。 稍后任務(wù)可以選擇定義任務(wù)本地流程變量。 任務(wù)變量是任務(wù)表單的一個很重要的部分。 任務(wù)表單顯示來自任務(wù)和流程實例的數(shù)據(jù)。 然后從用戶一側(cè)錄入的數(shù)據(jù)會轉(zhuǎn)換
成設(shè)置的任務(wù)變量。
獲得任務(wù)變量就像這樣:
List<Task> taskList = taskService.findPersonalTasks("johndoe");
Task task = taskList.get(0);
long taskDbid = task.getDbid();
Set<String> variableNames = taskService.getVariableNames(taskDbid);
Map<String, Object> variables = taskService.getVariables(taskDbid, variableNames);設(shè)置任務(wù)變量就像這樣:
variables = new HashMap<String, Object>();
variables.put("category", "small");
variables.put("lires", 923874893);
taskService.setVariables(taskDbid, variables);6.2.6.6. 在任務(wù)中支持e-mail
可以為分配人提供一個提醒, 當(dāng)一個任務(wù)添加到他們的列表時,以及在特定的時間間隔進(jìn)行提醒。 每個email信息都是根據(jù)一個模板生成出來的。模板可以在內(nèi)部指定, 或者在配置文件中的 process
-engine-context部分指定。
表 6.14. task元素
元素 數(shù)目 描述
notification 0..1 讓一個任務(wù)被分配的時候發(fā)送一個提醒消息。 如果沒有引用模板,也沒有提供內(nèi)部的模板, mail會使用task-notification名字的模板。
reminder 0..1 根據(jù)指定的時間間隔發(fā)送提醒信息。 如果沒有引用模板,也沒有提供內(nèi)部模板, mail會使用task-reminder名字的模板。
 
表 6.15. notification屬性
屬性 類型 默認(rèn)值 是否必填 描述
continue {sync | async | exclusive} sync 可選 指定在發(fā)送提醒郵件后, 是不是產(chǎn)生一個異步執(zhí)行。
 
表 6.16. reminder屬性:
屬性 類型 默認(rèn)值 是否必填 描述
duedate 持續(xù)時間(純字符串或包含表達(dá)式)   必填 在reminder email發(fā)送前的延遲時間。
repeat 持續(xù)時間(純字符串或包含表達(dá)式)   可選 在一個序列reminder email發(fā)送后延遲的時間
continue {sync | async | exclusive} sync 可選 指定在發(fā)送提醒郵件后, 是不是產(chǎn)生一個異步執(zhí)行。
 
這里有一個基本的例子,可以獲得默認(rèn)的模板。
<task name="review"
      assignee="#{order.owner}"
     <notification/>
     <reminder duedate="2 days" repeat="1 day"/>
</task>6.2.7. sub-process子流程
創(chuàng)建一個子流程實例然后等待直到它完成。 當(dāng)子流程實例完成,子流程中的流向就會 繼續(xù)。
表 6.17. 子流程屬性:
屬性 類型 默認(rèn)值 是否必填 描述
sub-process-id 字符串或表達(dá)式   這個或sub-process-key是必填的 根據(jù)id獲得子流程。 這意味著引用了一個流程定義的指定版本。 子流程id可以設(shè)置為字符串或表達(dá)式。
sub-process-key 字符串或表達(dá)式   這個或sub-process-id是必須的 根據(jù)key獲得子流程。 這意味著引用了一個指定了key的流程定義的最新版本。 流程定義的最新版本會在每次活動執(zhí)行的時候進(jìn)行
查找。 子流程key可以設(shè)置為字符串或表達(dá)式。
outcome 表達(dá)式   當(dāng)指定outcome-value時必填 當(dāng)子流程結(jié)束的時候執(zhí)行表達(dá)式。 值用來映射向外的流向。 添加outcome-value元素到sub-process 活動的外出流向中。
 
表 6.18. sub-process元素:
元素 多重 描述
parameter-in 0..* 聲明一個變量,傳遞給子流程實例, 在創(chuàng)建它時。
parameter-out 0..* 定義一個變量,在子流程結(jié)束時 設(shè)置到上級執(zhí)行中。
 
表 6.19. parameter-in屬性:
屬性 類型 默認(rèn)值 是否必填 描述
subvar 字符串   必填 已經(jīng)賦值的子流程變量的名稱。
var 字符串   'var'或'expr'其中之一必須指定值 上級流程環(huán)境中的變量名。
expr 字符串   'var'或'expr'其中之一必須指定值 這個表達(dá)式將會在super流程環(huán)境中被解析。 結(jié)果值會被設(shè)置到子流程變量中。
lang 字符串 juel 可選 表達(dá)式解析時使用的腳本語言。
 
表 6.20. parameter-out屬性:
屬性 類型 默認(rèn)值 是否必填 描述
var 字符串   必填 上級流程環(huán)境中需要設(shè)置的 變量名。
subvar 字符串   'subvar'或'expr'其中之一必須指定值 子流程中需要獲取的 變量名。
expr 字符串   'subvar'或'expr'其中之一必須指定值 這個表達(dá)式將會在sub流程環(huán)境下被解析。 結(jié)果值會被設(shè)置到上級流程變量中。
lang 字符串 juel 可選 表達(dá)式解析時使用的腳本語言。
 
表 6.21. 對外變量映射的額外transition元素:
元素 多重 描述
outcome-value 0..1 如果outcome與值匹配, 就會在子流程結(jié)束時進(jìn)入這個流向。 這個值是由一個子元素指定的。
 
6.2.7.1. sub-process變量
這個SubProcessVariables示例場景將展示子流程獲得基本工作方式, 如何向子流程中反饋信息,當(dāng)它啟動時, 如果從子流程中導(dǎo)出信息,當(dāng)它結(jié)束時。
上級流程調(diào)用一個需要重審的文檔。

圖 6.14. 子流程文檔示例流程
 
<process name="SubProcessDocument" xmlns="
  <start>
    <transition to="review" />
  </start>
  <sub-process name="review"
               sub-process-key="SubProcessReview">
    <variable name="document" init="#{document}" />
    <out-variable name="reviewResult" init="#{result}" />
    <transition to="wait" />
  </sub-process>
  <state name="wait"/>
</process>重審流程是一個可以對所有類型的重審工作重用的流程。

圖 6.15. 子流程重審示例流程
 
<process name="SubProcessReview" xmlns="
  <start>
    <transition to="get approval"/>
  </start>
  <task name="get approval"
        assignee="johndoe">
    <transition to="end"/>
  </task>
  <end name="end" />
</process>文檔流程被啟動,并授予一個文檔變量:
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("document", "This document describes how we can make more money...");
ProcessInstance processInstance = executionService
    .startProcessInstanceByKey("SubProcessDocument", variables);然后上級流程會到達(dá)子流程節(jié)點。 一個子流程會被創(chuàng)建并關(guān)聯(lián)到上級流程中。 當(dāng)SubProcessReview流程實例啟動時, 它到達(dá)了
task。 一個任務(wù)會為johndoe創(chuàng)建。
List<Task> taskList = taskService.findPersonalTasks("johndoe");
Task task = taskList.get(0);我們可以看到文檔已經(jīng)被通過, 從上級流程實例到子流程實例:
String document = (String) taskService.getVariable(task.getDbid(), "document");
assertEquals("This document describes how we can make more money...", document);然后我們在任務(wù)上設(shè)置一個變量。這一般都是通過一個表單來完成。 但是這是我們將演示如何使用編程方式完
成。
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("result", "accept");
taskService.setVariables(task.getDbid(), variables);完成這個任務(wù),會導(dǎo)致子流程實例結(jié)束。
taskService.completeTask(task.getDbid());當(dāng)子流程結(jié)束時,上級流程會被signal標(biāo)記(不是notify提醒)。 首先result變量會從子流程復(fù)制到 父流程的reviewResult變量中。 然后上級流程會繼
續(xù), 并離開review活動。
6.2.7.2. sub-process外出值
在SubProcessOutcomeValueTest示例中, 子流程實例變量的值被用來選擇sub-process活動 的外出流向。

圖 6.16. 子流程文檔示例流程
 
<process name="SubProcessDocument">
  <start>
    <transition to="review" />
  </start>
  <sub-process name="review"
               sub-process-key="SubProcessReview"
               outcome="#{result}">
    <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" />
</process>這個SubProcessReview和上面的 子流程變量示例相同:

圖 6.17. 子流程復(fù)審示例流程,為外向變量
 
<process name="SubProcessReview" xmlns="
  <start>
    <transition to="get approval"/>
  </start>
  <task name="get approval"
        assignee="johndoe">
    <transition to="end"/>
  </task>
  <end name="end" />
</process>一個新文檔實例會像通常一樣啟動:
ProcessInstance processInstance = executionService
    .startProcessInstanceByKey("SubProcessDocument");任務(wù)被獲得到j(luò)ohndoe的任務(wù)列表中
List<Task> taskList = taskService.findPersonalTasks("johndoe");
Task task = taskList.get(0);
        然后result變量被設(shè)置, 任務(wù)完成。
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("result", "ok");
taskService.setVariables(task.getId(), variables);
taskService.completeTask(task.getDbid());在這個場景中,ok流向被獲取在上級流程中 在子流程復(fù)審活動外。 這個例子測試用例也展示了其他場景。
6.2.7.3. sub-process外向活動
一個流程可以有多個結(jié)束節(jié)點。在SubProcessOutcomeActivityTest示例中, 結(jié)果的結(jié)束節(jié)點被用來選擇sub-process活動的 外出流向。

圖 6.18. 子流程文檔實例流程,對于外出活動
 
<process name="SubProcessDocument">
  <start>
    <transition to="review" />
  </start>
  <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" />
</process>這個SubProcessReview現(xiàn)在擁有多個結(jié)束活動:

圖 6.19. 子流程重審示例流程,為外出活動
 
<process name="SubProcessReview" xmlns="
  <start>
    <transition to="get approval"/>
  </start>
  <task name="get approval"
        assignee="johndoe">
    <transition name="ok" to="ok"/>
    <transition name="nok" to="nok"/>
    <transition name="reject" to="reject"/>
  </task>
  <end name="ok" />
  <end name="nok" />
  <end name="reject" />
</process>一個新文檔流程實例像通常一樣被啟動:
ProcessInstance processInstance = executionService
    .startProcessInstanceByKey("SubProcessDocument");任務(wù)被獲取到j(luò)ohndoe的任務(wù)列表中
List<Task> taskList = taskService.findPersonalTasks("johndoe");
Task task = taskList.get(0);
        任務(wù)會在ok結(jié)束。
taskService.completeTask(task.getDbid(), "ok");
        這將導(dǎo)致子流程結(jié)束在ok結(jié)束活動。 上級節(jié)點會通過ok流向 進(jìn)入next step。
這個示例測試用例也展示了其他場景。
6.2.8. custom
調(diào)用用戶代碼,實現(xiàn)一個自定義的活動行為。
一個自定義活動引用了用戶代碼。參考第 6.7 節(jié) “用戶代碼” 獲得特定屬性和元素的更多信息。 讓我們看這個例子:
<process name="Custom" xmlns="
  <start >
    <transition to="print dots" />
  </start>
  <custom name="print dots"
        class="org.jbpm.examples.custom.PrintDots">
    <transition to="end" />
  </custom>
  <end name="end" />
</process>這個自定義活動行為類PrintDots 演示了它有可能去控制流向,當(dāng)實現(xiàn)了自定義活動行為時。 在這種情況下PrintDots 活動實現(xiàn)將在打印點后在活動中暫停 直到出現(xiàn)一個signal。
public class PrintDots implements ExternalActivityBehaviour {
  private static final long serialVersionUID = 1L;
  public void execute(ActivityExecution execution) {
    String executionId = execution.getId();
    String dots = ...;
    System.out.println(dots);
    execution.waitForSignal();
  }
  public void signal(ActivityExecution execution,
                     String signalName,
                     Map<String, ?> parameters) {
    execution.take(signalName);
  }
}6.3. 自動活動
6.3.1. java
java任務(wù)。流程處理的流向會執(zhí)行 這個活動配置的方法。
表 6.22. java 屬性
屬性 類型 默認(rèn)值 是否必須 描述
class classname   'class'或'expr'之一必須指定 完全類名。參考第 6.7.2 節(jié) “用戶代碼類加載器” 來獲得類加載的信息。用戶代碼對象會被延遲加載, 并作為流程定義的一部分進(jìn)行緩存。
expr 表達(dá)式   'class'或'expr'之一必須指定 這個表達(dá)式返回方法被調(diào)用 產(chǎn)生的目標(biāo)對象。
method methodname   必須 調(diào)用的方法名
var variablename   可選 返回值存儲的 變量名
 
表 6.23. java 元素
元素 個數(shù) 描述
field 0..* 在方法調(diào)用之前給成員變量注入 配置值
arg 0..* 方法參數(shù)
 
思考下面的例子:

圖 6.20. java 任務(wù)(task)
 
<process name="Java" xmlns="
  <start >
    <transition to="greet" />
  </start>
  <java name="greet"
        class="org.jbpm.examples.java.JohnDoe"
        method="hello"
        var="answer"
        >
    <field name="state"><string value="fine"/></field>
    <arg><string value="Hi, how are you?"/></arg>
    <transition to="shake hand" />
  </java>
  <java name="shake hand"
        expr="#{hand}"
        method="shake"
        var="hand"
        >
    <arg><object expr="#{joesmoe.handshakes.force}"/></arg>
    <arg><object expr="#{joesmoe.handshakes.duration}"/></arg>
    <transition to="wait" />
  </java>
  <state name="wait" />
</process>
      調(diào)用的類:
public class JohnDoe {
  String state;
  Session session;
  public String hello(String msg) {
    if ( (msg.indexOf("how are you?")!=-1)
         && (session.isOpen())
       ) {
      return "I'm "+state+", thank you.";
    }
    return null;
  }
}public class JoeSmoe implements Serializable {
  static Map<String, Integer> handshakes = new HashMap<String, Integer>();
  {
    handshakes.put("force", 5);
    handshakes.put("duration", 12);
  }
  public Map<String, Integer> getHandshakes() {
    return handshakes;
  }
}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;
  }
}第一個java活動greet指定了,在它執(zhí)行期間,一個 org.jbpm.examples.java.JohnDoe類的實例會被初始化 這個類的hello方法會被調(diào)用,并獲得調(diào)用的返回對象。 名為answer的變量會獲得調(diào)用的結(jié)
果。
上面的類展示了它包含名字為state和session的兩個fields, 在整個流向中field指定的values和arg 這兩個配置元素會被調(diào)用。 流程處理實例預(yù)期的結(jié)果是流程處理的變量answer的值為 字符串I'm
fine,thank you.。
第二個java活動叫做shake hand。 它會處理#{hand}表達(dá)式, 把調(diào)用的結(jié)果對象作為目標(biāo)對象。在這個對象上, shake方法會被調(diào)用。這兩個參數(shù)會各自被 表達(dá)式#{joesmoe.handshakes.force}和 #
{joesmoe.handshakes.duration}計算。結(jié)果對象 是一個hand的修改版本,而var="hand"回導(dǎo)致修改hand, 通過覆蓋老hand的變量值。
6.3.2. script腳本
script腳本活動會解析一個script腳本。任何一種符合JSR-223規(guī)范 的腳本引擎語言都可以在這里運行。 腳本引擎的配置會在下面解釋:
下面有2種方式詳細(xì)說明如何使用腳本:
6.3.2.1. script expression腳本表達(dá)式
script腳本提供expr屬性。 這個短小的符號在屬性里比在文本元素里表達(dá)更簡單。 如果沒有指定語言(lang)會使用 默認(rèn)的表達(dá)式語言(default-expression-language)。
表 6.24. script腳本表達(dá)式屬性
屬性 類型 默認(rèn)值 是否必須 描述
expr text   必須 執(zhí)行表達(dá)式的文本
lang 腳本語言名字定義在第 8 章 Scripting腳本 默認(rèn)的表達(dá)式語言定義在第 8 章 Scripting腳本 可選 表達(dá)式指定的語言
var variablename   可選 返回值存儲的 變量名。
 
在下一個例子中,我們會看到script腳本如何 使用表達(dá)式活動和返回結(jié)果怎樣存儲在variable變量里。

圖 6.21. 流程處理的script.expression示例
 
<process name="ScriptExpression" xmlns="
  <start>
    <transition to="invoke script" />
  </start>
  <script name="invoke script"
          expr="Send packet to #{person.address}"
          var="text">
    <transition to="wait" />
  </script>
  <state name="wait"/>
</process>這個例子使用了person類,代碼如下:
public class Person implements Serializable {
  String address;
  public Person(String address) {
    this.address = address;
  }
  public String getAddress() {
    return address;
  }
  public void setAddress(String address) {
    this.address = address;
  }
}當(dāng)為這個流程處理啟動一個流程處理實例時, 我們提供一個的person的地址屬性的變量。
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("person", new Person("Honolulu"));
executionService.startProcessInstanceByKey("ScriptText", variables);然后script腳本活動中的整個流向, 變量中將包含'Send packet to Honolulu'
6.3.2.2. script 文本
第2種方式是用text元素指定script腳本。 當(dāng)script text有多行的時候這種方式更方便。
表 6.25. script text屬性
屬性 類型 默認(rèn)值 是否必須 描述
lang 腳本語言名字定義在第 8 章 Scripting腳本 默認(rèn)的表達(dá)式語言定義在第 8 章 Scripting腳本 可選 表達(dá)式指定的語言
var variablename   可選 返回值存儲的 變量名。
 
表 6.26. script text元素
元素 個數(shù) 描述
text 1 包含script腳本的文本
 
例如:

圖 6.22. 流程處理的script text示例
 
<process name="ScriptText" xmlns="
  <start>
    <transition to="invoke script" />
  </start>
  <script name="invoke script"
          var="text">
    <text>
      Send packet to #{person.address}
    </text>
    <transition to="wait" />
  </script>
  <state name="wait"/>
</process>這個流程處理的整個流向要求和上面的script腳本表達(dá)式一樣。
6.3.3. hql
使用hql活動,我們可以在database中執(zhí)行HQL query, 并將返回的結(jié)果保存到流程處理的變量中。
表 6.27. hql屬性
屬性 類型 默認(rèn)值 是否必須 描述
var variablename   可選 存儲結(jié)果的變量名
unique {true,false} false 可選 值為true是指從uniqueResult()方法中 獲得hibernate query的結(jié)果。 默認(rèn)值是false。 值為false的話會使用list()方法得到結(jié)果。
 
表 6.28. hql元素
元素 個數(shù) 描述
query 1 HQL query
parameter 0..* query的參數(shù)
 
例如:

圖 6.23. 流程處理的hql例子
 
<process name="Hql" xmlns="
  <start>
    <transition to="get process names" />
  </start>
  <hql name="get process names"
       var="activities with o">
    <query>
      select activity.name
      from org.jbpm.pvm.internal.model.ActivityImpl as activity
      where activity.name like :activityName
    </query>
    <parameters>
      <string name="activityName" value="%o%" />
    </parameters>
    <transition to="count activities" />
  </hql>
  <hql name="count activities"
       var="activities"
       unique="true">
    <query>
      select count(*)
      from org.jbpm.pvm.internal.model.ActivityImpl
    </query>
    <transition to="wait" />
  </hql>
  <state name="wait"/>
</process>6.3.4. sql
sql活動和hql活動十分相似, 唯一不同的地方就是 使用session.createSQLQuery(...)。
6.3.5. mail
通過使用mail活動,流程作者 可以指定一個郵件信息的內(nèi)容,一次發(fā)送給多個收件人。 每個email信息都是從一個模板生成的。 模板可能指定在元素內(nèi)部,或者在配置文件的 process-engine-context
部分指定。
表 6.29. mail屬性
屬性 類型 默認(rèn)值 是否必須 描述
template 字符串   否 引用配置文件中的一個mail-template元素。 如果沒找到, 必須使用子元素在內(nèi)部指定。
 
表 6.30. mail元素
元素 個數(shù) 描述
from 0..1 發(fā)件者列表
to 1 主要收件人列表
cc 0..1 抄送收件人列表
bcc 0..1 密送收件人列表
subject 1 這個元素的文字內(nèi)容會成為消息的主題
text 0..1 這個元素的文字內(nèi)容會成為消息的文字內(nèi)容
html 0..1 這個元素的文字內(nèi)容會成為消息的HTML內(nèi)容
attachments 0..1 每個附件都會配置在單獨的子元素中
 
表 6.31. attachment元素
屬性 類型 默認(rèn)值 是否必須 描述
name 字符串   不必填,除非使用了expression 與這個附件對應(yīng)的文件 名。 如果沒有設(shè)置這個值,會通過封裝的數(shù)據(jù)源,比如resource, file和url來提供一個 反饋值。
description 字符串   不必填 與這個附件對應(yīng)的描述信息。
expression string   必須指定 expression, file, url 或 resource 其中之一 解析表達(dá)式后的結(jié)果會作為附件的值,它是一個java對象。 這個功能可以把流程變量轉(zhuǎn)化成郵件附件。
file 字符串   文件系統(tǒng)中的附件路徑。 對應(yīng)的文件必須是存在的。
url 字符串   附件在網(wǎng)上的URL地址。 指向的資源必須是存在的。
resource 字符串   在classpath下放置包含附件內(nèi)容的資源名稱。 對應(yīng)的資源必須存在。
mime-type 字符串   不必填,除非使用了expression 指定表達(dá)式返回的對象的MIME type。
 
示例使用方法:
<process name="InlineMail" xmlns="  <start>
    <transition to="send birthday reminder note" />
  </start>
  <mail name="send birthday reminder note">
    <to addresses="
johnDoe@some-company.com" />
    <subject>Reminder: ${person} celebrates his birthday!</subject>
    <text>Do not forget: ${date} is the birthday of ${person} </text>
    <attachments>
      <attachment resource="org/example/birthday_card.png"/>
      <attachment name="picture.jpg" expression="${picture}" mime-type="image/jpeg"/>
    </attachments>
    <transition to="end" />
  </mail>
  <state name="end"/>
</process>6.4. Common activity contents通用活動內(nèi)容
除非在上面指定其他的元素,否則所有的活動都會包含 這些內(nèi)容模板:
表 6.32. common activity屬性
屬性 類型 默認(rèn)值 是否必須 描述
name any text   必須 activity活動的名字
 
表 6.33. common activity元素
元素 個數(shù) 描述
transition 0..* 向外的轉(zhuǎn)移

 
6.5. Events事件
事件指定流程中的特定點,那里注冊了一系列的時間監(jiān)聽器。 當(dāng)一個流程通過這一點時,事件監(jiān)聽器就會被提醒。 事件和監(jiān)聽器不會顯示在流程的圖形視圖中, 這是因為它們對實現(xiàn)技術(shù)細(xì)節(jié)更感興趣
。 一個事件會被流程定義中的一個元素觸發(fā),比如流程定義, 一個活動或一個流向。
事件監(jiān)聽器接口看起來就像這樣:
public interface EventListener extends Serializable {
  void notify(EventListenerExecution execution) throws Exception;
}所有的自動活動可以作為 事件監(jiān)聽器來使用。
為了給一個流程或一個活動分配一系列的事件監(jiān)聽器, 使用on元素來為事件監(jiān)聽器分組并指定事件。 on可以內(nèi)嵌到process 或任何活動的子節(jié)點。
為了分配一系列的事件監(jiān)聽器給流向的take事件, 只需要包含事件監(jiān)聽器,直接在transition 元素中。
表 6.34. on屬性:
屬性 類型 默認(rèn)值 是否必填 描述
event {start | end}   必填 事件名稱
 
表 6.35. on元素:
元素 個數(shù) 描述
event-listener 0..* 一個事件監(jiān)聽器實現(xiàn)對象。
任何自動活動 0..*  
 
表 6.36. 事件監(jiān)聽器屬性:
event-listener 是用戶代碼所以它可以 像第 6.7 節(jié) “用戶代碼”中一樣進(jìn)行配置。
任何自動活動(包括event-listener)在事件中, 可以指定下面的額外屬性:
屬性 類型 默認(rèn)值 是否必填 描述
propagation {enabled | disabled | true | false | on | off} disabled 可選 指定事件監(jiān)聽器應(yīng)該也被 傳播的事件調(diào)用。
continue {sync | async | exclusive} sync 可選 指定execution是否應(yīng)該被異步執(zhí)行, 在事件監(jiān)聽器執(zhí)行之前,可以參考 第 6.6 節(jié) “異步調(diào)用”
 
6.5.1. 事件監(jiān)聽器示例
讓我們看一個使用了事件監(jiān)聽器的示例流程:

圖 6.24. 事件監(jiān)聽器示例流程
 
<process name="EventListener" xmlns="
  <on event="start">
    <event-listener class="org.jbpm.examples.eventlistener.LogListener">
      <field name="msg"><string value="start on process definition"/></field>
    </event-listener>
  </on>
  <start>
    <transition to="wait"/>
  </start>
  <state name="wait">
    <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>
  <state name="park"/>
</process>LogListener將維護(hù)一系列日志,作為流程變量:
public class LogListener implements EventListener {
  // value gets injected from process definition
  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");然后流程實例執(zhí)行到等待活動。 所以我們提供了一個singal,那將導(dǎo)致它執(zhí)行到結(jié)束。
Execution execution = processInstance.findActiveExecutionIn("wait");
executionService.signalExecutionById(execution.getId());這個日志消息隊列會像這樣:
[start on process definition,
 start on activity wait,
 end on activity wait,
 take transition]6.5.2. 事件傳播
事件是從活動和轉(zhuǎn)移傳播到外部的活動, 最終傳播到流程定義。
默認(rèn)情況下,事件監(jiān)聽器只對當(dāng)前的事件監(jiān)聽器訂閱的元素 所觸發(fā)的事件起作用。 但是,通過指定propagation="enabled", 事件監(jiān)聽器也可以對所有在這個元素包含中的事件起作用。
6.6. 異步調(diào)用
每次對于ExecutionService.startProcessInstanceById(...) 或ExecutionService.signalProcessInstanceById(...)的調(diào)用 會讓流程執(zhí)行在發(fā)起的線程中(客戶端)。換句話說, 那些方法將只會流
程執(zhí)行到達(dá)一個等待狀態(tài)后才能返回。
這種默認(rèn)的行為有很多優(yōu)點:用戶系統(tǒng)的事務(wù)可以很容易的就傳遞給jBPM, 這樣jBPM的數(shù)據(jù)庫更新操作就可以在用戶的事務(wù)環(huán)境中完成了。 其次,當(dāng)流程執(zhí)行過程中某些操作出錯的時候, 客戶也可以
獲得一個異常。通常來說, 這些在流程的兩個等待狀態(tài)之間需要完成的工作量都是比較小的。 即便在兩個等待狀態(tài)之間有很多個自動的活動節(jié)點需要執(zhí)行。 所以在大多數(shù)情況下, 最好是在一個單獨
的事務(wù)中執(zhí)行所有這些工作。這也就解釋了jPDL的默認(rèn)行為, 它會在客戶端的線程中同步執(zhí)行流程的所有工作。
在一些情況下,你不想等待所有的自動活動都完成后再返回響應(yīng), jPDL允許在事務(wù)邊界上進(jìn)行良好的控制。 在流程中的不同環(huán)境中,可以使用異步調(diào)用這種方式。 異步調(diào)用一般用在以下環(huán)節(jié),異步調(diào)
用會提交事務(wù), jBPM方法調(diào)用會立即返回。jBPM會啟動一個新事務(wù), 以異步方式繼續(xù)執(zhí)行其他的自動流程工作。 jBPM在內(nèi)部使用異步消息來完成這些工作。
當(dāng)使用異步調(diào)用時,一個異步消息會被作為當(dāng)前事務(wù)的一部分發(fā)送出去。 然后原始調(diào)用方法,像是 startProcessInstanceById(...) 或signalProcessInstanceById(...)會直接返回。 當(dāng)異步消息被提
交執(zhí)行時,它會啟動一個新事務(wù), 在流程離開的地方重新開始執(zhí)行。
表 6.37. 任意活動屬性:
屬性 類型 默認(rèn)值 是否必填 描述
continue {sync | async | exclusive} sync 可選 用來表明在活動被執(zhí)行前, 是否使用異步調(diào)用。
 
 
sync (默認(rèn)值) 作為當(dāng)前事務(wù)的一部分,繼續(xù)執(zhí)行元素。
async 使用一個異步調(diào)用(又名安全點)。 當(dāng)前事務(wù)被提交,元素在一個新事務(wù)中執(zhí)行。 事務(wù)性的異步消息被jBPM用來 實現(xiàn)這個功能。
exclusive 使用一個異步調(diào)用(又名安全點)。 當(dāng)前事務(wù)被提交,元素在一個新事務(wù)中執(zhí)行。 事務(wù)性的異步消息被jBPM用來 實現(xiàn)這個功能。唯一性消息不會被同步執(zhí)行。 jBPM會確認(rèn)對同一個流程實
例,具有唯一性的定時計劃 不會同時執(zhí)行,即使你的jBPM配置了 多個異步消息執(zhí)行器(比如jobExecutor)在不同的系統(tǒng)中運行。 這可以用來防止樂觀鎖失敗, 如果多個,有潛在沖突可能的job被安
排在同一個事務(wù)中。
讓我們來看一些例子。
6.6.1. 異步活動
圖 6.25. 異步活動實例流程
 
<process name="AsyncActivity" xmlns="
  <start>
    <transition to="generate pdf"/>
  </start>
  <java name="generate pdf"
        continue="async"
        class="org.jbpm.examples.async.activity.Application"
        method="generatePdf" >
    <transition to="calculate primes"/>
  </java>
  <java name="calculate primes"
        continue="async"
        class="org.jbpm.examples.async.activity.Application"
        method="calculatePrimes">
    <transition to="end"/>
  </java>
  <end name="end"/>
</process>public class Application {
  public void generatePdf() {
    // assume long automatic calculations here
  }
  public void calculatePrimes() {
    // assume long automatic calculations here
  }
}ProcessInstance processInstance =
     executionService.startProcessInstanceByKey("AsyncActivity");
String processInstanceId = processInstance.getId();如果不使用異步調(diào)用,這將是一個完全自動的流程, 流程會在在startProcessInstanceByKey方法中 從頭執(zhí)行到尾。
可使用了continue="async"后 執(zhí)行只會執(zhí)行到generate pdf活動。 然后一個異步調(diào)用消息會被發(fā)送, startProcessInstanceByKey方法就會返回。
在一個通常的配置中,job執(zhí)行器會自動獲得消息并執(zhí)行它。 當(dāng)時在測試環(huán)境下,對于這些例子我們希望控制這些消息什么時候被執(zhí)行, 所以就沒有配置job執(zhí)行器。 因此我們必須像下面這樣手工執(zhí)行
job:
Job job = managementService.createJobQuery()
  .processInstanceId(processInstanceId)
  .uniqueResult();
managementService.executeJob(job.getDbid());這會獲得流程,直到它執(zhí)行calculate primes活動, 然后另一個異步消息又會 被發(fā)送。
然后這個消息又會被獲得,然后當(dāng)消息被執(zhí)行時, 那個事務(wù)就會將流程執(zhí)行到結(jié)束為止。
6.6.2. 異步分支
圖 6.26. 異步分支實例流程
 
<process name="AsyncFork" xmlns="
  <start >
    <transition to="fork"/>
  </start>
  <fork >
    <on event="end" continue="exclusive" />
    <transition />
    <transition />
  </fork>
  <java class="org.jbpm.examples.async.fork.Application" >
    <transition />
  </java>
  <java class="org.jbpm.examples.async.fork.Application" >
    <transition />
  </java>
  <join >
    <transition to="end"/>
  </join>
  <end />
</process>public class Application {
  public void shipGoods() {
    // assume automatic calculations here
  }
  public void sendBill() {
    // assume automatic calculations here
  }
}通過在fork的end事件上使用異步調(diào)用 (<on event="end" continue="exclusive" />), 每個沿著分支轉(zhuǎn)移生成的執(zhí)行都會 使用異步方式繼續(xù)執(zhí)行。
exclusive這個值被用來 將兩個來自分支的異步調(diào)用的job結(jié)果進(jìn)行持久化。 各自的事務(wù)會分別執(zhí)行ship goods和send bill, 然后這兩個執(zhí)行都會達(dá)到j(luò)oin節(jié)點。 在join節(jié)點中,兩個事務(wù)會同步到一
個相同的執(zhí)行上(在數(shù)據(jù)庫總更新同一個執(zhí)行), 這可能導(dǎo)致一個潛在的樂觀鎖失敗。
ProcessInstance processInstance = executionService.startProcessInstanceByKey("AsyncFork");
String processInstanceId = processInstance.getId();
List<Job> jobs = managementService.createJobQuery()
  .processInstanceId(processInstanceId)
  .list();
assertEquals(2, jobs.size());
Job job = jobs.get(0);
// here we simulate execution of the job,
// which is normally done by the job executor
managementService.executeJob(job.getDbid());
job = jobs.get(1);
// here we simulate execution of the job,
// which is normally done by the job executor
managementService.executeJob(job.getDbid());
Date endTime = historyService
  .createHistoryProcessInstanceQuery()
  .processInstanceId(processInstance.getId())
  .uniqueResult()
  .getEndTime();
assertNotNull(endTime);6.7. 用戶代碼
jPDL流程語言中的大量元素都引用一個對象, 它的一個接口方法將被調(diào)用。這一章介紹了通用屬性和元素, 對這些用戶編碼對象 進(jìn)行初始化和配置。
custom
event-listener
task中的assignment-handler
decision中的handler
transition中的condition
6.7.1. 用戶代碼配置
表 6.38. 屬性:
屬性 類型 默認(rèn)值 是否必填? 描述
class 類名   {class|expr}其中之一是必須的 全類名。初始化只會進(jìn)行一次, 用戶對象會被作為流程定義的一部分進(jìn)行緩存。
expr 表達(dá)式   {class|expr}其中之一是必須的 表達(dá)式的值會當(dāng)做目標(biāo)對象被獲得。 表達(dá)式會在每次使用時被執(zhí)行。換句話說, 執(zhí)行的結(jié)果值不會被緩存。
 
表 6.39. 用戶代碼配置元素:
元素 數(shù)目 描述
field 0..* 描述一個配置值,在用戶類使用之前 注入到成員變量中。
property 0..* 描述一個配置值,在用戶類使用之前 通過一個setter方法進(jìn)行注入。
 
表 6.40. field 和 property 的屬性:
屬性 類型 默認(rèn)值 是否必填? 描述
name string   必填 field或property的名稱。
 
表 6.41. field 和 property 包含的元素:
field 和 property 元素 都擁有一個子元素, 表示將被注入的值。
元素 數(shù)目 描述
string 0..1 a java.lang.String
int 0..1 a java.lang.Integer
long 0..1 a java.lang.Long
float 0..1 a java.lang.Float
double 0..1 a java.lang.Double
true 0..1 Boolean.TRUE
false 0..1 Boolean.FALSE
object 0..1 會通過反射初始化的對象
 
表 6.42. 基本類型 string, int, long, floatand double的屬性:
屬性 類型 默認(rèn)值 是否必填? 描述
value text   必填 text值會被解析成期望的類型

 
6.7.2. 用戶代碼類加載器
流程定義被緩存了。默認(rèn)情況,所有用戶代碼對象 都會作為流程定義的一部分被緩存。 對于所有通過類名引用的對象, 都會在解析期間被初始化。這就意味著,這些對象 不允許保存非無狀態(tài)化的數(shù)
據(jù)(比如可以改變)。 實際上因為這些對象實際都常常是不會改變的。 如果你需要使用“動態(tài)”數(shù)據(jù)在你的用戶代碼中,你通??梢?使用流程變量(或調(diào)用Environment.get(xxx))。
表達(dá)式中引用的對象 是動態(tài)計算的。
開發(fā)只能拿也解釋了未支持的屬性, 來預(yù)防用戶對象的緩存
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服