Node节点是一个自动完成节点,如果没有在Node节点上定义Action,流程达到Node节点后不会停留,而是继续往下执行到Node节点的下一个节点。利用之前的Helloworld例子,我们在Node节点上加上一个Action(Action的执行是在node-enter之后node-leave之前)
- <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类如下:
- 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……………………");
}
}
测试类如下:
- 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());
//触发流程流转到下一个节点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();
}
}
测试结果如下:(验证了在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 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这个人(实际开发中的任务分配会更加复杂)
接下来我们开始写测试类:
- 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 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。
- <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写回流程变量中生成具体的分支。
测试类如下:(为方便测试没有将流程发布到数据库,下面测试例子也是采用相同做法)
- 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 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的名称)
- 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 {
//通过流程变量来判断要走哪个transition
String whichWay = executionContext.getVariable("whichWay").toString();
return whichWay;
}
}
测试类:
- 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属性,如下所示:
- <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表达式来作为我们的判断依据
- <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 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 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>
测试类:
- 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 {
//主流程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();
}
}
运行上面程序,成功执行之后结果为:
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 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没有,所以需要我们自己加:
- <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要使用这个类去解析获取发送邮件的地址。
- 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的配置内容。
- <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自己提供的邮件发送类没有提供身份认证,直接使用会报异常。
修改之后的邮件发送类如下:
- 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
- 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^^^^^^^^^^^^
打开你的收件箱发现邮件发送成功并且信息都是根据模版生成的。。
至此,我们将流程的节点基本上都过了一遍。。。。 笔记终于写完。。。累就一个字
明天出差。。。。
回来继续。。。。。