Jbpm 流程节点
1、Node節點
Node節點是一個自動完成節點,如果沒有在Node節點上定義Action,流程達到Node節點后不會停留,而是繼續往下執行到Node節點的下一個節點。利用之前的Helloworld例子,我們在Node節點上加上一個Action(Action的執行是在node-enter之后node-leave之前)
Xml代碼 ?<node?name="node1">?? ????<action?class="com.royzhou.action.NodeAction"></action>?? ????<transition?to="state1"></transition>?? </node>??
<node name="node1"><action class="com.royzhou.action.NodeAction"></action><transition to="state1"></transition></node>
Action類如下:
Java代碼 ?package?com.royzhou.action; ?? ?? import?org.jbpm.graph.def.ActionHandler; ?? import?org.jbpm.graph.exe.ExecutionContext; ?? ?? public?class?NodeAction?implements?ActionHandler?{ ?? ????public?void?execute(ExecutionContext?executionContext)?throws?Exception?{ ?? ????????System.out.println("node?action……………………"); ?? ????} ?? }??
package com.royzhou.action;import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.exe.ExecutionContext;public class NodeAction implements ActionHandler {public void execute(ExecutionContext executionContext) throws Exception {System.out.println("node action……………………");}
}
測試類如下:
Java代碼 ?package?com.royzhou.test; ?? ?? import?java.io.FileInputStream; ?? import?java.io.FileNotFoundException; ?? import?java.util.zip.ZipInputStream; ?? ?? import?org.jbpm.JbpmConfiguration; ?? import?org.jbpm.JbpmContext; ?? import?org.jbpm.graph.def.ProcessDefinition; ?? import?org.jbpm.graph.exe.ProcessInstance; ?? import?org.jbpm.graph.exe.Token; ?? ?? public?class?HelloWorldDB?{ ?? ????public?static?void?deploy()?throws?FileNotFoundException?{ ?? ????????JbpmConfiguration?jbpmConfiguration?=?JbpmConfiguration.getInstance(); ?? ????????JbpmContext?jbpmContext?=?jbpmConfiguration.createJbpmContext(); ?? ????????try?{ ?? ????????????FileInputStream?fis?=?new?FileInputStream("F:/workspace/jbpm-test/src/main/jpdl/helloworld/helloworld.zip?"); ?? ????????????ZipInputStream?zis?=?new?ZipInputStream(fis); ?? ????????????ProcessDefinition?processDefinition?=?ProcessDefinition.parseParZipInputStream(zis); ?? ????????????jbpmContext.deployProcessDefinition(processDefinition); ?? ????????????fis.close(); ?? ????????????zis.close(); ?? ????????}?catch(Exception?e)?{ ?? ????????????e.printStackTrace(); ?? ????????}?finally?{ ?? ????????????jbpmContext.close(); ?? ????????} ?? ????} ?? ???? ?? ????public?static?void?main(String[]?args)?throws?FileNotFoundException?{ ?? ????????HelloWorldDB.deploy(); ?? ????????JbpmConfiguration?jbpmConfiguration?=?JbpmConfiguration.getInstance(); ?? ????????JbpmContext?jbpmContext?=?jbpmConfiguration.createJbpmContext(); ?? ????????jbpmContext.setActorId("royzhou"); ?? ????????//獲取最新版本的流程定義對象 ?? ????????ProcessDefinition?processDefinition?=?jbpmContext.getGraphSession().findLatestProcessDefinition("helloworld"); ?? ????????ProcessInstance?processInstance?=?new?ProcessInstance(processDefinition); ?? ????????//得到流程令牌 ?? ????????Token?token?=?processInstance.getRootToken(); ?? ???????? ?? ????????//打印流程當前所處節點 ?? ????????System.out.println("1:流程現在所處節點:"?+?token.getNode().getName()); ?? ???????? ?? ????????//觸發流程流轉到下一個節點Node ?? ????????token.signal(); ?? ???????? ?? ????????/** ? ?????????*?注意因為Node節點設置Action ? ?????????*?所以流程會停留在Node節點 ? ?????????*/?? ????????System.out.println("2:流程現在所處節點:"?+?token.getNode().getName()); ?? ???????? ?? ????????//觸發流程流轉到下一個節點State ?? ????????token.signal(); ?? ????????System.out.println("3:流程現在所處節點:"?+?token.getNode().getName()); ?? token.signal(); ?? ????????System.out.println("4:流程現在所處節點:"?+?token.getNode().getName()); ?? ????????System.out.println("流程狀態是否結束:"?+?token.getProcessInstance().hasEnded()); ?? ????????jbpmContext.close(); ?? ????} ?? }??
package com.royzhou.test;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.zip.ZipInputStream;import org.jbpm.JbpmConfiguration;
import org.jbpm.JbpmContext;
import org.jbpm.graph.def.ProcessDefinition;
import org.jbpm.graph.exe.ProcessInstance;
import org.jbpm.graph.exe.Token;public class HelloWorldDB {public static void deploy() throws FileNotFoundException {JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();try {FileInputStream fis = new FileInputStream("F:/workspace/jbpm-test/src/main/jpdl/helloworld/helloworld.zip ");ZipInputStream zis = new ZipInputStream(fis);ProcessDefinition processDefinition = ProcessDefinition.parseParZipInputStream(zis);jbpmContext.deployProcessDefinition(processDefinition);fis.close();zis.close();} catch(Exception e) {e.printStackTrace();} finally {jbpmContext.close();}}public static void main(String[] args) throws FileNotFoundException {HelloWorldDB.deploy();JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();jbpmContext.setActorId("royzhou");//獲取最新版本的流程定義對象ProcessDefinition processDefinition = jbpmContext.getGraphSession().findLatestProcessDefinition("helloworld");ProcessInstance processInstance = new ProcessInstance(processDefinition);//得到流程令牌Token token = processInstance.getRootToken();//打印流程當前所處節點System.out.println("1:流程現在所處節點:" + token.getNode().getName());//觸發流程流轉到下一個節點Nodetoken.signal();/*** 注意因為Node節點設置Action* 所以流程會停留在Node節點*/System.out.println("2:流程現在所處節點:" + token.getNode().getName());//觸發流程流轉到下一個節點Statetoken.signal();System.out.println("3:流程現在所處節點:" + token.getNode().getName());
token.signal();System.out.println("4:流程現在所處節點:" + token.getNode().getName());System.out.println("流程狀態是否結束:" + token.getProcessInstance().hasEnded());jbpmContext.close();}
}
測試結果如下:(驗證了在Node上設置Action的時候,流程處于等待狀態)
1:流程現在所處節點:start-state1
node action……………………
2:流程現在所處節點:node1
3:流程現在所處節點:state1
4:流程現在所處節點:end-state1
流程狀態是否結束:true
2、State節點
State節點屬性類似Node,不過當流程流轉到State節點時就會停下來,直到外部向其發送流轉的命令,如signal().
3、Task-Node
Task Node節點在Jbpm中是很重要的一個節點,可以用來添加任務,產生任務實例。一個Task Node 可以定義多個Task,為Task分配執行人等等。下面我們通過一個例子來解釋Task Node。
Xml代碼 ?????<?xml?version="1.0"?encoding="UTF-8"?>?? ?? <process-definition??xmlns="urn:jbpm.org:jpdl-3.2"??name="tasknode">?? ????<start-state?name="start-state1">?? ????????<transition?to="task-node1"></transition>?? ????</start-state>?? ????<task-node?name="task-node1">?? ????????<task?name="mytask">?? ????????????<assignment?actor-id="royzhou"></assignment>?? ????????</task>?? ????????<transition?to="end-state1"></transition>?? ????</task-node>?? ????<end-state?name="end-state1"></end-state>?? </process-definition>??
<?xml version="1.0" encoding="UTF-8"?><process-definition xmlns="urn:jbpm.org:jpdl-3.2" name="tasknode"><start-state name="start-state1"><transition to="task-node1"></transition></start-state><task-node name="task-node1"><task name="mytask"><assignment actor-id="royzhou"></assignment></task><transition to="end-state1"></transition></task-node><end-state name="end-state1"></end-state>
</process-definition>
上面流程文件定義了一個Task Node ,在Task Node下面定義了一個Task節點,分配給了royzhou這個人(實際開發中的任務分配會更加復雜)
接下來我們開始寫測試類:
Java代碼 ?????package?com.royzhou.test; ?? ?? import?java.io.FileInputStream; ?? import?java.io.FileNotFoundException; ?? import?java.util.zip.ZipInputStream; ?? ?? import?org.jbpm.JbpmConfiguration; ?? import?org.jbpm.JbpmContext; ?? import?org.jbpm.graph.def.ProcessDefinition; ?? import?org.jbpm.graph.exe.ProcessInstance; ?? import?org.jbpm.graph.exe.Token; ?? ?? public?class?TaskNode?{ ?? ???? ?? ????public?static?void?deploy()?throws?FileNotFoundException?{ ?? ????????JbpmConfiguration?jbpmConfiguration?=?JbpmConfiguration.getInstance(); ?? ????????JbpmContext?jbpmContext?=?jbpmConfiguration.createJbpmContext(); ?? ????????try?{ ?? ????????????FileInputStream?fis?=?new?FileInputStream("F:/workspace/jbpm-test/src/main/jpdl/tasknode/tasknode.zip"); ?? ????????????ZipInputStream?zis?=?new?ZipInputStream(fis); ?? ????????????ProcessDefinition?processDefinition?=?ProcessDefinition.parseParZipInputStream(zis); ?? ????????????jbpmContext.deployProcessDefinition(processDefinition); ?? ????????????fis.close(); ?? ????????????zis.close(); ?? ????????}?catch(Exception?e)?{ ?? ????????????e.printStackTrace(); ?? ????????}?finally?{ ?? ????????????jbpmContext.close(); ?? ????????} ?? ????} ?? ???? ?? ????public?static?void?main(String[]?args)?throws?FileNotFoundException?{ ?? ????????TaskNode.deploy(); ?? ????????JbpmConfiguration?jbpmConfiguration?=?JbpmConfiguration.getInstance(); ?? ????????JbpmContext?jbpmContext?=?jbpmConfiguration.createJbpmContext(); ?? ????????ProcessDefinition?processDefinition?=?jbpmContext.getGraphSession().findLatestProcessDefinition("tasknode"); ?? ????????ProcessInstance?processInstance?=?new?ProcessInstance(processDefinition); ?? ????????Token?token?=?processInstance.getRootToken(); ?? ????????token.signal(); ?? ????????System.out.println("流程現在所處節點:"?+?token.getNode().getName()); ?? ????????jbpmContext.close(); ?? ????} ?? }??
package com.royzhou.test;import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.zip.ZipInputStream;import org.jbpm.JbpmConfiguration;
import org.jbpm.JbpmContext;
import org.jbpm.graph.def.ProcessDefinition;
import org.jbpm.graph.exe.ProcessInstance;
import org.jbpm.graph.exe.Token;public class TaskNode {public static void deploy() throws FileNotFoundException {JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();try {FileInputStream fis = new FileInputStream("F:/workspace/jbpm-test/src/main/jpdl/tasknode/tasknode.zip");ZipInputStream zis = new ZipInputStream(fis);ProcessDefinition processDefinition = ProcessDefinition.parseParZipInputStream(zis);jbpmContext.deployProcessDefinition(processDefinition);fis.close();zis.close();} catch(Exception e) {e.printStackTrace();} finally {jbpmContext.close();}}public static void main(String[] args) throws FileNotFoundException {TaskNode.deploy();JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();ProcessDefinition processDefinition = jbpmContext.getGraphSession().findLatestProcessDefinition("tasknode");ProcessInstance processInstance = new ProcessInstance(processDefinition);Token token = processInstance.getRootToken();token.signal();System.out.println("流程現在所處節點:" + token.getNode().getName());jbpmContext.close();}
}
后臺打印結果:
流程現在所處節點:task-node1
查看jbpm_taskinstance表看到一個名叫mytask的任務,指定給了royzhou說明我們的任務創建成功了。
Task Node有幾個比較重要的屬性:
Signal:
last:最后一個完成之后繼續往下流轉,如果沒有任務被創建直接流轉到下一節點
last-wait:最后一個完成之后繼續往下流轉,如果沒有任務被創建處于等待狀態
first:只要有一個完成就繼續往下流轉,如果沒有任務被創建直接流轉下一個節點
first-wait:只要一個完成就繼續往下流轉,如果沒有任務被創建則處于等待狀態
never:必須顯示調用processInstance.signal()才繼續流轉
unsynchronized:不停留,直接流轉到下個節點
task-creates:流程流轉到這個節點上時是不是自動創建任務,默認是true。通過設置為false,配合其他事件我們可以實現會簽的功能。
4、Start節點
Start節點在每個流程中有且只能有一個。Start中可以配置Task,但是 配置在Start節點中的task不會調用任務分配機制。這點是不同于Task Node的。。。。
5、fork和join節點
二者是成對出現的。Fork節點可以將流程拆分成多條并行也就是將Token拆分成多個子Token,到join節點的時候再匯聚成一個Token繼續往下執行
一般來說,在經過fork節點之后,必須等待每個子token都走完才會通過join節點繼續往下走,但是我們可以通過修改join節點的discriminator屬性來,設置為true之后只要有一個子token到達join節點,就會往下流轉。不過這樣做產生的一個影響就是其他沒有結束的子token上的任務實例依然存在且處于等待狀態,任務參與者仍然可以處理這些任務。因此,在設置join節點的discriminator為true時,我們還需要手動結束掉其他分支上的任務實例。
在fork節點中,我們可以設定條件來選擇只走滿足條件的分支。主要是通過之前提到的beanshell腳本來實現。舉例說明一下這個實現:
流程定義由一個start節點,一個end節點,一對fork/join節點以及分支中的四個node節點組成,流程定義的xml文件如下:
Xml代碼 ?<?xml?version="1.0"?encoding="UTF-8"?>?? <process-definition??xmlns="urn:jbpm.org:jpdl-3.2"??name="forkjoin">?? ????<start-state?name="start-state1">?? ????????<transition?to="fork1"></transition>?? ????</start-state>?? ????<fork?name="fork1">?? ????????<script>?? ????????????<variable?name="forkTransition"?access="write"></variable>?? ????????????<expression>?? ????????????????forkTransition?=?new?ArrayList(); ?? ????????????????if(param?>?1000)?{ ?? ????????????????????forkTransition.add("to?node1"); ?? ????????????????????forkTransition.add("to?node2"); ?? ????????????????}?else?{ ?? ????????????????????forkTransition.add("to?node3"); ?? ????????????????????forkTransition.add("to?node4"); ?? ????????????????} ?? ????????????</expression>?? ????????</script>?? ????????<transition?to="node1"?name="to?node1"></transition>?? ????????<transition?to="node2"?name="to?node2"></transition>?? ????????<transition?to="node3"?name="to?node3"></transition>?? ????????<transition?to="node4"?name="to?node4"></transition>?? ????</fork>?? ????<join?name="join1">?? ????????<transition?to="end-state1"></transition>?? ????</join>?? ????<node?name="node1">?? ????????<event?type="node-enter">?? ????????????<script>?? ????????????????print("node1?enter"); ?? ????????????</script>?? ????????</event>?? ????????<transition?to="join1"></transition>?? ????</node>?? ????<node?name="node2">?? ????????<event?type="node-enter">?? ????????????<script>?? ????????????????print("node2?enter"); ?? ????????????</script>?? ????????</event>?? ????????<transition?to="join1"></transition>?? ????</node>?? ????<node?name="node3">?? ????????<event?type="node-enter">?? ????????????<script>?? ????????????????print("node3?enter"); ?? ????????????</script>?? ????????</event>?? ????????<transition?to="join1"></transition>?? ????</node>?? ????<node?name="node4">?? ????????<event?type="node-enter">?? ????????????<script>?? ????????????????print("node4?enter"); ?? ????????????</script>?? ????????</event>?? ????????<transition?to="join1"></transition>?? ????</node>?? ????<end-state?name="end-state1"></end-state>?? </process-definition>??
<?xml version="1.0" encoding="UTF-8"?>
<process-definition xmlns="urn:jbpm.org:jpdl-3.2" name="forkjoin"><start-state name="start-state1"><transition to="fork1"></transition></start-state><fork name="fork1"><script><variable name="forkTransition" access="write"></variable><expression>forkTransition = new ArrayList();if(param > 1000) {forkTransition.add("to node1");forkTransition.add("to node2");} else {forkTransition.add("to node3");forkTransition.add("to node4");}</expression></script><transition to="node1" name="to node1"></transition><transition to="node2" name="to node2"></transition><transition to="node3" name="to node3"></transition><transition to="node4" name="to node4"></transition></fork><join name="join1"><transition to="end-state1"></transition></join><node name="node1"><event type="node-enter"><script>print("node1 enter");</script></event><transition to="join1"></transition></node><node name="node2"><event type="node-enter"><script>print("node2 enter");</script></event><transition to="join1"></transition></node><node name="node3"><event type="node-enter"><script>print("node3 enter");</script></event><transition to="join1"></transition></node><node name="node4"><event type="node-enter"><script>print("node4 enter");</script></event><transition to="join1"></transition></node><end-state name="end-state1"></end-state>
</process-definition>
默認情況下,當流程進入fork節點之后會分成四個分支,分別經過Node節點。在上面定義文件中我們在fork節點下做了判斷,如果流程變量param>1000的時候走分支1和分支2,如果小于則走分支3和分支4。
Xml代碼 ?<script>?? ????????????<variable?name="forkTransition"?access="write"></variable>?? ????????????<expression>?? ????????????????forkTransition?=?new?ArrayList(); ?? ????????????????if(param?>?1000)?{ ?? ????????????????????forkTransition.add("to?node1"); ?? ????????????????????forkTransition.add("to?node2"); ?? ????????????????}?else?{ ?? ????????????????????forkTransition.add("to?node3"); ?? ????????????????????forkTransition.add("to?node4"); ?? ????????????????} ?? ????????????</expression>?? ????????</script>??
<script><variable name="forkTransition" access="write"></variable><expression>forkTransition = new ArrayList();if(param > 1000) {forkTransition.add("to node1");forkTransition.add("to node2");} else {forkTransition.add("to node3");forkTransition.add("to node4");}</expression></script>
注意這里的forkTransition必須是集合類型,它存放的是流程經過fork之后生成的分支名稱。access屬性設置為write表示是寫入流程實例對應的Fork節點,才能生成我們指定的分支。在Expression標簽下是我們做的邏輯處理,通過流程變量param來判斷Fork節點具體要生成那些分支。
默認我們不為Fork節點添加script,流程到Fork節點時將生成所有transition。一旦設置了script,還必須設置一個變量,如上面的forkTransition寫回流程變量中生成具體的分支。
測試類如下:(為方便測試沒有將流程發布到數據庫,下面測試例子也是采用相同做法)
Java代碼 ?package?com.royzhou.test; ?? ?? import?org.jbpm.graph.def.ProcessDefinition; ?? import?org.jbpm.graph.exe.ProcessInstance; ?? import?org.jbpm.graph.exe.Token; ?? ?? public?class?ForkJoin?{ ?? ???? ?? ????public?static?void?main(String[]?args)?{ ?? ????????ProcessDefinition?processDefinition?=?ProcessDefinition.parseXmlResource("forkjoin/processDefinition.xml"); ?? ????????ProcessInstance?processInstance?=?new?ProcessInstance(processDefinition); ?? ????????processInstance.getContextInstance().setVariable("param",?10); ?? ????????Token?token?=?processInstance.getRootToken(); ?? ????????token.signal(); ?? ????} ?? }??
package com.royzhou.test;import org.jbpm.graph.def.ProcessDefinition;
import org.jbpm.graph.exe.ProcessInstance;
import org.jbpm.graph.exe.Token;public class ForkJoin {public static void main(String[] args) {ProcessDefinition processDefinition = ProcessDefinition.parseXmlResource("forkjoin/processDefinition.xml");ProcessInstance processInstance = new ProcessInstance(processDefinition);processInstance.getContextInstance().setVariable("param", 10);Token token = processInstance.getRootToken();token.signal();}
}
執行成功之后后臺輸出:
node3 enter
node4 enter
修改我們的param改為2000再次執行,后臺變為:
node1 enter
node2 enter
6、Decision節點
Decision節點是判斷節點,當流程到達decision節點之后會有多個路由選擇,我們可以在這個節點上定義handler或者是使用腳本等等來決定流程走向。Decision節點不同于Fork節點,它只能選擇多個Transition中的其中一個,而Fork節點是并行的。
舉例說明一下:
流程定義由一個start節點,一個end節點,一個Decision節點以及分支中的3個node節點組成,在Decision我們設置了delegation,使用實現DecisionHandler接口的類SelectNode來判斷流程走向,它返回的結果是其中分支的名稱,流程定義的xml文件如下:
Xml代碼 ?????<?xml?version="1.0"?encoding="UTF-8"?>?? <process-definition??xmlns="urn:jbpm.org:jpdl-3.2"??name="decision">?? ????<start-state?name="start-state1">?? ????????<transition?to="decision1"></transition>?? ????</start-state>?? ????<decision?name="decision1">?? ????????<handler?class="com.royzhou.action.SelectNode"></handler>?? ????????<transition?to="node1"?name="to?node1"></transition>?? ????????<transition?to="node2"?name="to?node2"></transition>?? ????????<transition?to="node3"?name="to?node3"></transition>?? ????</decision>?? ????<node?name="node1">?? ????????<script>?? ????????????print("node1?enter") ?? ????????</script>?? ????</node>?? ????<node?name="node2">?? ????????<script>?? ????????????print("node2?enter") ?? ????????</script>?? ????????<transition?to="end-state1"></transition>?? ????</node>?? ????<node?name="node3">?? ????????<script>?? ????????????print("node3?enter") ?? ????????</script>?? ????</node>?? ????<end-state?name="end-state1"></end-state>?? </process-definition>??
<?xml version="1.0" encoding="UTF-8"?>
<process-definition xmlns="urn:jbpm.org:jpdl-3.2" name="decision"><start-state name="start-state1"><transition to="decision1"></transition></start-state><decision name="decision1"><handler class="com.royzhou.action.SelectNode"></handler><transition to="node1" name="to node1"></transition><transition to="node2" name="to node2"></transition><transition to="node3" name="to node3"></transition></decision><node name="node1"><script>print("node1 enter")</script></node><node name="node2"><script>print("node2 enter")</script><transition to="end-state1"></transition></node><node name="node3"><script>print("node3 enter")</script></node><end-state name="end-state1"></end-state>
</process-definition>
SelectNode類:(必須實現DecisionHandler接口并重寫decide方法,返回值是要走的transition的名稱)
Java代碼 ?package?com.royzhou.action; ?? ?? import?org.jbpm.graph.exe.ExecutionContext; ?? import?org.jbpm.graph.node.DecisionHandler; ?? //實現DecisionHandler接口 ?? public?class?SelectNode?implements?DecisionHandler?{ ?? ????public?String?decide(ExecutionContext?executionContext)?throws?Exception?{ ?? ????????//通過流程變量來判斷要走哪個transition ?? ????????String?whichWay?=?executionContext.getVariable("whichWay").toString(); ?? ????????return?whichWay; ?? ????} ?? }??
package com.royzhou.action;import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.graph.node.DecisionHandler;
//實現DecisionHandler接口
public class SelectNode implements DecisionHandler {public String decide(ExecutionContext executionContext) throws Exception {//通過流程變量來判斷要走哪個transitionString whichWay = executionContext.getVariable("whichWay").toString();return whichWay;}
}
測試類:
Java代碼 ?package?com.royzhou.test; ?? ?? import?org.jbpm.graph.def.ProcessDefinition; ?? import?org.jbpm.graph.exe.ProcessInstance; ?? import?org.jbpm.graph.exe.Token; ?? ?? public?class?Decision?{ ?? ???? ?? ????public?static?void?main(String[]?args)?{ ?? ????????ProcessDefinition?processDefinition?=?ProcessDefinition.parseXmlResource("decision/processDefinition.xml"); ?? ????????ProcessInstance?processInstance?=?new?ProcessInstance(processDefinition); ?? ????????processInstance.getContextInstance().setVariable("whichWay",?"to?node2"); ?? ????????Token?token?=?processInstance.getRootToken(); ?? ????????token.signal(); ?? ????} ?? }??
package com.royzhou.test;import org.jbpm.graph.def.ProcessDefinition;
import org.jbpm.graph.exe.ProcessInstance;
import org.jbpm.graph.exe.Token;public class Decision {public static void main(String[] args) {ProcessDefinition processDefinition = ProcessDefinition.parseXmlResource("decision/processDefinition.xml");ProcessInstance processInstance = new ProcessInstance(processDefinition);processInstance.getContextInstance().setVariable("whichWay", "to node2");Token token = processInstance.getRootToken();token.signal();}
}
運行測試類,后臺打印出? :
node2 enter
修改流程變量whichWay為to node1 重新運行,后臺打印出:
node1 enter
說明我們的decision節點正確處理了其走向。
除了使用delegation,我們也可以在流程文件中直接定義decision節點的expression屬性,如下所示:
Xml代碼 ?<decision?name="decision1"?expression="#{whichWay}">?? ????<!--? ? ????<handler?class="com.royzhou.action.SelectNode"></handler>? ?????-->?? ????<transition?to="node1"?name="to?node1"></transition>?? ????<transition?to="node2"?name="to?node2"></transition>?? ????<transition?to="node3"?name="to?node3"></transition>?? </decision>??
<decision name="decision1" expression="#{whichWay}"><!-- <handler class="com.royzhou.action.SelectNode"></handler>--><transition to="node1" name="to node1"></transition><transition to="node2" name="to node2"></transition><transition to="node3" name="to node3"></transition></decision>
這里采用的是JPDL表達式來指定流程走向,JPDL表達式是一種類似EL表達式的語言。
上面expression表示從流程變量中獲取whichWay這個變量作為我們流程走向的選擇。
重新運行我們的測試類。發現結果與上面一種做法相同。
此外我們還可以直接在transition節點下定義condition標簽,配合JPDL表達式來作為我們的判斷依據
Xml代碼 ?<decision?name="decision1">?? ????????<transition?to="node1"?name="to?node1">?? ????????????<condition?expression="#{whichWay?==?'to?node1'}"></condition>?? ????????</transition>?? ????????<transition?to="node2"?name="to?node2">?? ????????????<condition?expression="#{whichWay?==?'to?node2'}"></condition>?? ????????</transition>?? ????????<transition?to="node3"?name="to?node3">?? ????????????<condition?expression="#{whichWay?==?'to?node3'}"></condition>?? ????????</transition>?? ????</decision>??
<decision name="decision1"><transition to="node1" name="to node1"><condition expression="#{whichWay == 'to node1'}"></condition></transition><transition to="node2" name="to node2"><condition expression="#{whichWay == 'to node2'}"></condition></transition><transition to="node3" name="to node3"><condition expression="#{whichWay == 'to node3'}"></condition></transition></decision>
再次運行測試程序發現結果仍然一致。
由此可見 Decision節點支持我們使用delegation、condition以及expression三種方式來指定流程走向。
7、Process State節點
子流程節點可以簡化我們的復雜流程。主流程與子流程之間可以共享變量。如果使用子流程,在主流程發布之前必須先發布子流程。
舉例說明一下主流程與子流程及其變量共享:
主流程定義文件mainprocess/processdefinition.xml
Xml代碼 ?<?xml?version="1.0"?encoding="UTF-8"?>?? <process-definition??xmlns="urn:jbpm.org:jpdl-3.2"??name="mainprocess">?? ????<start-state?name="start-state1">?? ????????<transition?to="task-node1"></transition>?? ????</start-state>?? ????<task-node?name="task-node1">?? ????????<event?type="node-enter">?? ????????????<script>?? ????????????????print("main?task?node1?enter") ?? ????????????</script>?? ????????</event>?? ????????<task?name="maintask1">?? ????????????<assignment?actor-id="maintask1"></assignment>?? ????????</task>?? ????????<transition?to="process-state1"></transition>?? ????</task-node>?? ????<process-state?name="process-state1">?? ????????<sub-process?name="subprocess"/>?? ????????<variable?name="mainParam"?access="read"></variable>?? ????????<variable?name="subParam"?access="write"></variable>?? ????????<transition?to="task-node2"></transition>?? ????</process-state>?? ????<task-node?name="task-node2">?? ????????<event?type="node-enter">?? ????????????<script>?? ????????????????print("main?task?node2?enter") ?? ????????????</script>?? ????????</event>?? ????????<task?name="maintask2">?? ????????????<assignment?actor-id="maintask2"></assignment>?? ????????</task>?? ????????<transition?to="end-state1"></transition>?? ????</task-node>?? ????<end-state?name="end-state1"></end-state>?? </process-definition>??
<?xml version="1.0" encoding="UTF-8"?>
<process-definition xmlns="urn:jbpm.org:jpdl-3.2" name="mainprocess"><start-state name="start-state1"><transition to="task-node1"></transition></start-state><task-node name="task-node1"><event type="node-enter"><script>print("main task node1 enter")</script></event><task name="maintask1"><assignment actor-id="maintask1"></assignment></task><transition to="process-state1"></transition></task-node><process-state name="process-state1"><sub-process name="subprocess"/><variable name="mainParam" access="read"></variable><variable name="subParam" access="write"></variable><transition to="task-node2"></transition></process-state><task-node name="task-node2"><event type="node-enter"><script>print("main task node2 enter")</script></event><task name="maintask2"><assignment actor-id="maintask2"></assignment></task><transition to="end-state1"></transition></task-node><end-state name="end-state1"></end-state>
</process-definition>
主流程中設置了process-state節點,其sub-process 的name屬性表示子流程的名稱,
<variable name="mainParam" access="read"></variable>表示從主流程讀取mainParam變量
<variable name="subParam" access="write"></variable>表示子流程將變量subParam寫回主流程中。
子流程定義文件:subprocess/processdefinition.xml
Xml代碼 ?<?xml?version="1.0"?encoding="UTF-8"?>?? <process-definition??xmlns="urn:jbpm.org:jpdl-3.2"??name="subprocess">?? ????<start-state?name="start-state1">?? ????????<transition?to="task-node1"></transition>?? ????</start-state>?? ????<task-node?name="task-node1">?? ????????<event?type="node-enter">?? ????????????<script>?? ????????????????print("sub?task?node1?enter") ?? ????????????</script>?? ????????</event>?? ????????<task?name="subtask1">?? ????????????<assignment?actor-id="subtask1"></assignment>?? ????????</task>?? ????????<event?type="node-enter">?? ????????????<script>?? ????????????????print("sub?task?node1?enter") ?? ????????????</script>?? ????????</event>?? ????????<transition?to="end-state1"></transition>?? ????</task-node>?? ????<end-state?name="end-state1"></end-state>?? </process-definition>??
<?xml version="1.0" encoding="UTF-8"?>
<process-definition xmlns="urn:jbpm.org:jpdl-3.2" name="subprocess"><start-state name="start-state1"><transition to="task-node1"></transition></start-state><task-node name="task-node1"><event type="node-enter"><script>print("sub task node1 enter")</script></event><task name="subtask1"><assignment actor-id="subtask1"></assignment></task><event type="node-enter"><script>print("sub task node1 enter")</script></event><transition to="end-state1"></transition></task-node><end-state name="end-state1"></end-state>
</process-definition>
測試類:
Java代碼 ?package?com.royzhou.test; ?? ?? import?java.io.FileNotFoundException; ?? ?? import?org.jbpm.JbpmConfiguration; ?? import?org.jbpm.JbpmContext; ?? import?org.jbpm.graph.def.ProcessDefinition; ?? import?org.jbpm.graph.exe.ProcessInstance; ?? import?org.jbpm.graph.exe.Token; ?? import?org.jbpm.taskmgmt.exe.TaskInstance; ?? ?? public?class?SubProcess?{ ?? ???? ?? ????public?static?void?init()?{ ?? ????????JbpmConfiguration?jbpmConfiguration?=?JbpmConfiguration.getInstance(); ?? ????????jbpmConfiguration.createSchema(); ?? ????} ?? ???? ?? ????public?static?void?deploySub()?throws?FileNotFoundException?{ ?? ????????JbpmConfiguration?jbpmConfiguration?=?JbpmConfiguration.getInstance(); ?? ????????JbpmContext?jbpmContext?=?jbpmConfiguration.createJbpmContext(); ?? ????????try?{ ?? ????????????ProcessDefinition?subProcessDefinition?=?ProcessDefinition.parseXmlResource("subprocess/processDefinition.xml"); ?? ????????????jbpmContext.deployProcessDefinition(subProcessDefinition); ?? ????????}?catch(Exception?e)?{ ?? ????????????e.printStackTrace(); ?? ????????}?finally?{ ?? ????????????jbpmContext.close(); ?? ????????} ?? ????} ?? ???? ?? ????public?static?void?deployMain()?throws?FileNotFoundException?{ ?? ????????JbpmConfiguration?jbpmConfiguration?=?JbpmConfiguration.getInstance(); ?? ????????JbpmContext?jbpmContext?=?jbpmConfiguration.createJbpmContext(); ?? ????????try?{ ?? ????????????ProcessDefinition?mainProcessDefinition?=?ProcessDefinition.parseXmlResource("mainprocess/processDefinition.xml"); ?? ????????????jbpmContext.deployProcessDefinition(mainProcessDefinition); ?? ????????}?catch(Exception?e)?{ ?? ????????????e.printStackTrace(); ?? ????????}?finally?{ ?? ????????????jbpmContext.close(); ?? ????????} ?? ????} ?? ???? ?? ????public?static?void?startProcess()?{ ?? ????????JbpmConfiguration?jbpmConfiguration?=?JbpmConfiguration.getInstance(); ?? ????????JbpmContext?jbpmContext?=?jbpmConfiguration.createJbpmContext(); ?? ????????try?{ ?? ????????????ProcessDefinition?processDefinition?=?jbpmContext.getGraphSession().findLatestProcessDefinition("mainprocess"); ?? ????????????ProcessInstance?processInstance?=?new?ProcessInstance(processDefinition); ?? ????????????Token?token?=?processInstance.getRootToken(); ?? ????????????token.signal(); ?? ????????}?catch?(Exception?e)?{ ?? ????????????e.printStackTrace(); ?? ????????}?finally?{ ?? ????????????jbpmContext.close(); ?? ????????} ?? ????} ?? ???? ?? ????public?static?void?startMainTask1()?{ ?? ????????JbpmConfiguration?jbpmConfiguration?=?JbpmConfiguration.getInstance(); ?? ????????JbpmContext?jbpmContext?=?jbpmConfiguration.createJbpmContext(); ?? ????????try?{ ?? ????????????//主流程maintask1 ?? ????????????TaskInstance?mainTaskInstance1?=?jbpmContext.loadTaskInstance(1L); ?? ????????????//設置主流程變量 ?? ????????????mainTaskInstance1.getContextInstance().setVariable("mainParam",?"主流程設置的變量"); ?? ????????????mainTaskInstance1.end(); ?? ????????}?catch?(Exception?e)?{ ?? ????????????e.printStackTrace(); ?? ????????}?finally?{ ?? ????????????jbpmContext.close(); ?? ????????} ?? ????} ?? ???? ?? ????public?static?void?startSubTask1()?{ ?? ????????JbpmConfiguration?jbpmConfiguration?=?JbpmConfiguration.getInstance(); ?? ????????JbpmContext?jbpmContext?=?jbpmConfiguration.createJbpmContext(); ?? ????????try?{ ?? ????????????//子流程subtask1 ?? ????????????TaskInstance?subTaskInstance1?=?jbpmContext.loadTaskInstance(2L); ?? ????????????//獲取主流程變量 ?? ????????????System.out.println(subTaskInstance1.getName()?+?"子流程讀取主流程變量,mainParam的值是:"?+?subTaskInstance1.getContextInstance().getVariable("mainParam").toString()); ?? ????????????//設置子流程變量 ?? ????????????subTaskInstance1.getContextInstance().setVariable("subParam",?"子流程設置的變量"); ?? ????????????subTaskInstance1.end(); ?? ????????}?catch?(Exception?e)?{ ?? ????????????e.printStackTrace(); ?? ????????}?finally?{ ?? ????????????jbpmContext.close(); ?? ????????} ?? ????} ?? ???? ?? ????public?static?void?startMainTask2()?{ ?? ????????JbpmConfiguration?jbpmConfiguration?=?JbpmConfiguration.getInstance(); ?? ????????JbpmContext?jbpmContext?=?jbpmConfiguration.createJbpmContext(); ?? ????????try?{ ?? ????????????TaskInstance?mainTaskInstance2?=?jbpmContext.loadTaskInstance(3L); ?? ????????????//讀取子流程變量 ?? ????????????System.out.println(mainTaskInstance2.getName()+"主流程讀取子流程變量,subParam的值是:"?+?mainTaskInstance2.getContextInstance().getVariable("subParam").toString()); ?? ????????????mainTaskInstance2.end(); ?? ????????}?catch?(Exception?e)?{ ?? ????????????e.printStackTrace(); ?? ????????}?finally?{ ?? ????????????jbpmContext.close(); ?? ????????} ?? ????}? ?? ???? ?? ????public?static?void?main(String[]?args)?throws?FileNotFoundException?{ ?? ????????//數據庫清理 ?? ????????SubProcess.init(); ?? ????????//發布流程,必須先發布子流程再發布主流程 ?? ????????SubProcess.deploySub(); ?? ????????SubProcess.deployMain(); ?? ????????SubProcess.startProcess(); ?? ????????SubProcess.startMainTask1(); ?? ????????SubProcess.startSubTask1(); ?? ????????SubProcess.startMainTask2(); ?? ????} ?? }??
package com.royzhou.test;import java.io.FileNotFoundException;import org.jbpm.JbpmConfiguration;
import org.jbpm.JbpmContext;
import org.jbpm.graph.def.ProcessDefinition;
import org.jbpm.graph.exe.ProcessInstance;
import org.jbpm.graph.exe.Token;
import org.jbpm.taskmgmt.exe.TaskInstance;public class SubProcess {public static void init() {JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();jbpmConfiguration.createSchema();}public static void deploySub() throws FileNotFoundException {JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();try {ProcessDefinition subProcessDefinition = ProcessDefinition.parseXmlResource("subprocess/processDefinition.xml");jbpmContext.deployProcessDefinition(subProcessDefinition);} catch(Exception e) {e.printStackTrace();} finally {jbpmContext.close();}}public static void deployMain() throws FileNotFoundException {JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();try {ProcessDefinition mainProcessDefinition = ProcessDefinition.parseXmlResource("mainprocess/processDefinition.xml");jbpmContext.deployProcessDefinition(mainProcessDefinition);} catch(Exception e) {e.printStackTrace();} finally {jbpmContext.close();}}public static void startProcess() {JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();try {ProcessDefinition processDefinition = jbpmContext.getGraphSession().findLatestProcessDefinition("mainprocess");ProcessInstance processInstance = new ProcessInstance(processDefinition);Token token = processInstance.getRootToken();token.signal();} catch (Exception e) {e.printStackTrace();} finally {jbpmContext.close();}}public static void startMainTask1() {JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();try {//主流程maintask1TaskInstance mainTaskInstance1 = jbpmContext.loadTaskInstance(1L);//設置主流程變量mainTaskInstance1.getContextInstance().setVariable("mainParam", "主流程設置的變量");mainTaskInstance1.end();} catch (Exception e) {e.printStackTrace();} finally {jbpmContext.close();}}public static void startSubTask1() {JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();try {//子流程subtask1TaskInstance subTaskInstance1 = jbpmContext.loadTaskInstance(2L);//獲取主流程變量System.out.println(subTaskInstance1.getName() + "子流程讀取主流程變量,mainParam的值是:" + subTaskInstance1.getContextInstance().getVariable("mainParam").toString());//設置子流程變量subTaskInstance1.getContextInstance().setVariable("subParam", "子流程設置的變量");subTaskInstance1.end();} catch (Exception e) {e.printStackTrace();} finally {jbpmContext.close();}}public static void startMainTask2() {JbpmConfiguration jbpmConfiguration = JbpmConfiguration.getInstance();JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();try {TaskInstance mainTaskInstance2 = jbpmContext.loadTaskInstance(3L);//讀取子流程變量System.out.println(mainTaskInstance2.getName()+"主流程讀取子流程變量,subParam的值是:" + mainTaskInstance2.getContextInstance().getVariable("subParam").toString());mainTaskInstance2.end();} catch (Exception e) {e.printStackTrace();} finally {jbpmContext.close();}} public static void main(String[] args) throws FileNotFoundException {//數據庫清理SubProcess.init();//發布流程,必須先發布子流程再發布主流程SubProcess.deploySub();SubProcess.deployMain();SubProcess.startProcess();SubProcess.startMainTask1();SubProcess.startSubTask1();SubProcess.startMainTask2();}
}
運行上面程序,成功執行之后結果為:
main task node1 enter
sub task node1 enter
sub task node1 enter
subtask1子流程讀取主流程變量,mainParam的值是:主流程設置的變量
main task node2 enter
maintask2主流程讀取子流程變量,subParam的值是:子流程設置的變量
8、Mail Node 節點
該節點可以實現郵件的發送,具有通知提醒功能。
具體有兩種配置方式:
通過mail node節點的template屬性設置,采用此種方式需要修改jbpm.mail.template.xml文件里的模版,加上我們需要的模版,具體模版設置可參照里面已有模版。舉例說明下:
流程模版文件:start -> mail node ->end
Xml代碼 ?<?xml?version="1.0"?encoding="UTF-8"?>?? <process-definition??xmlns="urn:jbpm.org:jpdl-3.2"??name="mail">?? ????<start-state?name="start-state1">?? ????????<transition?to="mail-node1"></transition>?? ????</start-state>?? ????<mail-node?name="mail-node1"?template="test">?? ????????<event?type="node-enter">?? ????????????<script>?? ????????????????print("enter?mail?node") ?? ????????????</script>?? ????????</event>?? ????????<event?type="node-leave">?? ????????????<script>?? ????????????????print("leave?mail?node") ?? ????????????</script>?? ????????</event>?? ????????<transition?to="end-state1"></transition>?? ????</mail-node>?? ????<end-state?name="end-state1"></end-state>?? </process-definition>??
<?xml version="1.0" encoding="UTF-8"?>
<process-definition xmlns="urn:jbpm.org:jpdl-3.2" name="mail"><start-state name="start-state1"><transition to="mail-node1"></transition></start-state><mail-node name="mail-node1" template="test"><event type="node-enter"><script>print("enter mail node")</script></event><event type="node-leave"><script>print("leave mail node")</script></event><transition to="end-state1"></transition></mail-node><end-state name="end-state1"></end-state>
</process-definition>
我們使用的模版是test,但是jbpm.mail.template.xml沒有,所以需要我們自己加:
Xml代碼 ?<mail-template?name="test">?? ????????<actors>#{actorId}</actors>?? ????????<subject>mail?node?節點測試</subject>?? ????????<text><![CDATA[Hi?#{actorId},? ????????這是mail?node節點的測試內容。 ? ????????]]></text>?? ????</mail-template>????
<mail-template name="test"><actors>#{actorId}</actors><subject>mail node 節點測試</subject><text><![CDATA[Hi #{actorId},這是mail node節點的測試內容。]]></text></mail-template>
這個是我們自己定義的模版,其中actors標簽表示的是收件人的地址,需要使用郵件解析類解析,郵件解析類是一個實現AddressResolver接口的類。并且需要在jbpm.cfg.xml中配置,告訴jbpm要使用這個類去解析獲取發送郵件的地址。
Java代碼 ?package?com.royzhou.util; ?? ?? import?org.jbpm.mail.AddressResolver; ?? ?? public?class?MailAddressResolver?implements?AddressResolver?{ ?? ?? ????public?Object?resolveAddress(String?actorId)?{ ?? ????????return?"royzhou1985@foxmail.com"; ?? ????} ?? }??
package com.royzhou.util;import org.jbpm.mail.AddressResolver;public class MailAddressResolver implements AddressResolver {public Object resolveAddress(String actorId) {return "royzhou1985@foxmail.com";}
}
下面是jbpm.cfg.xml的配置內容。
Xml代碼 ?<jbpm-configuration>?? ????<!--?郵件解析類?-->?? ????<bean?name="jbpm.mail.address.resolver"?? ????????class="com.royzhou.util.MailAddressResolver"?singleton="true"?/>?? ????<!--?郵箱配置信息?-->?? ????<string?name="resource.mail.properties"?value="jbpm.mail.properties"?/>?? ????<!--?處理郵件發送的類?-->?? ????<string?name="mail.class.name"?value="com.royzhou.util.Mail"?/>?? ????<!--?發送人地址?-->?? ????<string?name="jbpm.mail.from.address"?value="test@163.com"></string>?? </jbpm-configuration>??
<jbpm-configuration><!-- 郵件解析類 --><bean name="jbpm.mail.address.resolver"class="com.royzhou.util.MailAddressResolver" singleton="true" /><!-- 郵箱配置信息 --><string name="resource.mail.properties" value="jbpm.mail.properties" /><!-- 處理郵件發送的類 --><string name="mail.class.name" value="com.royzhou.util.Mail" /><!-- 發送人地址 --><string name="jbpm.mail.from.address" value="test@163.com"></string>
</jbpm-configuration>
郵箱配置信息如下:
mail.smtp.host=smtp.163.com
mail.smtp.user=test
mail.smtp.password=test
mail.smtp.auth=true
配置中我們使用的是自己的郵件發送類,這是因為jbpm自己提供的郵件發送類沒有提供身份認證,直接使用會報異常。
修改之后的郵件發送類如下:
Java代碼 ?package?com.royzhou.util; ?? ?? import?java.io.InputStream; ?? import?java.io.Serializable; ?? import?java.util.ArrayList; ?? import?java.util.Arrays; ?? import?java.util.Collection; ?? import?java.util.Date; ?? import?java.util.HashMap; ?? import?java.util.Iterator; ?? import?java.util.List; ?? import?java.util.Map; ?? import?java.util.Properties; ?? import?java.util.StringTokenizer; ?? ?? import?javax.mail.Message; ?? import?javax.mail.PasswordAuthentication; ?? import?javax.mail.Session; ?? import?javax.mail.Transport; ?? import?javax.mail.internet.InternetAddress; ?? import?javax.mail.internet.MimeMessage; ?? import?javax.mail.Authenticator; ?? ?? import?org.apache.commons.logging.Log; ?? import?org.apache.commons.logging.LogFactory; ?? import?org.jbpm.JbpmConfiguration; ?? import?org.jbpm.JbpmException; ?? import?org.jbpm.graph.def.ActionHandler; ?? import?org.jbpm.graph.exe.ExecutionContext; ?? import?org.jbpm.jpdl.el.ELException; ?? import?org.jbpm.jpdl.el.VariableResolver; ?? import?org.jbpm.jpdl.el.impl.JbpmExpressionEvaluator; ?? import?org.jbpm.mail.AddressResolver; ?? import?org.jbpm.util.ClassLoaderUtil; ?? import?org.jbpm.util.XmlUtil; ?? ?? public?class?Mail?implements?ActionHandler?{ ?? ?? ??private?static?final?long?serialVersionUID?=?1L; ?? ?? ?? ??String?template?=?null; ?? ??String?actors?=?null; ?? ??String?to?=?null; ?? ??String?bcc?=?null; ?? ??String?bccActors?=?null; ?? ??String?subject?=?null; ?? ??String?text?=?null; ?? ?? ?? ??ExecutionContext?executionContext?=?null; ?? ?? ?? ??public?Mail()?{ ?? ??} ?? ?? ??public?Mail(String?template, ?? ??????????????String?actors, ?? ??????????????String?to, ?? ??????????????String?subject, ?? ??????????????String?text)?{ ?? ????this.template?=?template; ?? ????this.actors?=?actors; ?? ????this.to?=?to; ?? ????this.subject?=?subject; ?? ????this.text?=?text; ?? ??} ?? ?? ?? ??public?Mail(String?template, ?? ??????String?actors, ?? ??????String?to, ?? ??????String?bccActors, ?? ??????String?bcc, ?? ??????String?subject, ?? ??????String?text)?{ ?? ????this.template?=?template; ?? ????this.actors?=?actors; ?? ????this.to?=?to; ?? ????this.bccActors?=?bccActors; ?? ????this.bcc?=?bcc; ?? ????this.subject?=?subject; ?? ????this.text?=?text; ?? ??}?? ?? ?? ?? ??public?void?execute(ExecutionContext?executionContext)?{ ?? ????this.executionContext?=?executionContext; ?? ????send(); ?? ??} ?? ?? ??public?List?getRecipients()?{ ?? ????List?recipients?=?new?ArrayList(); ?? ????if?(actors!=null)?{ ?? ??????String?evaluatedActors?=?evaluate(actors); ?? ??????List?tokenizedActors?=?tokenize(evaluatedActors); ?? ??????if?(tokenizedActors!=null)?{ ?? ????????recipients.addAll(resolveAddresses(tokenizedActors)); ?? ??????} ?? ????} ?? ????if?(to!=null)?{ ?? ??????String?resolvedTo?=?evaluate(to); ?? ??????recipients.addAll(tokenize(resolvedTo)); ?? ????} ?? ????return?recipients; ?? ??} ?? ?? ??public?List?getBccRecipients()?{ ?? ????List?recipients?=?new?ArrayList(); ?? ????if?(bccActors!=null)?{ ?? ??????String?evaluatedActors?=?evaluate(bccActors); ?? ??????List?tokenizedActors?=?tokenize(evaluatedActors); ?? ??????if?(tokenizedActors!=null)?{ ?? ????????recipients.addAll(resolveAddresses(tokenizedActors)); ?? ??????} ?? ????} ?? ????if?(bcc!=null)?{ ?? ??????String?resolvedTo?=?evaluate(to); ?? ??????recipients.addAll(tokenize(resolvedTo)); ?? ????} ?? ????if?(JbpmConfiguration.Configs.hasObject("jbpm.mail.bcc.address"))?{ ?? ??????recipients.addAll(tokenize( ?? ??????????JbpmConfiguration.Configs.getString("jbpm.mail.bcc.address"))); ?? ????} ?? ????return?recipients; ?? ??} ?? ?? ?? ??public?String?getSubject()?{ ?? ????if?(subject==null)?return?null; ?? ????return?evaluate(subject); ?? ??} ?? ?? ??public?String?getText()?{ ?? ????if?(text==null)?return?null; ?? ????return?evaluate(text); ?? ??} ?? ?? ??public?String?getFromAddress()?{ ?? ????if?(JbpmConfiguration.Configs.hasObject("jbpm.mail.from.address"))?{ ?? ??????return?JbpmConfiguration.Configs.getString("jbpm.mail.from.address"); ?? ????}? ?? ????return?"jbpm@noreply"; ?? ??} ?? ?? ??public?void?send()?{ ?? ????if?(template!=null)?{ ?? ??????Properties?properties?=?getMailTemplateProperties(template); ?? ??????if?(actors==null)?{ ?? ????????actors?=?properties.getProperty("actors"); ?? ??????} ?? ??????if?(to==null)?{ ?? ????????to?=?properties.getProperty("to"); ?? ??????} ?? ??????if?(subject==null)?{ ?? ????????subject?=?properties.getProperty("subject"); ?? ??????} ?? ??????if?(text==null)?{ ?? ????????text?=?properties.getProperty("text"); ?? ??????} ?? ??????if?(bcc==null)?{ ?? ????????bcc?=?properties.getProperty("bcc"); ?? ??????} ?? ??????if?(bccActors==null)?{ ?? ????????bccActors?=?properties.getProperty("bccActors"); ?? ??????} ?? ????} ?? ???? ?? ????send(getMailServerProperties(),? ?? ????????????getFromAddress(),? ?? ????????????getRecipients(),? ?? ????????????getBccRecipients(), ?? ????????????getSubject(),? ?? ????????????getText()); ?? ??} ?? ?? ?? ??public?static?void?send(Properties?mailServerProperties,?String?fromAddress,?List?recipients,?String?subject,?String?text)?{ ?? ????send(mailServerProperties,?fromAddress,?recipients,?null,?subject,?text); ?? ??} ?? ?? ?? ??public?static?void?send(Properties?mailServerProperties,?String?fromAddress,?List?recipients,?List?bccRecipients,?String?subject,?String?text)?{ ?? ????if?(?(recipients==null) ?? ?????????||?(recipients.isEmpty()) ?? ???????)?{ ?? ??????log.debug("skipping?mail?because?there?are?no?recipients"); ?? ??????return; ?? ????} ?? ????mailServerProperties.put("mail.smtp.auth",?"true"); ?? ????log.debug("sending?email?to?'"+recipients+"'?about?'"+subject+"'"); ?? ???? ?? ????/** ? ?????*?添加認證類 ? ?????*?royzhou?2009.07.21 ? ?????*/?? ????String?userName?=?mailServerProperties.getProperty("mail.smtp.user").toString(); ?? ????String?password?=?mailServerProperties.getProperty("mail.smtp.password").toString(); ?? ????MyAuthentication?myAuthentication?=?new?MyAuthentication(userName,password); ?? ????Session?session?=?Session.getDefaultInstance(mailServerProperties,?myAuthentication); ?? ????MimeMessage?message?=?new?MimeMessage(session); ?? ????try?{ ?? ??????if?(fromAddress!=null)?{ ?? ????????message.setFrom(new?InternetAddress(fromAddress)); ?? ??????} ?? ??????Iterator?iter?=?recipients.iterator(); ?? ??????while?(iter.hasNext())?{ ?? ????????InternetAddress?recipient?=?new?InternetAddress((String)?iter.next()); ?? ????????message.addRecipient(Message.RecipientType.TO,?recipient); ?? ??????} ?? ??????if?(bccRecipients!=null)?{ ?? ????????iter?=?bccRecipients.iterator(); ?? ????????while?(iter.hasNext())?{ ?? ??????????InternetAddress?recipient?=?new?InternetAddress((String)?iter.next()); ?? ??????????message.addRecipient(Message.RecipientType.BCC,?recipient); ?? ????????}???????? ?? ??????} ?? ??????if?(subject!=null)?{ ?? ????????message.setSubject(subject); ?? ??????} ?? ??????if?(text!=null)?{ ?? ????????message.setText(text); ?? ??????} ?? ??????message.setSentDate(new?Date()); ?? ?????? ?? ??????Transport.send(message); ?? ????}?catch?(Exception?e)?{ ?? ??????throw?new?JbpmException("couldn't?send?email",?e); ?? ????} ?? ??} ?? ?? ??protected?List?tokenize(String?text)?{ ?? ????if?(text==null)?{ ?? ??????return?null; ?? ????} ?? ????List?list?=?new?ArrayList(); ?? ????StringTokenizer?tokenizer?=?new?StringTokenizer(text,?";:"); ?? ????while?(tokenizer.hasMoreTokens())?{ ?? ??????list.add(tokenizer.nextToken()); ?? ????} ?? ????return?list; ?? ??} ?? ?? ??protected?Collection?resolveAddresses(List?actorIds)?{ ?? ????List?emailAddresses?=?new?ArrayList(); ?? ????Iterator?iter?=?actorIds.iterator(); ?? ????while?(iter.hasNext())?{ ?? ??????String?actorId?=?(String)?iter.next(); ?? ??????AddressResolver?addressResolver?=?(AddressResolver)?JbpmConfiguration.Configs.getObject("jbpm.mail.address.resolver"); ?? ??????Object?resolvedAddresses?=?addressResolver.resolveAddress(actorId); ?? ??????if?(resolvedAddresses!=null)?{ ?? ????????if?(resolvedAddresses?instanceof?String)?{ ?? ??????????emailAddresses.add((String)resolvedAddresses); ?? ????????}?else?if?(resolvedAddresses?instanceof?Collection)?{ ?? ??????????emailAddresses.addAll((Collection)resolvedAddresses); ?? ????????}?else?if?(resolvedAddresses?instanceof?String[])?{ ?? ??????????emailAddresses.addAll(Arrays.asList((String[])resolvedAddresses)); ?? ????????}?else?{ ?? ??????????throw?new?JbpmException("Address?resolver?'"+addressResolver+"'?returned?'"+resolvedAddresses.getClass().getName()+"'?instead?of?a?String,?Collection?or?String-array:?"+resolvedAddresses); ?? ????????} ?? ??????} ?? ????} ?? ????return?emailAddresses; ?? ??} ?? ?? ??Properties?getMailServerProperties()?{ ?? ????Properties?mailServerProperties?=?new?Properties(); ?? ?? ????if?(JbpmConfiguration.Configs.hasObject("resource.mail.properties"))?{ ?? ??????String?mailServerPropertiesResource?=?JbpmConfiguration.Configs.getString("resource.mail.properties"); ?? ??????try?{ ?? ????????InputStream?mailServerStream?=?ClassLoaderUtil.getStream(mailServerPropertiesResource); ?? ????????mailServerProperties.load(mailServerStream); ?? ??????}?catch?(Exception?e)?{ ?? ????????throw?new?JbpmException("couldn't?get?configuration?properties?for?jbpm?mail?server?from?resource?'"+mailServerPropertiesResource+"'",?e); ?? ??????} ?? ???? ?? ????}?else?if?(JbpmConfiguration.Configs.hasObject("jbpm.mail.smtp.host"))?{ ?? ??????String?smtpServer?=?JbpmConfiguration.Configs.getString("jbpm.mail.smtp.host"); ?? ??????mailServerProperties.put("mail.smtp.host",?smtpServer); ?? ?????? ?? ????}?else?{ ?? ?????? ?? ??????log.error("couldn't?get?mail?properties"); ?? ????} ?? ?? ????return?mailServerProperties; ?? ??} ?? ?? ??static?Map?templates?=?null; ?? ??static?Map?templateVariables?=?null; ?? ??synchronized?Properties?getMailTemplateProperties(String?templateName)?{ ?? ????if?(templates==null)?{ ?? ??????templates?=?new?HashMap(); ?? ??????String?mailTemplatesResource?=?JbpmConfiguration.Configs.getString("resource.mail.templates"); ?? ??????org.w3c.dom.Element?mailTemplatesElement?=?XmlUtil.parseXmlResource(mailTemplatesResource).getDocumentElement(); ?? ??????List?mailTemplateElements?=?XmlUtil.elements(mailTemplatesElement,?"mail-template"); ?? ??????Iterator?iter?=?mailTemplateElements.iterator(); ?? ??????while?(iter.hasNext())?{ ?? ????????org.w3c.dom.Element?mailTemplateElement?=?(org.w3c.dom.Element)?iter.next(); ?? ?? ????????Properties?templateProperties?=?new?Properties(); ?? ????????addTemplateProperty(mailTemplateElement,?"actors",?templateProperties); ?? ????????addTemplateProperty(mailTemplateElement,?"to",?templateProperties); ?? ????????addTemplateProperty(mailTemplateElement,?"subject",?templateProperties); ?? ????????addTemplateProperty(mailTemplateElement,?"text",?templateProperties); ?? ????????addTemplateProperty(mailTemplateElement,?"bcc",?templateProperties); ?? ????????addTemplateProperty(mailTemplateElement,?"bccActors",?templateProperties); ?? ?? ????????templates.put(mailTemplateElement.getAttribute("name"),?templateProperties); ?? ??????} ?? ?? ??????templateVariables?=?new?HashMap(); ?? ??????List?variableElements?=?XmlUtil.elements(mailTemplatesElement,?"variable"); ?? ??????iter?=?variableElements.iterator(); ?? ??????while?(iter.hasNext())?{ ?? ????????org.w3c.dom.Element?variableElement?=?(org.w3c.dom.Element)?iter.next(); ?? ????????templateVariables.put(variableElement.getAttribute("name"),?variableElement.getAttribute("value")); ?? ??????} ?? ????} ?? ????return?(Properties)?templates.get(templateName); ?? ??} ?? ?? ??void?addTemplateProperty(org.w3c.dom.Element?mailTemplateElement,?String?property,?Properties?templateProperties)?{ ?? ????org.w3c.dom.Element?element?=?XmlUtil.element(mailTemplateElement,?property); ?? ????if?(element!=null)?{ ?? ??????templateProperties.put(property,?XmlUtil.getContentText(element)); ?? ????} ?? ??} ?? ?? ?? ??String?evaluate(String?expression)?{ ?? ????if?(expression==null)?{ ?? ??????return?null; ?? ????} ?? ????VariableResolver?variableResolver?=?JbpmExpressionEvaluator.getUsedVariableResolver(); ?? ????if?(variableResolver!=null)?{ ?? ??????variableResolver?=?new?MailVariableResolver(templateVariables,?variableResolver); ?? ????} ?? ????return?(String)?JbpmExpressionEvaluator.evaluate(expression,?executionContext,?variableResolver,?null); ?? ??} ?? ?? ??class?MailVariableResolver?implements?VariableResolver,?Serializable?{ ?? ????private?static?final?long?serialVersionUID?=?1L; ?? ????Map?templateVariables?=?null; ?? ????VariableResolver?variableResolver?=?null; ?? ?? ????public?MailVariableResolver(Map?templateVariables,?VariableResolver?variableResolver)?{ ?? ??????this.templateVariables?=?templateVariables; ?? ??????this.variableResolver?=?variableResolver; ?? ????} ?? ?? ????public?Object?resolveVariable(String?pName)?throws?ELException?{ ?? ??????if?(?(templateVariables!=null) ?? ???????????&&?(templateVariables.containsKey(pName)) ?? ?????????){ ?? ????????return?templateVariables.get(pName); ?? ??????} ?? ??????return?variableResolver.resolveVariable(pName); ?? ????} ?? ??} ?? ?? ??private?static?Log?log?=?LogFactory.getLog(Mail.class); ?? } ?? ?? /** ? ?*?郵箱認證類 ? ?*?@author?royzhou ? ?*?2009.07.21 ? ?*/?? class?MyAuthentication?extends?Authenticator?{ ?? ????private?String?userName; ?? ????private?String?password; ?? ????public?MyAuthentication(String?userName,?String?password)?{ ?? ????????this.userName?=?userName; ?? ????????this.password?=?password; ?? ????} ?? ????protected?PasswordAuthentication?getPasswordAuthentication()?{ ?? ????????return?new?PasswordAuthentication(userName,password); ?? ????} ?? }??
package com.royzhou.util;import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;import javax.mail.Message;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.Authenticator;import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jbpm.JbpmConfiguration;
import org.jbpm.JbpmException;
import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.exe.ExecutionContext;
import org.jbpm.jpdl.el.ELException;
import org.jbpm.jpdl.el.VariableResolver;
import org.jbpm.jpdl.el.impl.JbpmExpressionEvaluator;
import org.jbpm.mail.AddressResolver;
import org.jbpm.util.ClassLoaderUtil;
import org.jbpm.util.XmlUtil;public class Mail implements ActionHandler {private static final long serialVersionUID = 1L;String template = null;String actors = null;String to = null;String bcc = null;String bccActors = null;String subject = null;String text = null;ExecutionContext executionContext = null;public Mail() {}public Mail(String template,String actors,String to,String subject,String text) {this.template = template;this.actors = actors;this.to = to;this.subject = subject;this.text = text;}public Mail(String template,String actors,String to,String bccActors,String bcc,String subject,String text) {this.template = template;this.actors = actors;this.to = to;this.bccActors = bccActors;this.bcc = bcc;this.subject = subject;this.text = text;} public void execute(ExecutionContext executionContext) {this.executionContext = executionContext;send();}public List getRecipients() {List recipients = new ArrayList();if (actors!=null) {String evaluatedActors = evaluate(actors);List tokenizedActors = tokenize(evaluatedActors);if (tokenizedActors!=null) {recipients.addAll(resolveAddresses(tokenizedActors));}}if (to!=null) {String resolvedTo = evaluate(to);recipients.addAll(tokenize(resolvedTo));}return recipients;}public List getBccRecipients() {List recipients = new ArrayList();if (bccActors!=null) {String evaluatedActors = evaluate(bccActors);List tokenizedActors = tokenize(evaluatedActors);if (tokenizedActors!=null) {recipients.addAll(resolveAddresses(tokenizedActors));}}if (bcc!=null) {String resolvedTo = evaluate(to);recipients.addAll(tokenize(resolvedTo));}if (JbpmConfiguration.Configs.hasObject("jbpm.mail.bcc.address")) {recipients.addAll(tokenize(JbpmConfiguration.Configs.getString("jbpm.mail.bcc.address")));}return recipients;}public String getSubject() {if (subject==null) return null;return evaluate(subject);}public String getText() {if (text==null) return null;return evaluate(text);}public String getFromAddress() {if (JbpmConfiguration.Configs.hasObject("jbpm.mail.from.address")) {return JbpmConfiguration.Configs.getString("jbpm.mail.from.address");} return "jbpm@noreply";}public void send() {if (template!=null) {Properties properties = getMailTemplateProperties(template);if (actors==null) {actors = properties.getProperty("actors");}if (to==null) {to = properties.getProperty("to");}if (subject==null) {subject = properties.getProperty("subject");}if (text==null) {text = properties.getProperty("text");}if (bcc==null) {bcc = properties.getProperty("bcc");}if (bccActors==null) {bccActors = properties.getProperty("bccActors");}}send(getMailServerProperties(), getFromAddress(), getRecipients(), getBccRecipients(),getSubject(), getText());}public static void send(Properties mailServerProperties, String fromAddress, List recipients, String subject, String text) {send(mailServerProperties, fromAddress, recipients, null, subject, text);}public static void send(Properties mailServerProperties, String fromAddress, List recipients, List bccRecipients, String subject, String text) {if ( (recipients==null)|| (recipients.isEmpty())) {log.debug("skipping mail because there are no recipients");return;}mailServerProperties.put("mail.smtp.auth", "true");log.debug("sending email to '"+recipients+"' about '"+subject+"'");/*** 添加認證類* royzhou 2009.07.21*/String userName = mailServerProperties.getProperty("mail.smtp.user").toString();String password = mailServerProperties.getProperty("mail.smtp.password").toString();MyAuthentication myAuthentication = new MyAuthentication(userName,password);Session session = Session.getDefaultInstance(mailServerProperties, myAuthentication);MimeMessage message = new MimeMessage(session);try {if (fromAddress!=null) {message.setFrom(new InternetAddress(fromAddress));}Iterator iter = recipients.iterator();while (iter.hasNext()) {InternetAddress recipient = new InternetAddress((String) iter.next());message.addRecipient(Message.RecipientType.TO, recipient);}if (bccRecipients!=null) {iter = bccRecipients.iterator();while (iter.hasNext()) {InternetAddress recipient = new InternetAddress((String) iter.next());message.addRecipient(Message.RecipientType.BCC, recipient);} }if (subject!=null) {message.setSubject(subject);}if (text!=null) {message.setText(text);}message.setSentDate(new Date());Transport.send(message);} catch (Exception e) {throw new JbpmException("couldn't send email", e);}}protected List tokenize(String text) {if (text==null) {return null;}List list = new ArrayList();StringTokenizer tokenizer = new StringTokenizer(text, ";:");while (tokenizer.hasMoreTokens()) {list.add(tokenizer.nextToken());}return list;}protected Collection resolveAddresses(List actorIds) {List emailAddresses = new ArrayList();Iterator iter = actorIds.iterator();while (iter.hasNext()) {String actorId = (String) iter.next();AddressResolver addressResolver = (AddressResolver) JbpmConfiguration.Configs.getObject("jbpm.mail.address.resolver");Object resolvedAddresses = addressResolver.resolveAddress(actorId);if (resolvedAddresses!=null) {if (resolvedAddresses instanceof String) {emailAddresses.add((String)resolvedAddresses);} else if (resolvedAddresses instanceof Collection) {emailAddresses.addAll((Collection)resolvedAddresses);} else if (resolvedAddresses instanceof String[]) {emailAddresses.addAll(Arrays.asList((String[])resolvedAddresses));} else {throw new JbpmException("Address resolver '"+addressResolver+"' returned '"+resolvedAddresses.getClass().getName()+"' instead of a String, Collection or String-array: "+resolvedAddresses);}}}return emailAddresses;}Properties getMailServerProperties() {Properties mailServerProperties = new Properties();if (JbpmConfiguration.Configs.hasObject("resource.mail.properties")) {String mailServerPropertiesResource = JbpmConfiguration.Configs.getString("resource.mail.properties");try {InputStream mailServerStream = ClassLoaderUtil.getStream(mailServerPropertiesResource);mailServerProperties.load(mailServerStream);} catch (Exception e) {throw new JbpmException("couldn't get configuration properties for jbpm mail server from resource '"+mailServerPropertiesResource+"'", e);}} else if (JbpmConfiguration.Configs.hasObject("jbpm.mail.smtp.host")) {String smtpServer = JbpmConfiguration.Configs.getString("jbpm.mail.smtp.host");mailServerProperties.put("mail.smtp.host", smtpServer);} else {log.error("couldn't get mail properties");}return mailServerProperties;}static Map templates = null;static Map templateVariables = null;synchronized Properties getMailTemplateProperties(String templateName) {if (templates==null) {templates = new HashMap();String mailTemplatesResource = JbpmConfiguration.Configs.getString("resource.mail.templates");org.w3c.dom.Element mailTemplatesElement = XmlUtil.parseXmlResource(mailTemplatesResource).getDocumentElement();List mailTemplateElements = XmlUtil.elements(mailTemplatesElement, "mail-template");Iterator iter = mailTemplateElements.iterator();while (iter.hasNext()) {org.w3c.dom.Element mailTemplateElement = (org.w3c.dom.Element) iter.next();Properties templateProperties = new Properties();addTemplateProperty(mailTemplateElement, "actors", templateProperties);addTemplateProperty(mailTemplateElement, "to", templateProperties);addTemplateProperty(mailTemplateElement, "subject", templateProperties);addTemplateProperty(mailTemplateElement, "text", templateProperties);addTemplateProperty(mailTemplateElement, "bcc", templateProperties);addTemplateProperty(mailTemplateElement, "bccActors", templateProperties);templates.put(mailTemplateElement.getAttribute("name"), templateProperties);}templateVariables = new HashMap();List variableElements = XmlUtil.elements(mailTemplatesElement, "variable");iter = variableElements.iterator();while (iter.hasNext()) {org.w3c.dom.Element variableElement = (org.w3c.dom.Element) iter.next();templateVariables.put(variableElement.getAttribute("name"), variableElement.getAttribute("value"));}}return (Properties) templates.get(templateName);}void addTemplateProperty(org.w3c.dom.Element mailTemplateElement, String property, Properties templateProperties) {org.w3c.dom.Element element = XmlUtil.element(mailTemplateElement, property);if (element!=null) {templateProperties.put(property, XmlUtil.getContentText(element));}}String evaluate(String expression) {if (expression==null) {return null;}VariableResolver variableResolver = JbpmExpressionEvaluator.getUsedVariableResolver();if (variableResolver!=null) {variableResolver = new MailVariableResolver(templateVariables, variableResolver);}return (String) JbpmExpressionEvaluator.evaluate(expression, executionContext, variableResolver, null);}class MailVariableResolver implements VariableResolver, Serializable {private static final long serialVersionUID = 1L;Map templateVariables = null;VariableResolver variableResolver = null;public MailVariableResolver(Map templateVariables, VariableResolver variableResolver) {this.templateVariables = templateVariables;this.variableResolver = variableResolver;}public Object resolveVariable(String pName) throws ELException {if ( (templateVariables!=null)&& (templateVariables.containsKey(pName))){return templateVariables.get(pName);}return variableResolver.resolveVariable(pName);}}private static Log log = LogFactory.getLog(Mail.class);
}/*** 郵箱認證類* @author royzhou* 2009.07.21*/
class MyAuthentication extends Authenticator {private String userName;private String password;public MyAuthentication(String userName, String password) {this.userName = userName;this.password = password;}protected PasswordAuthentication getPasswordAuthentication() {return new PasswordAuthentication(userName,password);}
}
下面我們開始寫我們的測試類:MailNode
Java代碼 ?package?com.royzhou.test; ?? ?? import?org.jbpm.graph.def.ProcessDefinition; ?? import?org.jbpm.graph.exe.ProcessInstance; ?? import?org.jbpm.graph.exe.Token; ?? ?? public?class?MailNode?{ ?? ???? ?? ????public?static?void?main(String[]?args)?{ ?? ????????ProcessDefinition?processDefinition?=?ProcessDefinition.parseXmlResource("mail/processDefinition.xml"); ?? ????????ProcessInstance?processInstance?=?new?ProcessInstance(processDefinition); ?? ????????processInstance.getContextInstance().setVariable("actorId",?"royzhou"); ?? ????????Token?token?=?processInstance.getRootToken(); ?? ????????token.signal(); ?? ????????System.out.println("end^^^^^^^^^^^^"); ?? ????} ?? }??
package com.royzhou.test;import org.jbpm.graph.def.ProcessDefinition;
import org.jbpm.graph.exe.ProcessInstance;
import org.jbpm.graph.exe.Token;public class MailNode {public static void main(String[] args) {ProcessDefinition processDefinition = ProcessDefinition.parseXmlResource("mail/processDefinition.xml");ProcessInstance processInstance = new ProcessInstance(processDefinition);processInstance.getContextInstance().setVariable("actorId", "royzhou");Token token = processInstance.getRootToken();token.signal();System.out.println("end^^^^^^^^^^^^");}
}
運行測試程序,后臺打印:
enter mail node
test====test
leave mail node
end^^^^^^^^^^^^
打開你的收件箱發現郵件發送成功并且信息都是根據模版生成的。。
至此,我們將流程的節點基本上都過了一遍。。。。 筆記終于寫完。。。累就一個字
明天出差。。。。
回來繼續。。。。。?
Node節點是一個自動完成節點,如果沒有在Node節點上定義Action,流程達到Node節點后不會停留,而是繼續往下執行到Node節點的下一個節點。利用之前的Helloworld例子,我們在Node節點上加上一個Action(Action的執行是在node-enter之后node-leave之前)
Xml代碼 ?
Action類如下:
Java代碼 ?
測試類如下:
Java代碼 ?
測試結果如下:(驗證了在Node上設置Action的時候,流程處于等待狀態)
1:流程現在所處節點:start-state1
node action……………………
2:流程現在所處節點:node1
3:流程現在所處節點:state1
4:流程現在所處節點:end-state1
流程狀態是否結束:true
2、State節點
State節點屬性類似Node,不過當流程流轉到State節點時就會停下來,直到外部向其發送流轉的命令,如signal().
3、Task-Node
Task Node節點在Jbpm中是很重要的一個節點,可以用來添加任務,產生任務實例。一個Task Node 可以定義多個Task,為Task分配執行人等等。下面我們通過一個例子來解釋Task Node。
Xml代碼 ?
上面流程文件定義了一個Task Node ,在Task Node下面定義了一個Task節點,分配給了royzhou這個人(實際開發中的任務分配會更加復雜)
接下來我們開始寫測試類:
Java代碼 ?
后臺打印結果:
流程現在所處節點:task-node1
查看jbpm_taskinstance表看到一個名叫mytask的任務,指定給了royzhou說明我們的任務創建成功了。
Task Node有幾個比較重要的屬性:
Signal:
last:最后一個完成之后繼續往下流轉,如果沒有任務被創建直接流轉到下一節點
last-wait:最后一個完成之后繼續往下流轉,如果沒有任務被創建處于等待狀態
first:只要有一個完成就繼續往下流轉,如果沒有任務被創建直接流轉下一個節點
first-wait:只要一個完成就繼續往下流轉,如果沒有任務被創建則處于等待狀態
never:必須顯示調用processInstance.signal()才繼續流轉
unsynchronized:不停留,直接流轉到下個節點
task-creates:流程流轉到這個節點上時是不是自動創建任務,默認是true。通過設置為false,配合其他事件我們可以實現會簽的功能。
4、Start節點
Start節點在每個流程中有且只能有一個。Start中可以配置Task,但是 配置在Start節點中的task不會調用任務分配機制。這點是不同于Task Node的。。。。
5、fork和join節點
二者是成對出現的。Fork節點可以將流程拆分成多條并行也就是將Token拆分成多個子Token,到join節點的時候再匯聚成一個Token繼續往下執行
一般來說,在經過fork節點之后,必須等待每個子token都走完才會通過join節點繼續往下走,但是我們可以通過修改join節點的discriminator屬性來,設置為true之后只要有一個子token到達join節點,就會往下流轉。不過這樣做產生的一個影響就是其他沒有結束的子token上的任務實例依然存在且處于等待狀態,任務參與者仍然可以處理這些任務。因此,在設置join節點的discriminator為true時,我們還需要手動結束掉其他分支上的任務實例。
在fork節點中,我們可以設定條件來選擇只走滿足條件的分支。主要是通過之前提到的beanshell腳本來實現。舉例說明一下這個實現:
流程定義由一個start節點,一個end節點,一對fork/join節點以及分支中的四個node節點組成,流程定義的xml文件如下:
Xml代碼 ?
默認情況下,當流程進入fork節點之后會分成四個分支,分別經過Node節點。在上面定義文件中我們在fork節點下做了判斷,如果流程變量param>1000的時候走分支1和分支2,如果小于則走分支3和分支4。
Xml代碼 ?
注意這里的forkTransition必須是集合類型,它存放的是流程經過fork之后生成的分支名稱。access屬性設置為write表示是寫入流程實例對應的Fork節點,才能生成我們指定的分支。在Expression標簽下是我們做的邏輯處理,通過流程變量param來判斷Fork節點具體要生成那些分支。
默認我們不為Fork節點添加script,流程到Fork節點時將生成所有transition。一旦設置了script,還必須設置一個變量,如上面的forkTransition寫回流程變量中生成具體的分支。
測試類如下:(為方便測試沒有將流程發布到數據庫,下面測試例子也是采用相同做法)
Java代碼 ?
執行成功之后后臺輸出:
node3 enter
node4 enter
修改我們的param改為2000再次執行,后臺變為:
node1 enter
node2 enter
6、Decision節點
Decision節點是判斷節點,當流程到達decision節點之后會有多個路由選擇,我們可以在這個節點上定義handler或者是使用腳本等等來決定流程走向。Decision節點不同于Fork節點,它只能選擇多個Transition中的其中一個,而Fork節點是并行的。
舉例說明一下:
流程定義由一個start節點,一個end節點,一個Decision節點以及分支中的3個node節點組成,在Decision我們設置了delegation,使用實現DecisionHandler接口的類SelectNode來判斷流程走向,它返回的結果是其中分支的名稱,流程定義的xml文件如下:
Xml代碼 ?
SelectNode類:(必須實現DecisionHandler接口并重寫decide方法,返回值是要走的transition的名稱)
Java代碼 ?
測試類:
Java代碼 ?
運行測試類,后臺打印出? :
node2 enter
修改流程變量whichWay為to node1 重新運行,后臺打印出:
node1 enter
說明我們的decision節點正確處理了其走向。
除了使用delegation,我們也可以在流程文件中直接定義decision節點的expression屬性,如下所示:
Xml代碼 ?
這里采用的是JPDL表達式來指定流程走向,JPDL表達式是一種類似EL表達式的語言。
上面expression表示從流程變量中獲取whichWay這個變量作為我們流程走向的選擇。
重新運行我們的測試類。發現結果與上面一種做法相同。
此外我們還可以直接在transition節點下定義condition標簽,配合JPDL表達式來作為我們的判斷依據
Xml代碼 ?
再次運行測試程序發現結果仍然一致。
由此可見 Decision節點支持我們使用delegation、condition以及expression三種方式來指定流程走向。
7、Process State節點
子流程節點可以簡化我們的復雜流程。主流程與子流程之間可以共享變量。如果使用子流程,在主流程發布之前必須先發布子流程。
舉例說明一下主流程與子流程及其變量共享:
主流程定義文件mainprocess/processdefinition.xml
Xml代碼 ?
主流程中設置了process-state節點,其sub-process 的name屬性表示子流程的名稱,
<variable name="mainParam" access="read"></variable>表示從主流程讀取mainParam變量
<variable name="subParam" access="write"></variable>表示子流程將變量subParam寫回主流程中。
子流程定義文件:subprocess/processdefinition.xml
Xml代碼 ?
測試類:
Java代碼 ?
運行上面程序,成功執行之后結果為:
main task node1 enter
sub task node1 enter
sub task node1 enter
subtask1子流程讀取主流程變量,mainParam的值是:主流程設置的變量
main task node2 enter
maintask2主流程讀取子流程變量,subParam的值是:子流程設置的變量
8、Mail Node 節點
該節點可以實現郵件的發送,具有通知提醒功能。
具體有兩種配置方式:
通過mail node節點的template屬性設置,采用此種方式需要修改jbpm.mail.template.xml文件里的模版,加上我們需要的模版,具體模版設置可參照里面已有模版。舉例說明下:
流程模版文件:start -> mail node ->end
Xml代碼 ?
我們使用的模版是test,但是jbpm.mail.template.xml沒有,所以需要我們自己加:
Xml代碼 ?
這個是我們自己定義的模版,其中actors標簽表示的是收件人的地址,需要使用郵件解析類解析,郵件解析類是一個實現AddressResolver接口的類。并且需要在jbpm.cfg.xml中配置,告訴jbpm要使用這個類去解析獲取發送郵件的地址。
Java代碼 ?
下面是jbpm.cfg.xml的配置內容。
Xml代碼 ?
郵箱配置信息如下:
mail.smtp.host=smtp.163.com
mail.smtp.user=test
mail.smtp.password=test
mail.smtp.auth=true
配置中我們使用的是自己的郵件發送類,這是因為jbpm自己提供的郵件發送類沒有提供身份認證,直接使用會報異常。
修改之后的郵件發送類如下:
Java代碼 ?
下面我們開始寫我們的測試類:MailNode
Java代碼 ?
運行測試程序,后臺打印:
enter mail node
test====test
leave mail node
end^^^^^^^^^^^^
打開你的收件箱發現郵件發送成功并且信息都是根據模版生成的。。
至此,我們將流程的節點基本上都過了一遍。。。。 筆記終于寫完。。。累就一個字
明天出差。。。。
回來繼續。。。。。?
總結
- 上一篇: 社区架构培训班四期开始报名了
- 下一篇: 动态表单及动态建表实现原理