Osworkflow 简介
概念
Ø 在商用和开源世界里, OSWorkflow 都不同于这些已有的工作流系统。最大不同在于 OSWorkflow 有着非常优秀的灵活性。在开始接触 OSWorkflow 时可能较难掌握(有人说不适合工作流新手入门),比如, OSWorkflow 不要求图形化工具来开发工作流,而推荐手工编写 xml 格式的工作流程描述符。它能为应用程序开发者提供集成,也能与现有的代码 和数据库进行集成。这一切似乎给正在寻找快速 “ 即插即用 ” 工作流解决方案的人制造了麻烦,但研究发现,那些 “ 即插即用 ” 方案也不能在一个成熟的应用程序中 提供足够的灵活性来实现所有需求。
比较
Ø OSWorkflow 给你绝对的灵活性。 OSWorkflow 被认为是一种 “ 低级别 ” 工作流实现。与其他工作流系统能用图标表现 “Loops( 回路 )” 和 “Conditions( 条件 )” 相 比, OSWorkflow 只是手工 “ 编码 (Coded)” 来实现的。但这并不能说实际的代码是需要完全手工编码的,脚本语言能胜任这种情形。 OSWorkflow 不希望一个非技术用户修改工作流程,虽然一些其他工作流系统提供了简单的 GUI 用于工作流编辑,但像这样改变工作流,通常会破 坏这些应用。所以,进行工作流调整的最佳人选是开发人员,他们知道该怎么改变。不过,在最新的版本中, OSWorkflow 也提供了 GUI 设计器来 协助工作流的编辑。 OSWorkflow 基于有限状态机概念。每个 state 由 step ID 和 status 联合表现(可 简单理解为 step 及其 status 表示有限状态机的 state )。一个 state 到另一 state 的 transition 依赖于 action 的发生,在工作流生命期内有至少一个或多个活动的 state 。这些简单概念展现了 OSWorkflow 引擎的核心思想,并允许一个 简单 XML 文件解释工作流业务流程。
Osworkflow 流程定义
基本概念
步骤
Ø 一个 Step 描述的是工作流所处的位置。可能从一个 Step Transtion (流转)到另外一个 Step ,或者也可以在同一个 Step 内流转(因为 Step 可以通 Status 来细分,形成多个 State )。一个流程里面可以多个 Step 。
状态
Ø
工作流
Status
是用来描述工作流程中具体
Step
(步骤)状态的字符串。
OSWorkflow
的有
Underway
(进行中)、
Queued
(等候处理中)、
Finished
(完成)三种
Status
。一个实际
State
(状态)真正是由两部分组成:
State = (Step + Status)
。
流转
Ø 一个 State 到另一个 State 的转移。
动作
Ø
Action
触发了发生在
Step
内或
Step
间的流转,或者说是基于
State
的流转。一个
step
里面可以有多个
Action
。
Action
和
Step
之间的关系是,
Step
说明
“
在哪里
”
,
Action
说明
“
去哪里
”
。
一个
Action
典型地由两部分组成:可以执行此
Action
(动作)的
Condition
(条件),以及执行此动作后的
Result
(结果)。
条件
Ø 类似于逻辑判断,可包含 “AND” 和 “OR” 逻辑。比如一个请假流程中的 “ 本部门审批阶段 ” ,该阶段利用 “AND” 逻辑,判断流程状态是否为等候处理中,以及审批者是否为本部门主管。
结果
Ø Result 代表执行 Action (动作)后的结果,指向新的 Step 及其 Step Status ,也可能进入 Split 或者 Join 。 Result 分 为两种, Contidional-Result (有条件结果),只有条件为真时才使用该结果,和 Unconditional-Result (无条件 结果),当条件不满足或没有条件时使用该结果。
分离 / 连接
Ø 流程的切分和融合。很简单的概念, Split 可以提供多个 Result (结果); Join 则判断多个 Current Step 的态提供一个 Result (结果)。
描述
步骤、状态和动作 (Step, Status, and Action)
Ø
工作流要描述步骤
(Step)
、步骤的状态
(Status)
、各个步骤之间的关系以及执行各个步骤的条件和权限,每个步骤中可以含有一个或多个动作
(Action)
,动作将会使一个步骤的状态发生改变。对
于一个执行的工作流来讲,步骤的切换是不可避免的。一个工作流在某一时刻会有一个或多个当前步骤,每个当前步骤都有一个状态值,当前步骤的状态值组成了工
作流实例的状态值。一旦完成了一个步骤,那么这个步骤将不再是当前步骤(而是切换到一个新的步骤),通常一个新的当前步骤将随之建立起来,以保证工作流继
续执行。完成了的步骤的最终状态值是用
Old-Status
属性指定的,这个状态值的设定将发生在切换到其他步骤之前。
Old-Status
的值可以是任
意的,但在一般情况下,我们设置为
Finished
。切换本身是一个动作(
Action
)的执行结果。每个步骤可以含有多个动作,究竟要
载入哪个动作是由最终用户、外部事件或者
Tiggerd
的自动调用决定的。随着动作的完成,一个特定的步骤切换也将发生。动作可以被限制在用户、用户组或
当前状态。每一个动作都必须包含一个
Unconditional Result
和
0
个或多个
Conditional Results
。所
以,总体来说,一个工作流由多个步骤组成。每个步骤有一个当前状态(例如:
Queued, Underway or Finished
),一个步骤包含多
个动作。每个步骤含有多个可以执行的动作。每个动作都有执行的条件,也有要执行的函数。动作包含有可以改变状态和当前工作流步骤的
results
。
结果、分支和连接 (Results, Joins, and Splits)
Ø
无条件结果
(Unconditional Result)
对于每一个动作来讲,必须存在一个
Unconditional Result
。一个
result
是一系列指令,这些指令将告诉
OSWorkFlow
下一个任务要做什么。这包括使工作流从一个状态切换到另一个状态。
Ø
有条件结果
(Conditional Result)
Conditional Result
是
Unconditional Result
的一个扩展。它需要一个或多个
Condition
子标签。第一个为
true
的
Conditional
(使用
AND
或
OR
类型),会指明发生切换的步骤,这个切换步骤的发生是由于某个用户执行了某个动作的结果导致的。
Ø 三种不同的 Results(conditional or unconditional) 一个新的、单一的步骤和状态的组合。一个分裂成两个或多个步骤和状态的组合。将这个和其他的切换组合成一个新的单一的步骤和状态的组合。每种不同的 result 对应了不同的 xml 描述,注意:通常,一个 split 或一个 join 不会再导致一个 split 或 join 的发生。
自动步骤 (Auto actions)
Ø 有 的时候,我们需要一些动作可以基于一些条件自动地执行。为了达到这个目的,你可以在 action 中加入 auto="true" 属性。流程将考察这个动作的 条件和限制,如果条件符合,那么将执行这个动作。 Auto action 是由当前的调用者执行的,所以将对该动作的调用者执行权限检查。
整合抽象实例 (Integrating with Abstract Entities)
Ø 建 议在你的核心实体中,例如 "Document" 或 "Order" ,在内部创建一个新的属性: workflowId 。这样,当新的 "Document" 或 "Order" 被创建的时候,它能够和一个 workflow 实例关联起来。那么,你的代码可以通过 OSWorkflow API 查找到这个 workflow 实例并且得到这个 workflow 的信息和动作。
工作流实例状态 (Workflow Instance State)
Ø 有 的时候,为整个 workflow 实例指定一个状态是很有帮助的,它独立于流程的执行步骤。 OSWorkflow 提供一些 workflow 实例中可以包含的 "meta-states" 。这些 "meta-states" 可以是 CREATED, ACTIVATED, SUSPENDED, KILLED 和 COMPLETED 。当一个工作流实例被创建的时候,它将处于 CREATED 状态。然后,只要一个动作被执行,它就会自动的变成 ACTIVATED 状 态。如果调用者没有明确地改变实例的状态,工作流将一直保持这个状态直到工作流结束。当工作流不可能再执行任何其他的动作的时候,工作流将自动的变成 COMPLETED 状态。然而,当工作流处于 ACTIVATED 状态的时候,调用者可以终止或挂起这个工作流(设置工作流的状态为 KILLED 或 SUSPENDED )。一个终止了的工作流将不能再执行任何动作,而且将永远保持着终止状态。一个被挂起了的工作流会被冻结,他也不能 执行任何的动作,除非它的状态再变成 ACTIVATED 。
Osworkflow 表
OS_WFENTRY
工作流主表,存放工作流名称和状态 | ||
字段名 |
数据类型 |
说明 |
ID |
NUMBER |
自动编号 |
NAME |
VARCHAR2(20) |
工作流名称
|
STATE |
NUMBER |
工作流状态 |
OS_CURRENTSTEP
当前步骤表,存放当前正在进行步骤的数据 | ||
字段名 |
数据类型 |
说明 |
ID |
NUMBER |
自动编号 |
ENTRY_ID |
NUMBER |
工作流编号 |
STEP_ID |
NUMBER |
步骤编号 |
ACTION_ID |
NUMBER |
动作编号 |
OWNER |
VARCHAR2(20) |
步骤的所有者 |
START_DATE |
DATE |
开始时间 |
FINISH_DATE |
DATE |
结束时间 |
DUE_DATE |
DATE |
授权时间 |
STATUS |
VARCHAR2(20) |
状态 |
CALLER |
VARCHAR2(20) |
操作人员的帐号名称 |
OS_CURRENTSTEP_PREV
前步骤表,存放当前步骤和上一个步骤的关联数据 | ||
字段名 |
数据类型 |
说明 |
ID |
NUMBER |
当前步骤编号 |
PREVIOUS |
NUMBER |
前步骤编号 |
|
|
|
OS_HISTORYSTEP
历史步骤表,存放当前正在进行步骤的数据 | ||
字段名 |
数据类型 |
说明 |
ID |
NUMBER |
自动编号 |
ENTRY_ID |
NUMBER |
工作流编号 |
STEP_ID |
NUMBER |
步骤编号 |
ACTION_ID |
NUMBER |
动作编号 |
OWNER |
VARCHAR2(20) |
步骤的所有者 |
START_DATE |
DATE |
开始时间 |
FINISH_DATE |
DATE |
结束时间 |
DUE_DATE |
DATE |
授权时间 |
STATUS |
VARCHAR2(20) |
状态 |
CALLER |
VARCHAR2(20) |
操作人员的帐号名称 |
|
|
|
OS_HISTORYSTEP_PREV
前历史步骤表,存放历史步骤和上一个步骤的关联数据 | ||
字段名 |
数据类型 |
说明 |
ID |
NUMBER |
当前步骤编号 |
PREVIOUS |
NUMBER |
前步骤编号 |
|
|
|
OS_PROPERTYENTRY
属性表,存放临时变量 | ||
字段名 |
数据类型 |
说明 |
GLOBAL_KEY |
VARCHAR2(255) |
全局关键字 |
ITEM_KEY |
VARCHAR2(255) |
条目关键字 |
ITEM_TYPE |
NUMBER |
条目类型 |
STRING_VALUE |
VARCHAR2(255) |
字符值 |
DATE_VALUE |
DATE |
日期值 |
DATA_VALUE |
BLOB |
数据值 |
FLOAT_VALUE |
FLOAT |
浮点值 |
NUMBER_VALUE |
NUMBER |
数字值 |
Os_entryids
| ||
字段名 |
数据类型 |
说明 |
id |
bigint |
编号 |
|
|
|
|
|
|
Os_stepids
| ||
字段名 |
数据类型 |
说明 |
username |
|
|
passwordhash |
|
|
|
|
|
Os_group
| ||
字段名 |
数据类型 |
说明 |
groupname |
|
|
|
|
|
|
|
|
Os_membership
| ||
字段名 |
数据类型 |
说明 |
Username |
|
|
groupname |
|
|
|
|
|
| ||
字段名 |
数据类型 |
说明 |
|
|
|
|
|
|
|
|
|
Osworkflow 程序包
com.opensymphony.workflow
Ø 该包为整个 OSWorkflow 引擎提供核心接口。例如 com.opensymphony.workflow.Workflow 接口,可以说,实际开 发中的大部分工作都是围绕该接口展开的,该接口有 BasicWorkflow 、 EJBWorkflow 、 OfbizWorkflow 三个实现类。
com.opensymphony.workflow.basic
Ø
该包有两个类,
BasicWorkflow
与
BasicWorkflowContext
。
BasicWorkflow
不支持事务,尽管依赖持久实现,事务也不能包裹它。
BasicWorkflowContext
在实际开发中很少使用。
public void setWorkflow(int userId) {
Workflow workflow = new BasicWorkflow(Integer.toString(userId));
}
com.opensymphony.workflow.config
Ø
该
包有一个接口和两个该接口的实现类。在
OSWorkflow 2.7
以前,状态由多个地方的静态字段维护,这种方式很方便,但是有很多缺陷和约束。最
主要的缺点是无法通过不同配置运行多个
OSWorkflow
实例。实现类
DefaultConfiguration
用于一般的配置文件载入。而
SpringConfiguration
则是让
Spring
容器管理配置信息。
public void setWorkflow(int userId) {
Workflow workflow = new BasicWorkflow(Integer.toString(userId));
}
com.opensymphony.workflow.ejb
Ø 该包有两个接口 WorkflowHome 和 WorkflowRemote 。该包的若干类中,最重要的是 EJBWorkflow ,该类和 BasicWorkflow 的作用一样,是 OSWorkflow 的核心,并利用 EJB 容器管理事务,也作为工作流 session bean 的包装器。
com.opensymphony.workflow.loader
Ø
该包有若干类,用得最多的是
XxxxDescriptor
,如果在工作流引擎运行时需要了解指定的动作、步骤的状态、名字,等信息时,这些描述符会起到很大作用。
public String findNameByStepId(int stepId,String wfName) {
WorkflowDescriptor wd = workflow.getWorkflowDescriptor(wfName);
StepDescriptor stepDes = wd.getStep(stepId);
return stepDes.getName();
}
com.opensymphony.workflow.ofbiz
Ø OfbizWorkflow 和 BasicWorkflow 在很多方面非常相似,除了需要调用 ofbiz 的 TransactionUtil 来包装事务。
com.opensymphony.workflow.query
Ø
该
包主要为查询而设计,但不是所有的工作流存储都支持查询。通常,
Hibernate
和
JDBC
都支持,而内存工作流存储不支持。值得注意的是
Hibernate
存储不支持混合型查询(例如,一个查询同时包含了
history step
上下文和
current step
上下文)。执
行一个查询,需要创建
WorkflowExpressionQuery
实例,接着调用
Workflow
对象的
query
方法来得到最终查询结
果。
com.opensymphony.workflow.soap
Ø OSWorkflow 通过 SOAP 来支持远端调用。这种调用借助 WebMethods 实现。
com.opensymphony.workflow.spi
Ø
该包可以说是
OSWorkflow
与持久层打交道的途径,如当前工作流的实体,其中包括:
EJB
、
Hibernate
、
JDBC
、
Memory
、
Ofbiz
、
OJB
、
Prevayler
。
HibernateWorkflowEntry hwfe = (HibernateWorkflowEntry) getHibernateTemplate()
.find("from HibernateWorkflowEntry where Id="
+ wfIdList.get(i)).get(0);
com.opensymphony.workflow.util
Ø 该包是 OSWorkflow 的工具包,包括了对 BeanShell 、 BSF 、 EJB Local 、 EJB Remote 、 JNDI 的支持。
Basic |
Basicworkflow: 未实现事务回滚 Basicworkflowcontext: 上下文对象的 basic 实现方式 |
Config |
读取 xml 配置文件的一些常用类 |
Ejb |
Workflow 的 ejb 实现方式,主要对象是 ejbworkflow 和 ejbworkflowcontext |
loader |
在工作流初始化时必须要加载的一些类。主要有 workflowfactory 接口 xmlworkflowfactory 实现类,还有 workflowdescriptor 等描述 xml 配置文件的实体对象 |
Ofbiz |
Workflow 的 ofbiz 实现方式,只有两个对象分别是 ofbizworkflow 和 ofbizworkflowcontext |
Query |
查询分析器,完成单一、嵌套、组合查询,主要对象 workflowExpressionquery |
Soap |
Soap 实现方式 |
Spi |
与后台打交道的关于 workflow 存储的一些类或者对象有 workflowentry 接口,此接口返回一个工作流实例对象; workflowstore 里面定义的全部都是工作流的存储和查询方法; JDBCworkflowstore 实现 simpleworkflowentry ; Hibernate 或者 spring+hibernate 实现 hibernateworkflowentry ; Step 接口被 simplestep 或者 hibernatestep 调用; Simplestep 是 jdbcworkflowstep 实现方式; Hibernatestep 是 hibernateworkflowstore 或者 springhibernate 我 rkflow 实现方式 |
Spi.ejb |
Ejb 方式存储 |
Spi.hibernate |
Hibernate2 或者 spring+hibernate2 形势存储实现方式 |
Spi.hibernate3 |
Hibernate3 或者 spring+hibernate3 形势存储实现方式,目前主流方式 |
Spi.jdbc |
Jdbc 存储方式,常见且经典方式 |
Spi.memory |
Memory 存储方式 |
Spi.ofbiz |
Ofbiz 形势存储方式 |
Spi.ojb |
Obj 存储方式 |
Spi.prevayler |
Prevayler 存储方式 |
Timer |
定时器,用来处理定时任务 |
util |
扩展,实现支持 jndi , jmail , springframework , log4j , osuer |
Util.beanshell |
Beanshell 形势的 condition , function , registor 和 validator 实现方式 |
Util.bsf |
Bsf 形势的 condition , function , registor 和 validator 实现方式 |
Util.ejb |
Ejb 形势的 condition , function , registor 和 validator 实现方式 |
Util.jndi |
Jndi 形势的 condition , function , registor 和 validator 实现方式 |
Osworkflow 方法接口
Workflow 的 initialize 、 doAction ,还有 wf 。 GetAvailableActions(id,map) 方法都有一个 hashmap 类型参数,是用来传递工作流中所需的数据或者对象,其有效范围只包括当前步骤或者下一步。可应用在 function,condition,validator 等当中
Workflow 主要方法
getAvailableActions(id,null) |
得到当前工作流实例所有有效动作 ID :工作流实例 ID 要传递的对象 |
getCurrentSteps(id) |
得到当前步骤 |
getEntryState(id) |
得到当前状态 |
getHistorySteps(id) |
得到历史步骤 |
getPropertySet(id) |
得到 PropertySet 对象 |
getSecurityPermissions(id) |
得到状态权限列表 |
getWorkflowDescriptor(id) |
得到工作流 XML 描述文件对象 |
getWorkflowName(id) |
得到当前实例名称 |
getWorkflowNames() |
得到工作流名称 |
|
|
Workflow 接口中的核心方法
Ø 得到 workflowstore 实现类,利用里面的 createEntry 方法创建一个 workflowentry 对象
Ø 执行 populateTransientMap 方法,将 context ( workflowcontext ), entry ( workflowentry ), strore ( workflowstore ), configuration ( configuration ), descriptor ( workflowDescriptor )装进 transientvars ;将当前要执行的 actionid 和 currentsteps 装进 transientvars ;将所有 XML 中配置的 register 装进 transientvars
Ø 根据 restrict-to 里配置的条件来判断是否能初始化工作流,如果不能则回滚并抛出异常
Ø 执行 transitionworkflow 方法传递工作流,这个 transitionworkflow 方法是 workflow 中重要方法
Ø 返回当前工作流程实例 ID
Transition workflow 方法
是工作流最核心的方法,它主要是完成以下功能
Ø 利用 getCurrentStep 方法取得当前步骤:如果只有一个有效步骤,直接返回;如果有多个有效当前步骤,返回符合条件的第一个
Ø 调用动作验证器来验证 transientvars 里面的变量
Ø 执行当前步骤中的所有 post-function
Ø 执行当前动作中的所有 pre-function
Ø 检查当前动作中的所有有条件结果,如果符合条件,验证 transientvars 里面的变量并执行有条件结果里面的 pre-function ;如果动作里面没有一个有条件结果,执行无条件结果,验证 transientvars 里面的变量并执行无条件结果里面的 pre-function
Ø 如果程序进入到一个 split 中: 1 )验证 transientvars 里面的变量; 2 )执行 split 中的 pre-function ; 3 )如果动作中的 finish 不等于 true ,执行 split 中所有 result ; 4 )结束当前步骤并将其移到历史步骤中去,创建新的步骤并执行步骤中的 pre-function ; 5 )执行 split 中的 post-function
Ø 如果进入 join : 1 )结束当前步骤并将其移入历史步骤中; 2 )将刚结束的步骤和在 join 中的当前步骤还有历史步骤夹道 joinsteps 集合中并产生 joinnodes 对象,将此对象 put 到 transientvars 里; 3 )检查 join 条件; 4 )执行 join 条件结果中的 validator ; 5 )执行 join 有条件结果中的 pre-function ; 6 )如果当前步骤不再历史步骤里面,把它移到历史步骤里面去; 7 )如果刚刚结束的当前动作中的 finish 不等于 true ,创建新步骤并执行新步骤中的 pre-function ; 8 )执行 join 的 post-function
Ø 如果程序进入到另一个 step 中:结束当前步骤并将其移到历史步骤中去,并创建新的步骤并执行新步骤中的 pre-function
Ø 如果动作里面有符合条件的有条件结果,执行有条件结果里面的 post-functioin ;如果动作里面没有一个有条件结果,则执行无条件结果里面的 post-function
Ø 执行动作里面的 post-function
Ø 如果动作一开始是一个初始化状态,将设置 activated 标识,如果动作在 XML 里面有完成状态的标识,将设置 compleleted 标识
Ø 执行有效的自动动作( auto action )
Ø 最后返回流程是否完成的布尔值:如果流程实例已经完结,返回 true ;否则返回 false 。
DoAction 方法
Ø 判断工作流的实例状态,如果状态不为 activated ( 1 ),直接返回
Ø 利用 findCurrentsteps 方法得到当前所有步骤列表
Ø 执行 populatetransientmap 方法,将 context ( workflowcontext ), entry ( workflowentry ), strore ( workflowstore ), configuration ( configuration ), descriptor ( workflowDescriptor )装进 transientvars ;将当前要执行的 actionid 和 currentsteps 装进 transientvars ;将所有 XML 中配置的 register 装进 transientvars
Ø 检查全局动作( Global Action )和当前步骤里面所有动作的有效性。如果有无效动作,直接抛出异常
Ø 执行 transientionworkflow 方法传递工作流,如果捕获到 workflowexception ,抛出异常并回滚
Ø 如果动作中没有显示地表明 finish 的状态 true ,那么这时要执行 checkimplicitfinish 方法,查找当前步骤中是否还有有效动作,如果没有一个有效动作,则直接调用 completeentry 方法结束流程并将流程的状态设置为 completed ( 4 )
workflowDescriptor 对象里面的主要方法
getAction(id) |
根据动作 ID 得到当前动作描述信息 |
getCommonActions() |
得到通用动作列表 |
getGlobalActions() |
得到全局动作列表 |
getGlobalConditions() |
得到全局条件类标 |
getInitialAction(id) |
根据初始动作 ID 得到初始动作描述信息 |
getInitialActions() |
得到所有动作描述信息 |
getJoin(id) |
得到联合描述信息 |
getJoins() |
得到所有联合描述信息 |
getName() |
得到工作流描述文件名称 |
getRegisters() |
得到所有注册器 |
getSplit(id) |
根据分支 ID 得到分支描述信息 |
getSplits() |
得到所有分支描述信息 |
getStep(id) |
根据步骤 ID 得到步骤描述信息 |
getSteps() |
得到所有步骤描述信息 |
getTriggerFunction(id) |
根据触发 ID 得到触发描述信息 |
getTriggerFunctions() |
得到所有触发描述信息 |
|
|
|
|