当前位置导航:炫浪网>>网络学院>>编程开发>>JAVA教程>>J2EE

JBPM源码解读之:Fork

    Fork节点在整个JBPM流程运转过程中配合Join使用提供使多于一个的节点如:TaskNode、State等并行运行的作用,很可惜我们不能利用Fork提供的现有机制实现需求中经常遇到的并发子流程的效果,当然虽然JBPM并不支持并发子流程的机制,并不代表我们不能变通的实现,我将在另一篇文章中详细说明我的并发子流程的实现方式。

    Fork类的注释中说:if this fork behaviour is not sufficient for your needs, consider writing your own custom TokenHandler.看来连JBPM开发小组也意识到Fork可能不能满足某些特殊的需求。注释中还说Fork节点有三种配置方式,我很奇怪为什么代码中只能找到两种:

    1、without configuration : in that case the fork will launch one new sub-token over each of the leaving tranisions of the fork node.

    2、a script : can be used to calculate a collection of transition names at runtime. if a script is configured, the script must have exactly one variable with 'write' access. that variable should be assigned a java.util.Collection in the script expression.

    Fork类继承自Node并实现了Parsable接口。Fork类相对简单,他的私有成员变量只有一个:

1/**
2       * a script that calculates the transitionNames at runtime.
3       */

4      Script script = null;

    Fork中的Script可以在运行时对Fork节点选择Transition,所以在实际应用中可以使用Fork+Script的方式进行多路路由选择。但是有一点要特别注意:JBBM User Guide文档中说:the script in a fork is not persisted.  script in fork might be removed in later versions of jPDL,原本以为这句话的前半句是说Script不会被持久化进数据库,实验了才知道其实Script还是被存进了数据库,这半句的意思应该是说"fork中的script不被坚持",JBPM开发小组要在新版本中放弃Script,我相信他们一定会提供更好的解决方案,让我们拭目以待。 Fork重写了Node类的read(Element forkElement, JpdlXmlReader jpdlReader)方法,其主要作用是解析JPDL中Fork节点Script的配置,并初始化自己的成员变量script.下面是execute(ExecutionContext executionContext)方法中的相关处理。

 1Token token = executionContext.getToken();
 2        // 声明离开转向的集合
 3        Collection transitionNames = null;
 4        //声明子Token容器
 5        List forkedTokens = new ArrayList();
 6
 7        // 如果没有Script,默认按照其父类Node的getLeavingTransitionsMap取得所有离开转向
 8        if (script == null{
 9            transitionNames = getLeavingTransitionsMap().keySet();
10        }
 else 
11            //如果有Script,按照规范该Script应该返回一个Collection类型的数据,作为Fork的离开转向集合
12            Map outputMap = null;
13            try {
14                //执行Script,得到返回的Collection数据
15                outputMap = script.eval(token);
16            }
 catch (Exception e) {
17                this.raiseException(e, executionContext);
18            }

19            if (outputMap.size() == 1{
20                Object result = outputMap.values().iterator().next();
21                if (result instanceof Collection) {
22                    transitionNames = (Collection) result;
23                }

24            }

25            if (transitionNames == null{
26                throw new JbpmException("script for fork '" + name + "' should produce one collection (in one writable variable): " + transitionNames);
27            }

28        }

    下面让我们来看一下,Fork产生的子Token的命名方式:

 1    /**
 2     * 功能描述:为子Token取名字<BR>
 3     * @param parent 父Token
 4     * @param transitionName 离开转向的名字
 5     */

 6    protected String getTokenName(Token parent, String transitionName) {
 7        String tokenName = null;
 8        //如果transitionName不为空
 9        if (transitionName != null{
10            //如果父Tokehn中不存在以transitionName命名的子Token
11            if (!parent.hasChild(transitionName)) {
12                //以transitionName的名字为子Token命名
13                tokenName = transitionName;
14            }
 else {
15                //如果已经存在则以transitionName+2的方式命名
16                int i = 2;
17                tokenName = transitionName + Integer.toString(i);
18                //如果加2之后依然存在,开始循环,直到父Token中不存在相同名称的子Token
19                while (parent.hasChild(tokenName)) {
20                    i++;
21                    tokenName = transitionName + Integer.toString(i);
22                }

23            }

24        }
 else {
25            //如果transitionName为空,则判但父Token中是否有子Token,如果有则以parent.getChildren().size() + 1法命名,
26            //如果没有直接命名为1
27            int size = (parent.getChildren() != null ? parent.getChildren().size() + 1 : 1);
28            tokenName = Integer.toString(size);
29        }

30        return tokenName;
31    }

    名字定好之后,Fork为每个离开转向分别创建了一个子Token,加入一开始创建的forkedTokens中。最后开始循环列表,执行Node的leave方法,并触发Node-leave事件。

iter = forkedTokens.iterator();
        
while (iter.hasNext()) {
            ForkedToken forkedToken 
= (ForkedToken) iter.next();
            Token childToken 
= forkedToken.token;
            String leavingTransitionName 
= forkedToken.leavingTransitionName;
            ExecutionContext childExecutionContext 
= new ExecutionContext(childToken);
            
if (leavingTransitionName != null{
                leave(childExecutionContext, leavingTransitionName);
            }
 else {
                leave(childExecutionContext);
            }

        }

    看了这段代码我们应该明白:Fork的Node-leave事件是会执行多遍的,具体要看产生的子Token的个数。至于ForkedToken类,其实是Fork的一个内被类,只起到一个普通Bean的作用。

    到这里,Fork类的大体内容我们已经解读完毕,但是还有一点要注意的地方:

    add some way of blocking the current token here and disable that blocking when the join reactivates this token Then an exception can be thrown by in case someone tries to signal a token that is waiting in a fork.Suspend and resume can NOT be used for this since that will also suspend any related timers, tasks and messages……So a separate kind of blocking should be created for this.

相关内容
赞助商链接