jbpm的流程定义从发布上看有2种方式,一种是发布持久化到关系型数据库中,另一种则是由java代码实时生成。
之前一直关注持久化的方式,每次都是生成好固定的xml文件,然后发布到数据中,然后再从数据库中检索出来使用。
不过通过2天的学习,第二种代码生成的方式有着第一种持久化方式所不能替代的优点。下面通过一个简单但是非常实用的例子来说明。
网上有很多人在问,fork分出去的多条路线,为什么一定要全部到达join后才能延续流程,能不能其中部分路线到达后流程就继续呢。很多人通过了很复杂的方法实现了这个功能。下面我就介绍一个简单的办法,在fork下增加script元素:
package com.jeffentest; import org.jbpm.*; import org.jbpm.graph.def.ProcessDefinition; import org.jbpm.graph.exe.*; import org.jbpm.context.exe.ContextInstance; public class Jeffentest { static JbpmConfiguration jbpmConfiguration = JbpmConfiguration .getInstance(); static ProcessDefinition processDefinition = null ; static ProcessInstance processInstance = null ; //static SchedulerSession schedulerSession =null ; private static void run(){ //JbpmContext jbpmContext = jbpmConfiguration .createJbpmContext(); try { processDefinition = ProcessDefinition .parseXmlString( "< process-definition > " + " < start-state name ='start' > " + " < transition to ='a' /> " + " start-state > " + " < state name ='a' > " + " < transition to ='multichoice' /> " + " state > " + " < fork name ='multichoice' > " + " < script > " + " < variable name ='transitionNames' access ='write' /> " + " < expression > " + " transitionNames = new ArrayList();" + " if ( scenario == 1 ) {" + " transitionNames.add( \"to b\" );" + " } else if ( scenario == 2 ) {" + " transitionNames.add( \"to c\" );" + " } else if ( scenario > = 3 ) {" + " transitionNames.add( \"to b\" );" + " transitionNames.add( \"to c\" );" + " }" + " expression > " + " script > " + " < transition name ='to b' to ='b' /> " + " < transition name ='to c' to ='c' /> " + " fork > " + " < state name ='b' > " + " < transition to ='syncmerge' /> " + " state > " + " < state name ='c' > " + " < transition to ='syncmerge' /> " + " state > " + " < join name ='syncmerge' > " + " < transition to ='end' /> " + " join > " + " < end-state name ='end' > end-state > " + "process-definition > " ); processInstance = new ProcessInstance(processDefinition); ContextInstance ci = (ContextInstance) processInstance.getInstance( ContextInstance.class ); Token root = processInstance .getRootToken(); System.out.println(root.getNode());//StartState(start) root.signal(); System.out.println(root.getNode());//State(a) ci.setVariable( "scenario", new Integer(2) ); root.signal(); Token tokenB = root .getChild("to b"); Token tokenC = root .getChild("to c"); System.out.println(root.getNode());//Fork(multichoice) System.out.println("tokenC:"+tokenC.getNode());//tokenC:State(c) tokenC.signal(); System.out.println("tokenC:"+tokenC.getNode());//tokenC:Join(syncmerge) System.out.println(root.getNode());//EndState(end) }catch(Exception e){ e.printStackTrace(); }finally { //jbpmContext.close(); } } public static void main(String[] args) { run(); } }
运行结果如下:
StartState(start) State(a) Fork(multichoice) tokenC:State(c) tokenC:Join(syncmerge) EndState(end)
大家看到了,fork出去2条路线b/c,c完成了而b没有完成,但是整个流程已经结束了。
这里有一点需要注意,这种方式仅仅适合代码生成流程定义,如果你想通过持久化流程定义到数据库的方式,那么结果就仍然要等待路线b的完成了。为什么呢?因为这是jbpm3.1.2的限制:the script in a fork is not persisted. script in fork might be removed in later versions of jPDL.
有兴趣的朋友可以验证一下。
还有一个节点类型Merge也是同样的情况,它有一个Synchronized属性。对于这个节点jbpm的开发者有这么一段话,大家见仁见智吧。 /** * TODO is the merge node usefull ? * i don't think the merge node is usefull because every node has an * implicit merge in front of it (= multiple transitions can arrive in * the same node). maybe we should just leave this in for the sake * of workflow patterns ? */ 附:merge的使用语句 Merge merge = (Merge) processDefinition.getNode("xor"); merge.setSynchronized( false );