osworkflow 学习札记

osworkflow 学习笔记
OSWorkflow是opensymphony组织开发的一个工作流引擎,目前的版本是2.8。OSWorkflow用纯Java语言编写,并且开放源代码。它最大的特点就是极其的灵活。它面向的人群是具有技术背景的软件开发人员。OSWorkflow不提倡用可视化工具定义流程。用户可以根据自己的实际需求,来设计出完全符合自身业务逻辑的系统,而并不需要使用复杂的代码去实现。换句话说OSWorkflow让我们真正解放了,使得我们从底层的代码堆中爬了出来,轻松地用一套通用的引擎机制去实现各种业务流程。OSWorkflow提供我们所有工作流中可能用到的元素例如:步骤(step)、条件(conditions)、循环(loops)、分支(spilts)、合并(joins)、角色(roles)、函数(function)等等。 首先我们来谈谈步骤:步骤是工作流中很重要的概念。如果我们把工作流比喻成一条从起点站驶向终点站的公共汽车路线,那么步骤就相当于汽车站台。而汽车有的正在排队等候进站,有的还没有进站,有的刚出站,这样就形成了所谓的“已完成”、“正在处理”、“已添加至处理队列”、“未处理”等状态。 另外一个重要的概念就是动作,动作就是工作流中每一步骤中"需要处理的事情",每一个动作执行完毕以后都有一个结果。公共汽车停站下客就好比一个动作,动作完成以后,开向下一站,或者加油,或者返程等等就是一个结果。当然,实际上的工作流远比这辆汽车来的复杂,它涉及到的结果还包括原地踏步停留在同一步骤,或者是流转到另外的步骤中去,或者是流转到一个分支中去,或者汇集到一个合并中等。如果动作被设置成为auto,那么只要触发器满足条件或者有来自外部的事件工作流便可自动执行。 在许多流程中,如果遇到并行处理某些事情,这就是分支。分支一般是指并行处理多件事情而没有先后顺序。若有一条分支进行了回退处理,整个流程都将回退。
        与之相对的,合并就是把几条符合条件的分支聚合起来,使得事情变成"殊途同归"。这也是非常常见的流程,同时也是最复杂的一种流程。
在步骤、动作和结果中都提供了函数功能,函数按执行的先后时机可分为pre-functions和post-functions。顾名思义,pre-functions就是在事情发生之前执行的,而post-functions就是在事情发生以后执行的。

<pre-functions>
      <function type="class">
       <arg name="class.name">
        com.ewns.service.WfBglcapproveService
       </arg>
      </function>
     </pre-functions>
     <results>
     <result old-status="Finished" status="Underway"
       step="3" >
       <conditions type="AND">
        <condition type="beanshell">
         <arg name="script">
          propertySet.getInt("opinion")==2
         </arg>
        </condition>
       </conditions>
       <post-functions>
        <function type="beanshell">
         <arg name="script">
          System.out.println("step 2 shenpi did not  get through ...");
         </arg>
        </function>
       </post-functions>
      </result>
       验证器是用来验证用户输入的数据是否合法的。它也可以被应用在步骤,动作或结果中。 动作的执行结果可以是有条件的(conditional)也可以是无条件的(unconditional)。对于有条件结果,可以允许有多个条件。引擎将首先检查是否有满足的条件,它会逐一进行检查,直到符合的条件被找到才能执行。如果没有一个条件被满足,那么最终引擎将产生无条件结果。 在每个步骤中调用工作流的人被称之为调用者(caller),而每个步骤都也会有一个所有者(owner),以代表在当前步骤中负责执行动作的角色或用户。
当前用户在执行当前步骤的时候,这些步骤被保留在当前表中(current),而一旦步骤被执行完毕,引擎会马上将这个当前步骤从当前表中移到历史表中(history)。
        OSWorkfow的高级特性有发送邮件,注册器功能,通用动作和全局动作,触发器和定时器等等,以下会一一讲解。

所需JAR包
OSWorkflow自身
OSWorkflow自身(%osworkflow解压包%\):
osworkflow-2.8.0.jar
OSWorkflow核心引用包
OSWorkflow核心引用包(%osworkflow解压包%\lib\ core):
commons-logging.jar:必要,支持日志。
propertyset-1.4.jar:必要,支持propertyset的aggregate ,cached ,memory ,jdbc ,file ,javabeans ,map ,xml接口实现,并不支持hibernate3,如果要支持hibernate3,要自己写代码。这个下面再谈。
oscore-2.2.5.jar:必要,提供了一些工具等。
OSWorkflow可选包
OSWorkflow可选包(%osworkflow解压包%\lib\ optional):
bsf.jar:支持bsf,可选。
bsh-1.2b7.jar:支持beanshell,可选。
ehcache.jar:支持缓存,可选。
osuser-1.0-dev-2Feb05.jar:支持例子里面的用户和群组管理,在涉及到用户和群组的操作建议加上此包。

与Spring2联用所需包
spring2所需的包(%spring解压包%\dist) :
spring.jar(version:2.05)

与Hibernate3联用所需包
Hibernate3所需的包(%hibernate解压包%\lib) :
antlr.jar
cglib.jar
asm.jar
asm-attrs.jars
commons-collections.jar
hibernate3.jar
jta.jar
dom4j.jar
log4j.jar

WorkflowStore
配置osworkflow的核心之一就是配置WorkflowStore。有多种不同的配置方法,下面逐一讲解。
MemoryWorkflowStore
在官方文档里面有现成的例子可以参照,最重要的也就是要把persistence class设置成为com.opensymphony.workflow.spi.memory.MemoryWorkflowStore
具体来说,在\%webapp%\WEB-INF\classes下的osworkflow.xml中:
<osworkflow>
<persistence class="com.opensymphony.workflow.spi.memory.MemoryWorkflowStore"/>
<factory class="com.opensymphony.workflow.loader.XMLWorkflowFactory">
<property key="resource" value="workflows.xml" />
</factory>
</osworkflow>

JDBCWorkflowStore
第一步:web.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<display-name>OSWorkflow Example App</display-name>
<description>OSWorkflow Example App</description>
<servlet>
8
<servlet-name>SOAPWorkflow</servlet-name>

<servlet-class>com.opensymphony.workflow.soap.SOAPWorkflowServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SOAPWorkflow</servlet-name>
<url-pattern>/soap/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app>

第二步:配置数据源

第三步:配置osworkflow必要文件。
确定在\%webapp%\WEB-INF\classes\下面有以下几个文件:
example.xml:例子,不作修改。
workflows.xml:例子,不作修改。
osuser.xml,修改如下,请注意红色的数据源部分:

调用接口中的参数和方法详解
Input Map
workflow的initialize、doAction,还有wf.getAvailableActions(id, map)方法都有一个HashMap类型的参数,是用来传递工作流中所需要的数据或者对象的,其有效范围只包括当前步骤或者下一步的 pre-function。可应用在function,condition,validator等当中。

1、Workflow接口里面的主要方法

2、WorkflowDescriptor对象里面的主要方法

3、OSUser几大功能
9 对用户数据进行维护
9 对群组数据进行维护
9 对用户所属群组进行维护
9 提供用户安全验证
9 提供对propertyset数据进行增加删除的支持

OSUser的优点
灵活性:只需要配置一个osuser.xml文件就可以轻松搞定所有与用户和权限相关的逻辑。
通用性:OSUser的设计规范实用于所有的业务系统。

OSWorkflow包的描述

basic
只有两个对象:
BasicWorkflow,未实现事务回滚;
BasicWorkflowContext:上下文对象的Basic实现方式
config
里面主要是读取XML配置文件的一些常用类

loader
在工作流初始化时必须要加载的一些类.主要有WorkflowFactory接口及XMLWorkflowFactory实现类,还有WorkflowDescriptor等描述XML配置文件的实体对象等

OSWorkflow数据库的描述
JDBCWorkflowStore 和JDBCTemplateWorkflowStore这两种方式所用的表结构完全相同。现将所要用到的表结构开列如下并对每一张表每一字段进行详细说明。在此我不对hibernate表结构进行说明,因为有一点点的不同,请自行到我网站上下载源代码进行对比。

Workflow接口中的核心方法

initialize方法
初始化方法主要完成了以下几个功能
z 得到WorkflowStore实现类,利用里面的createEntry方法创建一个WorkflowEntry对象。
z 执行populateTransientMap方法,将context(WorkflowContext),entry(WorkflowEntry),store(WorkflowStore), configuration(Configuration), descriptor(WorkflowDescriptor)装进transientVars;将当前要执行的actionId和currentSteps装进transientVars;将所有XML中配置的register装进transientVars。
z 根据restrict-to里面配置的条件来判断是否能初始化工作流,如果不能则回滚并抛出InvalidRoleException异常。
z 执行transitionWorkflow方法传递工作流,这个transitionWorkflow方法是Workflow中重中之重的方法,以下会进行详细讲解。
z 返回当前工作流程实例ID。

transitionWorkflow方法
transitionWorkflow方法是工作流最最核心的方法,它主要是完成以下功能
z 利用getCurrentStep方法取得当前步骤:如果只有一个有效当前步骤,直接返回;如果有多个有效当前步骤,返回符合条件的第一个。
z 调用动作验证器来验证transientVars里面的变量。
38
z 执行当前步骤中的所有post-function。

执行当前动作中的所有pre-function。
z 检查当前动作中的所有有条件结果,如果有符合条件的,验证transientVars里面的变量并执行有条件结果里面的pre-function;如果动作里面没有一个有条件结果,执行无条件结果,验证transientVars里面的变量并执行无条件结果里面的pre-function。
z 如果程序进入到一个split中:1)验证transientVars里面的变量;2)执行split中的pre-functions;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。
z 如果程序进入到另一个step中:结束当前步骤并将其移至历史步骤中去,创建新的步骤并执行新步骤中的pre-function。
z 如果动作里面有符合条件的有条件结果,执行有条件结果里面的post-function;如果动作里面没有一个有条件结果,则执行无条件结果里面的post-function。
z 执行动作里面的post-function。
z 如果动作一开始是一个初始状态,将设置ACTIVATED标识;如果动作在XML里面有完成状态的标识,将设置COMPLETED标识。
z 执行有效的自动动作(auto action)。
z 最后返回流程是否完成的布尔值:如果流程实例已经完结,返回true;否则返回false。

doAction方法
z 判断工作流程实例的状态,如果状态不为ACTIVATED(1),直接返回。
z 利用findCurrentSteps方法得到当前所有步骤列表。
z 执行populateTransientMap方法,将context(WorkflowContext),entry(WorkflowEntry),store(WorkflowStore), configuration(Configuration), descriptor(WorkflowDescriptor)装进transientVars;将当前要执行的actionId和currentSteps装进transientVars;将所有XML中配置的register装进transientVars。
z 检查全局动作(Global Action)和当前步骤里面所有动作的有效性。如果有无效动作,直接抛出InvalidActionException异常。
z 执行transitionWorkflow方法传递工作流,如果捕获到WorkflowException,抛出异常并回滚。

如果动作中没有显式地标明finish的状态为true,那么这时要执行checkImplicitFinish方法,查找当前步骤中是否还有有效动作,如果没有一个有效动作,则直接调用completeEntry方法结束流程并将流程的状态设置成为COMPLETED(4)。

如何与现有系统集成

在初始化一个新的工作流时,必须要在你的Service层执行以下方法:
public long doInitialize(String un, String title, String content) throws Exception {
Workflow wf = new BasicWorkflow(un);
long wf_id = -1;
try {
wf_id = wf.initialize("example", 100, null);
}
catch (Exception e) {
throw e;
}
return wf_id;
}
这时候要加入自己的业务逻辑代码,例如:
workflowDAO.saveDocumentation(wf_id, title, content);
os_doc有三个字段: wf_id(非常重要,绑定工作流ID,主键);title(文档标题),要从创建工作流的前台newdoc.jsp中传过来;content(文档内容)也要从创建工作流的前台newdoc.jsp中传过来。
1 楼 kookse 2012-05-02  
谢谢了 正需要