1服務(wù) API 設(shè)計
jBPM4 工作流引擎的核心 PVM 主要依靠 4 組服務(wù) API :
- 流程定義服務(wù) - Process Service。
- 流程執(zhí)行服務(wù)- Execution Service。
- 流程管理服務(wù) - Managerment Service。
- 指令服務(wù) - Command Service。

應(yīng)用通過這些服務(wù)與 PVM 進(jìn)行數(shù)據(jù)交互,這些都是在支持事務(wù)的持久化模式下運(yùn)行的。比如:
- ExecutionService.startProcessInstanceByKey - 發(fā)起流程實(shí)例。
- TaskService.completeTask - 完成任務(wù)。
客戶端 API 是核心工作流模型對象對外暴露的公共方法,我們可以直接使用客戶端 API 來執(zhí)行一些流程操作,客戶端 API 不會進(jìn)行任何持久化操作,它操作的結(jié)果是通過調(diào)用相應(yīng)服務(wù)的 API 后才會被持久化。比如:
- ProcessInstance.getName - 獲取流程實(shí)例名稱。
- Task.setAssignee - 設(shè)置任務(wù)分配者。
1.1 活動 API
活動 API 用于實(shí)現(xiàn)流程活動在運(yùn)行時的行為。所有的活動類型都要實(shí)現(xiàn) ActivityBehaviour 接口,它提供了控制流程執(zhí)行的方法,接口定義如下:
public interface ActivityBehaviour extends Serializable {
/** invoked when an execution arrives in an activity.
*
* <p>An ActivityBehaviour can control the propagation
* of execution. ActivityBehaviour's can become external activities when they
* invoke {@link ActivityExecution#waitForSignal()}. That means the
* activity will become a wait state. In that case, {@link ExternalActivityBehaviour}
* should be implemented to also handle the external signals.
* </p> */
void execute(ActivityExecution execution) throws Exception;
}
執(zhí)行對象的類型需要實(shí)現(xiàn) ActivityExecution 接口,這個接口定義了控制流程推進(jìn)的方法:
| 活動定義 | 說明 |
|---|---|
| String getActivityName() | 獲取當(dāng)前活動名稱。 |
| void waitForSignal() | 等待執(zhí)行信號。 |
| void takeDefaultTransition() | 選擇一個默認(rèn)的流出轉(zhuǎn)移。 |
| void take(String transitionName) | 選擇一個指定名稱的流出轉(zhuǎn)移。 |
| void execute(String activityName) | 執(zhí)行子活動。 |
| void end() | 結(jié)束當(dāng)前流程(包括子流程)。 |
| void end(String state) | 結(jié)束當(dāng)前流程(包括子流程),并為子流程指定結(jié)束狀態(tài)。 |
| void setPriority(int priority) | 設(shè)置活動優(yōu)先級。 |
1.2 事件監(jiān)聽 API
事件監(jiān)聽 API 用于自定義事件監(jiān)聽器,它可以用來處理被監(jiān)聽到的流程事件。
它與活動 API 的區(qū)別是:它不能控制流程的執(zhí)行。假設(shè)一個活動通過 execution 已經(jīng)確定了一個轉(zhuǎn)移,這時就會觸發(fā)它所對應(yīng)的事件監(jiān)聽器,因為轉(zhuǎn)移已經(jīng)先被確定,所以事件監(jiān)聽器必然無法改變流程的推進(jìn)路線。
自定義的事件監(jiān)聽器,需要實(shí)現(xiàn) EventListener 接口,這個接口定義如下:
public interface EventListener extends Serializable {
/** is invoked when an execution crosses the event on which this listener is registered */
void notify(EventListenerExecution execution) throws Exception;
}
這里的 notify 方法需要一個 EventListenerExecution 類型的參數(shù),它與 ActivityExecution 的相同之處是,它們都繼承自 OpenExecution 接口,但它只定義了一個設(shè)置優(yōu)先級的方法:
public interface EventListenerExecution extends OpenExecution {
/** setter for the priority. The default priority is 0, which means
* NORMAL. Other recognized named priorities are HIGHEST (2), HIGH (1),
* LOW (-1) and LOWEST (-2). For the rest, the user can set any other
* priority integer value, but then, the UI will have to display it as
* an integer and not the named value.*/
void setPriority(int priority);
}
再次強(qiáng)調(diào):事件監(jiān)聽器無法改變流程的推進(jìn)路徑。
2 運(yùn)行環(huán)境設(shè)計
為了讓流程可以在不同的事務(wù)環(huán)境(Java EE 或 Spring )中運(yùn)行,PVM 定義了運(yùn)行環(huán)境對象,它會根據(jù)配置的環(huán)境,執(zhí)行服務(wù)延遲加載與獲取事務(wù)管理等操作。
運(yùn)行環(huán)境是 EnvironmentFactory 對象,它有兩個實(shí)現(xiàn):
- ProcessEngineImpl - 默認(rèn)的 Java EE 環(huán)境。
- SpringProcessEngine - 基于 Spring 框架的環(huán)境。
通過以下方式獲取默認(rèn)環(huán)境工廠對象,從而執(zhí)行任意流程操作:
ConfigurationImpl cfg = new ConfigurationImpl();
cfg.setResource("jbpm.cfg.xml");//指定配置文件
//創(chuàng)建環(huán)境工廠對象
EnvironmentFactory factory=new ProcessEngineImpl(cfg);
//執(zhí)行任意流程操作
Environment environment=factory.openEnvironment();
try {
RepositoryService repositoryService = environment.get(RepositoryService.class);
} finally {
factory.close();
}
注意:通過 Environment 對象獲取的流程服務(wù)受到事務(wù)的控制。
也可以通過 Configuration 類加載默認(rèn)的配置文件,獲取各項流程服務(wù),這種方式更方便:
ProcessEngine engine= Configuration.getProcessEngine();
RepositoryService repositoryService=engine.getRepositoryService();
3 命令設(shè)計模式
命令設(shè)計模式是 jBPM4 實(shí)現(xiàn)流程邏輯的核心思想。所有的命令都需要實(shí)現(xiàn) Command 接口,并在 execute() 方法中實(shí)現(xiàn)邏輯:
public interface Command<T> extends Serializable {
T execute(Environment environment) throws Exception;
}
注意: 每個命令都是獨(dú)立的事務(wù)操作,即每一個 execute() 方法的實(shí)現(xiàn)都被一個 Hibernate 事務(wù)所包含。
public class CustomCommand implements Command<Void> {
private String executionId;
@Override
public Void execute(Environment environment) throws Exception {
//從環(huán)境對象中獲取執(zhí)行服務(wù)
ExecutionService executionService = environment.get(ExecutionService.class);
//執(zhí)行服務(wù),完成流程邏輯
executionService.signalExecutionById(executionId);
return null;
}
}
命令定義后,可以通過流程引擎對象來執(zhí)行自定義的命令:
ProcessEngine engine = Configuration.getProcessEngine();
engine.execute(new CustomCommand());
4 服務(wù)設(shè)計
外部應(yīng)用程序(比如客戶端)會調(diào)用服務(wù) API 來作為操作工作流引擎,也可以通過它來持久化 PVM 的操作。
三個基本的服務(wù)接口:
| 服務(wù)類 | 說明 |
|---|---|
| RepositoryService | 流程定義及其相關(guān)資源的服務(wù) |
| ExecutionService | 流程實(shí)例及其執(zhí)行的服務(wù) |
| ManagementService | Job 相關(guān)服務(wù) |
所有的流程邏輯都被封裝為命令,因此上述的三個服務(wù)類的方法實(shí)現(xiàn)執(zhí)行的都是命令。比如 ManagementService 中的 createJobQuery 的實(shí)現(xiàn):
public JobQuery createJobQuery() {
JobQueryImpl query = commandService.execute(new CreateJobQueryCmd());
query.setCommandService(commandService);
return query;
}
所有的 PVM 命令都統(tǒng)一委派給 CommandService,由它來執(zhí)行這些命令:
public interface CommandService {
String NAME_TX_REQUIRED_COMMAND_SERVICE = "txRequiredCommandService";
String NAME_NEW_TX_REQUIRED_COMMAND_SERVICE = "newTxRequiredCommandService";
/**
* @throws JbpmException if command throws an exception.
*/
<T> T execute(Command<T> command);
}
CommandService 有兩種工作模式:
- NAME_TX_REQUIRED_COMMAND_SERVICE :在同一線程中使用一個事務(wù)來執(zhí)行所有的命令。
- NAME_NEW_TX_REQUIRED_COMMAND_SERVICE :一個命令執(zhí)行一個事務(wù)。
CommandService 只定義了一個用于執(zhí)行命令方法 execute()。
在默認(rèn)的配置文件 jbpm.default.cfg.xml 中,預(yù)設(shè)了以下這些服務(wù):
<repository-service />
<repository-cache />
<execution-service />
<history-service />
<management-service />
<identity-service />
<task-service />
CommandService 的設(shè)計采用了職責(zé)鏈的設(shè)計模式,它是環(huán)繞在命令周圍的一群攔截器所組成的一條職責(zé)鏈。我們可以組合不同的攔截器,按照不同的順序,在不同的環(huán)境下實(shí)現(xiàn)不同的持久化事務(wù)策略。
在 jbpm.tx.hibernate.cfg.xml 中,描述了 CommandService 的實(shí)現(xiàn)策略:
<command-service name="txRequiredCommandService">
<skip-interceptor />
<retry-interceptor />
<environment-interceptor />
<standard-transaction-interceptor />
</command-service>
<command-service name="newTxRequiredCommandService">
<retry-interceptor />
<environment-interceptor policy="requiresNew" />
<standard-transaction-interceptor />
</command-service>
這就是我們之前所說的 CommandService 存在的兩種工作模式的配置方式。
各個服務(wù)會按照需要來選擇合適的 CommandService 工作模式來執(zhí)行命令。各個攔截器繼承自 Interceptor 抽象類,而它實(shí)現(xiàn)的就是 CommandService 接口:
public abstract class Interceptor implements CommandService {
protected CommandService next;
public CommandService getNext() {
return next;
}
public void setNext(CommandService next) {
this.next = next;
}
}
多個 CommandService 被配置為一條職責(zé)鏈來攔截命令,這樣各個服務(wù)就通過職責(zé)鏈來選擇不同的策略,而無須改變命令本身啦O(∩_∩)O哈哈~
我們以 newTxRequiredCommandService 的 CommandService 實(shí)現(xiàn)為例,來說明這條職責(zé)鏈的作用,調(diào)用一條命令后,它會依次執(zhí)行以下的攔截器——
- retry-interceptor:在數(shù)據(jù)庫的樂觀鎖失敗時,捕獲 Hibernate 的 StaleObjectException,并嘗試重新調(diào)用命令。
- environment-interceptor:為命令的調(diào)用提供一個環(huán)境對象。
- standard-transaction-interceptor:初始化標(biāo)準(zhǔn)事務(wù)對象(StandardTransaction)。
- 最后,由 DefaultCommandService 來調(diào)用命令。
也可以在此通過配置,使用其他的方式來調(diào)用命令——
- EjbLocalCommandService:把命令委派給一個本地的 EJB,這樣可以啟動一個 EJB 內(nèi)容管理事務(wù)。
- EjbRemoteCommandService:把命令委派給一個遠(yuǎn)程的 EJB,這樣命令可以在另一個 JVM 上被執(zhí)行。
- AsyncCommandService:命令被包裝為一個異步消息,這樣命令就會在一個新的事務(wù)中被異步執(zhí)行。
5 流程歷史庫
在整個流程實(shí)例執(zhí)行過程的各個關(guān)鍵階段,都設(shè)計了歷史事件觸發(fā)器,它會把流程實(shí)例數(shù)據(jù)存入歷史庫,實(shí)現(xiàn)了運(yùn)行中的流程數(shù)據(jù)與歷史流程數(shù)據(jù)的分離。
在流程實(shí)例的運(yùn)行過程中,或觸發(fā)歷史流程事件,然后根據(jù)分類被分發(fā)到配置好的 HistorySession 中,HistorySession 的默認(rèn)實(shí)現(xiàn) HistorySessionImpl 會調(diào)用相應(yīng)的歷史事件對象 (HistoryEvent )的 process 方法來執(zhí)行相應(yīng)的歷史事件處理邏輯:
public class HistorySessionImpl implements HistorySession {
public void process(HistoryEvent historyEvent) {
historyEvent.process();
}
}
抽象類 HistoryEvent 的事件本身不會被持久化,它的抽象方法 process() 在它的實(shí)現(xiàn)類中,創(chuàng)建了歷史實(shí)體,比如 HistoryEvent 的一個實(shí)現(xiàn)類 ActivityStart:
public void process() {
DbSession dbSession = EnvironmentImpl.getFromCurrent(DbSession.class);
long processInstanceDbid = execution.getProcessInstance().getDbid();
HistoryProcessInstance historyProcessInstanceImpl = dbSession.get(HistoryProcessInstanceImpl.class, processInstanceDbid);
HistoryActivityInstanceImpl historyActivityInstance =
createHistoryActivityInstance(historyProcessInstanceImpl);
String activityType = execution.getActivity().getType();
historyActivityInstance.setType(activityType);
dbSession.save(historyActivityInstance);
execution.setHistoryActivityInstanceDbid(historyActivityInstance.getDbid());
}
這里創(chuàng)建了 HistoryActivityInstanceImpl ,并執(zhí)行了持久化操作。
在 process() 中歷史事件創(chuàng)建的實(shí)體與當(dāng)前的流程實(shí)體是對應(yīng)、歸并的關(guān)系,比如 ProcessInstanceCreate 事件會創(chuàng)建與持久化 HistoryProcessInstance;而 ProcessInstanceEnd 事件會設(shè)置與持久化對應(yīng)的 HistoryProcessInstance 對象的狀態(tài)(結(jié)束)。
歷史流程庫維護(hù)著過往流程的歸檔信息。但流程實(shí)例或活動實(shí)例結(jié)束時,就會在歷史流程庫中寫入數(shù)據(jù),因為這些數(shù)據(jù)對于當(dāng)前運(yùn)行著的流程來說,是歷史(過時)信息。
歷史流程庫使用 5 張表維護(hù)著 4 種實(shí)體歷史信息:
| 實(shí)體 | 表名 |
|---|---|
| 歷史流程實(shí)例 | jbpm4_hist_procinst |
| 歷史活動實(shí)例 | jbpm4_hist_actinst |
| 歷史任務(wù) | jbpm4_hist_task |
| 歷史流程變量 | jbpm4_hist_var |
最后一張是 jbpm4_hist_detail,它記錄著上述這些實(shí)體的歷史明細(xì)表。
可以使用 HistoryService 的 createHistroyXxxQuery() 方法來獲取上述實(shí)體的查詢對象,來獲取歷史流程實(shí)體信息:

在 HistoryService 中還提供了一些用于數(shù)據(jù)分析的方法,比如:
| 方法 | 說明 |
|---|---|
| avgDurationPerActivity(String processDefinitionId) | 獲取活動的平均執(zhí)行時間。 |
| choiceDistribution(String processDefinitionId, String activityName) | 獲取流程轉(zhuǎn)移的選擇次數(shù)。 |
需要的話,也可以根據(jù)歷史明細(xì)表 jbpm4_hist_detail,擴(kuò)展出我們自己的流程數(shù)據(jù)分析方法哦O(∩_∩)O哈哈~