您的位置:首页 > 产品设计 > 产品经理

Jbpm 流程节点

2014-03-16 11:00 399 查看
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());
		
		//触发流程流转到下一个节点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代码








<?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 {
		//通过流程变量来判断要走哪个transition
		String 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 {
			//主流程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代码








<?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^^^^^^^^^^^^

打开你的收件箱发现邮件发送成功并且信息都是根据模版生成的。。

至此,我们将流程的节点基本上都过了一遍。。。。 笔记终于写完。。。累就一个字

明天出差。。。。

回来继续。。。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: