Activiti學習(3)Activiti 6.0 快速開始指南
參考Activiti官網提供的幫助文件構建demo,原文網址:https://www.activiti.org/quick-start。其間結合其他大牛的筆記做了調整,在文中會具體說明。
在國內網上眾多快速入門的文章中,個人認為copywang在CSDN上的部落格文章【Activiti工作流引擎】官方快速入門demo比較出眾,若非自己要通過博文深化自己的理解和認識,就直接轉載了。在撰寫本文的過程中,對上文多有參考,深表感謝。
(1)$mvnProject:Maven專案的根目錄
(2)$actUnzipedPack:從http://www.activiti.org/download.html下載的解壓縮檔案的根目錄
(3)$quickStartJavaProjectName:Java專案的名稱,此處命名為ActivitiQuickStart
(4)...: 為簡化說明而省略的內容
(5)$actVer:當前執行的Activiti版本
(6)資料庫:mysql-8.0.12-winx64(原文中使用h2)
一、簡介
本文介紹使用Activiti將業務流程管理(BPM)嵌入到應用程式中的基本方法。將建立一個嵌入了BPMN標準邏輯的命令列應用程式。Activiti有先進的流程設計工具,包括基於Eclipse和基於Web的BPMN編輯器。原文中使用Activiti的Java API程式設計實現,此處會結合Eclipse中的外掛Activiti Eclipse BPMN2.0 Designer實現流程建模。
二、建立並配置Maven專案
(一)建立專案
在Eclipse中建立名為ActivitiQuickStart的Maven專案,考慮擬將程式部署到Tomcat上執行,選擇【archetype-webapp】模板原型,如圖1、圖2所示。
(二)編譯環境設定
1、完成專案建立後,會出現如下錯誤,如圖3所示:
這個問題的解決方法就是在專案的BuildPath中增加Sever Runtime庫,此處選擇安裝好的Tomcat v9.0,如圖4所示。
2、由於JRE的版本問題,專案可能會給出如下兩個警告資訊。
(1)警告資訊1:Java執行環境JRE配置不當。
Build path specifies execution environment J2SE-1.5. There are no JREs installed in the workspace that are strictly compatible with this environment.
這個問題的解決方法就是重新配置專案中的JRE環境,【點選專案右鍵】→【Build Path】→【Configure Build Path】,在Libraries中刪除原來的JRE1.5,增加已經安裝的JRE,此處安裝的是JRE10,如圖5所示。
(2)警告資訊2:編譯器配置不當。
The compiler compliance specified is 1.5 but a JRE 10 is used
在專案的【Properties】中設定【Maven】的【Project Facets】,將Jave的版本修改為10,如圖6所示。
至此,新建立的Maven專案就不再有錯誤和警告資訊,專案結構如圖7所示。
另外,在專案Properties中,設定Jave Compiler,將Compiler compliance level修改為10。
(三)配置pom.xml
在pom.xml檔案增加如下屬性和依賴:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jdk.version>10.0.0.2</jdk.version>
<junit>3.8.1</junit>
<activiti>6.0.0</activiti>
<slf4j>1.7.21</slf4j>
<mysql.connector>8.0.11</mysql.connector>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>${activiti}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.connector}</version>
</dependency>
</dependencies>
【右鍵單擊專案】→【Maven】→【Update Project...】,更新專案的依賴庫。
在Eclipse的Terminal中,進入專案根目錄$mvnProject,執行命令【mvn compile】,成功編譯的資訊如圖8所示。
或者直接在Tomcat伺服器上執行:【右鍵單擊專案】→【Run As】→【Run on Sever】,將專案加入到Tomcat伺服器上執行,訪問http://localhost:8080/ActivitiQuickStart/,結果如圖9所示,則表明基於Maven的web專案建立成功。
三、建立流程引擎
利用Simple Logging Facade for Java (slf4j)進行日誌記錄。將log4j.properties檔案新增到專案中。
File: $mvnProject/src/main/resources/log4j.properties
log4j.rootLogger=WARN, ACT
log4j.appender.ACT=org.apache.log4j.ConsoleAppender
log4j.appender.ACT.layout=org.apache.log4j.PatternLayout
log4j.appender.ACT.layout.ConversionPattern= %d{hh:mm:ss,SSS} [%t] %-5p %c %x - %m%n
新建一個包含空main函式的Java類,命名為OnboardingRequest.java。
File: $mvnProject/src/main/java/com/seu/liuds/ActivitiQuickStart/OnboardingRequest.java
package com.seu.liuds.activiti.ActivitiQuickStart;
public class OnboardingRequest {
public static void main(String[] args) {
}
}
使用Java程式碼的方式建立流程引擎。
package com.seu.liuds.activiti.ActivitiQuickStart;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration;
public class OnboardingRequest {
public static void main(String[] args) {
ProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration()
.setJdbcUrl("jdbc:mysql://localhost:3306/activiti?useSSL=false&serverTimezone=GMT%2B8")
.setJdbcUsername("root")
.setJdbcPassword("")
.setJdbcDriver("com.mysql.cj.jdbc.Driver")
.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
ProcessEngine processEngine = cfg.buildProcessEngine();
String pName = processEngine.getName();
String ver = ProcessEngine.VERSION;
System.out.println("ProcessEngine [" + pName + "] Version: [" + ver + "]");
}
}
【右鍵單擊OnboardingRequest.java】→【Run As】→【Java Application】,控制檯Console中將顯示如下資訊(版本資訊會根據實際使用的版本有所不同):
ProcessEngine [default] Version: [6.0.0.4]
此外,可以在Eclipse的Terminal中對專案進行打包。首先在專案的pom.xml檔案中增加以下內容:
<build>
<plugins>
<!-- Maven compiler Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${jdk.version}</source>
<target>${jdk.version}</target>
<encoding>UTF-8</encoding>
<skipTests>true</skipTests>
<verbose>true</verbose>
<showWarnings>true</showWarnings>
</configuration>
</plugin>
<!-- Maven Assembly Plugin -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4.1</version>
<configuration>
<!-- get all project dependencies -->
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<!-- MainClass in mainfest make a executable jar -->
<archive>
<manifest>
<mainClass>com.seu.liuds.activiti.OnboardingRequest</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<!-- bind to the packaging phase -->
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
在Terminal中執行【mvn package】,視窗中將顯示以下資訊:
[INFO] Building jar: D:\Workspace\EclipseWorkspace\ActivitiQuickStart\target\ActivitiQuickStart-jar-with-d
ependencies.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 36.922 s
[INFO] Finished at: 2018-08-07T00:38:50+08:00
[INFO] ------------------------------------------------------------------------
在Terminal中執行打包好的jar檔案,結果出現以下錯誤提示:
D:\Workspace\EclipseWorkspace\ActivitiQuickStart\target>java -jar ActivitiQuickStart-jar-with-dependencies.jar
錯誤: 找不到或無法載入主類 com.seu.liuds.activiti.OnboardingRequest
原因: java.lang.ClassNotFoundException: com.seu.liuds.activiti.OnboardingRequest
在網上查詢解決方案未果,按照CSDN上劉少明flylib的博文《Java命令列執行報:找不到或無法載入主類》,採用Eclipse的【Export】功能匯出jar檔案,執行後結果如下:
D:\Workspace\test>java -jar Onboarding.jar
log4j:WARN No appenders could be found for logger (org.activiti.engine.impl.cfg.
ProcessEngineConfigurationImpl).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
ProcessEngine [default] Version: [6.0.0.4]
至此,Activiti的BPM引擎成功嵌入ActivitiQuickStart程式中。
四、部署流程例項
以一個簡單的入職流程為例,輸入資料,如果工作經驗3年以上,釋出個性化入職歡迎資訊,使用者將手動將資料輸入到後臺系統中;如果工作經驗不高於3年,資料將自動存到後臺系統。基於BPMN 2.0規範構建的Activiti流程例項如圖10所示。
流程以xml檔案形式存放,可採用視覺化的流程建模工具(如Activiti BPMN Designer)構建。此處採用官方提供的是xml配置檔案 onboarding.bpmn20.xml,存放在【/ src / main / resources /】目錄下。onboarding.bpmn20.xml加入專案後,出現瞭如圖11所示的錯誤提示。
wangpf2011的部落格中《Activiti內建動態表單的date格式問題》一文對此有較為詳細的說明,初步嘗試後沒有解決問題,暫時略過,將時間格式的屬性刪除處理。最終加入專案的onboarding.bpmn20.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:activiti="http://activiti.org/bpmn"
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"
typeLanguage="http://www.w3.org/2001/XMLSchema"
expressionLanguage="http://www.w3.org/1999/XPath"
targetNamespace="http://www.activiti.org/processdef">
<process id="onboarding" name="Onboarding" isExecutable="true">
<startEvent id="startOnboarding" name="Start"
activiti:initiator="initiator"></startEvent>
<userTask id="enterOnboardingData" name="Enter Data"
activiti:assignee="${initiator}" activiti:candidateGroups="managers">
<extensionElements>
<activiti:formProperty id="fullName"
name="Full Name" type="string"></activiti:formProperty>
<activiti:formProperty id="yearsOfExperience"
name="Years of Experience" type="long" required="true"></activiti:formProperty>
</extensionElements>
</userTask>
<sequenceFlow
id="sid-1337EA98-7364-4198-B5D9-30F5341D6918"
sourceRef="startOnboarding" targetRef="enterOnboardingData"></sequenceFlow>
<exclusiveGateway id="decision"
name="Years of Experience" default="automatedIntroPath"></exclusiveGateway>
<sequenceFlow
id="sid-42BE5661-C3D5-4DE6-96F5-73D34822727A"
sourceRef="enterOnboardingData" targetRef="decision"></sequenceFlow>
<userTask id="personalizedIntro"
name="Personalized Introduction and Data Entry"
activiti:assignee="${initiator}" activiti:candidateGroups="managers">
<extensionElements>
<activiti:formProperty
id="personalWelcomeTime" name="Personal Welcome Time" type="date"></activiti:formProperty>
</extensionElements>
</userTask>
<endEvent id="endOnboarding" name="End"></endEvent>
<sequenceFlow
id="sid-37A73ACA-2E23-400B-96F3-71F77738DAFA"
sourceRef="automatedIntro" targetRef="endOnboarding"></sequenceFlow>
<scriptTask id="automatedIntro"
name="Generic and Automated Data Entry" scriptFormat="javascript"
activiti:autoStoreVariables="false">
<script>var dateAsString = new Date().toString();
execution.setVariable("autoWelcomeTime", dateAsString);
</script>
</scriptTask>
<sequenceFlow id="automatedIntroPath"
sourceRef="decision" targetRef="automatedIntro"></sequenceFlow>
<sequenceFlow id="personalizedIntroPath" name=">3"
sourceRef="decision" targetRef="personalizedIntro">
<conditionExpression xsi:type="tFormalExpression"><![CDATA[${yearsOfExperience > 3}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow
id="sid-BA6F061B-47B6-428B-8CE6-739244B14BD6"
sourceRef="personalizedIntro" targetRef="endOnboarding"></sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_onboarding">
<bpmndi:BPMNPlane bpmnElement="onboarding"
id="BPMNPlane_onboarding">
<bpmndi:BPMNShape bpmnElement="startOnboarding"
id="BPMNShape_startOnboarding">
<omgdc:Bounds height="35.0" width="35.0" x="155.0"
y="142.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="enterOnboardingData"
id="BPMNShape_enterOnboardingData">
<omgdc:Bounds height="80.0" width="100.0" x="240.0"
y="120.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="decision"
id="BPMNShape_decision">
<omgdc:Bounds height="40.0" width="40.0" x="385.0"
y="140.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="personalizedIntro"
id="BPMNShape_personalizedIntro">
<omgdc:Bounds height="80.0" width="100.0" x="519.0"
y="15.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endOnboarding"
id="BPMNShape_endOnboarding">
<omgdc:Bounds height="35.0" width="35.0" x="720.0"
y="159.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="automatedIntro"
id="BPMNShape_automatedIntro">
<omgdc:Bounds height="80.0" width="100.0" x="520.0"
y="255.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge
bpmnElement="sid-1337EA98-7364-4198-B5D9-30F5341D6918"
id="BPMNEdge_sid-1337EA98-7364-4198-B5D9-30F5341D6918">
<omgdi:waypoint x="190.0" y="159.0"></omgdi:waypoint>
<omgdi:waypoint x="240.0" y="160.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge
bpmnElement="sid-42BE5661-C3D5-4DE6-96F5-73D34822727A"
id="BPMNEdge_sid-42BE5661-C3D5-4DE6-96F5-73D34822727A">
<omgdi:waypoint x="340.0" y="160.0"></omgdi:waypoint>
<omgdi:waypoint x="385.0" y="160.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge
bpmnElement="sid-37A73ACA-2E23-400B-96F3-71F77738DAFA"
id="BPMNEdge_sid-37A73ACA-2E23-400B-96F3-71F77738DAFA">
<omgdi:waypoint x="570.0" y="255.0"></omgdi:waypoint>
<omgdi:waypoint x="570.0" y="176.0"></omgdi:waypoint>
<omgdi:waypoint x="720.0" y="176.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="automatedIntroPath"
id="BPMNEdge_automatedIntroPath">
<omgdi:waypoint x="405.0" y="180.0"></omgdi:waypoint>
<omgdi:waypoint x="405.0" y="295.0"></omgdi:waypoint>
<omgdi:waypoint x="520.0" y="295.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="personalizedIntroPath"
id="BPMNEdge_personalizedIntroPath">
<omgdi:waypoint x="405.0" y="140.0"></omgdi:waypoint>
<omgdi:waypoint x="405.0" y="55.0"></omgdi:waypoint>
<omgdi:waypoint x="519.0" y="55.0"></omgdi:waypoint>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="16.0" width="100.0" x="405.0"
y="140.0"></omgdc:Bounds>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge
bpmnElement="sid-BA6F061B-47B6-428B-8CE6-739244B14BD6"
id="BPMNEdge_sid-BA6F061B-47B6-428B-8CE6-739244B14BD6">
<omgdi:waypoint x="619.0" y="55.0"></omgdi:waypoint>
<omgdi:waypoint x="737.0" y="55.0"></omgdi:waypoint>
<omgdi:waypoint x="737.0" y="159.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
修改OnboardingRequest.java,將流程例項onboarding部署到Activiti引擎上,程式碼如下。
package com.seu.liuds.activiti.ActivitiQuickStart;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
public class OnboardingRequest {
public static void main(String[] args) {
ProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration()
.setJdbcUrl("jdbc:mysql://localhost:3306/activiti?useSSL=false&serverTimezone=GMT%2B8")
.setJdbcUsername("root").setJdbcPassword("").setJdbcDriver("com.mysql.cj.jdbc.Driver")
.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
ProcessEngine processEngine = cfg.buildProcessEngine();
String pName = processEngine.getName();
String ver = ProcessEngine.VERSION;
System.out.println("ProcessEngine [" + pName + "] Version: [" + ver + "]");
// Loads the supplied BPMN model and deploys it to Activiti Process Engine.
RepositoryService repositoryService = processEngine.getRepositoryService();
Deployment deployment = repositoryService.createDeployment().addClasspathResource("onboarding.bpmn20.xml")
.deploy();
// Retrieves the deployed model, proving that it is in the Activiti repository.
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.deploymentId(deployment.getId()).singleResult();
System.out.println("Found process definition [" + processDefinition.getName() + "] with id ["
+ processDefinition.getId() + "]");
}
}
在Eclipse中以Java Application執行OnboardingRequest.java,結果如下:
ProcessEngine [default] Version: [6.0.0.4]
Found process definition [Onboarding] with id [onboarding:3:5004]
五、啟動流程例項
已經部署的流程例項,可以通過Activiti API來啟動,執行,檢視歷史記錄,並以其他方式管理流程例項。這裡使用Java code來啟動和執行例項。
修改OnboardingRequest.java,官方提供的程式碼中存在以下錯誤。
選擇將丟擲異常處理,修改後的OnboardingRequest.java程式碼如下。程式碼的註釋後續慢慢補上。
package com.seu.liuds.activiti.ActivitiQuickStart;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import org.activiti.engine.FormService;
import org.activiti.engine.HistoryService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.form.FormData;
import org.activiti.engine.form.FormProperty;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration;
import org.activiti.engine.impl.form.DateFormType;
import org.activiti.engine.impl.form.LongFormType;
import org.activiti.engine.impl.form.StringFormType;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
public class OnboardingRequest {
public static void main(String[] args) throws ParseException {
ProcessEngineConfiguration cfg = new StandaloneProcessEngineConfiguration()
.setJdbcUrl("jdbc:mysql://localhost:3306/activiti?useSSL=false&serverTimezone=GMT%2B8")
.setJdbcUsername("root").setJdbcPassword("").setJdbcDriver("com.mysql.cj.jdbc.Driver")
.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
ProcessEngine processEngine = cfg.buildProcessEngine();
String pName = processEngine.getName();
String ver = ProcessEngine.VERSION;
System.out.println("ProcessEngine [" + pName + "] Version: [" + ver + "]");
// Loads the supplied BPMN model and deploys it to Activiti Process Engine.
RepositoryService repositoryService = processEngine.getRepositoryService();
Deployment deployment = repositoryService.createDeployment().addClasspathResource("onboarding.bpmn20.xml")
.deploy();
// Retrieves the deployed model, proving that it is in the Activiti repository.
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.deploymentId(deployment.getId()).singleResult();
System.out.println("Found process definition [" + processDefinition.getName() + "] with id ["
+ processDefinition.getId() + "]");
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService
.startProcessInstanceByKey("onboarding");
System.out.println("Onboarding process started with process instance id ["
+ processInstance.getProcessInstanceId()
+ "] key [" + processInstance.getProcessDefinitionKey() + "]");
TaskService taskService = processEngine.getTaskService();
FormService formService = processEngine.getFormService();
HistoryService historyService = processEngine.getHistoryService();
Scanner scanner = new Scanner(System.in);
while (processInstance != null && !processInstance.isEnded()) {
List<Task> tasks = taskService.createTaskQuery()
.taskCandidateGroup("managers").list();
System.out.println("Active outstanding tasks: [" + tasks.size() + "]");
for (int i = 0; i < tasks.size(); i++) {
Task task = tasks.get(i);
System.out.println("Processing Task [" + task.getName() + "]");
Map<String, Object> variables = new HashMap<String, Object>();
FormData formData = formService.getTaskFormData(task.getId());
for (FormProperty formProperty : formData.getFormProperties()) {
if (StringFormType.class.isInstance(formProperty.getType())) {
System.out.println(formProperty.getName() + "?");
String value = scanner.nextLine();
variables.put(formProperty.getId(), value);
} else if (LongFormType.class.isInstance(formProperty.getType())) {
System.out.println(formProperty.getName() + "? (Must be a whole number)");
Long value = Long.valueOf(scanner.nextLine());
variables.put(formProperty.getId(), value);
} else if (DateFormType.class.isInstance(formProperty.getType())) {
System.out.println(formProperty.getName() + "? (Must be a date m/d/yy)");
DateFormat dateFormat = new SimpleDateFormat("m/d/yy");
Date value = dateFormat.parse(scanner.nextLine());
variables.put(formProperty.getId(), value);
} else {
System.out.println("<form type not supported>");
}
}
taskService.complete(task.getId(), variables);
HistoricActivityInstance endActivity = null;
List<HistoricActivityInstance> activities =
historyService.createHistoricActivityInstanceQuery()
.processInstanceId(processInstance.getId()).finished()
.orderByHistoricActivityInstanceEndTime().asc()
.list();
for (HistoricActivityInstance activity : activities) {
if (activity.getActivityType() == "startEvent") {
System.out.println("BEGIN " + processDefinition.getName()
+ " [" + processInstance.getProcessDefinitionKey()
+ "] " + activity.getStartTime());
}
if (activity.getActivityType() == "endEvent") {
// Handle edge case where end step happens so fast that the end step
// and previous step(s) are sorted the same. So, cache the end step
//and display it last to represent the logical sequence.
endActivity = activity;
} else {
System.out.println("-- " + activity.getActivityName()
+ " [" + activity.getActivityId() + "] "
+ activity.getDurationInMillis() + " ms");
}
}
if (endActivity != null) {
System.out.println("-- " + endActivity.getActivityName()
+ " [" + endActivity.getActivityId() + "] "
+ endActivity.getDurationInMillis() + " ms");
System.out.println("COMPLETE " + processDefinition.getName() + " ["
+ processInstance.getProcessDefinitionKey() + "] "
+ endActivity.getEndTime());
}
}
// Re-query the process instance, making sure the latest state is available
processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId(processInstance.getId()).singleResult();
}
scanner.close();
}
}
執行OnboardingRequest.java,並根據提示輸入資料,工作經驗輸入2年,執行結果如下。
Active outstanding tasks: [1]
Processing Task [Enter Data]
Full Name?
Jerry
Years of Experience? (Must be a whole number)
2
-- Start [startOnboarding] 2 ms
-- Enter Data [enterOnboardingData] 6992 ms
-- Years of Experience [decision] 5 ms
-- Generic and Automated Data Entry [automatedIntro] 683 ms
-- End [endOnboarding] 0 ms
再次執行,工作經驗輸入6年,執行結果如下。
Active outstanding tasks: [1]
Processing Task [Enter Data]
Full Name?
Tom
Years of Experience? (Must be a whole number)
6
-- Start [startOnboarding] 2 ms
-- Enter Data [enterOnboardingData] 13549 ms
-- Years of Experience [decision] 9 ms
Active outstanding tasks: [1]
Processing Task [Personalized Introduction and Data Entry]
Personal Welcome Time? (Must be a date m/d/yy)
4/8/13
-- Start [startOnboarding] 2 ms
-- Enter Data [enterOnboardingData] 13549 ms
-- Years of Experience [decision] 9 ms
-- Personalized Introduction and Data Entry [personalizedIntro] 34586 ms
-- End [endOnboarding] 0 ms
六、用Java編寫任務(未成功,待解決)
如前所述,入職流程中有一個活動“Generic and Automated Data Entry”,當工作經驗不大於3時作為“指令碼任務”執行。下面將把這個指令碼任務遷移到Java中。
建立一個新的Java類AutomatedDataDelegate。官方程式碼在Eclipse中存在如圖13所示的錯誤提示。
根據提示,刪除丟擲異常的語句,修改後的AutomatedDataDelegate.java如下。
package com.seu.liuds.activiti.ActivitiQuickStart;
import java.util.Date;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.JavaDelegate;
public class AutomatedDataDelegate implements JavaDelegate {
public void execute(DelegateExecution execution) {
Date now = new Date();
execution.setVariable("autoWelcomeTime", now);
System.out.println("Faux call to backend for ["
+ execution.getVariable("fullName") + "]");
}
}
編輯onboarding.bpmn20.xml,將<scriptTask>替換為<serviceTask>,即將以下程式碼
……
<scriptTask id="automatedIntro"
name="Generic and Automated Data Entry" scriptFormat="javascript"
activiti:autoStoreVariables="false">
<script>var dateAsString = new Date().toString();
execution.setVariable("autoWelcomeTime", dateAsString);
</script>
</scriptTask>
……
替換為
……
<serviceTask id="automatedIntro"
name="Generic and Automated Data Entry"
activiti:class="com.seu.liuds.activiti.AutomatedDataDelegate">
</serviceTask>
……
在Eclipse中執行OnboardingRequest.java,並未出現意想之中的結果,在工作經驗輸入2之後,出現瞭如下錯誤:
Full Name?
liuds
Years of Experience? (Must be a whole number)
2
09:16:55,115 [main] ERROR org.activiti.engine.impl.interceptor.CommandContext - Error while closing command context
org.activiti.engine.ActivitiException: couldn't instantiate class com.seu.liuds.activiti.AutomatedDataDelegate
at org.activiti.engine.impl.util.ReflectUtil.instantiate(ReflectUtil.java:137)
at org.activiti.engine.impl.bpmn.helper.ClassDelegate.defaultInstantiateDelegate(ClassDelegate.java:306)
at org.activiti.engine.impl.bpmn.helper.ClassDelegate.instantiateDelegate(ClassDelegate.java:295)
at org.activiti.engine.impl.bpmn.helper.ClassDelegate.getActivityBehaviorInstance(ClassDelegate.java:273)
at org.activiti.engine.impl.bpmn.helper.ClassDelegate.execute(ClassDelegate.java:217)
at org.activiti.engine.impl.agenda.ContinueProcessOperation.executeActivityBehavior(ContinueProcessOperation.java:180)
at org.activiti.engine.impl.agenda.ContinueProcessOperation.executeSynchronous(ContinueProcessOperation.java:131)
at org.activiti.engine.impl.agenda.ContinueProcessOperation.continueThroughFlowNode(ContinueProcessOperation.java:89)
at org.activiti.engine.impl.agenda.ContinueProcessOperation.run(ContinueProcessOperation.java:55)
at org.activiti.engine.impl.interceptor.CommandInvoker.executeOperation(CommandInvoker.java:73)
at org.activiti.engine.impl.interceptor.CommandInvoker.executeOperations(CommandInvoker.java:57)
at org.activiti.engine.impl.interceptor.CommandInvoker.execute(CommandInvoker.java:42)
at org.activiti.engine.impl.interceptor.TransactionContextInterceptor.execute(TransactionContextInterceptor.java:48)
at org.activiti.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:63)
at org.activiti.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:29)
at org.activiti.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:44)
at org.activiti.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:39)
at org.activiti.engine.impl.TaskServiceImpl.complete(TaskServiceImpl.java:186)
at com.seu.liuds.activiti.ActivitiQuickStart.OnboardingRequest.main(OnboardingRequest.java:91)
Caused by: org.activiti.engine.ActivitiClassLoadingException: Class not found: com.seu.liuds.activiti.AutomatedDataDelegate
at org.activiti.engine.impl.util.ReflectUtil.loadClass(ReflectUtil.java:87)
at org.activiti.engine.impl.util.ReflectUtil.instantiate(ReflectUtil.java:134)
... 18 more
Caused by: java.lang.ClassNotFoundException: com.seu.liuds.activiti.AutomatedDataDelegate
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(Unknown Source)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(Unknown Source)
at java.base/java.lang.ClassLoader.loadClass(Unknown Source)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Unknown Source)
at org.activiti.engine.impl.util.ReflectUtil.loadClass(ReflectUtil.java:288)
at org.activiti.engine.impl.util.ReflectUtil.loadClass(ReflectUtil.java:68)
... 19 more
Exception in thread "main" org.activiti.engine.ActivitiException: couldn't instantiate class com.seu.liuds.activiti.AutomatedDataDelegate
at org.activiti.engine.impl.util.ReflectUtil.instantiate(ReflectUtil.java:137)
at org.activiti.engine.impl.bpmn.helper.ClassDelegate.defaultInstantiateDelegate(ClassDelegate.java:306)
at org.activiti.engine.impl.bpmn.helper.ClassDelegate.instantiateDelegate(ClassDelegate.java:295)
at org.activiti.engine.impl.bpmn.helper.ClassDelegate.getActivityBehaviorInstance(ClassDelegate.java:273)
at org.activiti.engine.impl.bpmn.helper.ClassDelegate.execute(ClassDelegate.java:217)
at org.activiti.engine.impl.agenda.ContinueProcessOperation.executeActivityBehavior(ContinueProcessOperation.java:180)
at org.activiti.engine.impl.agenda.ContinueProcessOperation.executeSynchronous(ContinueProcessOperation.java:131)
at org.activiti.engine.impl.agenda.ContinueProcessOperation.continueThroughFlowNode(ContinueProcessOperation.java:89)
at org.activiti.engine.impl.agenda.ContinueProcessOperation.run(ContinueProcessOperation.java:55)
at org.activiti.engine.impl.interceptor.CommandInvoker.executeOperation(CommandInvoker.java:73)
at org.activiti.engine.impl.interceptor.CommandInvoker.executeOperations(CommandInvoker.java:57)
at org.activiti.engine.impl.interceptor.CommandInvoker.execute(CommandInvoker.java:42)
at org.activiti.engine.impl.interceptor.TransactionContextInterceptor.execute(TransactionContextInterceptor.java:48)
at org.activiti.engine.impl.interceptor.CommandContextInterceptor.execute(CommandContextInterceptor.java:63)
at org.activiti.engine.impl.interceptor.LogInterceptor.execute(LogInterceptor.java:29)
at org.activiti.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:44)
at org.activiti.engine.impl.cfg.CommandExecutorImpl.execute(CommandExecutorImpl.java:39)
at org.activiti.engine.impl.TaskServiceImpl.complete(TaskServiceImpl.java:186)
at com.seu.liuds.activiti.ActivitiQuickStart.OnboardingRequest.main(OnboardingRequest.java:91)
Caused by: org.activiti.engine.ActivitiClassLoadingException: Class not found: com.seu.liuds.activiti.AutomatedDataDelegate
at org.activiti.engine.impl.util.ReflectUtil.loadClass(ReflectUtil.java:87)
at org.activiti.engine.impl.util.ReflectUtil.instantiate(ReflectUtil.java:134)
... 18 more
Caused by: java.lang.ClassNotFoundException: com.seu.liuds.activiti.AutomatedDataDelegate
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(Unknown Source)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(Unknown Source)
at java.base/java.lang.ClassLoader.loadClass(Unknown Source)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Unknown Source)
at org.activiti.engine.impl.util.ReflectUtil.loadClass(ReflectUtil.java:288)
at org.activiti.engine.impl.util.ReflectUtil.loadClass(ReflectUtil.java:68)
... 19 more
嘗試了網上的各種方法,沒有解決問題的辦法,只好先擱置。猜想是新建的AutomatedDataDelegate類並沒有被編譯成.class,所以無法被識別。希望有知道的高手指點。