`
royzhou1985
  • 浏览: 249113 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

Jbpm 流程节点

    博客分类:
  • JBPM
阅读更多
1、Node节点
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>

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();
	}
}

测试结果如下:(验证了在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>

上面流程文件定义了一个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();
	}
}

后台打印结果:
流程现在所处节点: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 &gt; 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(&quot;node1 enter&quot;);
			</script>
		</event>
		<transition to="join1"></transition>
	</node>
	<node name="node2">
		<event type="node-enter">
			<script>
				print(&quot;node2 enter&quot;);
			</script>
		</event>
		<transition to="join1"></transition>
	</node>
	<node name="node3">
		<event type="node-enter">
			<script>
				print(&quot;node3 enter&quot;);
			</script>
		</event>
		<transition to="join1"></transition>
	</node>
	<node name="node4">
		<event type="node-enter">
			<script>
				print(&quot;node4 enter&quot;);
			</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 &gt; 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();
	}
}

执行成功之后后台输出:
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(&quot;node1 enter&quot;)
		</script>
	</node>
	<node name="node2">
		<script>
			print(&quot;node2 enter&quot;)
		</script>
		<transition to="end-state1"></transition>
	</node>
	<node name="node3">
		<script>
			print(&quot;node3 enter&quot;)
		</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.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>

这里采用的是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节点支持我们使用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(&quot;main task node1 enter&quot;)
			</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(&quot;main task node2 enter&quot;)
			</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(&quot;sub task node1 enter&quot;)
			</script>
		</event>
		<task name="subtask1">
			<assignment actor-id="subtask1"></assignment>
		</task>
		<event type="node-enter">
			<script>
				print(&quot;sub task node1 enter&quot;)
			</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();
	}
}

运行上面程序,成功执行之后结果为:
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(&quot;enter mail node&quot;)
			</script>
		</event>
		<event type="node-leave">
			<script>
				print(&quot;leave mail node&quot;)
			</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>	

这个是我们自己定义的模版,其中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";
	}
}

下面是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>


邮箱配置信息如下:
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);
	}
}


下面我们开始写我们的测试类: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^^^^^^^^^^^^");
	}
}

运行测试程序,后台打印:
enter mail node
test====test
leave mail node
end^^^^^^^^^^^^

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


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

明天出差。。。。
回来继续。。。。。

分享到:
评论
2 楼 Storm_Four 2011-06-18  
牛叉
1 楼 zhuchao_ko 2011-03-08  


Mail这个类写的太牛逼了。

相关推荐

    JBPM采购申请系统——08_JBPM流程节点.7z

    JBPM采购申请系统——08_JBPM流程节点.7z JBPM采购申请系统——08_JBPM流程节点.7z JBPM采购申请系统——08_JBPM流程节点.7z JBPM采购申请系统——08_JBPM流程节点.7z JBPM采购申请系统——08_JBPM流程节点.7z JBPM...

    JBPM流程引擎资料

    文档详细介绍了如何利用JBPM开发流程应用,从框架构造到节点处理。

    JBPM数据库表说明.doc

    1.2 JBPM_NODE:流程节点表 2 1.3 JBPM_TRANSITION:流程迁移表 3 1.4 JBPM_ACTION:流程动作表 4 1.5 JBPM_EVENT:流程事件表 5 1.6 JBPM_DELEGATION:流程代理类表(用于实例化jbpm中的action类) 6 1.7 JBPM_TASK...

    揭秘jbpm流程引擎内核设计思想及构架

    JBPM 3 中文 API jbpm开发指南.pdf JPDL3.1规范手册.pdf JBPM数据库表说明.doc Packt.Publishing.Business.Process.Management.with.JBoss.jBPM.Jul.2007.pdf 揭秘jbpm流程引擎内核设计思想及构架

    jbpm流程控制初学者容易接触的domo

    jbpm流程控制初学者容易接触的domo package com.sxdx.jbpm; import java.io.FileInputStream; import java.io.InputStream; import java.util.zip.ZipInputStream; import org.jbpm.JbpmConfiguration; import org...

    JBPM5流程图设计规则

    1、JBPM5流程图设计规则。 2、bpmn2.0。 3、节点使用规则 4、下载后重命名为名称.doc即可打开

    JBPM数据库表说明 jbpm 工作流 详细到每一个字段,不要下载分

    1.2 JBPM_NODE:流程节点表 2 1.3 JBPM_TRANSITION:流程迁移表 3 1.4 JBPM_ACTION:流程动作表 4 1.5 JBPM_EVENT:流程事件表 5 1.6 JBPM_DELEGATION:流程代理类表(用于实例化jbpm中的action类) 6 1.7 JBPM...

    jbpm3.2表说明

    1.2 JBPM_NODE:流程节点表 关系表 外键存在表 外键名称 外键存在表字段 存在原因 可能 关系 JBPM_TRANSITION JBPM_TRANSITION FK_TRANSITION_TO TO_ 一个NODe可以做为多个TRANSITION的结束 JBPM_...

    JBPM工作流开发指南

    八、JBPM流程建模与应用 64 8.1 JBPM的建模工具 64 8.1.1 建模工具的安装 64 8.2 公司报销流程示例 67 8.2.1 流程建模 67 8.2.2 流程数据库搭建 76 8.2.3 构建业务表 79 8.2.4 搭建工程 82 8.2.5 报销流程的发布 83 ...

    jbpm7示例(jbpm-7.3.0.Final-examples)

    jBPM 是一个完全开源的业务流程管理(BPM)和案例管理解决方案。它支持流程和案例的完整生命周期,从编写工具到执行一直到监控和管理。 在核心引擎之上,提供了许多功能和工具来支持整个生命周期中的业务流程: ...

    JBPM4.4视频教程(22集附源码)

    【】(01)jbpm4.4简介【】(02)JBPM4.4开发环境搭建【】(03)jbpm4.4环境搭建续【】(04)jbpm4.4配置文件和开发前准备【】(05)jbpm4.4部署流程发起流程实例【】(06)jbpm4.4完成任务查看任务当前节点【】(07)jbpm4.4 资源...

    jbpm安装及使用方法

    只有当节点中的三个任务都完成后,流程才进入后面的节点 对于这样的流程定义: xml 代码 1. &lt;task-node name='a' signal='first'&gt;&gt; 2. &lt;task name='laundry' /&gt; 3. &lt;task name='dishes' /&gt; 4. ...

    JBPM5 用户指南

    13.2.4 执行服务节点 89 第十四章 测试和调试 91 14.1 单元测试 91 14.1.1 使用Helper方法创建你的session 92 14.1.2 断言 92 14.1.3测试与外部服务的结合 93 14.1.4 配置持久化 94 14.2 调试 94 14.2.1 流程实例...

    JBPM例子源码 PM4.3流程实例的执行 任务与事件 用户、组与Java节点

    风中叶老师讲的OA工作流系统中JBPM4部分的源码, 源码实现部分包括jBPM4.3流程实例的执行 任务与事件 用户、组与Java节点

    JBPM5权威指南

    13.2.4 执行服务节点 89 第十四章 测试和调试 91 14.1 单元测试 91 14.1.1 使用Helper方法创建你的session 92 14.1.2 断言 92 14.1.3测试与外部服务的结合 93 14.1.4 配置持久化 94 14.2 调试 94 14.2.1 流程实例...

    jbpm4.4学习笔记

    4.6 JBPM在MyEclipse画流程图时出现乱码: 14 5 核心API 14 5.1ProcessEngine 14 5.2RepositoryService 15 5.3ExecutionService 16 5.4TaskService 16 5.5ProcessDefinition 16 5.6ProcessInstance 16 5.7Execution ...

    jbpm各种表单之间的关系

    流程配置类数据库表 JBPM_NODE:流程节点表 JBPM_ TRANSITION:流程迁移表 JBPM_ACTION:流程动作表 JBPM_EVENT:流程事件表

    JBPM4.4+SSH2完整请假流程(部署即可用)

    利用JBPM4.4编写的完整请假流程,涉及申请人填写,经理审批以及决策节点后的老板审批,加入SSH2与JBPM4.4的jar包过后部署好就可以用,只用修改applicationContext.xml中的数据库相关信息即可!

Global site tag (gtag.js) - Google Analytics