Flowable本地配置開發
阿新 • • 發佈:2021-07-08
Flowable
Flowable是一個基於Java開發的開源的企業流程engine,因為專案需要使用flowable作為一個流轉平臺,所以再本地搭載了一個平臺進行測試,主要使用Tomcat及JAVA,並且測試瞭如何使用java呼叫Python程式碼,所以我們可以使用Python實現相應的業務邏輯,並使用Java呼叫Python程式碼來進行業務流程流轉。如果想詳細瞭解Flowable可以參考Flowable Start. Flowable支援REST呼叫
下載Tomcat
可以通過官網下載相應的binary版本然後進行解壓。
下載Flowable
可以下載最新版本的Flowable的binary file
wars
裡面的2個對應的war檔案,flowable-ui.war
是用來啟動UI,flowable-rest.war
是用來提供REST支援。
啟動flowable頁面
在啟動Tomcat之前,我們需要將解壓的Flowable的wars裡面的兩個war檔案複製到tomcat的webapps
的資料夾裡面,然後我們可以開啟一個terminal(windows直接cmd),進入到Tomcat的bin
資料夾裡面,輸入catalina run
,Tormcat則會將全部的webapps裡面的war檔案執行,啟動完畢之後可以可以在本地瀏覽器輸入http://localhost:8080/flowable-ui
admin
, 密碼是test
。Java程式碼實現Flowable業務流轉
Java呼叫Flowable core
以一個簡單的員工申請休假領導進行批准的例子,以下為相應的java程式碼:
package process_engine.build.test; import java.util.List; import java.util.Map; import java.util.HashMap; import java.util.Scanner; import org.flowable.engine.HistoryService; import org.flowable.engine.ProcessEngine; import org.flowable.engine.ProcessEngineConfiguration; import org.flowable.engine.RepositoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.TaskService; import org.flowable.engine.history.HistoricActivityInstance; import org.flowable.engine.impl.cfg.StandaloneProcessEngineConfiguration; import org.flowable.engine.repository.Deployment; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.task.api.Task; public class BuildFirstClass { public static void main(String[] args) { // First to build a configuration ProcessEngineConfiguration pec = new StandaloneProcessEngineConfiguration() .setJdbcUrl("jdbc:h2:mem:flowable;DB_CLOSE_DELAY=-1") .setJdbcUsername("sa") .setJdbcPassword("") .setJdbcDriver("org.h2.Driver") .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE); // create process engine based on configuration ProcessEngine peEngine = pec.buildProcessEngine(); // get service by creating a deployment and read the XML files. RepositoryService repositoryService = peEngine.getRepositoryService(); Deployment deployment = repositoryService .createDeployment() .addClasspathResource("holiday-request.bpmn20.xml") .deploy(); // query the result ProcessDefinition processDefinition = repositoryService .createProcessDefinitionQuery() .deploymentId(deployment.getId()).singleResult(); System.out.println("Get process definition:" + processDefinition.getName()); // provide with some variables by keyboard input. Scanner scanner = new Scanner(System.in); System.out.println("Who are you?"); String employString = scanner.nextLine(); System.out.println("How many days do you need?"); Integer daysInteger = Integer.valueOf(scanner.nextLine()); System.out.println("Why do you need?"); String descString = scanner.nextLine(); // start a process engine RuntimeService runtimeService = peEngine.getRuntimeService(); HashMap<String,Object> variables = new HashMap<String, Object>(); variables.put("employee", employString); variables.put("nrOfHolidays", daysInteger); variables.put("description", descString); // which flow to process? ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("holidayRequest", variables); // get full task list based on which group to use? TaskService taskService = peEngine.getTaskService(); List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("managers").list(); System.out.println("You have "+ tasks.size() + " tasks:"); for (int i = 0; i < tasks.size(); i++) { System.out.println(tasks.get(i).getName()); } System.out.println("Which task would you love to process?"); int taskindex = Integer.valueOf(scanner.nextLine()); Task task = tasks.get(taskindex- 1); Map<String, Object> processvaluesMap = taskService.getVariables(task.getId()); System.out.println(processvaluesMap.get("employee") + " wants " + processvaluesMap.get("nrOfHolidays") + " of holidays for reason: '"+ processvaluesMap.get("description") + "' Do you approve this request?"); String approveString = scanner.nextLine().toLowerCase(); boolean approve; if (approveString.toLowerCase().equalsIgnoreCase("y") | approveString.equalsIgnoreCase("yes")){ approve = true; }else { approve = false; } // boolean approve = scanner.nextLine().toLowerCase().equals("1"); variables = new HashMap<String, Object>(); variables.put("approved", approve); taskService.complete(task.getId(), variables); //System.out.println("get approve status:" + approve); // Test with historical data that in stored in the DB // HistoryService historyService = peEngine.getHistoryService(); // List<HistoricActivityInstance> listActivityInstances = historyService.createHistoricActivityInstanceQuery() // .processDefinitionId(processInstance.getActivityId()) // .finished() // .orderByHistoricActivityInstanceEndTime() // .asc() // .list(); // // for (HistoricActivityInstance historicActivityInstance: listActivityInstances) { // System.out.println(historicActivityInstance.getActivityId() + " took " + historicActivityInstance.getDurationInMillis() + " milli seconds"); // } } }
JAVA呼叫Python
我們可以編寫另一個類來呼叫我們的python程式碼,流程的流轉主要依賴於XML檔案定義。
package process_engine.build.test;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;
class RunPython{
Process process;
public void runPython() {
try {
// I have also test that we could try to fit a model, so the logic here is that we
// try to call a process to run code with python engine and will wait until finished.
process = Runtime.getRuntime().exec("C:\\Users\\Public\\anaconda\\python.exe C:\\Users\\sample.py");
process.waitFor();
} catch (Exception e) {
// TODO: handle exception
System.err.println("Get error to call Python code: " + e);
}
InputStream inputStream = process.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
String lineString;
try {
while ((lineString = bufferedReader.readLine()) != null) {
System.out.println("get Python output: " + lineString);
}
} catch (Exception e) {
// TODO: handle exception
System.err.println("get python output error:" + e);
}
}
}
public class CallExternalSystemDelegate implements JavaDelegate{
// if we implement this func, then will be called based on xml file in process step.
public void execute(DelegateExecution execution) {
// TODO Auto-generated method stub
System.err.println("Now try to call external system for " + execution.getVariable("employee"));
// Here just to test how to call python code here with Java!
RunPython runPython =new RunPython();
runPython.runPython();
System.out.println("Finished for External Parts!");
}
// public static void main(String[] args) {
// RunPython runPython = new RunPython();
// runPython.runPython();
// }
}
XML流轉
需要建立一個xml檔案描述
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
xmlns:flowable="http://flowable.org/bpmn"
typeLanguage="http://www.w3.org/2001/XMLSchema"
expressionLanguage="http://www.w3.org/1999/XPath"
targetNamespace="http://www.flowable.org/processdef">
<process id="holidayRequest" name="Holiday Request" isExecutable="true">
<startEvent id="startEvent"/>
<sequenceFlow sourceRef="startEvent" targetRef="approveTask"/>
<!--
<userTask id="approveTask" name="Approve or reject request"/>
<sequenceFlow sourceRef="approveTask" targetRef="decision"/>
-->
<userTask id="approveTask" name="Approve or reject request" flowable:candidateGroups="managers"/>
<userTask id="holidayApprovedTask" name="Holiday approved" flowable:assignee="${employee}"/>
<sequenceFlow sourceRef="approveTask" targetRef="decision"/>
<exclusiveGateway id="decision"/>
<sequenceFlow sourceRef="decision" targetRef="externalSystemCall">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[
${approved}
]]>
</conditionExpression>
</sequenceFlow>
<sequenceFlow sourceRef="decision" targetRef="sendRejectionMail">
<conditionExpression xsi:type="tFormalExpression">
<![CDATA[
${!approved}
]]>
</conditionExpression>
</sequenceFlow>
<!--
<serviceTask id="externalSystemCall" name="Enter holidays in external system"
flowable:class="org.flowable.CallExternalSystemDelegate"/>
-->
<serviceTask id="externalSystemCall" name="Enter holidays in external system"
flowable:class="process_engine.build.test.CallExternalSystemDelegate"/>
<sequenceFlow sourceRef="externalSystemCall" targetRef="approveEnd"/>
<!--
<userTask id="holidayApprovedTask" name="Holiday approved"/>
<userTask id="approveTask" name="Approve or reject request" flowable:candidateGroups="managers"/>
<userTask id="holidayApprovedTask" name="Holiday approved" flowable:assignee="${employee}"/>
-->
<sequenceFlow sourceRef="holidayApprovedTask" targetRef="externalSystemCall"/>
<serviceTask id="sendRejectionMail" name="Send out rejection email"
flowable:class="org.flowable.SendRejectionMail"/>
<sequenceFlow sourceRef="sendRejectionMail" targetRef="rejectEnd"/>
<endEvent id="approveEnd"/>
<endEvent id="rejectEnd"/>
</process>
</definitions>
執行Java類
在我們輸入相應的資訊之後我們可以在Console看到對應的結果:
最後一句
現在主要是使用Flowable利用Java程式碼來執行flowable,並且可以證明我們可以使用Java程式碼來呼叫Python程式碼來完成業務的流轉。