jbpm.cfg.xml">
这时候,需要jbpm.cfg.xml,这是JBPM的配置文件,
从JBPM_HOME\src\jpdl\org\jbpm目录下拷贝default.jbpm.cfg.xml文件,并重新命名为jbpm.cfg.xml
3.3如何实现业务逻辑类,请参考WorkflowManage.java
用到jbpmConfiguration对象的时候,需要注入()
当我们使用JbpmContext对象来操纵JBPM的时候,需要将JbpmContext的HibernateSession对象设置为当前的HibernateSession对象
privateJbpmContextgetJbpmContext(){
JbpmContexttJbpmContext=jbpmConfiguration.createJbpmContext();
tJbpmContext.setSession(getSession());
returntJbpmContext;
}
2.2与系统用户模型结合
在JBPM中定义了一个简单的用户模型。
但是在实际项目中,我们的用户模型可能远比这个模型复杂,或者有很大的差异。
JBPM有两种分配任务的模式,推模式和拉模式。
推模式:
系统主动分配任务给actorId。
推模式只有一个人能参与这个任务。
通过actorId指定用户。
拉模式:
actorId去申请这个任务。
拉模式的任务可以是多个人参与的,但是只能有其中的一个人能结束。
通过PooledActors指定用户群。
当同时指定了这两个属性的时候,actorId才是真正被指定的参与者。
只有这个参与者才能完成这个任务,其他的在PooledActors里面的用户,是无法得到这个任务的,所以也没有办法处理这个任务。
设置用户
推模式:
Assignable.setActorID(StringactorId);
拉模式:
Assignable.setPooledActors(String[]actorIds);
得到用户任务列表
推模式:
TaskMgmtSession.findTaskInstances(StringactorId)
拉模式:
TaskMgmtSesion.findPooledTaskInstances(StringactorId)或
TaskMgmtSession.findPooledTaskInstances(ListactorIds)
为了防止冲突,需要把执行任务的这个候选参与者升格为参与者,即把这个用户设置到actorId里面,如果需要重新把这个任务作为共享任务,只需要把actorId这个属性设置为null。
实际业务中参与者实体模式,最基础的分配模式可包括直接分配(DirectAllocation)、基于角色分配(Role-BasedAllocation)、基于规则分配(Rule-BasedAllocation)。
直接分配模式:
在工作流设计期,直接为某个活动节点指定具体的用户为参与者;
基于角色分配模式:
在工作流设计期,直接为某个活动节点指定某个角色为参与者;
基于规则的分配模式:
在设计期或运行期,根据某个具体的规则来决定某个活动节点的参与者(通过JBPM中的自己来创建任务实例来实现);
图1
图2
图1是请假流程图,图2为JBPM流程代码。
用上述两图的例子来具体说明分配任务的模式。
直接分配模式采用推模式,如图3所示任务“申请人核假”的操作者直接指定为发起申请人。
图3
角色分配模式采用拉模式,如图4所示任务“主管审核”的操作者是通过具体的类进行指定的,属于即竞争分配模式。
活动节点的参与者有多个人,多个人共同竞争此活动,某个人竟得此活动后,其他的参与者不能再竞争。
所以系统处理时,任务角色可以查询无操作者的任务实例,如果一个用户申请处理此处务时,那么任务实例的操作者置为此用户。
图4
图5
基于规则的分配模式:
设置属性create-tasks="false"。
自己通过业务规则来创建不同任务实例。
图6
图7
延期分配/动态分配(DeferredAllocation/DynamicAllocation)、基于历史分配(History-BasedAllocation)
延期/动态分配模式:
在运行期动态的分配某个参与实体给某个活动节点;(通过JBPM的定时器来实现)
基于历史分配模式:
当前活动节点的参与者为前续某个已经执行过的活动节点的参与者,此模式在电子政务的审批流程中经常会用到,例如某个工作项由秘书办理后发给领导,然后又从领导处返回个秘书。
(采用直接分配模式,在JBPM的变量进行存储前续已经某个已经执行过活动节点的参与者)
2.3流程业务应用模式
2.3.1流程挂起
在Jbpm的processInstance与taskInstance都对外提供了挂起进程实例或工作实例的的接口,同时也提供了释放的接口。
分别是:
1、processinstance.suspend();
processInstance.resume();
2、taskInstance.suspend();
taskInstance.resume();
同时processinstance与taskInstance还提供了一个标志位,isSuspended与isOpen分别表示是挂起还是开放状态,如果标志位isSuspended=true&&isOpen=fasle,那么这个进程实例或任务实例在用户界面就不会被找出来,但在管理界面还是可以看到。
如果isSuspended=false&&isOpen=true,结果就是正常情况了。
2.3.2流程加签
工作流加签即在流程节点上可选择增加已流程模板订制的可增加的流程节点。
节点进行加签后,回到节点时的任务分配与流程退签一致,故此点在流程退签中讨论。
1、正常加签,如图1所示,流程节点主管审核可以选择发起节点领导审批。
2、加签拆分:
某一流程实例在某一节点是否允许发起多个加签,如签核者A同时发起B和C的并行加签。
图8
图9
3、层次加签:
加签者可以再次发起到指定人的加签。
如图10所示,使用Fork中的Script实现多路路由选择。
流程节点task1用Fork产生节点task2或task4的同时也会产生task1,这样task1就又可以发起的流程加签。
如果不允许同一加签流程或发起的加签流程还未结束前不允许再次发起。
可以通过流程决策节点decision进行判断。
如下图所示:
图10
图11
4、加签流程同步或异步:
图9与图10的例子,就是多个加签后,主流程节点与加签流程节点可进行异步处理。
如果需要进行流程的同步处理,需要在流程决策节点decision增加判断发起的加签流程是否完成的判断,如果没有完成,则退回到主流程节点。
2.3.3流程跳签
流程跳签:
即跳过接下来的一个或连续的多个节点,直接到指定的节点执行。
通过流程转换实现。
但无法实现从流程分支外跳入流程分支内。
如果要从流程分支内跳出到流程分支外,需要取消该任务实例对应的未完成的兄弟令牌的任务实例。
2.3.4流程退签
流程退签可以认为是流程跳签的一种特例。
流程退签的任务分配采用直接分配,操作者是此流程节点已操作过的操作者。
流程退签的实现方法有两种:
1、流程转换
流程退签的任务分配,需要先查询此流程实例回退节点之前是否存在已经进行过操作的任务实例。
如果有则说明是流程退签,没有则正常实例。
流程退签时,如果已有的流程任务实例如果采用直接分配模板,不需要进行特殊处理;如果采用角色分配模板,则在分配类中校验流程变量中是否有当前节点的退签变量,如果有则查询流程中上次任务实例的操作者,并对任务进行直接分配。
如图1所示,由主管发起加签操作,领导进行了审批后,回到主管审批节点。
此时,节点任务分配就需要进行退签判断。
图12
2、修改流程实例的状态
如何开关一个任务:
任务实例TaskInstance有三个重要的属性end(任务结束时间),isOpen(任务是否开启),isSignalling(任务是否可流转)。
控制这3个属性就可以控制任务实例.如果要开启一个任务,就让end=null,isOpen=true,isSignalling=true。
如果要关闭一个任务就是end=newDate(),isOpen=false,isSignalling=false。
另外还有一个属性isCancelled,如果你关闭的任务作废了,不需要作为历史任务保留,就让isCancelled=true;
流程回退大致分为三种:
1、简单流程回退(无流程分支)
先做个假设有流程a—>b—>c—>d—>e当前流程已经跑到d,如果要回到b重新跑怎么做那.步骤是:
一、关闭任务b后创建的并且没有关闭的任务。
(与任务b同在一个节点上没有完成的任务除外)
二、开启任务b。
三、将令牌指向任务b所在的节点。
图13
经过测试发现对分支的回退会出现两个问题:
1)当流程执行到分支内节点时(b,c,d),如果要退回到a任务,并把分支内的任务关闭掉,当再次进入分支节点后,流程就只能运行到join节点,不能到达e。
2)当流程执行到F时,如果要将流程回退到一个分支内的任务上(b,c,d),流程就也只能运行到join节点,不能再到达e。
当流程执行到fork时,会根据分支的个数创建几个子令牌(本例是创建2个),子令牌会各自流转,直到到达join节点。
join节点会检查子令牌是否都到达join节点,如果是就流转主令牌到点一个节点,如果不是就继续等待其他子令牌。
完成这个逻辑主演是依靠Token的isAbleToReactivateParent属性来完成这个功能。
isAbleToReactivateParent这个属性的含义可以理解成,当前持有这个属性的子令牌是否可以激活(恢复)其父令牌。
所以这个属性只有分支的子令牌才具有。
isAbleToReactivateParent在fork创建分支令牌为true,当子令牌到达join是会被设为false。
具体说一下过程(详细的可以看看join的execute方法)每一个子令牌到达join节点,都会触发join的execute方法。
该方法的第一步就是检测子令牌的isAbleToReactivateParent是否为true,不是ture你这个子令牌就什么都别想干了。
当为true时子令牌才有权尝试(仅仅是尝试)驱动一下父令牌。
子令牌过这第一关,isAbleToReactivateParent就会被赋值false。
(每个子令牌只有一次机会去见他的父令牌)下面还要过好多关。
所有的关卡都通过,来到它的父令牌面前。
这时这个父令牌会找到它的全部子令牌,检查子令牌的isAbleToReactivateParent属性是否都为fasle。
如果还有为true的子令牌,说明还有子令牌没有完成。
父令牌会等待。
要是全为fasle。
那就是全部子令牌都完成,父令牌就可以执行到下一个节点。
2、回退到流程分支中
关闭里分支内的任务,但没有让子令牌放弃见父令牌。
所以当你再次由a进入分支时,又创建了两个子令牌.这个老爸现在有4个子令牌了。
其中先前的两个子令牌,却永远都见不到父令牌,父令牌会在join会一直等。
所以要从分支往分支外跳的时候,除了杀掉任务,也别忘了让子令牌放弃见父令牌的想法。
isAbleToReactivateParent=false。
3、从流程分支中回退
从e跳到分支里。
历史任务开启,任务上的子令牌不想见父令牌(此时isAbleToReactivateParent=false)。
这个子令牌到了join,该方法的第一步就是检测子令牌的isAbleToReactivateParent是否为true,不是ture你这个子令牌就什么都别想干了。
所以,开启分支里的任务时,不要忘记将该任务的令牌属性isAbleToReactivateParent=true。
这样他才能见到父令牌让他走人。
2.5子流程
2.5.1子流程部署与删除
1、jbpm的流程deploy方法之一是:
JbpmConfiguration.getInstance().createJbpmContext()
.deployProcessDefinition(processDefinition);
如果流程中涉及子流程的话,deploy需要注意先后顺序,否则父流程会找不到子流程.
发布流程遵循的顺序是先子后父。
2、流程实例结束后删除流程实例的方法之一是:
JbpmConfiguration.getInstance().createJbpmContext().getGraphSession()
.deleteProcessInstance(processInstance.getId());
如果流程涉及子流程的话,删除流程实例同样需要注意先后顺序。
如果先删除子流程实例:
子流程已结束,会将Token表中父令牌的SubProcessInstance字段清空,此时可以删除子流程;如果子流程未结束,Token表中父令牌的SubProcessInstance(外键关联子流程的流程ID)字段未清空,会报约束错误。
如果要删除父流程实例直接删除,JBPM会按照先子后父的关系将此父流程的子孙流程全部删除。
JBPM的删除流程的方法存在一个BUG。
例如图17所示:
如果父流程实例中存在多个令牌,且存在子流程实例。
如果子流程实例未结束时,无法删除父流程实例。
因为JBPM删除父流程实例同时也会删除子流程实例。
如图14所示,JBPM是用令牌来来查询发起的子流程实例会进行删除。
但实际任何查询令牌都可查询到子流程。
查询条件如图15所示,在首个令牌中已经将子流程删除,在后续令牌进行操作时则会报约束错误。
如果发生此类情况,先判断删除的流程实例是否有父流程实例。
没有则查询流程实例中的令牌,将Token表中令牌的SubProcessInstance字段清空后,再调用上述流程实例的删除方法即可。
图14
图15
图16
2.5.2发起子流程
1、子流程就是流程模板是的一个流程节点,发起子流程与发起其他流程节点一致。
2、流程节点可任意发起子流程,即将子流程可由当前流程操作者自行判断是否发起。
可以通过类似于加签的方法。
如图17所示:
task1拥有两个流向选择,一是处理当前节点任务,二是发起子流程协助处理。
task1的操作者,如果选择处理当前节点任务后流程进入task4进行;如果是选择发起子流程协助处理,流程发起子流程与task3,模板设置task3与task1的操作者一致,但只有唯一的流向即处理当前节点任务。
图17
3、子流程的同步与异步
如图17所示,子流程实例与父流程实例是同步的,父流程实例必须的子流程实例完成后才能继续流程,父流程实例与子流程实例是一个整体。
如图18所示,子流程实例与父流程实例是异步的,父流程实例只是负责发起子流程实例,子流程的完成情况对父流程实例没有影响。
图18
2.6JBPM定时器
2.6.1定时器的配置
1、jbpm.cfg.xml中配置,关于定时器运行时间的配置如图19所示:
图19
2、jbpm.cfg.xml中配置,关于业务日历的配置如图20所示:
图20
jbpm.business.calendar.properties是关于业务日历的具体配置如图21所示:
图20
对于上面定义通俗的理解就是它归定了从星期一到星期五的9:
00-12:
00&12:
30-17:
00这段时间为上班时间也就是业务时间,星期六和星期日没有定义也就是放假的时间,再往下就是定义了12个节假日,最后是一些工作时长的统计如一天7.5小时,一个星期37.5小时等。
3、web.xml中配置,如图21所示
图21
4、配置时需要注意,JBPM的定时器本身有一个BUG,无法让JobExecutorServlet使用自己的jbpm.cfg.xml,因为Java类JbpmConfiguration源码(如图22所示)实现就是使用“org/jbpm/default.jbpm.cfg.xml”。
在default.jbpm.cfg.xml使用中默认hibernate配置文件如下所示:
。
图22
2.6.2定时器的运用
定时器timer可以被用于decision、fork、join、node、process-state、state、super-state、task-node,可以设置开始时间duedate和频率repeat,定时器动作可以是所支持的任何动作元素,如action或script。
timer还有一个很重要的属性cancel-event,这个是timer和task结合时使用的,任务定时器的cancel-event可以被定制。
默认情况下,当任务被结束时(=完成)任务上的定时器将被取消,这是通过在定时器上使用cancel-event属性,流程开发者可以定制诸如task-assign或task-start。
cancel-event支持多个事件,通过