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(消息內容),調用業務對象發送消息。

相關文章
相關標籤/搜索