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() | 得到所有触发描述信息 |
|
|
|
|