OSWorkflow不是一种开箱即用的工作流解决方案,要想让OSWorkflow能更好的为我们服务,我们还需要做一些编码相关的工作,因此他可以很灵活的实现各种各样的复杂流程。你可以把这看做是OSWorkflow的优点,但作为优点的同时,这也是他的缺点,因为也许这样额外的工作并不是你想要的,你可能需要一些相对简单一点的工作方式,这就需要大家自己去权衡啦!
我们准备使用Spring的相关特性,也许还有Hibernate,所以我们探讨的重点会放在这些方面。
初步印象
OSWorkflow是通过XML来完成流程的定制化的。下面是一个流程定义的例子,让我们先建立一个初步的印象。
图1:
从这张图上,我们能看到一个流程的原型:一个流程(workflow)我们可以定制他的初始行为(initial-actions ),他包括哪些步骤(steps ),每个步骤下面我们还可以有很多行为(actions ),这些行为他可以有自己的约束条件(restrict-to ),同时我们还看到了一些前置(pre-functions )或者后置处理(post-functions ),每个行为(action)也都有自己的结果(results ),当所有步骤都结束时,这个流程的生命周期也就结束啦。当然我们还可以定义多个流程,就像这样:
图2:
workflows.xml
我们可以定义多个像图1那样的流程文件,像图2所示的那样定义到workflows元素中去。
OSWorkflow流程的流转
我们通过一个简单的例子来更深入的了解和学习OSWorkflow,并构建我们自己的工作流引擎。
从一个简单的流程定义开始:
example.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE workflow PUBLIC "-//OpenSymphony Group//DTD OSWorkflow 2.6//EN" "http://www.opensymphony.com/osworkflow/workflow_2_8.dtd"> <workflow> <initial-actions> <action id="100" name="Start Workflow"> <pre-functions> <function type="class"> <arg name="class.name">com.opensymphony.workflow.util.Caller</arg> </function> </pre-functions> <results> <unconditional-result old-status="Finished" status="Underway" step="1" owner="${caller}"/> </results> </action> </initial-actions> <steps> <step id="1" name="First Draft"> <actions> <action id="1" name="Finish First Draft"> <pre-functions> <function type="beanshell"> <arg name="script"> String caller = context.getCaller(); propertySet.setString("caller", caller); boolean test = true; String yuck = null; String blah = "987654321"; System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"); </arg> </function> </pre-functions> <results> <unconditional-result old-status="Finished" status="Finished" step="-1"/> </results> <post-functions> <function type="spring"> <arg name="bean.name">myFunction</arg> </function> </post-functions> </action> </actions> </step> </steps> </workflow>
workflows.xml
<workflows> <workflow name="example" type="resource" location="example.xml"/> </workflows>
我们可以在workflows元素中定义多个workflow,这里我们只有一个example.xml
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>
osworkflow.xml是OSWorkflow的基础配置,我们之前的配置文件,最后通过WorkflowFactory的resource关联起来了。
认识Configuration
Configuration接口有两个实现类,分别是DefaultConfiguration和SpringConfiguration,我们打算使用Spring的相关特性,我们就从SpringConfiguration说起。
我们看Configuration接口,会不会有一种似曾相识的感觉,尤其是看SpringConfiguration中的factory和store,怎么看都和osworkflow.xml中的factory、persistence如此相似,这不就是osworkflow.xml的定义嘛!
WorkflowFactory
WorkflowFactory接口定义了一些常用的关于Workflow的增删改查操作,可以说他掌握了Workflow的生杀大权。
上图是我们将会重点关注的一些类的定义,因为他们和Spring的关系比较密切,WorkflowFactor的族谱其实要丰富得多。不难看出,XMLWorkflowFactory基本上完成了WorkflowFactory接口的所有实现,而SpringWorkflowFactory只新引入了一个resource属性,用来保存workflows.xml文件的路径,看来如果通过XML的方式定义流程,XMLWorkflowFactory才是幕后的支持者。
WorkflowStore
WorkflowStore接口里定义了workflow实例持久化相关的操作。在OSWorkflow中一共定义了两个AbstractHibernateWorkflowStore抽象类及其对应的实现,用来支持不同版本的hibernate。一个在hiberna包中,一个在hibernate3包中。所以在使用过程中,我们要注意他们的完整类名。这里主要介绍的是hibernate3的相关支持。
AbstractHibernateWorkflowStore有两个子类,分别是HibernateWorkflowStore和SpringHibernateWorkflowStore,其中前者是单一的hibernate3的实现,而后者是在Spring下的hibernate3实现。
上图中还列出了JDBCWorkflowStore的相关实现,这也是OSWorkflow给我们的最早的WorkflowStore实现,我们可以根据情况选择使用。
对比WorkflowStore和WorkflowFactory两个接口,好像他们都是对workflow进行增删改查操作,但他们是不同的。WorkflowFactory中引入了一个WorkflowDescriptor类,而WorkflowStore中则是强调WorkflowEntry类。WorkflowDescriptor他是workflow关于XML描述的类型定义,OSWorkflow把定义在XML中的流程,根据DTD文件中定义好的各种XML元素,抽象出了很多种Descriptor(后面介绍),WorkflowFactory则针对的是WorkflowDescriptor。
XML中定义的流程只是一种Descriptor,他是这个流程的蓝图,而WorkflowEntry则是流程的一个个实例。如果我们定义了一个关于文件审批的流程,若干个文件在这个审批流程的生命周期中审批,他们每一个文件都会对应着这个流程的一个实例,WorkflowStore则是负责WorkflowEntry的CRUD工作。
至此,我们分析了Configuration、WorkflowFactory、WorkflowStore他们各自的职业以及他们三者的关系。
Configuration:与osworkflow.xml对应,同时定义了WorkflowFactory和WorkflowStore;
WorkflowStore:负责workflow实例的CRUD工作;
WorkflowFactory:创建和管理WorkflowDescriptor,是我们流程的发源地。
应用集成
<bean id="workflowStore" class="com.opensymphony.workflow.spi.hibernate3.SpringHibernateWorkflowStore"> <property name="sessionFactory"> <ref bean="sessionFactory" /> </property> </bean> <bean id="workflowFactory" class="com.opensymphony.workflow.spi.hibernate.SpringWorkflowFactory" init-method="init"> <property name="resource"> <value>workflows.xml</value> </property> <property name="reload"> <value>false</value> </property> </bean> <bean id="osworkflowConfiguration" class="com.opensymphony.workflow.config.SpringConfiguration"> <property name="store"> <ref local="workflowStore" /> </property> <property name="factory"> <ref local="workflowFactory" /> </property> </bean>