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
图2

(二)编译环境设置

1、完成项目创建后,会出现如下错误,如图3所示:

图3

这个问题的解决方法就是在项目的BuildPath中增加Sever Runtime库,此处选择安装好的Tomcat v9.0,如图4所示。

图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所示。

图5

(2)警告信息2:编译器配置不当。

The compiler compliance specified is 1.5 but a JRE 10 is used

在项目的【Properties】中设置【Maven】的【Project Facets】,将Jave的版本修改为10,如图6所示。

图6

至此,新创建的Maven项目就不再有错误和警告信息,项目结构如图7所示。

图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所示。

图8

或者直接在Tomcat服务器上运行:【右键单击项目】→【Run As】→【Run on Sever】,将项目加入到Tomcat服务器上运行,访问http://localhost:8080/ActivitiQuickStart/,结果如图9所示,则表明基于Maven的web项目创建成功。

图9

三、创建流程引擎

利用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>

(有关Maven Compile Plugin和Maven Assembly Plugin两个插件,参见《Maven 教程(21)— maven-compiler-plugin 插件详解》和《Maven build插件之Maven-assembly-plugin自定义打包》两篇博文)

在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]

出现的日志警告,参考《log4j:WARN Please initialize the log4j system properly解决办法》应该可以解决,此处暂时略过

至此,Activiti的BPM引擎成功嵌入ActivitiQuickStart程序中。

四、部署流程实例

以一个简单的入职流程为例,输入数据,如果工作经验3年以上,发布个性化入职欢迎信息,用户将手动将数据输入到后台系统中;如果工作经验不高于3年,数据将自动存到后台系统。基于BPMN 2.0规范构建的Activiti流程实例如图10所示。

图10

流程以xml文件形式存放,可采用可视化的流程建模工具(如Activiti BPMN Designer)构建。此处采用官方提供的是xml配置文件 onboarding.bpmn20.xml,存放在【/ src / main / resources /】目录下。onboarding.bpmn20.xml加入项目后,出现了如图11所示的错误提示。

图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="&gt;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,官方提供的代码中存在以下错误。

图12

选择将抛出异常处理,修改后的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所示的错误提示。

图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,所以无法被识别。希望有知道的高手指点。

猜你喜欢

转载自blog.csdn.net/Liu_desheng/article/details/81452083