1. 程式人生 > 其它 >Flowable本地配置開發

Flowable本地配置開發

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

,我們就可以進入到flowable的視覺化的app網站。預設的使用者名稱是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程式碼來完成業務的流轉。