Osworkflow定義工作流是通過一個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>
都是按照標(biāo)準(zhǔn)xml指定。通過指定的dtd來校驗xml文件。一般需要輔助工具如xmlspy來自動校驗xml文件里的錯誤,這樣會方便大家檢測xml文件中的錯誤。
Step和actions
理解這個xml文件的第一個重點在于理解step和actions的在工作流系統(tǒng)中的概念。一個step就是這樣的一個概念:一個工作流中所處的不同位置。如在一個文檔管理系統(tǒng)中??梢允瞧鸩荨⒕庉嬰A段、發(fā)布等等。
Actions指定不同step中的變遷。還是用例子來理解更為形象一些。在一個文檔管理系統(tǒng)當(dāng)中如在“第一個起草”中的“開始第一個起草”和“結(jié)束第一個起草”就是actions。
Initial actions 是一個action的特殊類型。在工作流開始時候,是沒有狀態(tài)的,也沒有任何step。用戶必須利用某個action來開始流程,這個用來開始工作流的action就被指定為initial-actions。
例如,我們假定我們只有一個initial-action,非常簡單,如下面:
<action id="1" name="Start Workflow">
<results>
<unconditional-result old-status="Finished" status="Queued" step="1"/>
</results>
</action>
這個action是個action的最簡單的例子。他只是簡單定義我們需要走想的step。
工作流status
Status用來描述特定step中的一個工作流的狀態(tài)。如在文檔管理系統(tǒng)當(dāng)中,我們的“第一次起草”step可以有兩個statuses,“underway”和“queued”
用“queued”來指示此條已經(jīng)在“first draft”中排隊。沒有安排誰來處理此文檔,但是已經(jīng)發(fā)出請求。而“underway”是指示一個作者已經(jīng)從隊列中取出此文檔并且或許已經(jīng)上鎖。表明正在first draft上工作。
Step實例:在這個例子中有兩個actions。第一個action(開始第一個起草)是在一個step中進(jìn)行。但是,更改狀態(tài)為underway,第二個action是工作流流轉(zhuǎn)到下一個step,我們假定下一個工作流step為“finish”。
<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" />
Old-status這個屬性用來指示對于當(dāng)前state(結(jié)束)將要進(jìn)入哪個history table。在絕大多數(shù)情況下,被寫為“finished”。
Conditions
Osworkflow有一些內(nèi)建的conditon。請參看javadocs,如果需要一個特定的condition。Condition可以接收參數(shù)。如本例就接收了一個參數(shù)“status”用來指定status
<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>
這樣就能保證在initial action被調(diào)用后才可以正確執(zhí)行。因為它需要確信當(dāng)前status是“queued”。
Functions
接下來,如果我們希望指定一個用戶開始first draft,他們變?yōu)?#8220;owner”為了達(dá)到這樣的目的,我們需要這樣做:
1、在當(dāng)前context中防止一個“caller”變量
2、 設(shè)置result的“owner”屬性為call變量。
function是osworkflow的一個非常強(qiáng)大的特征,一個function基本上是一個系列在工作流變遷之間執(zhí)行的工作,并不影響工作流本身。例如,你能夠有個sendmail功能,它的職責(zé)就是當(dāng)一個特定的變遷發(fā)生后發(fā)送郵件通知。
Functions能夠給當(dāng)前context添加變量。可以在其他functions或者scripts中使用。
Osworkflow 有自己內(nèi)建function。其中一個比較有用的就是“caller”。這個function的作用就是通過查找當(dāng)前的能夠調(diào)用工作流的用戶,把該用戶以字符串形式命名為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>
整合后的結(jié)果:
<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>
如果我們想要確保僅僅當(dāng)用戶開始first draft才能完成它(我們在以前的result的owner屬性中指定)。Status condition同樣確保“finish first draft” action只能在狀態(tài)為“underway”的情況下才可以被執(zhí)行。這就確保必須得有一個用戶已經(jīng)開始第一個起草工作。
<?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>
創(chuàng)建osworkflow.xml,例子如下:
<osworkflow>
<persistence class="com.opensymphony.workflow.spi.memory.MemoryWorkflowStore"/>
<factory class="com.opensymphony.workflow.loader.XMLWorkflowFactory">
<property name="resource" value="workflows.xml" />
</factory>
</osworkflow>
指定使用內(nèi)存數(shù)據(jù)庫。即非永久保存性質(zhì)。用做測試目的,這樣做足夠了。
Factory概念
上面的xml文件同時指定了xml workflowfactory。Workflow factory是用來管理workflowworkflow描述符。Xml workflow factory有一個特定屬性叫做“resource”它用來指定在哪里可以找到xml文件,這個resource必須從classpath中加載,所以對于上面的例子,你將要創(chuàng)建一個文件名為workflows.xml的文件追加到classpath中。文件樣本如下;
<workflows>
<workflow name="mytest" type="resource" location="myworkflow.xml"/>
</workflows>
所以,你還需要一個myworkflow.xml,
到此,我們完成了配置,首先初始化然后調(diào)用我們的workflow。
Initializing osworkflow:
Osworkflow有一個相對簡單的的調(diào)用模式,有一個main入口,這個main入口點是workflow的接口,經(jīng)常是作為實現(xiàn)AbstractWorkflow的子類。
首先,我們創(chuàng)建我們自己的工作流。這個對象應(yīng)該存儲在一個通用區(qū)域,并且能夠被重復(fù)使用。這樣做的一個辦法就是做一個static。BasicWorkflow的構(gòu)造器參數(shù)是一個當(dāng)前調(diào)用者的。
需要初始化和配置工作流的session,
Workflow workflow = new BasicWorkflow("testuser");
DefaultConfiguration config = new DefaultConfiguration();
workflow.setConfiguration(config);
調(diào)用初始化方法:
long workflowId = workflow.initialize("mytest", 1, null);
參數(shù)含義:工作流名字,action的id,action。
校驗工作流:
現(xiàn)在我們完成初始化工作流實例,我們期待著他的表現(xiàn)是我們所期待那樣的。
Collection currentSteps = workflow.getCurrentSteps(workflowId);
//verify we only have one current step
assertEquals("Unexpected number of current steps", 1, currentSteps.size());
//verify it‘s step 1
Step currentStep = (Step)currentSteps.iterator().next();
assertEquals("Unexpected current step", 1, currentStep.getStepId());
int[] availableActions = workflow.getAvailableActions(workflowId);
//verify we only have one available action
assertEquals("Unexpected number of available actions", 1, availableActions.length);
//verify it‘s action 1
assertEquals("Unexpected available action", 1, availableActions[0]);
調(diào)用action
workflow.doAction(workflowId, 1, null);