下面開始接觸 JBPM 的 JPDL 部分:
在 jpdl 參考手冊中主要介紹了 processdefinition.xml 文件的格式( schema )
一、 Process archive:
如前面所描述的, process archive 是商務流程的規則描述。它被打成 jar 包,通常以擴展名 .par 結束, jbpm 識別一個流程需要三種類型的文件數據:
1、 ? 業務流程的正式聲明:在 jpdl 中,是以 processdefinition.xml 文件來表達。這一章節我們就來解析這個文件的格式。
2、 ? 設計邏輯:在流程上加上規劃邏輯這些也是在 processdefinition.xml 中給予描述的,在 process archive 中可以嵌套 java-classes 。所有在 process archive 中的 classes 應該放在子目錄 /classes 中。
3、 ? 其他資源文件。作為工作流引擎的客戶端,在運行時你可能想要在流程中包含一些資源文件的變量。例如。窗體( form )的描述與某人執行任務相關聯。 Jbpm 不能在你想要包含在一個流程定義中的資源文件的類型上做任何手腳。
二、
version
機制(此處
versioning
可以大致理解為:一個
process archive
部署后轉變成過程定義的過程。)(此部分需要參考原文理解。感覺不對勁)
作為最最基本的, jbpm 的翻譯機制遵從下面原理:
l ???? 每次 process archive 部署的時候,一個新的流程定義就在 jbpm 數據庫中被創建。
l
????
在部署的時候,
jbpm
安排一個
version
號碼(數字)給過程定義。只有當過程名與被定義的號相同時候才會執行
Process archives
。為了實現安排
version
號碼,如果它是第一個
version jbpm
采取
1+
(the highest version number of the current process definitions with the same name)
或者
1
。從
jbpm-api
中可以通過一個給定的
name
查找最近的過程定義。
l
????
在一個給定的定義執行某一個過程實例,過程實例將會(等到同個定義結束前)一直在這個定義中保持執行狀態。
l
????
通過這種方式能通過最近定義啟動過程并且在相同定義中的完整的生命周期中保持運行狀態。
l
????
注意:
jbpm
可以把一個設計的程序邏輯與一個過程相關聯起來。通過在
process archive
中包含類文件,
jbpm
將會對于每個過程定義把
classes
分離出來。
?
三、
Processdefinition.xml
格式
?
文檔類型定義:
<!DOCTYPE process-definition PUBLIC
? "-//jBpm/jBpm Mapping DTD 2.0 beta3//EN"
? "http://jbpm.org/dtd/processdefinition-2.0-beta3.dtd">
the document type definition of processdefinition.xml
?
過程定義:
<!ELEMENT process-definition (? description?,
??????????????????????????????? swimlane*,
??????????????????????????????? type*,
??????????????????????????????? start-state,
??????????????????????????????? ( state |
????????????????????????????????? milestone |
????????? ????????????????????????process-state |
????????????????????????????????? decision |
????????????????????????????????? fork |
????????????????????????????????? join
??????????????????????????????? )*,
??????????????????????????????? end-state,
?????????? ?????????????????????action*? ) >
<!ATTLIST process-definition name CDATA #REQUIRED >
dtd fragment for process-definition
?
狀態:
<!ELEMENT state ( description?, assignment?, action*, transition+ ) >
<!ATTLIST state name? CDATA #REQUIRED >
dtd fragment for a state
?
在
JBPM
中,狀態(
state
)這個術語與
FSM
(有限狀態機)和
UML
狀態圖中有著同樣的意思。
對商務流程進行建模的目的就是創建一個軟件系統。我們考慮到過程定義是軟件系統建立的一部分。所以
jbpm
中的狀態術語是以一個軟件系統的角度來解釋的。
狀態是
jbpm
的核心概念。在
jbpm
中當開始模仿一個流程,首要需要考慮的事情就是過程的狀態。狀態將成為你所設計的過程的基本框架。
以狀態概念為核心的另外一個特殊原因是:狀態在程序語言中沒有容易重復混淆的概念。一個軟件程序或者運行或者不運行。一個有代表性的商業過程都會與被分別執行的所設計的邏輯部分關聯。
Jbpm
允許在相關聯的程序邏輯中對狀態建模。
Jpdl
也在狀態之間定義了變遷,決定,分支,合并,
milestone
(
flow
)。需要注意的,控制流定義在狀態與狀態之間。無論什么情況下,一個正常的設計邏輯是更合適的,過程開發人員就能夠在一個過程事件上指定一個
action
。
Jbpl
與其他過程定義語言如
bpel
等之間的區別之一是,
jpdl
具備以狀態管理方式對
java
進行擴展和與程序語言最小交疊性的能力。
委派(
assignment
):
?
<ELEMENT assignment EMPTY >
<!ATTLIST assignment swimlane CDATA #IMPLIED
???????????????????? assignment (optional|required) #IMPLIED
???????????????????? authentication (optional|required|verify) #IMPLIED >
dtd fragment for an assignment
?
當一個流程執行到某種狀態時候,工作流引擎將會等到一個外部的trigger(通過調用jbpm的api,ExecutionService.endOfState(…))。在這個方法里面,jbpm將會算出流程實例的下一個狀態。
所以一個狀態能夠依賴于一個外部參與者。外部參與者可以是一個人也可以是某個系統。有許多種方式來實現業務流程中的狀態與任務相關聯。這些任務基本上可以分為兩組:
1、 ??????????? 基于客戶端的委派
在這種策略中,jbpm的用戶管理他們自己用戶的任務列表。在這種情況,jbpm只是用一個執行引擎來控管有限狀態機。Jbpm的職責是計算并且追蹤過程執行中的狀態,然而客戶端的職責就是確保每個人知道做了什么。然后客戶端一般就是在一個給定的狀態中找到過程實例(或者說tokens)
2、 ??????????? 基于流程的委派
(在jbpm2.0 beta3之前支持這種委派策略)
在這種委派策略中,jbpm將會擔負給參與者安排狀態并且追蹤任務列表的職責。在jbpm中,流程在執行過程中是以一個token來進行跟蹤的。一個token有一個指針來指向狀態和一個參與者。在jbpm中一個參與者總是引用java.lant.string。為了詳細說明jbpm如何必須安排tokens給參與者們,一些事件與此相關。接下來一個一個介紹:
只要一個客戶端啟動一個過程實例或者發出結束狀態的信號,jbpm都會計算過程實例的下一個狀態。
首要的事情是關于這些api方法中的第一個參數:參與者。這個參與用來識別誰來執行。
在啟動一個新的過程實例的情況下,一個root-token在開始狀態中被創建好。在結束一個狀態的情況下,一個tokenid需要以一個某種狀態中的參數形式指定。然后,jbpm將會開始為token計算下一個新狀態。為了簡單起見,我們忽略并發這種情況。Token將會經歷變遷和節點,然后抵達下一個狀態節點。在那時,一個token需要安排一個參與者。選擇一個參與者之后,jbpm將通過token和調用的方法(開始過程實例或者結束狀態)的返回值進行關聯選擇的參與者
所以當一個狀態中的token具備有一個參與者時,那就意味著過程實例的執行是在等待這個參與者提供一個外部trigger給jbpm引擎。在這種情形下,過程中的狀態對應一個用戶的任務,jbpm通過檢索所有已經安排給指定的參與者的tokens計算任務列表。現在,參與者能夠從列表中選擇一個token和發出結束狀態信號量。
Assignmentn (委派)有許多屬性:assignment和authentication。Assingement屬性可以有兩個值:optional和required。Required意思指當執行到某一個狀態時,jbpm將會檢測是否把一個token安排了一個參與者。如果非法則會拋出AssignmentException的異常。如果assingment是optional(=default),當到達一個狀態時jbpm就允許離開一個未安排的token。屬性authentication指定約束條件來限定哪個參與者可以發出結束狀態的信號。參與者通過endOfState方法中的參數actorid來指定。如果optional=default意思指對于指定誰來擔當結束狀態并不是必須的。Required意味著一個參與者需要被指定。Vertify意味著結束狀態只可以安排給已經安排token的參與者。
更多信息請查看群組委派。查看faqs。
Swimlane
dtd fragment for swimlane
非常典型的情況,一個人具備一個流程中多個狀態的職責。在
jbpm
中通過創建一個
swimlane
并且通過
swimlane
安排狀態給參與者(
actor
)。一個業務流程中的
swimlane
可以被看做為一個針對參與者的角色名字。
Jbpm
對
swimlane
的解釋與
UML1.5
中的術語
swimlane
解釋一致。首次執行到達一個狀態,給定一個
swimlane
,就會算出參與者。
public interface AssignmentHandler {
? String selectActor( AssignmentContext assignerContext );
}
the AssignmentHandler interface
在
swimalne
中的
delegation
標簽指向一個
AssignmentHandler
的具體實現。
然后那個參與者(
actor
)以
swimlane
中同名的方式被存儲為過程變量。下次當一個
process archives
在一個狀態,利用
swimlane
,
jbpm
引擎將會檢測變量并且安排
token
給參與者(存儲在變量中的)
計算的結果是存儲一個
swimlane
同名的過程變量。所以當下一個狀態到達相同的
swimlane
時,狀態就會安排給相同的
actor
,而不再用
AssignmentHandler
。因為在
swimlane
和參與者之間的關聯已經存儲在變量中,而且可以通過更新變量來改變
swimlane
參與者(
actor
)之間的關聯關系。
變量和類型:
?
<!ELEMENT type ( description?, (delegation|transient), variable* ) >
<!ATTLIST type java-type CDATA #IMPLIED >
<!ELEMENT transient EMPTY >
?
<!ELEMENT variable EMPTY >
<!ATTLIST variable name CDATA #REQUIRED >
dtd fragment for type and variable
一個是變量是一種key-value對。它與過程實例(一次過程執行)相關聯。Key是java.lang.string,value是任何java類型的任何pojo。所以任何是java類型,即使不給jbpm知道也能被應用到變量中。
變量存儲過程實例的上下文信息(context)。變量可以通過下面幾種方式進行設置讀取:
- ExecutionService.startProcessInstance( String actorId, Long definitionId, Map variables, String transitionName )
- ExecutionService.endOfState( String actorId, Long tokenId, Map variables, String transitionName )
- ExecutionService.setVariables( String actorId, Long tokenId, Map variables )
- ExecutionContext.setVariable( String name, Object value )
- ExecutionService.getVariables( String actorId, Long tokenId )
- ExecutionContext.getVariable( String name )
當設計變量時候,jbpm盡量模仿java.util.map的語義。這一點可以通過jbpm-api來了解。也就是說一個變量只能當它被插入時被賦值,任何java類型都可以作為變量中的value。
?
一個type描述jbpm如何存儲變量的值到數據庫中。Jbpm有一個文本域用來存儲值,中間以serializer來實現文本和對象之間的轉換。
public interface Serializer {
? String serialize( Object object );
? Object deserialize( String text );
}
the Serializer interface
一個type 可以被看做一個serializer的引用。Jbpm包含了一些默認serializer的實現,其中包括下面java類型:
java.lang.String
java.lang.Long
java.lang.Double
by default supported java-types
?
變量類型匹配:
Java 類型聲明的變量已經被默認支持,而不用在processdefinition.xml聲明。Jbpm有個自動轉換機制:當一個變量初始化后,jbpm按照下面步驟,首先檢測java類型。如果它默認被java支持,那么就可以正常使用,否則,jbpm檢查聲明的類型是否與processdefinition.xml中java-type屬性定義的變量是否一致。在匹配過程中jbpm也考慮變量值的父類。如果沒有找不到這種類型,jbpm就把這樣的變量作為transient(瞬間)變量對待。為了避免jbpm不得不執行匹配過程,你可以把變量指定到它各自類型中去。
Transient (瞬間)變量:
一些變量并不需要做持久層處理存儲在數據庫中,jbpm支持瞬時變量。這樣的變量不存儲到數據庫中并且只可以在jbpm-api的方法中使用。換句話說,瞬時變量的使用范圍是:jbpm-api的方法。
開始狀態:
<!ELEMENT start-state ( description?, transition+ ) >
<!ATTLIST start-state name? CDATA #REQUIRED
????????????????????? swimlane CDATA #IMPLIED >
dtd fragment for start-state
開始狀態是一個過程中的唯一狀態,所有的過程實例從這里開始,注意在過程實例開始的時刻你能夠把變量放在過程當中了。另外重點的概念是你能夠有多個從開始狀態出發的變遷,這樣的話,就需要你指定哪個變遷是你啟動過程實例時候需要執行的。
milestone :
<!ELEMENT milestone ( description?, action*, transition ) >
<!ATTLIST milestone name? CDATA #REQUIRED>
dtd fragment for a milestone
?
一個
milestone
是一種特殊的狀態,它用來處理兩個并發事件中的同步的作用。一個
milestone
可以被應用在下面情形:一條執行路徑需要等待另外一個執行路徑上的事件。如果
milestone
沒有到達,接下來的執行就必須在
milestone
處等待,知道另外一個并發路徑到達的時候,才可繼續執行。如果
milestone
已經到達了(條件具備了),接下來的執行就是通過這個
milestone
狀態。關于
milestone
的更多信息和圖形請參看工作流模式。一個
milestone
與一或多個
actions
(用信號通知
milestones
到達)相關聯。那些
actions
能夠用默認
ActionHandler
:
org.jbpm.delegation.action.MilestoneReachdActionHandler
在流程中建模。所以
actions
(發信號給
jbpm
引擎告訴它一個
milestone
已經到達)能夠像下面這樣在
processdefition.xml
中調度:
? ...
? <milestone name="theMilestone">
??? <transition to="stateAfterMilestone" />
? </milestone>
? ...
? <state name="stateBeforeReachingMilestone" swimlane="initiator">
??? <transition to="stateAfterReachingMilestone">
? ???? <action>
????? ??? <delegation class="org.jbpm.delegation.action.MilestoneReachedActionHandler">theMilestone</delegation>
????? </action>
??? </transition>
? </state>
? ...
modelling a milestone in the processdefinition.xml
?
process-state:
<!ELEMENT process-state ( description?, delegation, action*, transition+ ) >
<!ATTLIST process-state name CDATA #REQUIRED>
dtd fragment for a process-state
一個
process-state
符合父過程的
invocation
。父過程執行到一個
procee-state
時候就開始一個子過程的執行。過程殘留在
process-state
中用以子過程的持續。當一個子過程完成后,就從一個
process-state
中離開了。
Decision:
<!ELEMENT decision ( description?, delegation, action*, transition+ ) >
<!ATTLIST decision name CDATA #REQUIRED>
dtd fragment for a decision
一個
decision
用以決定在多個執行路徑中哪個才可以被執行。如果你是一個程序員,把它可以理解成
if-then=else
結構即可,當然。一個
decision
能夠具有許多離開的變遷。
需要注意的是一個
decision
對某個情況建模,在這里工作流引擎根據上下文(
=variables
)和一些其他外部資源決定哪個路由可以被接受。作為可供選擇的(可以替代的做法),你設計從一個狀態出發具備多個變遷。在那種情況下,
jbpm
客戶端必須通過調用以一個選擇變遷名字作為一個參數的
endOfState
方法決定哪個變遷被執行。
Fork
(分支):
<!ELEMENT fork ( description?, delegation?, action*, transition+ ) >
<!ATTLIST fork name?????????????? CDATA #REQUIRED
?????????????? corresponding-join CDATA #IMPLIED>
dtd fragment for a fork
這個比較好理解,定義一般普通的 fork 行為一般是通過 ForkHandler 接口。但是默認行為(當在 fork 中沒有 delegetion 時候)是 fork 的每個變遷都將獲得一個子 token 。所以僅僅對一些高級外來的并發,你才需要實現 ForkHandler.
一般情況下,一個
fork
會有一個相關的
join
。這個
join
定義個并發的
block
。默認下
fork
和
join
僅支持嚴格的嵌套,并且默認情況不支持繞過并發
block
邊界的變遷。
public interface ForkHandler {
? void fork( ForkContext forkContext ) throws ExecutionException;
}
the ForkHandler interface
?
join
:
<!ELEMENT join ( description?, delegation?, action*, transition ) >
<!ATTLIST join name?????????????? CDATA #REQUIRED
?????????????? corresponding-fork CDATA #IMPLIED>
dtd fragment for a join
?
一個 fork joins 用于多條路徑執行,用 JoinHandler 接口指定普通的 join 行為。但是默認情況下(沒有 delegation )是所有 fork 上的子 token 合并。最后 token 到達 join 將會觸發父 token 最后引發 join 上的離開變遷。所以僅僅高級外來的并發你才需要實現 JoinHandler.
約束:一個
join
只能有一個離開變遷。
public interface JoinHandler {
?? void join( JoinContext joinContext ) throws ExecutionException;
}
the JoinHandler interface
?
結束狀態:
<!ELEMENT end-state EMPTY >
<!ATTLIST end-state name CDATA #REQUIRED>
dtd fragment for end-state
一個過程定義有一個精確的結束狀態。當一個過程實例執行到一個結束狀態時候,這個過程實例就結束了。
?
變遷(
transition
):
<!ELEMENT transition ( action* )>
<!ATTLIST transition name CDATA #IMPLIED
?????? ??????????????to?? CDATA #REQUIRED>
dtd fragment for a transition
變遷用來指定節點之間的連接。變遷元素應該放在
node
里面,那么這個變遷就會從這個節點出離開。
?
Action
:
<!ELEMENT action ( delegation ) >
<!ATTLIST action event-type (process-start|process-end|
???????????????????????????? state-enter|state-leave|state-after-assignment|
???????????????????????????? milestone-enter|milestone-leave|
???????????????????????????? decision-enter|decision-leave|
???????????????????????????? fork-enter|fork-every-leave|
???????????????????????????? join-every-enter|join-leave|
???????????????????????????? transition) #IMPLIED>
dtd fragment for an action
一個
action
是一段
java
代碼。在過程執行期間在一些事件基礎之上
action
在工作流引擎上執行。
Action
總是被定義成一個過程定義元素(
process-definition, state, transition, decision, ...
)的子元素。當
action
執行的時候,父元素會在過程執行時候外加一些事件類型來定義精確時刻。可以想象得到,一個
action
的可能事件類型依賴于
action
被包含進去的元素。
事件類型名字(
event-type-names
)被暗示出是他們應用到的元素(理解有誤)。
在
javadoc
中有事件類型,可以進行查看。
?
public interface ActionHandler {
? void execute( ExecutionContext executionContext );
}
the ActionHandler interface
?
delegation
:
<!ELEMENT delegation ( #PCDATA ) >
<!ATTLIST delegation class CDATA #REQUIRED>
dtd fragment for a delegation
解釋在約束元素和
delegation
類必須實現的接口之間的關聯。
四、其他
process archive
文件
當一個
process archive
被部署,
processdefinition.xml
將被解析并且把信息存儲到
jbpm
數據庫中去。所有其他的文件或者被存儲在數據庫中或者文件系統,與被創建的過程定義相關聯。作為一
jbpm api
的客戶端你能夠通過
ExecutionReadService.getFile( Long processDefinitionId, String fileName )
訪問這些文件。
一個
process archive
和一個過程定義(
process definition
)之間的區別參看
versioning mechanism
。