(转)JBPM案例详解(四)

一、该版本中应用了在start-state节点中加入task元素,之前的项目中则没有用到,
     所以之前的项目在创建流程实例是都会调用processInstance.signal()方法来立刻从start-state节点流转到下一个节点,
     然后才开始进入正常,本来我们的效果应该是,当申请人提交请假表单之后,就等同于提交了,
     不应该在申请的代办列表中再进行一次提交,该项目解决了该问题。
    
   **注意:在start-state节点中使用task节点的化和在<task-node>节点中的使用方法有点区别。
  
     1.start-state节点中的task不能指定assignment的actor-id,
       因为TaskMgmtInstance中的createStartTaskInstance()方法中会设置actor-id的值,
       所以即便是在task节点中设置了actor-id的值也会被覆盖
      
     2.当创建流程实例的时候,token指向start-state节点,但是不会直接进入task任务节点,这和在task-node中的task不同。
       所以我们要手动通过代码进入task节点,即启动任务,
       此时要调用TaskMgmtInstance.createStartTaskInstance()方法来手动启动task任务。
   
     3.启动任务之前我们要注意,因为actor-id 还没有哦赋值,
       虽然TaskMgmtInstance.createStartTaskInstance()方法内部给actor-id赋值了,
       但是它的赋值方式是taskInstance.setActorId(SecurityHelper.getAuthenticatedActorId());
       很明显SecurityHelper.getAuthenticatedActorId()是用来获取actor-id的值,所以我们要向这里存放actor-id的值,
       也就是说在调用TaskMgmtInstance.createStartTaskInstance()方法之前,
       要调用JbpmContext.setActorId()方法来设置actor-id的值,以便顺利将start-state节点中的task任务分配给参与者,
       因为是开始节点,所以该任务的参与者肯定是登陆者,也就是填写请假单的人。
      
    **这样就可以达到预计需求的效果了!
   
    **注意:上面提过了start-state中的task不能指定actor-id值,但是它可以指定swimlane,
        可以借助swimlane来为任务分配参与者(实现见下一个项目)

 

 **注意:上面提过了start-state中的task不能指定actor-id值,但是它可以指定swimlane,
        可以借助swimlane来为任务分配参与者(本项目就是这样实现)
       
       
    **细节提示:这里使用swimlane的原理和不是用actor-id的原理差不多,因为虽然我们可以指定swimlane,
        但是并没有为swimlane指定assignment进而制定actor-id,因为使用swimlane的时候,
        JBPM内部也会通过创建任务的时候自动的为actor-id赋值,仍然和上一个项目中一样,
        也是先获得actor-id的值,然后付给start-state的task任务节点,
        这样的目的就是能很好的和登陆人员,即申请人进行绑定。

 

一、该版本实现了会签功能!!

 二、在一个节点<task-node>中加入多个任务<task>,每个任务指定一个参与者,这样当流程流转到该节点后,
     只有所有的任务都审批通过后才能流向下一个节点<task-node>,这样也可以实现会签的功能。
    
      形式:
       <task-node name="nodeName">
         <task name="taskName">
          <assignment actor-id="1"/>
         </task>
         <task name="taskName">
          <assignment actor-id="2"/>
         </task>
         <task name="taskName">
          <assignment actor-id="3"/>
         </task>
         ..........
         <transition name="transitionName" to="flowToNode"></transition>
      </task-node>
     
   解释:就是当节点流转到该节点后,首先由1参与者审批,然后由2参与者审批,
     然后由3参与着审批,知道所有任务结束,那么才会向下一个节点<task-node>流转
    
   限制:这样有很大的限制,这样的应用的前提是事先要知道到底有多少个参与者要参与该节点的任务,
     只有知道参与者都包括谁后才能应用这种方法,但是实际的应用中,我们知道很多公司的人员都不是一沉不变的,
     一般都不能确定参与者的个数,比如可能经常参与的这个人请假或怎么了,如果用这种方案就要该程序了。
     (这里同样也有下边那种方案的缺点)
    
   改善方法:一般都是动态获得参与者的个数的,所以可以用下边的把一个任务分配给多个参与者。

 三、这种方案使用了把把一个任务分配给多个参与者,这样可以实现一种类似会签的效果,
     也就是节点流转到了下一个节点,流转到的这个节点(task-node)中有一个任务,
     但是这个任务有点儿特别,我们不让JBPM给我们自动创建这个任务的实例,
     也就是说回想我们在start-state节点中的task任务中的处理,这个任务的处理是我们手动创建的任务实例,
     进而对其启动、结束。这里我们仍然采用这让的方案,然后创建的时候我们动态为该任务分配多个不同的参与者,
     此时任务表中会同时产生分配的参与者个数个记录,但是他们的end字段都是空,
     所以这样的意思就是等待这些参与者分别进行审批后才能流向该任务所在节点的下一个节点。
     这样就实现了会签的功能即等待所有参与者审批都结束后才会流向下一个节点。
    
    
    缺点:这样将一个任务分配给多个不同的参与者,虽然间接实现了会签的功能,
         但是有一个不好的地方就是这些参与者是有顺序的,比如:分配的时候是共有3个参与者,
         分配时是0,1,2这三个ID值都作为该任务的actor-id,那么就是说要等到这三个人都审批后才能想下一个节点流转,
         但是这三个参与者的审批过程必须是0先审批,然后1,最后2审批,因为分配给这三个参与者后在数据库中保存的记录是从上到下的,
         而顺据就是分配的0,1,2这样的顺序,之后先让0审批后,那条对应的记录的end字段才会有值,
         只有此时1参与者才会在代办列表中看到有审批的任务。
     
      **注意:经过亲身试验,该缺点不存在,没有顺序的限制,也就是所有的参与者不论谁的编号排在前排在后,
        这些参与者登陆后都可以看到任务列表,并且都可以审核,而且不论谁先审核谁后审核,
        都将是所有的审核结束后才会流转到下一个节点。
        这正是会签的需求所在。
        
    实现:
      放弃让JBPM自动创建任务实例对象
       - 就像使用创建start-state节点中的任务实例一样;
       - 而不是像之前那样流转到节点后让JBPM自动创建任务,如果这样我们就只负责任务的结束了,如果这样不能实现给任务分配多个参与者
          <task-node name="nodeName" create-tasks="false">
            <event type="node-enter">
             <action class="className" />
            </event>
         <task name="taskName">
         </task>
         <transition name="transitionName" to="flowToName"></transition>
      </task-node>
   
     **说明:
      * create-tasks="false"表示当流程进入该节点后JBPM不会自动给我们创建任务实例对象(TaskInstance对象)
      * <event>是一个事件,type="node-enter"表示流程进入节点后触发该事件,会调用<action>指定的类中的方法
      * 我们让<action>指定的类必须实现ActionHandler类,在该类中我们要为<task>分配一个或多个的任务参与者
      * 就是因为<action>中会分配任务的参与者,所以<task>中不用显示的指定分配参与者的标签元素
   
    
     **细节:<action>指定的类的实现:
       public class UserActionHandler implements ActionHandler{
        public void execute(ExecutionContext executionContext) throws Exception{
         //得到任务管理对象
         TaskMgmtInstance tmi = executionContext.getTaskMgmtInstance();
         //得到当前的节点对象<task-node>
         TaskNode taskNode = (TaskNode)executionContext.getNode();
         //得到我们要把任务分配个的参与者,可以是多个,如果是多个可以用逗号分割
         String userIds = (String)executionContext.getContextInstance().getVariable("userId");
         //取出每个参与者
         if(userIds != null && !"".equals(userIds)){
          String[] ids = userIds.split(",");
          //循环,这样的效果将根据一个任务声明,创建多个任务实例对象,但每个任务对象的参与者不同
          for(int i = 0; i < ids.length; i++){
           //创建任务对象,因为JBPM已经不给我们自动创建了
           TaskInstance ti = tmi.createTaskInstance(
                //要创建的任务的名称
                taskNode.getTask("taskName"),
                //当前的token指向
                executionContext.getToken()
                 );
           //为任务指派参与者
           ti.setActorId(ids[i]);
          }
         }
         //如果没有指定任何参与者,代表也没有创建任何任务对象那么此时JBPM会认为<task-node>中没有任务,
         //那么将会直接流转到下一个节点!
         //所以这样的if语句加上之后最后再加一个else的处理情况,这样比较完整
        }
       }
      
       注意:在ActionHandler的实现类中只能调用executionContext.leaveNode()方法
         离开当前<action>所在节点,而不能调用signal()方法离开节点
     **一、<event>事件中通过<script>脚本来处理一些任务的准备等业务逻辑!
       <script>中是用的是BeanShell脚本,靠bsh.jar包的支持,可以看看BeanShell的文档,
       可以到JBPM的文档中看一下JBPM为BeanShell脚本提供了什么扩展
      
       我们可以在脚本中使用流程变量,这样我们再项目完成后,如果需要添加新流程,
       那么新流程中就可以通过脚本来改变一些程序内部的是,这样就不需要添加流程后需要修改源代码,
       我们把代码加入到这里边来用脚本实现就行了。(可参看userguide文档)
   
   四、有时我们会碰到这样的需求:就是像上面一样把同一个任务分配给多个参与者,
     但是等到审批的时候呢,我不想用会签的机制,而是所有的参与者,只要有一个审批提交了,
     那么整个任务就等于结束了,就立刻让流程流向下一个节点<task-node>,而不是像上面那样,
     必须等待所有的参与者都审批提交后才算任务结束,才能让流程向下一个节点流转。
    
    解决方法:JBPM为我们提供了一个机制手段,
      就是可以通过设置节点(<task-node>)元素signal属性,来完成这种需求。
     
     例:<task-node name="taskNodeName" signal="{unsynchronized|never|first|first-wait|last|last-wait}">
      ....
        </task-node>
       
        **说明:signal的默认值是last。signal指定了任务的完成对流程执行继续的影响。
       
        **为了帮助理解task-node节点的signal属性,这里举例如下:
    <task-node name='a'>
     <task name='laundry' />
     <task name='dishes' />
     <task name='change nappy' />
     <transition to='b' />
    </task-node>
   a)这里没有定义signal属性的值,这就表明当节点中的三个任务都完成后,流程才进入后面的节点
  
   b)当<task-node name='a' signal='unsynchronized'>表明token不会在本节点(不是任务,注意区别)停留,而是直接到后面的节点
  
     c)当<task-node name='a' signal='never'>表明三个任务都完成后,token仍然不会指向后面的节点;
       需要自己手动调用processInstance.signal()才会驱动流程到下面的节点
      
   d)当<task-node name='a' signal='first'>表明只要有一个任务完成后,token就指向后面的节点(这就是上面的需求)
    **注意:这个方案确实能实现需求,但是会有个不好的地方,
        虽然参与者中有一个审批通过后就会使流程向下一个节点流转,
        但是其他没审核的参与者那里仍然可以看到任务列表,
        因为数据库表中这些没有审核提交的参与者的任务实例对象记录的end字段还没有值,
        只不过此时他们是否再提交审批已经没有意义了,因为流程已经流向了下一个节点,
        如果他们继续审批提交,造成的影响就是在数据库表中的end字段中填入了数据,对流程将不会起到什么作用。
  
   e)当<task-node name='a' signal='first-wait'>表明当第一个任务实例完成时继续执行;
     当在a节点入口处没有任务创建时,token在a任务节点处等待,直到任务被创建或完成。
    
   f)当<task-node name='a' signal='last'>时,这是默认值,和不设置signal属性的情况相同。
  
   g)当<task-node name='a' signal='last-wait'>时,当最后一个任务实例完成时候继续执行下去。
     当a这个任务节点没有任务被建立时,任务节点等待直到任务被建立。

 

posted on 2011-07-01 10:16  唐朝  阅读(656)  评论(0编辑  收藏  举报