Activiti 流程
流程引擎
流程啟動,運行的具體環(huán)境粱挡。
創(chuàng)建流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
創(chuàng)建流程引擎時层坠,會在classpath下搜索activiti.cfg.xml配置文件,并基于此文件進行構(gòu)建买猖。
如果沒有查詢到配置文件改橘,則會基于默認配置創(chuàng)建引擎。
可以通過編程的方式實現(xiàn)引擎配置玉控。
配置內(nèi)容包括: 數(shù)據(jù)庫飞主, 是否啟用Job執(zhí)行器,郵件服務(wù)器高诺,歷史存儲碌识,緩存配置,日志虱而,事件監(jiān)聽等筏餐。
流程引擎的服務(wù)
// 創(chuàng)建引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 從引擎中獲取各類服務(wù)
RuntimeService runtimeService = processEngine.getRuntimeService();
RepositoryService repositoryService = processEngine.getRepositoryService();
TaskService taskService = processEngine.getTaskService();
ManagementService managementService = processEngine.getManagementService();
IdentityService identityService = processEngine.getIdentityService();
HistoryService historyService = processEngine.getHistoryService();
FormService formService = processEngine.getFormService();
所有的服務(wù)都是無狀態(tài)的。
Repository Service 提供了管理和控制發(fā)布包和流程的定義操作牡拇。如:部署流程定義魁瞪;查詢引擎中的已有發(fā)布包和流程定義穆律;暫停或激活發(fā)布包佩番;獲取發(fā)布包中的資源众旗,如xml文件或是流程圖片等。
Runtime Service 負責啟動一個流程頂?shù)男聦嵗宋贰τ诿總€流程定義來說贡歧,同一個時間內(nèi),可以有多個實例在執(zhí)行赋秀。runtime service還可以用于獲取和保存流程實例中的變量利朵。或是用于查詢流程實例猎莲,執(zhí)行實例绍弟,觸發(fā)實例等。
Task Service 任務(wù)相關(guān)的服務(wù)著洼。包含功能:查詢分配給用戶或用戶組的任務(wù)的信息樟遣;創(chuàng)建獨立運行于流程實例外的任務(wù);手段設(shè)置任務(wù)與用戶的關(guān)聯(lián)關(guān)系身笤;認領(lǐng)(claim)任務(wù)豹悬, 完成(complete)任務(wù)等。
Identity Service 負責管理(創(chuàng)建液荸,更新瞻佛,刪除,查詢...)群組和用戶娇钱。注意伤柄,activiti執(zhí)行時不會對用戶執(zhí)行檢查。任務(wù)可以分配給任何人文搂,無論這個用戶是否存在适刀。
Form Service 表單服務(wù)∶翰洌可選的笔喉。提供啟動表單和任務(wù)表單兩個概念。即在流程實例啟動前展示給用戶的疯兼,和完成任務(wù)時展示給用戶的兩種表單然遏。注意贫途,這是個可選服務(wù)吧彪,表單不一定需要嵌入到流程定義中。
History Service 歷史數(shù)據(jù)服務(wù)丢早。執(zhí)行流程時姨裸,引擎會保存如實例啟動時間秧倾,任務(wù)參與者,完成時間傀缩,執(zhí)行路徑等數(shù)據(jù)那先。Histroy Service通過查詢功能獲取這些數(shù)據(jù)。
Management Service 管理服務(wù)赡艰。提供查詢和管理異步操作的功能售淡。異步操作的用途包含定時器,延遲慷垮,暫停揖闸,激活等。
流程定義
流程定義的發(fā)布
編寫bpmn的xml文件料身,并通過使用Repository Service進行發(fā)布汤纸。
示例: 某個請假流程的定義xml
<?xml version="1.0" encoding="UTF-8" ?>
<!-- targetNamespace用于指定用戶自定義的類別category, 主要用于對流程實例進行分類 -->
<!-- 其等同 repositoryService.createDeployment().category("yourCatagory")...deploy(); -->
<definitions id="definitions"
targetNamespace="http://activiti.org/bpmn20"
xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:activiti="http://activiti.org/bpmn">
<!-- process 流程。 建議一個xml中只包含一個流程 -->
<!-- id: 必要的標識屬性 -->
<process id="vacationRequest" name="Vacation request">
<!-- 事件芹血。 事件有多種類型:開始贮泞,結(jié)束,定時器幔烛,錯誤啃擦,邊境等等-->
<!-- startEvent 開始事件,流程的入口 -->
<!-- activiti:initiator 初始化變量说贝。 這里使用流程變量employeeName記錄流程發(fā)起者的名字 -->
<startEvent id="request" activiti:initiator="employeeName">
<!-- 擴展议惰。這里是一個啟動表單form -->
<extensionElements>
<activiti:formProperty id="numberOfDays" name="Number of days" type="long" value="1" required="true"/>
<activiti:formProperty id="startDate" name="First day of holiday (dd-MM-yyy)" datePattern="dd-MM-yyyy hh:mm" type="date" required="true" />
<activiti:formProperty id="vacationMotivation" name="Motivation" type="string" />
</extensionElements>
</startEvent>
<!-- 順序流,用于說明流程的流向 -->
<sequenceFlow id="flow1" sourceRef="request" targetRef="handleRequest" />
<!-- 任務(wù). 包含多種類型: userTask, serviceTask, scriptTask等 -->
<!-- 用戶任務(wù)乡恕。需要由人完成的任務(wù) -->
<userTask id="handleRequest" name="Handle vacation request" >
<!-- 說明文件言询。可以使用代碼 task.getDescription() 獲取 -->
<documentation>
<!-- ${} UEL表達式傲宜。 可以獲取到流程內(nèi)的變量 -->
${employeeName} would like to take ${numberOfDays} day(s) of vacation (Motivation: ${vacationMotivation}).
</documentation>
<extensionElements>
<activiti:formProperty id="vacationApproved" name="Do you approve this vacation" type="enum" required="true">
<activiti:value id="true" name="Approve" />
<activiti:value id="false" name="Reject" />
</activiti:formProperty>
<activiti:formProperty id="managerMotivation" name="Motivation" type="string" />
</extensionElements>
<!-- 任務(wù)的執(zhí)行人或群組运杭。 這里是指定群組為management -->
<potentialOwner>
<resourceAssignmentExpression>
<formalExpression>management</formalExpression>
</resourceAssignmentExpression>
</potentialOwner>
</userTask>
<sequenceFlow id="flow2" sourceRef="handleRequest" targetRef="requestApprovedDecision" />
<!-- 網(wǎng)關(guān)。包含類型: 排他函卒, 并行辆憔, 包含, 事件等 -->
<!-- 排他網(wǎng)關(guān)。 是否同意請假? -->
<exclusiveGateway id="requestApprovedDecision" name="Request approved?" />
<!-- management同意請假后的流程 -->
<sequenceFlow id="flow3" sourceRef="requestApprovedDecision" targetRef="sendApprovalMail">
<conditionExpression xsi:type="tFormalExpression">${vacationApproved == 'true'}</conditionExpression>
</sequenceFlow>
<task id="sendApprovalMail" name="Send confirmation e-mail" />
<sequenceFlow id="flow4" sourceRef="sendApprovalMail" targetRef="theEnd1" />
<!-- 結(jié)束事件, 請假成功劈愚。 -->
<endEvent id="theEnd1" />
<!-- management不同意請假后的流程-->
<sequenceFlow id="flow5" sourceRef="requestApprovedDecision" targetRef="adjustVacationRequestTask">
<conditionExpression xsi:type="tFormalExpression">${vacationApproved == 'false'}</conditionExpression>
</sequenceFlow>
<userTask id="adjustVacationRequestTask" name="Adjust vacation request">
<documentation>
Your manager has disapproved your vacation request for ${numberOfDays} days.
Reason: ${managerMotivation}
</documentation>
<extensionElements>
<activiti:formProperty id="numberOfDays" name="Number of days" value="${numberOfDays}" type="long" required="true"/>
<activiti:formProperty id="startDate" name="First day of holiday (dd-MM-yyy)" value="${startDate}" datePattern="dd-MM-yyyy hh:mm" type="date" required="true" />
<activiti:formProperty id="vacationMotivation" name="Motivation" value="${vacationMotivation}" type="string" />
<!-- 表單元素:是否再次發(fā)起請假舍咖? -->
<activiti:formProperty id="resendRequest" name="Resend vacation request to manager?" type="enum" required="true">
<activiti:value id="true" name="Yes" />
<activiti:value id="false" name="No" />
</activiti:formProperty>
</extensionElements>
<!-- 任務(wù)執(zhí)行人為發(fā)起流程的人 -->
<humanPerformer>
<resourceAssignmentExpression>
<formalExpression>${employeeName}</formalExpression>
</resourceAssignmentExpression>
</humanPerformer>
</userTask>
<sequenceFlow id="flow6" sourceRef="adjustVacationRequestTask" targetRef="resendRequestDecision" />
<!-- 排他網(wǎng)關(guān)。 是否再次發(fā)起請假流程? -->
<exclusiveGateway id="resendRequestDecision" name="Resend request?" />
<!-- 再次請假列荔, 流程回到management handleRequest處 -->
<sequenceFlow id="flow7" sourceRef="resendRequestDecision" targetRef="handleRequest">
<conditionExpression xsi:type="tFormalExpression">${resendRequest == 'true'}</conditionExpression>
</sequenceFlow>
<!-- 不再請假 -->
<sequenceFlow id="flow8" sourceRef="resendRequestDecision" targetRef="theEnd2">
<conditionExpression xsi:type="tFormalExpression">${resendRequest == 'false'}</conditionExpression>
</sequenceFlow>
<!-- 結(jié)束事件2。請假失敗 -->
<endEvent id="theEnd2" />
</process>
</definitions>
xml對應(yīng)的流程圖
流程的部署
repositoryService.createDeployment()
.name("my-process-name")
.addClasspathResource("org/activiti/myProcess.bpmn20.xml")
.addClasspathResource("org/activiti/myProcess.png")
.deploy();
如果發(fā)布使用的是bpmn文件,則改為addClassResource("org/activiti/myProcess.bpmn")即可绘沉。
使用這種方式時煎楣,沒有提供流程圖片文件。因此可以通過配置流程引擎自動生成圖片车伞。
<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
...
<property name="createDiagramOnDeploy" value="true" />
</bean>
通過API獲取流程定義的圖片資源
ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery()
.processDefinitionKey("yourProcessId").singleResult();
String diagramResourceName = procDef.getDiagramResourceName();
InputStream imageStream =
repositoryService.getResourceAsStream(procDef.getDeploymentId(), diagramResourceName);
流程定義的掛起與激活
// 掛起流程择懂。流程將無法新建實例,繼續(xù)執(zhí)行另玖,異步操作困曙。
repositoryService.suspendProcessDefinitionByKey("vacationRequest");
try {
runtimeService.startProcessInstanceByKey("vacationRequest");
} catch (ActivitiException e) {
e.printStackTrace(); // 無法再啟動該定義流程的實例
}
// 重新激活流程
repositoryService.activateProcessDefinitionByKey("vacationRequest");
流程實例
流程實例都共享一個流程定義。
在啟動流程實例之前谦去,必須保證流程定義已經(jīng)被發(fā)布赂弓。然后通過流程定義的ID啟動實例。(在流程模型中定義的ID哪轿,在activiti中對應(yīng)于key盈魁。如流程id,任務(wù)id等)
ProcessInstance proInst = runtimeService.startProcessInstanceByKey("yourProcessId");
創(chuàng)建一個流程實例窃诉,首先會進入開始事件杨耙。
在開始事件之后,它會沿著所有的外出連線執(zhí)行飘痛,到達第一個任務(wù)珊膜。Activiti會把一個任務(wù)保存到數(shù)據(jù)庫中。這時宣脉,分配到這個任務(wù)的用戶或群組會被解析车柠,也會保存到數(shù)據(jù)庫里。
Activiti引擎會繼續(xù)執(zhí)行流程環(huán)節(jié)塑猖,直至遇到一個等待狀態(tài)竹祷,如用戶任務(wù)等。在等待狀態(tài)下羊苟,當前的流程實例的狀態(tài)會保存到數(shù)據(jù)庫中塑陵。直至用戶決定完成任務(wù)才能改變這個狀態(tài)。
之后引擎會繼續(xù)執(zhí)行蜡励,直至遇到下一個等待狀態(tài)令花,或是流程結(jié)束。
在引擎運行過程中凉倚,如果出現(xiàn)重啟或是崩潰情況兼都,流程狀態(tài)也會安全的保存在數(shù)據(jù)庫中。
流程的啟動
流程定義發(fā)布到引擎后稽寒,就可以基于它發(fā)起新的流程實例扮碧。如先前所說,基于同一個流程定義可以同時存在多個流程實例瓦胎。
// 表單元素
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("employeeName", "Kermit");
variables.put("numberOfDays", new Integer(4));
variables.put("vacationMotivation", "I'm really tired!");
// 使用RuntimeService獲得與流程運行相關(guān)的信息
RuntimeService runtimeService = processEngine.getRuntimeService();
// 從流程引擎中查找一個定義為"vacationRequest"的流程并啟動
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("vacationRequest", variables);
// Verify that we started a new process instance
Log.info("Number of process instances: " + runtimeService.createProcessInstanceQuery().count());
流程中的任務(wù)的完成
// Fetch all tasks for the management group
TaskService taskService = processEngine.getTaskService();
// 查找當前未完成的且執(zhí)行用戶組是management的任務(wù)
List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("management").list();
for (Task task : tasks) {
Log.info("Task available: " + task.getName());
}
// 此處應(yīng)獲取到任務(wù): adjustVacationRequestTask
Task task = tasks.get(0);
Map<String, Object> taskVariables = new HashMap<String, Object>();
taskVariables.put("vacationApproved", "false"); //拒絕了請假
taskVariables.put("managerMotivation", "We have a tight deadline!");
// 任務(wù)完成芬萍。流程進入到下一個步驟
taskService.complete(task.getId(), taskVariables);
// !!如何需要獲取流程中的變量
runtimeService.getVariables(processInstanceId);