EOS工作流引擎原理docWord文档下载推荐.docx
《EOS工作流引擎原理docWord文档下载推荐.docx》由会员分享,可在线阅读,更多相关《EOS工作流引擎原理docWord文档下载推荐.docx(28页珍藏版)》请在冰豆网上搜索。
标准
FINISH_PROCESS:
结束流程
1009
PRESTART_ACTIVITY:
重起流程
2000
START_ACTIVITY:
启动活动实例
2001
RESTART_ACTIVITY:
重起活动实例
2002
CHANGE_ACTIVITY_STATE:
改变活动实例状态
2003
FINISH_ACTIVITY:
结束活动实例
2004
TERMINATE_ACTIVITY
:
终止活动实例
2005
ABORT_ACTIVITY:
2006
SUSPEND_ACTIVITY
挂起活动实例
2007
RESUME_ACTIVITY
启动挂起的活动实例
2008
SUSPEND_WORKITEM
挂起工作项
3001
RESUME_WORKITEM
启动挂起工作项
3002
CHANGE_WORKITEM_STATE:
改变工作项状态
3003
FINISH_WORKITEM
结束工作项
3004
TERMINATE_WORKTIEM:
终止工作项
3005
ABORT_WORKTIEM
3006
EXCEPTION_PROC_TIMEOUT:
流程超时事件
4002
EXCEPTION_PROC_REMIND:
流程临近超时事件
4003
EXCEPTION_ACT_TIMEOUT:
活动超时事件
4004
EXCEPTION_ACT_REMIND:
活动临近超时事件
4005
APPLICATION_RETURN:
5001
以上的每个事件都是原子的不可分割的。
其中一系列事件的集合通过
EOS引擎事件调
度机制实现我们平时在工作中经常遇到的如启动流程,结束工作项等等。
(在事件类型类中
EOS定义了29种事件,但在事件工厂类中EOS定义了26种类型。
)
2.1.2.EOS工作流事件调度机制
EOS事件的调度服务是在工作流引擎初始化时通过服务工厂类加载到内存中
(ServiceFactory.initEventService())。
用户可以通过服务工厂类(ServiceFactory)取得
JVM的唯一事件服务实例进行事务调度。
所有的事件程序入口都是事件类(EventService),
这个类其实是个接口,其有两个实现类,一个是单线程的实现类SingleThreadEventService
(在实现代码中其实不是单线程,而是单例的对象),一个是多线程的实现类
MulThreadThreadSvc,(其实现方式不在这里详细说明,多线程的类后面又跟了一大堆的
线程池实现代码),在事件服务类中有一个属性类是WFEventDisposer,这个类包含了事
件的注册,事件的发布,事件的注册是一个静态代码块实现的。
注册了上节描述的29种事
件,其实就是把相应的事件代码注册到相应的处理类,事件处理类共用5个
(ProcessScheduler,ActivityExecuter,ExceptionHandler,WorkItemHandler,
ApplicationHandler),对应事件代码的前5个数字;
共有事件的发布有两种,一种是正常
发布,一种是无异常的发布(即在具体执行事件时关闭了异常处理)。
所谓的事件发布是给
事件服务类传递一个事件对象(WFEvent类),这个事件对象包含了事件类型,线程名,事
件ID,流程定义ID,活动定义ID,活动实例ID,和工作项ID等等。
以上简要的描述了事件模型,下面来拿我们平时用的最多的一个构件:
结束工作项来详
细跟踪它的事件处理。
结束工作项可能是最具有代表性的一个流程动作,因为在做这个时间
后遍历了整个流程实例的流程:
1,
用户通过引擎的API调用WorkItemManager
类的finishWorkItem
方法,该
方法通过服务工厂取得持久层的数据访问服务,并根据
workitemID
取得
WFWorkItem对象。
做相关的判断后通过事件工厂类的
createFinishWorkItemEvent方法创建个事件代码为3004的事件对象
(WFEvent)。
然后通过服务工厂类取得事件服务类把该事件对象发布给事件
处理服务。
从此刻就开始了EOS事件调度服务的运转。
2,事件服务类(拿单线程事件服务类做例子)拿到这个事件类后把该事件通过
WFEventDisposer发布该事件。
具体的发布过程很简单,即判断该事件类型
是否已注册,如果已经注册则取到改事件代码的注册类。
该代码是
3004,则
应取WorkItemHandler。
然后调用WorkItemHandler
的invoke()方法,
3,
WorkItemHandler类invoke()中写到:
if(event.getType()
==30004)
{finishWorkItem(event);
}则找到该方法,该方法开始做了相关的判断后做相关
标志位的修改:
置当前工作项的状态为
12,然后判断当前活动是否结束。
(大
概的算法是取得已经结束的工作项和该活动总的工作项,取得活动定义的多工
作项是否启动。
如果是多工作项则判断完成个数策略:
是按百分比还是按操作
员个数等等,做一系列的判断后得到应该结束的工作项,如果小于等于已经结
束的工作项则该活动结束,没有启动多工作项则相应的处理要简单点)
,如果该
活动已完成,则调用事件服务的结束活动实例事件createFinishActivityEvent
;
如果没有结束则判断工作项启动的策略是“at_the_same_time
”还是
“one_by_one”,如果是“one_by_one”则找本活动实例下的工作项状态为
1的工作并启动它。
4,
结束活动实例是调用事件工厂的方法createFinishActivityEvent
,新建一个事
件代码为2004的事件。
用createFinishWorkItemEvent
的方法发布该事件。
到ActivityExecuter
类中找到finishActivity,该方法修改活动实例状态为
7,
填写活动结束时间。
如果该活动注册了时限则取消活动时限的注册。
如果该活
动实例定义了结束活动的触发动作则触发该动作(通过WFAppCaller调用)。
最后由事件工厂产生一个事件代码为1002的
createScheduleNextActivityEvent事件。
由事件服务发布事件。
5,
启动下个活动实例的事件动作是事件工厂调用
scheduleNextActivity
方法通过流程定义找到下个环节的转移条件,并根据转移条件和分支模式(全
部分支:
AND;
多路分支:
XOR;
单一分支:
OR)生成一个环节定义列表。
引擎首先把未启动的活动实例和挂起的活动实例找到,如果没有则生成一个活
动实例。
然后生成一个转移对象(WFTransition
),最后把待启动的活动实例
对象放到一个列表中。
根据该列表中的活动定义的启动策略(直接启动,待激
活,由规则逻辑指定)来启动活动实例;
如果是直接启动活动实例则由事件工
厂新建一个事件代码为
2001的事件startActivity
,如果待激活策略则由事件
工厂产生事件代码为
的事件preStartActivity
。
同样如果在流程定义中定
义了创建活动实例触发的事件则触发该事件,
方法做了
很多业务处理的事情,所以比较复杂。
6,
事件服务调用startActivity
方法,修改当前活动状态位为
2,并向时限管理服
务注册时限,然后通过活动执行类的帮助类分派工作项,分派工作项的过程是
判断是否是多工作项,如果不是则按参与人员分派,如果是则判断多工作项的
启动策略,启动工作项业务处理比较复杂,并没有相应的事件代码对应,在这
里不详细介绍。
以上的六个步骤完成了我们平时最常用的完成工作项的方法。
综上所述应该能够对
EOS工作流的事件调度机制有个清楚的认识,比如结束工作项的事件调度有
3004->
2004->
1002->
2001这几种事件的触发。
同样还有我们平时比较常用的启动流程
实例方法首先是创建一个流程实例,然后开始事件调度:
10001->
10002->
2001,最后是分
派工作项。
OSWorkflow里也有自己的调度机制,但在业务上要比EOS简单的多,准确的讲
OSWorkflow只有两个概念:
steps(步骤)和actions(动作)。
一个简单的调度过程
它可能从一个步骤流转到另外一个步骤(或者有时候还是停留在一样的步骤)。
它的调度其
实就是一个类:
AbstractWorkflow,这个类里面有两个方法:
doAction和
transitionWorkflow基本实现了所有的调度(其实也不能算是调度,只能算是状态的迁移)。
OSWorkflow最大的优点是在执行调度过程中执行的一系列的Function(在SOA里叫服务
模型,在EOS里叫展现逻辑),它在执行客户端的服务时的机制时还是比较复杂的,如果感
兴趣在工作之余可以看一下。
还有个最近比较流行的开源的引擎,JBpm,我没看过这个,好象现在又整合到JBOSS
下去了,好象很复杂。
2.2.
时限管理服务
2.2.1.
时限的分类
时限名称
时限代码
活动提醒时限
ACT_PRE_REMIND
活动执行时限
ACT_OVERTIME_REMIND
流程提醒时限
PROCESS_PRE_REMIND
流程执行时限
PROC_OVERTIME_REMIND
时限类型有两种:
一种是一次触发完成时限,还有一种是循环触发(譬如隔多长时间进
行一次提醒)并可设置触发的次数。
2.2.2.时限计算器
在工作流引擎启动时就启动一个JVM唯一实例的时限计算器,该类可以使用引擎默认
的。
也可以自己去实现一个自定义的计算方法,在配置文件中注册要重写的类名即可。
引擎
的时限计算器只有两个方法,一个是计算结束时间,还有一个是计算提醒时间。
其实是个静
态类。
2.2.3.时限服务的启动
在引擎中的时限服务有两个,一个是引擎启动的时候启动的时限服务,该服务初始化了
时限对象列表;
一个是在引擎启动后启动的服务,该服务是对列表中的时限对象进行轮询,
触发超时的时限对象对应的触发事件,并移除该对象时限。
时限的线程处理用了大量的过程
化程序的结构,在这里还是比较绕人的。
2.2.4.时限的注册和移除
在流程引擎中的时限服务其实就是在维护一个时限对象的列表,该列表记载了处于运行
状态的活动的时限对象。
在启动一个环节或启动一个流程时判断该活动或该流程的时限,如果该活动或该流程定
义了时限则向时限服务注册该时限;
在TimerManager类中的注册方法的实现是调用时限服
务类的registeTimer方法,往时限对象列表(Vector)追加一条记录。
在结束活动事件时或结束流程时如果是超时的操作则时限对象列表中没有该活动的时
限对象,因为该对象已被时限触发器触发并移除。
如果没有超时则要把这个向量列表中的那
条时限对象给去掉。
在TimerManager类中的注册移除方法的实现是调用时限服务类的
unregisteTimer方法,往时限对象列表(Vector)移除一条记录。
2.2.5.时限事件的触发
时限的触发完全是后台的线程做的事情。
该线程对时限服务所维护的时限对象列表进行
轮询,如果发现有超时的对象则触发已定义好的动作,该动作就是我们平时在studio中设
的如果超时则干什么事的触发动作。
对时限的处理是通过java.util.Timer这个类来实现的。
是通过新建一个时限任务
(MyTimerTask)让Timer来执行。
并向该类传递一个OnceTimerHandler对象实例。
该
对象有个方法timerTrigged就是到了预定时限时触发的方法。
该方法首先调用
timerHandler类的handlerTimer方法,即如果有触发事件的话就调用上节讨论的事件代
码以4开头的事件。
然后修改时限类的当前状态为3,完成一次时限触发动作。
3.流程同步服务
流程同步服务是引擎自定义的一个对流程实例和流程定义的锁的定义,譬如在做指定下
一个环节的参与人(WFAppointParticipantManager中的appointNextActParticipant
方法)时先把当前的流程实例给琐住
(ServiceFactory.getLockService().lockProcInstance)。
然后在方法结束后再把流程实例
的锁给释放(ServiceFactory.getLockService().releaseProcInstance)。
在同步服务中定
义了两种类型的锁,一种是流程定义锁,一种是流程实例琐(两个list),在加琐时检查改
ID(流程实例ID或流程定义ID)是否已经在琐列表中,如果在则加琐。
在加琐与解锁之间
是通过一个线程来操作锁列表(waitingList)实现的。
其实现方法大概是在加锁的时候向
waitingList添加一个锁对象,然后把线程wait();
在解锁的时候向waitingList减去一个锁
对象,并把线程notify()。
流程同步服务的实现方式还是比较复杂的。
尽管只用了七八个类。
4.组织机构管理
EOS提供了一套自己的组织机构模型,我们在安徽服务保障三期中引用了该模型。
该
组织机构模型的服务会话面的实现是在配置文件中配置的,然后引擎采用java的反射机制
加载配置类(在引擎中有个叫做“服务定位器”来实现,该服务定位器和我们平时用的一样,
只是它是从文件中读取服务定义,隐藏了具体寻址细节)。
如果不用EOS提供的组织机构模
型可以实现WFOMService接口,并实现里面的方法。
估计EOS的原意是提供组织机构模
型和引擎服务的松偶合,但在其引擎的实现上好象并没有做到。
OMServiceImpl类是引擎默认加载的组织机构模型会话面类。
该类定义了人员,角色,
机构等等,OMServiceImpl2类包含岗位的组织机构模型(目前的引擎的还不支持,没有
搞清楚EOS没有把岗位纳入组织机构模型中),但EOS提供的开源的组织机构模型中并不
支持。
5.审计服务
该服务记载了所有流程模板的变更和对流程实例的操作历史。
引擎共定义了39种审计
类型,包括模板变更,启动流程,完成工作项等等。
由于审计的类型代码和引擎的事件代码,
所以引擎在中间做了一层映射,把事件代码和审计代码一一对应(审计代码多于事件代码)。
在审计过程中其实是往审计表中加一条历史记录。
6.日志服务
引擎的LOG服务很简单,和我们平时用的LOG差不多。
在打日志的时候传入JVM唯
一实例的日志上下文WFLogContext,在该类中定义了一条日志所需要的日志头,比如等
级(@level),操作员(@operator),sql(@sql),时间戳(@timestamp)等等。
然后
在具体打某一条日志的时候把日志头和日志内容拼装起来形成一条日志。
引擎的日志实现了日志的读写,引用了log4j的RollingFileAppender和
PatternLayout。
并提供了类似AOP的方法前后拦截打日志的服务(不知道方法前后的拦
截日志是在代码中人工加上的还是由AOP代理自动加载的,因为采用了AOP时在编译的
时候就把代码插入到要拦截的切入点中去)。
7.持久层服务
引擎的持久层和studio里的持久层是采用一样的设计。
大概是把数据库的字段和持久
层的XML定义一一对应,没有采用像hibernate或者EJB的CMP或者BMP那样很复杂
的OR_mapping。
由JDBC驱动持久层在系统中显的很高效,但采用数据库和持久层的XML
描述文件一一对应所以没有把关系数据的对象化做的很别致。
(没有深入的看过代码,可能
我理解的不对)。
8.引擎的缓存
工作流引擎的缓存是通过一个HashMap来维护的。
用有以下几类缓存:
流程实例的
缓存;
活动实例的缓存;
工作项的缓存;
相关数据的缓存;
相关数据Dom的缓存;
流程属
性数据的缓存,以上几类的实例缓存个数是通过在配置文件中配置的,还有一类流程模板的
缓存是在引擎启动的时候就解析流程模板的XML文的定义(因为流程定义是通过XML文
来存储在数据库中),解析成流程定义对象并加载到内存中。
8.1.缓存的配置
工作流缓存的配置是在wfconfig.xml文件中配置的。
共有以下几种配置:
<
!
--对数据库的访问是否使用Cache-->
configValuekey="
enabled"
>
false<
/configValue>
--流程实例Cache个数-->
processCacheCount"
1000<
--活动实例Cache个数-->
activityInstCacheCount"
5000<
--工作项Cache个数-->
workItemCacheCount"
10000<
configValue
key="
workItemViewCacheCount"
--相关数据Cache个数-->
relatDataCacheCount"
--相关数据DomCache个数-->
relatDataDomCacheCount"
--流程属性数据Cache个数-->
procInstAttrDomCacheCount"
8.2.缓存的实现
在引擎启动的时候取得工作流配置信息,如果允许使用缓存则初始化上面所述的六类缓
存。
在初始化的时候引擎默认缓存的存活时间为0x1499700L。
缓存的大小为配置文件所配。
这样则生成在JVM里的六个Cache类的实例。
每个Cache都有一个HashMap的属性,
这里面存储了要缓存的对象。
在CacheFactory类中又有一个HashMap对象属性,这个对
象存储的是Cache对象的集合。
就是上面所述的六个Cache类实例的集合。
普元的开发人
员把该对象起名为Caches。
那么在取某个活动实例时就先中缓存中读取,如果找到则直接
返回,如果没有则从数据库中加载。
引擎的缓存并不是直接把从数据库中取得的对象put到map中,而是做了一层优化,
把从数据库中找到的对象封装成CacheObject对象,该对象有个链表(Linke