OSWorkFlow 学习

1.OSWorkFlow基本概念html

 


    在商用和开源世界里,OSWorkflow 都不一样于这些已有的工做流系统。最大不一样在于 OSWorkflow 有着很是优秀的灵活性。在开始 接触 OSWorkflow 时可能较难掌握(有人说不适合工做流新手入门),好比,OSWorkflow 不要求图形化工具来开发工做流,而推荐手工编 写 xml 格式的工做流程描述符。它能为应用程序开发者提供集成,也能与现有的代码和数据库进行集成。这一切彷佛给正在寻找快速“即插即用”工做流解决 方案的人制造了麻烦,但研究发现,那些“即插即用”方案也不能在一个成熟的应用程序中提供足够的灵活性来实现全部需求。java


2. OSWorkFlow主要优点
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 文件解释工 做流业务流程。mysql

3.    OSWorkFlow核心概念web

3.1.    概念定义
步骤(Step)
一个 Step 描述的是工做流所处的位置。可能从一个 Step Transtion(流转)到另一个 Step,或者也能够在同一个 Step 内流转(由于 Step 能够通 Status 来细分,造成多个State)。一个流程里面能够多个Step。

状态(Status)
工做流 Status 是用来描述工做流程中具体Step(步骤)状态的字符串。OSWorkflow 的有 Underway(进行中)、 Queued(等候处理中)、Finished(完成)三种 Status。一个实际State(状态)真正是由两部分组 成:State = (Step + Status) 。

流转(Transtion)
一个State到另外一个State的转移。

动做(Action)
Action 触发了发生在 Step 内或 Step 间的流转,或者说是基于 State 的流转。一个 step 里面能够有多个 Action。Action 和Step 之间的关系是,Step 说明“在哪里”,Action 说明“去哪里”。 一个 Action 典型地由两部 分组成:能够执行此Action(动做)的
Condition(条件),以及执行此动做后的 Result(结果)。    

条件(Condition)
相似于逻辑判断,可包含“AND”和“OR”逻辑。好比一个请假流程中的“本部门审批阶段”,该阶段利用“AND”逻辑,判断流程状态是否为等候处理中,以及审批者是否为本部门主管。

结果(Result)
Result 表明执行Action(动做)后的结果,指向新的 Step 及其 Step Status,也可能进入 Split 或者 Join。 Result 分为两种, Contidional-Result (有条件结果),只有条件为真时才使用该结果,和 Unconditional- Result(无条件结果),当条件不知足或没有条件时使用该结果。

分离/链接(Split/Join)
流程的切分和融合。很简单的概念,Split 能够提供多个 Result(结果);Join 则判断多个 Current Step 的态提供一个 Result(结果)。sql


3.2.    步骤、状态和动做(Step, Status, and Action)
工做流要描述步骤(Step)、步骤的状态(Status)、各个步骤之间的关系以及执行各个步骤的条件和权限,每一个步骤中能够含有一个或多个动做(Action),动做将会使一个步骤的状态发生改变。

对于一个执行的工做流来说,步骤的切换是不可避免的。一个工做流在某一时刻会有一个或多个当前步骤,每一个当前步骤都有一个状态值,当前步骤的状态值组成了 工做流实例的状态值。一旦完成了一个步骤,那么这个步骤将再也不是当前步骤(而是切换到一个新的步骤),一般一个新的当前步骤将随之创建起来,以保证工做流 继续执行。完成了的步骤的最终状态值是用Old-Status属性指定的,这个状态值的设定将发生在切换到其余步骤以前。Old-Status的值能够是 任意的,但在通常状况下,咱们设置为Finished。

切换自己是一个动做(Action)的执行结果。每一个步骤能够含有多个动做,究竟要载入哪一个动做是由最终用户、外部事件或者Tiggerd的自动调用决定 的。随着动做的完成,一个特定的步骤切换也将发生。动做能够被限制在用户、用户组或当前状态。每个动做都必须包含一个 Unconditional Result和0个或多个Conditional Results。

因此,整体来讲,一个工做流由多个步骤组成。每一个步骤有一个当前状态(例如:Queued, Underway or Finished),一个步骤包含 多个动做。每一个步骤含有多个能够执行的动做。每一个动做都有执行的条件,也有要执行的函数。动做包含有能够改变状态和当前工做流步骤的results。
3.3.    结果、分支和链接(Results, Joins, and Splits)shell


3.3.1.    无条件结果(Unconditional Result)
对于每个动做来说,必须存在一个Unconditional Result。一个result是一系列指令,这些指令将告诉OSWorkFlow下一个任务要作什么。这包括使工做流从一个状态切换到另外一个状态。数据库

 

3.3.2.    有条件结果(Conditional Result)
Conditional Result是Unconditional Result的一个扩展。它须要一个或多个Condition子标签。第一个为 true的Conditional(使用AND或OR类型),会指明发生切换的步骤,这个切换步骤的发生是因为某个用户执行了某个动做的结果致使的。express


3.3.3.    三种不一样的Results(conditional or unconditional)
一个新的、单一的步骤和状态的组合。
一个分裂成两个或多个步骤和状态的组合。
将这个和其余的切换组合成一个新的单一的步骤和状态的组合。
每种不一样的result对应了不一样的xml描述,你能够阅读http://www.opensymphony.com/osworkflow/workflow_2_7.dtd,获取更多的信息。
注意:一般,一个split或一个join不会再致使一个split 或 join的发生。apache

3.4.    自动步骤(Auto actions)
有的时候,咱们须要一些动做能够基于一些条件自动地执行。为了达到这个目的,你能够在action中加入auto="true"属性。流程将考察这个动做 的条件和限制,若是条件符合,那么将执行这个动做。 Auto action是由当前的调用者执行的,因此将对该动做的调用者执行权限检查。数组


3.5.    整合抽象实例(Integrating with Abstract Entities)
建议在你的核心实体中,例如"Document" 或 "Order",在内部建立一个新的属性:workflowId。这样,当新 的"Document" 或 "Order"被建立的时候,它可以和一个workflow实例关联起来。那么,你的代码能够经过 OSWorkflow API查找到这个workflow实例而且获得这个workflow的信息和动做。


3.6.    工做流实例状态(Workflow Instance State)
有的时候,为整个workflow实例指定一个状态是颇有帮助的,它独立于流程的执行步骤。OSWorkflow提供一些workflow实例中能够包含 的"meta-states"。这些"meta-states"能够是 CREATED, ACTIVATED, SUSPENDED, KILLED 和 COMPLETED。当一个工做流实例被建立的时候,它将处于 CREATED状态。而后,只要一个动做被执行,它就会自动的变成ACTIVATED状态。若是调用者没有明确地改变实例的状态,工做流将一直保持这个状 态直到工做流结束。当工做流不可能再执行任何其余的动做的时候,工做流将自动的变成COMPLETED状态。

然而,当工做流处于ACTIVATED状态的时候,调用者能够终止或挂起这个工做流(设置工做流的状态为KILLED 或 SUSPENDED)。一个终 止了的工做流将不能再执行任何动做,并且将永远保持着终止状态。一个被挂起了的工做流会被冻结,他也不能执行任何的动做,除非它的状态再变成 ACTIVATED。

 


4.    OSWorkFlow包用途分析及代码片段
4.1.    com.opensymphony.workflow
该包为整个OSWorkflow 引擎提供核心接口。例如 com.opensymphony.workflow.Workflow 接口,能够说,实际 开发中的大部分工做都是围绕该接口展开的,该接口有 BasicWorkflow、EJBWorkflow、OfbizWorkflow 三个实现类。


4.2.    com.opensymphony.workflow.basic
该包有两个类,BasicWorkflow 与 BasicWorkflowContext。BasicWorkflow 不支持事务,尽管依赖持久实现,事务也不能包裹它。BasicWorkflowContext 在实际开发中不多使用。

[java]  view plain  copy
 
 print?
  1. public void setWorkflow(int userId) {  
  2. Workflow workflow = new BasicWorkflow(Integer.toString(userId));  
  3. }  



4.3.    com.opensymphony.workflow.config
该包有一个接口和两个该接口的实现类。在 OSWorkflow 2.7 之前,状态由多个地方的静态字段维护,这种方式很方便,可是有不少缺陷和约束。 最主要的缺点是没法经过不一样配置运行多个 OSWorkflow 实例。实现类 DefaultConfiguration 用于通常的配置文件载入。 而 SpringConfiguration 则是让 Spring 容器管理配置信息。

[java]  view plain  copy
 
 print?
  1. public void setWorkflow(int userId) {  
  2. Workflow workflow = new BasicWorkflow(Integer.toString(userId));  
  3. }  

 


4.4.    com.opensymphony.workflow.ejb
该包有两个接口 WorkflowHome 和 WorkflowRemote。该包的若干类中,最重要的是 EJBWorkflow,该类 和 BasicWorkflow 的做用同样,是 OSWorkflow 的核心,并利用 EJB 容器管理事务,也做为工做 流 session bean 的包装器。


4.5.    com.opensymphony.workflow.loader
该包有若干类,用得最多的是 XxxxDescriptor,若是在工做流引擎运行时须要了解指定的动做、步骤的状态、名字,等信息时,这些描述符会起到很大做用。

[java]  view plain  copy
 
 print?
  1. public String findNameByStepId(int stepId,String wfName) {  
  2. WorkflowDescriptor wd = workflow.getWorkflowDescriptor(wfName);  
  3. StepDescriptor stepDes = wd.getStep(stepId);  
  4. return stepDes.getName();  
  5. }  

 


4.6.    com.opensymphony.workflow.ofbiz
OfbizWorkflow 和 BasicWorkflow 在不少方面很是类似,除了须要调用 ofbiz 的 TransactionUtil 来包装事务。

4.7.    com.opensymphony.workflow.query
该包主要为查询而设计,但不是全部的工做流存储都支持查询。一般,Hibernate 和 JDBC 都支持,而内存工做流存储不支持。值得注意的 是 Hibernate 存储不支持混合型查询(例如,一个查询同时包含了 history step 上下文和 current step 上下文)。 执行一个查询,须要建立 WorkflowExpressionQuery 实例,接着调用 Workflow 对象的 query 方法来获得最终查询 结果。

 

[java]  view plain  copy
 
 print?
  1. public List queryDepAdmin(int userId,int type) {  
  2. int[] arr = getSubPerson(userId,type);  
  3.   
  4. //构造表达式  
  5. Expression[] expressions = new Expression[1 + arr.length];  
  6. Expression expStatus = new FieldExpression(FieldExpression.STATUS,  
  7. FieldExpression.CURRENT_STEPS, FieldExpression.EQUALS, "Queued");  
  8. expressions[0] = expStatus;  
  9.   
  10. for (int i = 0; i < arr.length; i++) {  
  11. Expression expOwner = new FieldExpression(FieldExpression.OWNER,  
  12. FieldExpression.CURRENT_STEPS, FieldExpression.EQUALS,  
  13. Integer.toString(arr[i]));  
  14. expressions[i + 1] = expOwner;  
  15. }  
  16.   
  17. //查询未完成流编号  
  18. List wfIdList = null;  
  19. try {  
  20. WorkflowExpressionQuery query = new WorkflowExpressionQuery(  
  21. new NestedExpression(expressions, NestedExpression.AND));  
  22. wfIdList = workflow.query(query);  
  23. catch (Exception e) {  
  24. e.printStackTrace();  
  25. }  
  26. }  

 

 

4.8.    com.opensymphony.workflow.soap
OSWorkflow 经过 SOAP 来支持远端调用。这种调用借助 WebMethods 实现。


4.9.    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);


4.10.    com.opensymphony.workflow.util
该包是 OSWorkflow 的工具包,包括了对 BeanShell、BSF、EJB Local、EJB Remote、JNDI 的支持。


5.    OSWorkFlow表结构分析

 


5.1.    OS_WFENTRY
工做流主表,存放工做流名称和状态
字段名    数据类型    说明
ID    NUMBER    自动编号
NAME    VARCHAR2(20)    工做流名称
STATE    NUMBER    工做流状态

5.2.    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)    操做人员的账号名称

5.3.    OS_CURRENTSTEP_PREV
前步骤表,存放当前步骤和上一个步骤的关联数据

字段名    数据类型    说明
ID    NUMBER    当前步骤编号
PREVIOUS    NUMBER    前步骤编号

5.4.    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)    操做人员的账号名称

5.5.    OS_HISTORYSTEP_PREV
前历史步骤表,存放历史步骤和上一个步骤的关联数据

字段名    数据类型    说明
ID    NUMBER    当前历史步骤编号
PREVIOUS    NUMBER    前历史步骤编号

5.6.    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    浮点值

 

osworkflow代码分析

 

1.com.opensymphony.workflow.Workflow 工做流的用户接口。

       主要定义了用户对工做流的操做方法和用户得到工做流信息的方法。如doAction(long id, int actionId, Map inputs)方法能够执行工做流的Action并产生transaction;用户调用getAvailableActions(long id, Map inputs)能够得到知道工做流实例中符合条件的能够执行的Action。

 

2.com.opensymphony.workflow.WorkflowContext 工做流的Context接口。

      只有两个方法,其中getCaller()得到调用者,setRollbackOnly()能够回滚Action形成的transaction。

      setRollbackOnly()方法很是重要,能够在此方法中实现工做流数据与业务数据的事务处理。因为工做流引擎将流程数据与业务数据分离开管理, 因此工做流数据与业务数据之间的事务处理每每比较困难,甚至有不少商业的工做流引擎都没有解决这个问题,形成软件上的漏洞。惋惜在 BasicWorkflowContext中并无实现回滚时的事务处理,但实现起来应该不会很困难,在之后会单独考虑。

 

3.com.opensymphony.workflow.spi.WorkflowEntry 工做流实例的接口。

     定义了得到工做流实例信息的方法。

 

4.com.opensymphony.workflow.config.Configuration 工做流配置接口。

      得到osworkflw的配置信息和流程的定义信息, osworkflow中的例子就是使用此接口的默认实现。若是想让osworkflw与本身的系统更好的整合,这个接口须要本身实现。

 

5.com.opensymphony.workflow.loader.AbstractWorkflowFactory 流程定义的解析器。

      osworkflow中提供了此抽象类的3种实现,最经常使用的是XMLWorkflowFactory,能够对编写的工做流定义xml文件进行解析。

 

6.com.opensymphony.workflow.spi.WorkflowStore 工做流存储接口。

      实现此接口能够实现用多种途径保存工做流信息,jdbc,hibernate,ejb,memory.........

 

AbstractWorkflow类是workflow接口的最基本的实现。

1.public int[] getAvailableActions(long id, Map inputs)方法:

返回当前能够执行的Ation。

  • 获得工做流流程实例。
  • 获得工做流实例的定义。
  • 获得工做流实例的PropertySet。
  • 获得工做流的当前Step。
  • 产生TransientVars。
  • 获得Global Actions。
  • 判断能够执行的Global Action增长到可执行Action列表中。
  • 得到当前Steps中的可执行Action并添加到可执行Action列表中。
  • 返回可执行Actions。

2. public void setConfiguration(Configuration configuration)方法:

设置工做流配置方法。

3.public Configuration getConfiguration()方法:

返回工做流配置方法,若是没有得到配置信息,初始化配置信息。

4.public List getCurrentSteps(long id):

得到工做流当前所在步骤。

5.public int getEntryState(long id):

得到工做流的状态。

6.public List getHistorySteps(long id)

得到工做流的历史步骤。

7. public Properties getPersistenceProperties()

得到设置的持久化参数。

8.public PropertySet getPropertySet(long id)

获得工做流的PropertySet,调用store中的方法。

9.public List getSecurityPermissions(long id)

获得工做流当前Step的permissions。

10.public WorkflowDescriptor getWorkflowDescriptor(String workflowName)

获得工做流的定义。

11.public String getWorkflowName(long id)

根据工做流实例返回工做流定义名。

12. public String[] getWorkflowNames()

返回系统中配置的全部工做流的名字。

13.public boolean canInitialize(String workflowName, int initialAction),public boolean canInitialize(String workflowName, int initialAction, Map inputs),private boolean canInitialize(String workflowName, int initialAction, Map transientVars, PropertySet ps) throws WorkflowException

判断指定的工做流初始化Action是否是能够执行。

14.public boolean canModifyEntryState(long id, int newState)

判断工做流是否是能够转换到指定状态。

  • 不能够转换到CREATED状态。
  • CREATED,SUSPENDED能够转换到ACTIVATED状态。
  • ACTIVATED能够转换到SUSPENDED状态。
  • CREATED,ACTIVATED,SUSPENDED 能够转换到KILLED状态。

15.public void changeEntryState(long id, int newState) throws WorkflowException

转换工做流状态。

16.public void doAction(long id, int actionId, Map inputs) throws WorkflowException

执行Action。

  • 得到工做流store,和流程实例entry。
  • 判断是否是活动的工做流,不是就返回。
  • 得到工做流的定义。
  • 得到工做流当前所再Steps。
  • 得到工做流PropertySet。
  • 生成transientVars。
  • 从GlobalActions中和当前Steps的普通Actions中判断执行的Action是否试可执行的。
  • 完成Action的Transition。

17.public void executeTriggerFunction(long id, int triggerId) throws WorkflowException

调用工做流的Trigger Function

18.public long initialize(String workflowName, int initialAction, Map inputs) throws InvalidRoleException, InvalidInputException, WorkflowException

初始化一个新的流程实例。返回流程实例id。

19.public List query(WorkflowQuery query),public List query(WorkflowExpressionQuery query)

查询流程实例。

20.public boolean removeWorkflowDescriptor(String workflowName) throws FactoryException

删除已经配置的工做流定义。

21.public boolean saveWorkflowDescriptor(String workflowName, WorkflowDescriptor descriptor, boolean replace) throws FactoryException

保存工做流定义。

22.protected List getAvailableActionsForStep(WorkflowDescriptor wf, Step step, Map transientVars, PropertySet ps) throws WorkflowException

得到指定步骤的可用Actions。

23.protected int[] getAvailableAutoActions(long id, Map inputs)

返回可执行的AutoActions。

24.protected List getAvailableAutoActionsForStep(WorkflowDescriptor wf, Step step, Map transientVars, PropertySet ps) throws WorkflowException

返回指定Step中可执行的AutoActions。

25.protected WorkflowStore getPersistence() throws StoreException

返回配置的store。

26.protected void checkImplicitFinish(long id) throws WorkflowException

判断工做流是否是还有可执行的Action,若是没有,完成此工做流实例。

27.protected void completeEntry(long id, Collection currentSteps) throws StoreException

结束工做流实例,就是把改变流程实例的状态并把当前的Steps都放入到历史表中。 

28.protected boolean passesCondition(ConditionDescriptor conditionDesc, Map transientVars, PropertySet ps, int currentStepId) throws WorkflowException

29.protected boolean passesCondition(ConditionDescriptor conditionDesc, Map transientVars, PropertySet ps, int currentStepId) throws WorkflowException,protected boolean passesConditions(String conditionType, List conditions, Map transientVars, PropertySet ps, int currentStepId) throws WorkflowException

判断条件是否是符合。

30.protected void populateTransientMap(WorkflowEntry entry, Map transientVars, List registers, Integer actionId, Collection currentSteps) throws WorkflowException

产生临时变量transientVars,包含context,entry,store,descriptor,actionId,currentSteps,以及定义的register和用户的输入变量。

31.protected void verifyInputs(WorkflowEntry entry, List validators, Map transientVars, PropertySet ps) throws WorkflowException

验证用户的输入。

32.private boolean isActionAvailable(ActionDescriptor action, Map transientVars, PropertySet ps, int stepId) throws WorkflowException

判断Action是否可用。

33.private Step getCurrentStep(WorkflowDescriptor wfDesc, int actionId, List currentSteps, Map transientVars, PropertySet ps) throws WorkflowException

得到Action所在Step。

34.private boolean canInitialize(String workflowName, int initialAction, Map transientVars, PropertySet ps) throws WorkflowException

判断工做流是否是能够实例化。

35.private Step createNewCurrentStep(ResultDescriptor theResult, WorkflowEntry entry, WorkflowStore store, int actionId, Step currentStep, long[] previousIds, Map transientVars, PropertySet ps) throws WorkflowException

产生新的当前Step。

  • 从resulte中得到nextStep,若是为-1,nextStep为当前Step。
  • 得到定义中的owner,oldStatus,status。
  • 完成当前Step,而且将当前Step保存到历史库中。
  • 生成新的Step。

36.private void executeFunction(FunctionDescriptor function, Map transientVars, PropertySet ps) throws WorkflowException

执行Function。

37.private boolean transitionWorkflow(WorkflowEntry entry, List currentSteps, WorkflowStore store, WorkflowDescriptor wf, ActionDescriptor action, Map transientVars, Map inputs, PropertySet ps) throws WorkflowException

完成工做流的transation。

DefaultConfiguration是Configuration接口的默认实现,用于初始化系统的基本配置信息。
1.public WorkflowDescriptor getWorkflow(String name) throws FactoryException
根据工做流的定义名得到工做流的定义。
2.public WorkflowStore getWorkflowStore() throws StoreException
得到配置的持久化类Store。
3.public void load(URL url) throws FactoryException
装载配置信息。
  • 获得配置文件流,并解析。
  • 得到持久化信息,包括持久化类的路径和持久化类初始化参数。
  • 得到工做流信息解析类路径,并初始化。

4. public WorkflowStore getWorkflowStore() throws StoreException

得到工做流初始化类。

XMLWorkflowFactory用于解析工做流定义xml文件,得到工做流信息。
1.public WorkflowDescriptor getWorkflow(String name) throws FactoryException
根据工做流定义名得到工做流定义。
2.public String[] getWorkflowNames()
获得全部已经定义的工做流名称。
3.public void initDone() throws FactoryException
初始化workflows.xml文件中列出的工做流定义文件信息。
osworkflow概念

1. Step:工做流所在的位置,整个工做流的步骤,也能够是一系列工做中的某个工做,好比某个审批流程中的文件交到某个领导处审批,此过程可能包括接收秘书 交来的文件、而后阅读、提出本身的意见、签字、叫给秘书继续处理,这整个过程能够是一个Step。但并非FSM中的Status。

2. Status:某个Step的状态,每一个Step能够有多个Status。好比上例中阅读,等待提意见,等待签字,等待交秘书处理,都是Step的状态。 Step+Status共同组成了工做流的状态,也就实现了FSM中的Status。Step的Status在OSWorkflow中就是一段文本,状态 的判断其实就是自定义的一段文本的比较,很是灵活。

3. Action:形成工做流状态转换的动做,好比”阅读文件“动做,形成了工做流状态从”领导审批+等待阅读"转换成“领导审批+等待提出意见”。因为工做流的状态是Step+Status,因此Action能够形成Stats的变化,也能够形成Step的变化。

4. Result:工做流状态的转换,也就是Action形成的结果。也就是FSM中的Transition。每一个Action中至少包含一个 unconditional result和包含0或多个conditional result,Result的优先级顺序是 第一个符合条件的conditional result > 其余符合条件的conditional result  > unconditional result。

5.Split/Join:字面意思就能够解释。Split能够产生多个unconditional result;而Join能够判断多个Step的状态,若是都知足条件的时候,Join产生一个unconditional result。能够用来实现其余工做流产品定义中的同步区的做用,好比一个投标文件的评标过程,分别要在技术方面和商务方面对标书进行评分,这样就可使 用Split将工做流分开进入商务评标组和技术评标组分别进行评标,当两个评标过程都完成后使用Join将两个流程合并,并对两个评标作的评分进行汇总。

6.External Functions:执行的功能和动做。任何的工做流引擎都要与实际的业务操做相结合,External Functions就是OSWorkflow中执行业务操做的部分,好比审批流程中,领导填写意见后将领导的意见更新到业务数据库中的过程。 Functions有两种类型,pre step function和post step function,分别发生转移前和发生转移后执行。Functions能够被定义到Step中和Action中。

7.Trigger Functions,一种不是定义在Action中的Function,依靠计划自动执行。

8.Validators:用来检验用户输入是否符合条件,只有符合条件,Action对应的转移才能执行,若是不符合条件,返回InvalidInputException异常。

 

配置OSWorkFlow经过Hibernate持久化

 

1.修改WEB-INF/classes/目录下的osworkflow.xml,改成

    

[xhtml]  view plain  copy
 
 print?
  1. <persistence class="com.opensymphony.workflow.spi.hibernate.HibernateWorkflowStore">  
  2. lt;/persistence>  

 

2.将下面的Hibernate.cfg.xml加到WEB-INF/classes/下面,这里使用mysql数据库。

 

[xhtml]  view plain  copy
 
 print?
  1. <?xml version='1.0' encoding='utf-8'?>  
  2. <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 2.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">  
  3. <hibernate-configuration>  
  4. <session-factory>  
  5. <!-- properties -->  
  6. <property name="connection.driver_class">org.gjt.mm.mysql.Driver</property>  
  7. <property name="connection.url">jdbc:mysql://redhat:3306/osworkflow</property>  
  8. <property name="connection.username">redhat</property>  
  9. <property name="connection.password">redhat</property>  
  10.   
  11.  <property name="dialect">net.sf.hibernate.dialect.MySQLDialect</property>  
  12. <property name="show_sql">true</property>  
  13. <property name="use_outer_join">true</property>  
  14.   
  15.  <property name="connection.pool_size">10</property>  
  16. <property name="statement_cache.size">25</property>  
  17.   
  18.  <property name="hibernate.hbm2ddl.auto">update</property>  
  19.   
  20.  <mapping resource="com/opensymphony/workflow/spi/hibernate/HibernateCurrentStep.hbm.xml"></mapping>  
  21. <mapping resource="com/opensymphony/workflow/spi/hibernate/HibernateHistoryStep.hbm.xml"></mapping>  
  22. <mapping resource="com/opensymphony/workflow/spi/hibernate/HibernateWorkflowEntry.hbm.xml"></mapping>  
  23. <mapping resource="com/opensymphony/module/propertyset/hibernate/PropertySetItemImpl.hbm.xml"></mapping>  
  24.   
  25. </session-factory>  
  26. </hibernate-configuration>  

 

3.把hibernate的jar及hibernate全部要到的jar,到WEB-INF/lib/
4.OSWorkflow要求客户端提供SessionFactory,自主控制session和transaction。在容器里能够直接生成 SessionFactory而后注入到workflow中。这里只是演示,因此直接修改.jsp文件,生成SessionFactory,传入 workflow中。

 

[java]  view plain  copy
 
 print?
  1. Workflow wf = new BasicWorkflow((String) session.getAttribute("username"));  
  2.   
  3. // osworkflow和hibernate居然都是经过Configuration类来进行配置的,没办法,谁让你们都须要进行配置而就那么几个单词呢  
  4. com.opensymphony.workflow.config.Configuration conf = new DefaultConfiguration();  
  5.   
  6.  SessionFactory sessionFactory = new net.sf.hibernate.cfg.Configuration().configure().buildSessionFactory();  
  7. conf.getPersistenceArgs().put("sessionFactory", sessionFactory);  
  8. wf.setConfiguration(conf);  

 

5.基本搞定。

 

 

OsWorkFlow工做流学习笔记

osworkflow学习笔记

 

     接口选择:
  osworkflow提供几种实现com.opensymphony.workflow.Workflow接口的类。
   
  BasicWorkflow:
  不提供事务支持,你能够经过持久层来实现事务处理。
  Workflow wf = new BasicWorkflow(username)
  这里的username是用来关联当前请求的用户。
   
  EJBWorkflow:
  用ejb容器来管理事务。在ejb-jar.xml中进行配置。
  Workflow wf = new EJBWorkflow()
  这里没有必要想basicworkflow和ofbizworkflow那样给出username。由于ejb容器已经校验过的。
   
  Ofbizworkflow:
  与basicworkflow比较类似,不一样只在于须要事务支持的方法由ofbiz TransactionUtil calls来包装。
   
  建立新的工做流实例:
  这里是以basicworkflow为例子
   

  Workflow wf = new BasicWorkflow(username);
  HashMap inputs = new HashMap();
  inputs.put("docTitle", request.getParameter("title");
  wf.initialize("workflowName", 1, inputs);


   
  执行action:

  Workflow wf = new BasicWorkflow(username);
  HashMap inputs = new HashMap();
  inputs.put("docTitle", request.getParameter("title");
  long id = Long.parseLong(request.getParameter("workflowId");
  wf.doAction(id, 1, inputs);


  查询:
  值得注意的是:并非全部的 workflow stores支持查询。当前的hibernate,jdbc和内存工做流存储支持查询。Hibernate存储不支持mixed-type查询(如,一个查询使用到了历史和当前step contexts)。为了执行一个查询,须要构造出一个WorkflowExpressionQuery对象。查询方法是在这个对象上被调用的。
  简单查询、嵌套查询、mixed-context查询(不支持hibernate工做流存储)在docs文档的5.4部分都有。
   
Step
大体至关于流程所在的位置。譬如企业年检,年检报告书在企业端算一个step,在工商局算第二个step,在复核窗口算第三个step。每一个step能够有多种状态(status)和多个动做(action),用Workflow.getCurrentSteps()能够得到全部当前的step(若是有并列流程,则可能同时有多个step,例如一次年检可能同时位于“初审”step和“广告经营资格审查”step)。
 
Status
流程在某个step中的状态。很容易理解,譬如“待认领”、“审核不经过”之类的。OSWorkflow中的状态彻底是由开发者自定义的,状态判别纯粹是字符串比对,灵活性至关强,并且能够把定义文件作得很好看。
 
Action
致使流程状态变迁的动做。一个action典型地由两部分组成:能够执行此动做的条件(conditions),以及执行此动做的结果(results)。条件能够用BeanShell脚原本判断,所以具备很大的灵活性,几乎任何与流程相关的东西均可以用来作判断。
 
Result
执行动做后的结果。这是个比较重要的概念。result分为两种,conditional-result和unconditional-result。执行一个动做以后,首先判断全部conditional-result的条件是否知足,知足则使用该结果;若是没有任何contidional-result知足条件,则使用unconditional-result。unconditional-result须要指定两部分信息:old-status,表示“当前step的状态变成什么”;后续状态,多是用step+status指定一个新状态,也可能进入split或者join。
 
conditional-result很是有用。仍是以年检为例,一样是提交年检报告书,“未提交”和“被退回”是不一样的状态,在这两个状态基础上执行“提交”动做,结果分别是“初次提交”和“退回以后再次提交”。这时能够考虑在“提交”动做上用conditional-result。
 
Split/Join
流程的切分和融合。很简单的概念,split提供多个result;join则判断多个current step的状态,提供一个result。
 
*     *     *
 
熟悉这些概念,在流程定义中尽可能使用中文,能够给业务代码和表现层带来不少方便。

 

目的
这篇指导资料的目的是介绍OSWorkflow的全部概念,指导你如何使用它,而且保证你逐步理解OSWorkflow的关键内容。

本指导资料假定你已经部署OSWorkflow的范例应用在你的container上。范例应用部署是使用基于内存的数据存储,这样你不须要担忧如何配置其余持久化的例子。范例应用的目的是为了说明如何应用OSWorkflow,一旦你精通了OSWorkflow的流程定义描述符概念和要素,应该能经过阅读这些流程定义文件而了解实际的流程。

本指导资料目前有3部分:
1. 你的第一个工做流
2. 测试你的工做流
3. 更多的流程定义描述符概念


1. Your first workflow 
建立描述符
首先,让咱们来定义工做流。你可使用任何名字来命名工做流。一个工做流对应一个XML格式的定义文件。让咱们来开始新建一个“myworkflow.xml”的文件,这是样板文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE workflow PUBLIC 
  "-//OpenSymphony Group//DTD OSWorkflow 2.7//EN"
  "http://www.opensymphony.com/osworkflow/workflow_2_7.dtd"> 
<workflow>
  <initial-actions>
    ...
  </initial-actions>
  <steps>
    ...
  </steps>
</workflow>
首先是标准的XML头部,要注意的是OSWorkflow将会经过这些指定的DTD来验证XML内容的合法性。你可使用绝大多数的XML编辑工具来编辑它,而且能够highlight相应的错误。

步骤和动做
接下来咱们来定义初始化动做和步骤。首先须要理解的OSWorkflow重要概念是steps (步骤) 和 actions (动做)。一个步骤是工做流所处的位置,好比一个简单的工做流过程,它可能从一个步骤流转到另一个步骤(或者有时候仍是停留在同样的步骤)。举例来讲,一个文档管理系统的流程,它的步骤名称可能有“First Draft - 草案初稿”,“Edit Stage -编辑阶段”,“At publisher - 出版商”等。

动做指定了可能发生在步骤内的转变,一般会致使步骤的变动。在咱们的文件管理系统中,在“草案初稿”这个步骤可能有“start first draft - 开始草案初稿”和“complete first draft - 完成草案初稿”这样2个动做。

简单的说,步骤是“在哪里”,动做是“能够去哪里”。

初始化步骤是一种特殊类型的步骤,它用来启动工做流。在一个工做流程开始前,它是没有状态,不处在任何一个步骤,用户必须采起某些动做才能开始这个流程。这些特殊步骤被定义在 <initial-actions>。

在咱们的例子里面,假定只有一个简单的初始化步骤:“Start Workflow”,它的定义在里面
<initial-actions>:

<action id="1" name="Start Workflow">
  <results>
    <unconditional-result old-status="Finished" status="Queued" step="1"/>
  </results>
</action>
这个动做是最简单的类型,只是简单地指明了下一个咱们要去的步骤和状态。

工做流状态
工做流状态是一个用来描述工做流程中具体步骤状态的字符串。在咱们的文档管理系统中,在“草案初稿”这个步骤可能有2个不一样的状态:“Underway - 进行中”和“Queued - 等候处理中”

咱们使用“Queued”指明这个条目已经被排入“First Draft”步骤的队列。好比说某人请求编写某篇文档,可是尚未指定做者,那么这个文档在“First Draft”步骤的状态就是“Queued”。“Underway”状态被用来指明一个做者已经挑选了一篇文档开始撰写,并且可能正在锁定这篇文档。

第一个步骤
让咱们来看第一个步骤是怎样被定义在<steps>元素中的。咱们有2个动做:第一个动做是保持当前步骤不变,只是改变了状态到“Underway”,第二个动做是移动到工做流的下一步骤。咱们来添加以下的内容到<steps>元素:

<step id="1" name="First Draft">
  <actions>
    <action id="1" name="Start First Draft">
      <results>
        <unconditional-result old-status="Finished"
        status="Underway" step="1"/>
      </results>
    </action>
    <action id="2" name="Finish First Draft">
      <results>
        <unconditional-result old-status="Finished"
        status="Queued" step="2"/>
      </results>
    </action>
  </actions>
</step>
<step id="2" name="finished" />这样咱们就定义了2个动做,old-status属性是用来指明当前步骤完成之后的状态是什么,在大多数的应用中,一般用"Finished"表示。

上面定义的这2个动做是没有任何限制的。好比,一个用户能够调用action 2而不用先调用action 1。很明显的,咱们若是没有开始撰写草稿,是不可能去完成一个草稿的。一样的,上面的定义也容许你开始撰写草稿屡次,这也是毫无心义的。咱们也没有作任何的处理去限制其余用户完成别人的草稿。这些都应该须要想办法避免。

让咱们来一次解决这些问题。首先,咱们须要指定只有工做流的状态为“Queued”的时候,一个caller (调用者)才能开始撰写草稿的动做。这样就能够阻止其余用户屡次调用开始撰写草稿的动做。咱们须要指定动做的约束,约束是由Condition(条件)组成。

条件
OSWorkflow 有不少有用的内置条件可使用。在此相关的条件是“StatusCondition - 状态条件”。 条件也能够接受参数,参数的名称一般被定义在javadocs里(若是是使用Java Class实现的条件的话)。在这个例子里面,状态条件接受一个名为“status”的参数,指明了须要检查的状态条件。咱们能够从下面的xml定义里面清楚的理解:

<action id="1" name="Start First Draft">
  <restrict-to>
    <conditions>
      <condition type="class">
        <arg name="class.name">
          com.opensymphony.workflow.util.StatusCondition</arg>
        <arg name="status">Queued</arg>
      </condition>
    </conditions>
  </restrict-to>
  <results>
    <unconditional-result old-status="Finished" status="Underway" step="1"/>
  </results>
</action>但愿对于条件的理解如今已经清楚了。上面的条件定义保证了动做1只能在当前状态为“Queued”的时候才能被调用,也就是说在初始化动做被调用之后。

函数
接下来,咱们想在一个用户开始撰写草稿之后,设置他为“owner”。为了达到这样的目的,咱们须要作2件事情:

1) 经过一个函数设置“caller”变量在当前的环境设置里。
2) 根据“caller”变量来设置“owner”属性。

函数是OSWorkflow的一个功能强大的特性。函数基本上是一个在工做流程中的工做单位,他不会影响到流程自己。举例来讲,你可能有一个“SendEmail”的函数,用来在某些特定的流程流转发生时来发送email提醒。

函数也能够用来添加变量到当前的环境设置里。变量是一个指定名称的对象,能够用来在工做流中被之后的函数或者脚本使用。

OSWorkflow提供了一些内置的经常使用函数。其中一个称为“Caller”,这个函数会得到当前调用工做流的用户,并把它放入一个名为“caller”的字符型变量中。

由于咱们须要追踪是哪一个用户开始了编写草稿,因此可使用这个函数来修改咱们的动做定义:

<action id="1" name="Start First Draft">
  <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>h3 组合起来
把这些概念都组合起来,这样咱们就有了动做1:

<action id="1" name="Start First Draft">
  <restrict-to>
    <conditions>
      <condition type="class">
        <arg name="class.name">
          com.opensymphony.workflow.util.StatusCondition
        </arg>
        <arg name="status">Queued</arg>
      </condition>
    </conditions>
  </restrict-to>
  <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>咱们使用相似想法来设置动做2:

<action id="2" name="Finish First Draft">
  <restrict-to>
    <conditions type="AND">
      <condition type="class">
        <arg name="class.name">
          com.opensymphony.workflow.util.StatusCondition
        </arg>
        <arg name="status">Underway</arg>
      </condition>
      <condition type="class">
        <arg name="class.name">
          com.opensymphony.workflow.util.AllowOwnerOnlyCondition
       </arg>
      </condition>
    </conditions>
  </restrict-to>
  <results>
    <unconditional-result old-status="Finished" status="Queued" step="2"/>
  </results>
</action>在这里咱们指定了一个新的条件:“allow owner only”。这样可以保证只有开始撰写这份草稿的用户才能完成它。而状态条件确保了只有在“Underway”状态下的流程才能调用“finish first draft”动做。

把他们组合在一块儿,咱们就有了第一个流程定义:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE workflow PUBLIC 
  "-//OpenSymphony Group//DTD OSWorkflow 2.7//EN"
  "http://www.opensymphony.com/osworkflow/workflow_2_7.dtd"> 
<workflow>
  <initial-actions>
    <action id="1" name="Start Workflow">
      <results>
        <unconditional-result old-status="Finished"
        status="Queued" step="1"/>
      </results>
    </action>
  </initial-actions>
  <steps>
    <step id="1" name="First Draft">
      <actions>
        <action id="1" name="Start First Draft">
          <restrict-to>
            <conditions>
              <condition type="class">
                <arg name="class.name">
                   com.opensymphony.workflow.util.StatusCondition
                </arg>
                <arg name="status">Queued</arg>
              </condition>
            </conditions>
          </restrict-to>
          <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>
        <action id="2" name="Finish First Draft">
          <restrict-to>
            <conditions type="AND">
              <condition type="class">
                <arg name="class.name">
                    com.opensymphony.workflow.util.StatusCondition
                </arg>
                <arg name="status">Underway</arg>
              </condition>
              <condition type="class">
                <arg name="class.name">
                  com.opensymphony.workflow.util.
                  AllowOwnerOnlyCondition
                </arg>
              </condition>
            </conditions>
          </restrict-to>
          <results>
            <unconditional-result old-status="Finished"
            status="Queued" step="2"/>
          </results>
        </action>
      </actions>
    </step>
    <step id="2" name="finished" />
  </steps>
</workflow>如今这个工做流的定义已经完整了,让咱们来测试和检查它的运行。

2. Testing your workflow
如今咱们已经完成了一个完整的工做流定义,下一步是检验它是否按照咱们预想的方式执行。

在一个快速开发环境中,最简单的方法就是写一个测试案例。经过测试案例调用工做流,根据校验结果和捕捉可能发生的错误,来保证流程定义的正确性。

咱们假设你已经熟悉Junit和了解怎样编写测试案例。若是你对这些知识还不了解的话,能够去JUnit的网站查找、阅读相关文档。编写测试案例会成为你的一个很是有用的工具。

在开始载入流程定义、调用动做之前,咱们须要配置OSWorkflow的数据存储方式和定义文件的位置等。

配置 osworkflow.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>这个例子指明了咱们准备使用内存 (MemoryWorkflowStore) 来保存流程数据。这样能够减小设置数据库的相关信息,减小出问题的可能性。用内存持久化对于测试来讲是很是方便的。

Workflow factories
上面的配置文件还指明了咱们工做流工厂(XMLWorkflowFactory),工做流工厂的主要功能是管理流程定义文件,包括读取定义文件和修改定义文件的功能。经过'resource'这个属性指明了采用经过从classpath中读取流程定义文件的方式,按照这个定义,接下来咱们须要在classpath中建立一个名为workflows.xml的文件。

workflows.xml 的内容:

<workflows>
  <workflow name="mytest" type="resource" location="myworkflow.xml"/>
</workflows>咱们把 myworkflow.xml 和workflows.xml放在同一目录,这样它就可以被工做流工厂读取了。

这样就完成了配置,接下来是初始化一个流程并调用它。

Initialising OSWorkflow
OSWorkflow 的调用模式至关简单:经过一个主要的接口来执行大部分操做。这个接口就是 Workflow interface,及其扩展 AbstractWorkflow 的实现,例如EJBWorkflow 和 SOAPWorkflow. 为了简单起见,咱们使用最基本的一种: BasicWorkflow。

首先,咱们来建立Workflow。在实际项目中,这个对象应该被放在一个全局的位置上以供重用,由于每次都建立一个新的Workflow对象是须要耗费比较昂贵的系统资源。在这里的例子,咱们采用BasicWorkflow,它的构建器由一个当前调用者的用户名构成,固然咱们不多看到单用户的工做流应用,能够参考其余的Workflow实现有不一样的方式去得到当前调用者。

为了简单起见,咱们采用BasicWorkflow来建立一个单一的用户模式,避免编写其余获取用户方法的麻烦。

这样咱们来建立一个'testuser'调用的workflow:

Workflow workflow = new BasicWorkflow("testuser";下一步是提供配置文件,在大多数状况下,只是简单的传递一个DefaultConfiguration实例:

DefaultConfiguration config = new DefaultConfiguration();
workflow.setConfiguration(config);如今咱们已经建立而且配置好了一个workflow,接下来就是开始调用它了。

启动和进行一个工做流程
首先咱们须要调用initialize 方法来启动一个工做流程,这个方法有3个参数,workflow name (定义在workflows.xml里,经过workflow factory处理), action ID (咱们要调用的初始化动做的ID),和初始化变量。 由于在例子里面不需初始化变量,因此咱们只是传递一个null,

long workflowId = workflow.initialize("mytest", 1, null);咱们如今已经有了一个工做流实例,返回的workflowId能够在后续的操做中来表明这个实例。这个参数会在Workflow interface的绝大部分方法中用到。

检验工做流
如今让咱们来检验启动的工做流实例是否按照咱们所预期的那样运行。根据流程定义,咱们指望的当前步骤是第一步,并且应该能够执行第一个动做(开始编写草稿)。

Collection currentSteps = workflow.getCurrentSteps
(workflowId);
//校验只有一个当前步骤
assertEquals("Unexpected number of current steps", 
1, currentSteps.size());
//校验这个步骤是1
Step currentStep = (Step)currentSteps.iterator().next();
assertEquals("Unexpected current step", 1,
currentStep.getStepId());

int[] availableActions = 
workflow.getAvailableActions(workflowId);
//校验只有一个可执行的动做
assertEquals("Unexpected number of available actions", 1, 
availableActions.length);
//校验这个步骤是1
assertEquals("Unexpected available action", 1, availableActions[0]);执行动做
如今已经校验完了工做流实例正如咱们所指望的到了第一个步骤,让咱们来开始执行第一个动做:

workflow.doAction(workflowId, 1, null);这是简单的调用第一个动做,工做流引擎根据指定的条件,改变状态到‘Underway’,而且保持在当前步骤。

如今咱们能够相似地调用第2个动做,它所须要的条件已经都知足了。

在调用完第2个动做之后,根据流程定义就没有可用的动做了,getAvailableActions将会返回一个空数组。

Congratulations, 你已经完成了一个工做流定义而且成功地调用了它。下一节咱们将会讲解osworkflow一些更深刻的概念。

3. Further descriptor concepts 
定义条件和函数
你也许已经注意到,到目前为止,咱们定义的条件和函数类型都是“class”。这种类型的条件和函数接受一个参数:“class.name”,以此来指明一个实现FunctionProvider或Condition接口的完整类名。

在osworkflow里面也有一些其余内置的类型,包括beanshell,无状态的session bean,JNDI树上的函数等。咱们在下面的例子里使用beanshell类型。

Property sets
咱们可能须要在工做流的任意步骤持久化一些少许数据。在osworkflow里,这是经过OpenSymphony的PropertySet library来实现。一个PropertySet基本上是一个能够持久化的类型安全map,你能够添加任意的数据到propertyset(一个工做流实例对应一个propertyset),并在之后的流程中再读取这些数据。除非你特别指定操做,不然propertyset中的数据不会被清空或者被删除。任意的函数和条件均可以和propertyset交互,以beanshell script来讲,能够在脚本上下文中用“propertyset”这个名字来获取。下面来看具体写法是怎么样的,让咱们增长以下的代码在“Start First Draft”动做的pre-functions里面:

<function type="beanshell">
  <arg name="script">propertySet.setString("foo", "bar"</arg>
</function>这样咱们就添加了一个持久化的属性“foo”,它的值是“bar”。这样在之后的流程中,咱们就能够得到这个值了。

Transient Map 临时变量
另一个和propertyset变量相对的概念是临时变量:“transientVars”。临时变量是一个简单的map,只是在当前的工做流调用的上下文内有效。它包括当前的工做流实例,工做流定义等对应值的引用。你能够经过FunctionProvider的javadoc来查看这个map有那些可用的key。

还记得咱们在教程的第2部分传入的那个null吗?若是咱们不传入null的话,那么这些输入数据将会被添加到临时变量的map里。

inputs 输入
每次调用workflow的动做时能够输入一个可选的map,能够在这个map里面包含供函数和条件使用的任何数据,它不会被持久化,只是一个简单的数据传递。

Validators 校验器
为了让工做流可以校验输入的数据,引入了校验器的概念。一个校验器和函数,条件的实现方式很是相似(好比,它能够是一个class,脚本,或者EJB)。在这个教程里面,咱们将会定义一个校验器,在“finish first draft”这个步骤,校验用户输入的数据“working.title”不能超过30个字符。这个校验器看起来是这样的:

package com.mycompany.validators;

public class TitleValidator implements Validator
{
  public void validate(Map transientVars, Map args,
  PropertySet ps) 
        throws InvalidInputException, WorkflowException
  {
    String title = 
    (String)transientVars.get("working.title"; 
    if(title == null)
      throw new InvalidInputException("Missing working.title";
    if(title.length() > 30)
      throw new InvalidInputException("Working title too long";
  }
}而后经过在流程定义文件添加validators元素,就能够登记这个校验器了:

<validators>
  <validator type="class">
    <arg name="class.name">
      com.mycompany.validators.TitleValidator
    </arg>
  </validator>
</validators>这样,当咱们执行动做2的时候,这个校验器将会被调用,而且检验咱们的输入。这样在测试代码里面,若是加上:

Map inputs = new HashMap();
inputs.put("working.title", 
  "the quick brown fox jumped over the lazy dog," +
  " thus making this a very long title";
workflow.doAction(workflowId, 2, inputs);咱们将会获得一个InvalidInputException,这个动做将不会被执行。减小输入的title字符,将会让这个动做成功执行。

咱们已经介绍了输入和校验,下面来看看寄存器。

Registers 寄存器
寄存器是一个工做流的全局变量。和propertyset相似,它能够在工做流实例的任意地方被获取。和propertyset不一样的是,它不是一个持久化的数据,而是每次调用时都须要从新计算的数据。

它能够被用在什么地方呢?在咱们的文档管理系统里面,若是定义了一个“document”的寄存器,那么对于函数、条件、脚原本说就是很是有用的:能够用它来得到正在被编辑的文档。

寄存器地值会被放在临时变量(transientVars map)里,这样可以在任意地方得到它。

定义一个寄存器和函数、条件的一个重要区别是,它并非依靠特定的调用(不用关心当前的步骤,或者是输入数据,它只是简单地暴露一些数据而已),因此它不用临时变量里的值。

寄存器必须实现Register接口,而且被定义在流程定义文件的头部,在初始化动做以前。

举例来讲,咱们将会使用一个osworkflow内置的寄存器:LogRegister。这个寄存器简单的添加一个“log”变量,可以让你使用Jakarta的commons-logging输出日志信息。它的好处是会在每条信息前添加工做流实例的ID。

<registers>
  <register type="class" variable-name="log">
    <arg name="class.name">
      com.opensymphony.workflow.util.LogRegister
    </arg>
    <arg name="addInstanceId">true</arg>
    <arg name="Category">workflow</arg>
  </register>
</registers>这样咱们定义了一个可用的“log”变量,能够经过其余的pre-function的脚本里面使用它:

<function type="beanshell">
  <arg name="script">transientVars.get("log".info("executing action 2"
  </arg>
</function>日志输出将会在前面添加工做流实例的ID

结论
这个教程的目的是但愿能够阐明一些主要的osworkflow概念。你还能够经过API和流程定义格式去获取更多的信息。有一些更高级的特性没有在此提到,好比splits 分支、joins 链接, nested conditions 复合条件、auto stpes 自动步骤等等。你能够经过阅读手册来得到更进一步的理解。
 
osworkflow基础配置tomcat5+oracle8+win2k(1)

 首先,下载URL https://osworkflow.dev.java.net/files/documents/635/27138/osworkflow-2.8.0.zip
。解压后。
一、将osworkflow-2.8.0-example.war拷贝至tomcat的webapp下,启动tomcat,访问http://localhost/osworkflow-2.8.0-example。 
二、src/webapp直接拷贝到%tomcat_home%/webapp,还须要拷贝lib   os.war里面有,拷贝主要是war部署的路径比较讨厌。
    osworkflow提供了多种持久化机制MemoryStore (default), SerializableStore, JDBCStore, OfbizStore等等。因为下载的example是为了方便初学者尽快的将程序运行起来,因此采用了MemoryStore。呵呵,实际的系统可不会让数据全呆在内存里哦。改为JDBCStore试试。

    一、修改tomcat的sever.xml(中间=后面须要加双引号)  添加:

<Context path=/osworkflow docBase=osworkflow  debug=5 reloadable=true crossContext=true>
  <Logger className=org.apache.catalina.logger.FileLogger
             prefix=localhost_osworkflow_log. suffix=.txt
             timestamp=true/>
  <Resource name= jdbc/mydb auth=Container
              type=javax.sql.DataSource/> 
<ResourceParams name=jdbc/mydb>
  <parameter>
    <name>factory</name>
    <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
  </parameter>
  <parameter>
    <name>driverClassName</name>
    <value>oracle.jdbc.driver.OracleDriver</value>
  </parameter>
  <parameter>
    <name>url</name>
    <value>jdbc:oracle:thin:@127.0.0.1:1521:orcl</value>
  </parameter>
  <parameter>
    <name>username</name>
    <value>oswf</value>
  </parameter>
  <parameter>
    <name>password</name>
    <value>oswf</value>
  </parameter>
  <parameter>
    <name>maxActive</name>
    <value>20</value>
  </parameter>
  <parameter>
    <name>maxIdle</name>
    <value>10</value>
  </parameter>
  <parameter>
    <name>maxWait</name>
    <value>-1</value>
  </parameter>
</ResourceParams>
</Context>


    二、修改WEB-INF/classes/osworkflow.xml(红色部分根据您的数据库做相应修改)

<osworkflow>
    <persistence class="com".opensymphony.workflow.spi.jdbc.JDBCWorkflowStore>
       <!-- For jdbc persistence, all are required. -->
       <property key=datasource value= jdbc/mydb />
       <property key=entry.sequence value= SELECT seq_os_wfentry.nextVal from dual />
       <property key=entry.table value=OS_WFENTRY/>
       <property key=entry.id value=ID/>
       <property key=entry.name value=NAME/>
       <property key=entry.state value=STATE/>
       <property key=step.sequence value= SELECT seq_os_currentsteps.nextVal from dual />
       <property key=history.table value=OS_HISTORYSTEP/>
       <property key=current.table value=OS_CURRENTSTEP/>
       <property key=historyPrev.table value=OS_HISTORYSTEP_PREV/>
       <property key=currentPrev.table value=OS_CURRENTSTEP_PREV/>
       <property key=step.id value=ID/>
       <property key=step.entryId value=ENTRY_ID/>
       <property key=step.stepId value=STEP_ID/>
       <property key=step.actionId value=ACTION_ID/>
       <property key=step.owner value=OWNER/>
       <property key=step.caller value=CALLER/>
       <property key=step.startDate value=START_DATE/>
       <property key=step.finishDate value=FINISH_DATE/>
       <property key=step.dueDate value=DUE_DATE/>
       <property key=step.status value=STATUS/>
       <property key=step.previousId value=PREVIOUS_ID/>
      </persistence>
    <factory class="com".opensymphony.workflow.loader.XMLWorkflowFactory>
        <property key=resource value=workflows.xml />
    </factory>
</osworkflow>


    三、在WEB-INF/classes里新建propertyset.xml

<propertysets>
    <propertyset name=jdbc 
      class="com".opensymphony.module.propertyset.database.JDBCPropertySet>
        <arg name=datasource value= jdbc/mydb />
        <arg name=table.name value=OS_PROPERTYENTRY/>
        <arg name=col.globalKey value=GLOBAL_KEY/>
        <arg name=col.itemKey value=ITEM_KEY/>
        <arg name=col.itemType value=ITEM_TYPE/>
        <arg name=col.string value=STRING_VALUE/>
        <arg name=col.date value=DATE_VALUE/>
        <arg name=col.data value=DATA_VALUE/>
        <arg name=col.float value=FLOAT_VALUE/>
        <arg name=col.number value=NUMBER_VALUE/>
    </propertyset>
</propertysets>


    四、修改WEB-INF/classes下的osuser.xml

<opensymphony-user>
    <provider class="com".opensymphony.user.provider.jdbc.JDBCAccessProvider>
        <property name=user.table>OS_USER</property>
        <property name=group.table>OS_GROUP</property>
        <property name=membership.table>OS_MEMBERSHIP</property>
        <property name=user.name>USERNAME</property>
        <property name=user.password>PASSWORDHASH</property>
        <property name=group.name>GROUPNAME</property>
        <property name=membership.userName>USERNAME</property>
        <property name=membership.groupName>GROUPNAME</property>
        <property name=datasource>java:comp/env/ jdbc/mydb </property>
    </provider>
    <provider class="com".opensymphony.user.provider.jdbc.JDBCCredentialsProvider>
        <property name=user.table>OS_USER</property>
        <property name=group.table>OS_GROUP</property>
        <property name=membership.table>OS_MEMBERSHIP</property>
        <property name=user.name>USERNAME</property>
        <property name=user.password>PASSWORDHASH</property>
        <property name=group.name>GROUPNAME</property>
        <property name=membership.userName>USERNAME</property>
        <property name=membership.groupName>GROUPNAME</property>
        <property name=datasource>java:comp/env /jdbc/mydb </property>
    </provider>
    <provider class="com".opensymphony.user.provider.jdbc.JDBCProfileProvider>
        <property name=user.table>OS_USER</property>
        <property name=group.table>OS_GROUP</property>
        <property name=membership.table>OS_MEMBERSHIP</property>
        <property name=user.name>USERNAME</property>
        <property name=user.password>PASSWORDHASH</property>
        <property name=group.name>GROUPNAME</property>
        <property name=membership.userName>USERNAME</property>
        <property name=membership.groupName>GROUPNAME</property>
        <property name=datasource>java:comp/env/ jdbc/mydb </property>
    </provider>
    
    <!--
  Authenticators can take properties just like providers.
  This smart authenticator should work for 'most' cases - it dynamically looks up
  the most appropriate authenticator for the current server.
 -->
 <authenticator class="com".opensymphony.user.authenticator.SmartAuthenticator />
</opensymphony-user>

    五、在sql-plus里运行下载包里的 src\etc\deployment\jdbc\oracle.sql

    六、启动tomcat

    七、OK。
    
    八、以上都是借鉴过来的,
    九、在os_user中加入test用户,pass空,登录提示空指针,郁闷!!!


 
用osworkflow写一个请假例子 
    osworkflow扩展很是容易,跟咱们的应用结合起来使用也很容易。假设一个请假流程:员工请假,须要通过部门经理和人力资源部经理两人共同审批,只有当两人都许可时才经过,任一人驳回就失效,也就是一个and split和and Join流程,而且咱们附加一个要求,当发送请假请求、许可和驳回这几个操做时都将发送一条消息给相应的用户。
    流程定义文件以下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE workflow PUBLIC "-//OpenSymphony Group//DTD OSWorkflow 2.7//EN" 
"http://www.opensymphony.com/osworkflow/workflow_2_7.dtd">
<workflow>
    <initial-actions>
        <action id="0" name="开始">
            <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="填假单">
            <external-permissions>
                <permission name="permA">
                    <restrict-to>
                        <conditions type="AND">
                            <condition type="class"><!--流程处于Underway状态(流程已经启动)-->
                                <arg name="class.name">
                                    com.opensymphony.workflow.util.StatusCondition
                                </arg>
                                <arg name="status">Underway</arg>
                            </condition>
                            <condition type="class">
                                <arg name="class.name">
                                    com.opensymphony.workflow.util.AllowOwnerOnlyCondition
                                </arg>
                            </condition>
                        </conditions>
                    </restrict-to>
                </permission>
            </external-permissions>
            <actions>
                <action id="1" name="送出">
                    <restrict-to>
                        <conditions type="AND">
                            <condition type="class"><!--流程处于Underway状态(流程已经启动)-->
                                <arg name="class.name">
                                    com.opensymphony.workflow.util.StatusCondition
                                </arg>
                                <arg name="status">Underway</arg>
                            </condition>
                            <condition type="class">
                                <arg name="class.name">
                                    com.opensymphony.workflow.util.AllowOwnerOnlyCondition
                                </arg>
                            </condition>
                        </conditions>
                    </restrict-to>
                    <pre-functions>
                        <function type="class">
                            <arg name="class.name">
                                com.opensymphony.workflow.util.Caller
                            </arg>
                        </function>

                    </pre-functions>
                    <results>
                        <unconditional-result old-status="Finished"
                            split="1" status="Queued">
                            <post-functions>
                                <function type="class">
                                    <arg name="class.name">
                                        net.rubyeye.leavesys.service.workflow.SendRemindInfFunction
                                    </arg>
                                    <arg name="groupName">
                                        AND (GROUPNAME='dept_manager' or
                                        GROUPNAME='comp_manager')
                                    </arg>
                                    <arg name="content">
                                        you have leavemsg to
                                        check!please check it!
                                    </arg>
                                </function>
                            </post-functions>
                        </unconditional-result>
                    </results>
                </action>
            </actions>
        </step>
        <step id="2" name="部门经理批假单">
            <actions>
                <action id="2" name="准许">
                    <restrict-to>
                        <conditions>
                            <condition type="class">
                                <arg name="class.name">
                                    com.opensymphony.workflow.util.OSUserGroupCondition
                                </arg>
                                <arg name="group">dept_manager</arg>
                            </condition>
                        </conditions>
                    </restrict-to>
                    <pre-functions>
                        <function type="class">
                            <arg name="class.name">
                                com.opensymphony.workflow.util.Caller
                            </arg>
                        </function>
                        <function type="beanshell">
                            <arg name="script">
                                propertySet.setString("action1",
                                "success");
                            </arg>
                        </function>
                    </pre-functions>
                    <results>
                        <unconditional-result old-status="Finished"
                            status="Queued" join="1" owner="${caller}" />
                    </results>
                </action>

                <action id="3" name="驳回">
                    <restrict-to>
                        <conditions>
                            <condition type="class">
                                <arg name="class.name">
                                    com.opensymphony.workflow.util.OSUserGroupCondition
                                </arg>
                                <arg name="group">dept_manager</arg>
                            </condition>
                        </conditions>
                    </restrict-to>
                    <pre-functions>
                        <function type="class">
                            <arg name="class.name">
                                com.opensymphony.workflow.util.Caller
                            </arg>
                        </function>
                        <function type="beanshell">
                            <arg name="script">
                                propertySet.setString("action1",
                                "fail");
                            </arg>
                        </function>
                    </pre-functions>
                    <results>
                        <unconditional-result old-status="Finished"
                            status="Queued" join="2" owner="${caller}" />
                    </results>
                </action>
            </actions>
        </step>

        <step id="3" name="公司经理批假单">
            <actions>
                <action id="4" name="准许">
                    <restrict-to>
                        <conditions>
                            <condition type="class">
                                <arg name="class.name">
                                    com.opensymphony.workflow.util.OSUserGroupCondition
                                </arg>
                                <arg name="group">comp_manager</arg>
                            </condition>
                        </conditions>
                    </restrict-to>
                    <pre-functions>
                        <function type="class">
                            <arg name="class.name">
                                com.opensymphony.workflow.util.Caller
                            </arg>
                        </function>
                        <function type="beanshell">
                            <arg name="script">
                                propertySet.setString("action2",
                                "success");
                            </arg>
                        </function>
                    </pre-functions>
                    <results>
                        <unconditional-result old-status="Finished"
                            status="Queued" join="1" owner="${caller}" />
                    </results>
                </action>

                <action id="5" name="驳回">
                    <restrict-to>
                        <conditions>
                            <condition type="class">
                                <arg name="class.name">
                                    com.opensymphony.workflow.util.OSUserGroupCondition
                                </arg>
                                <arg name="group">dept_manager</arg>
                            </condition>
                        </conditions>
                    </restrict-to>
                    <pre-functions>
                        <function type="class">
                            <arg name="class.name">
                                com.opensymphony.workflow.util.Caller
                            </arg>
                        </function>
                        <function type="beanshell">
                            <arg name="script">
                                propertySet.setString("action2",
                                "fail");
                            </arg>
                        </function>
                    </pre-functions>
                    <results>
                        <unconditional-result old-status="Finished"
                            status="Queued" join="2" owner="${caller}" />
                    </results>
                </action>
            </actions>
        </step>

        <step id="4" name="中止" />
    </steps>
    <splits>
        <split id="1">
            <unconditional-result old-status="Finished" status="Queued"
                step="2" />
            <unconditional-result old-status="Finished" status="Queued"
                step="3" />
        </split>
    </splits>
    <joins>
        <join id="1">
            <conditions type="AND">
                <condition type="beanshell">
                    <arg name="script">
                        <![CDATA[
     "Finished".equals(jn.getStep(2).getStatus()) &&
        "Finished".equals(jn.getStep(3).getStatus())&&"success".equals(propertySet.getString("action1"))&&
        "success".equals(propertySet.getString("action2"))
      ]]>
                    </arg>
                </condition>
            </conditions>
            <unconditional-result old-status="Finished" status="Queued"
                step="4"/>
        </join>

        <join id="2">
            <conditions type="OR">
                <condition type="beanshell">
                    <arg name="script">
                        <![CDATA[
     "Finished".equals(jn.getStep(2).getStatus()) &&"fail".equals(propertySet.getString("action1"))
      ]]>
                    </arg>
                </condition>
                <condition type="beanshell">
                    <arg name="script">
                        <![CDATA[
    
        "Finished".equals(jn.getStep(3).getStatus())&&"fail".equals(propertySet.getString("action2"))
      ]]>
                    </arg>
                </condition>
            </conditions>
            <unconditional-result old-status="Finished" step="4"
                status="Queued">
                <post-functions>
                    <function type="class">
                        <arg name="class.name">
                            net.rubyeye.leavesys.service.workflow.SendRemindInfFunction
                        </arg>
                        <arg name="groupName">
                            AND GROUPNAME='employee' 
                        </arg>
                        <arg name="content">
                            you leveamsg is fail!!!
                        </arg>
                    </function>
                </post-functions>
            </unconditional-result>
        </join>
    </joins>
</workflow>
请注意,咱们在许可或者经过的时候propertySet.setString("action2",......),propertySet.setString("action3",......),而后在join点判断,若是两个都是success,流程结束;若是一个是fail,就发送一个消息给员工。

发送消息的function像这样:
package net.rubyeye.leavesys.service.workflow;

import java.sql.SQLException;
import java.util.Map;

import net.rubyeye.leavesys.domain.RemindInf;
import net.rubyeye.leavesys.service.ManagerFactory;

import com.opensymphony.module.propertyset.PropertySet;
import com.opensymphony.workflow.FunctionProvider;
import com.opensymphony.workflow.WorkflowException;

public class SendRemindInfFunction implements FunctionProvider {

    public void execute(Map transientVars, Map args, PropertySet ps)
            throws WorkflowException {
        String groupName = (String) args.get("groupName");
        String content = (String) args.get("content");
        RemindInf remindInf = new RemindInf();
        remindInf.setContent(content);
        try {
            ManagerFactory.getRemindService().addRemindInfByGroupName(
                    groupName, remindInf);
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

}

获得两个参数groupName和content(消息内容),调用业务对象发送消息。

相关文章
相关标签/搜索