# demo09
**Repository Path**: getjava/demo09
## Basic Information
- **Project Name**: demo09
- **Description**: 任务:
1、人工任务
2、脚本任务
3、Java服务任务
4、Web服务任务
5、业务规则任务
6、邮件任务
7、Http任务
8、Mule任务
9、Camel任务
10、手动任务
11、Java接收任务
12、Shell任务
13、执行监听器
14、任务监听器
15、多实例(for each)
16、补偿处理器
- **Primary Language**: Java
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 47
- **Created**: 2022-05-29
- **Last Updated**: 2022-05-29
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
------
环境:
[jkd8+]()
[mysql5.6+]()
[flowable6.4.0]()
# 1、原理
## 1.1 用户任务
一个*用户任务*被用来模拟需要由人来完成的工作。当流程执行到达此类用户任务时,将在分配给该任务的任何用户或组的任务列表中创建新任务。
## 1.2 脚本任务
### 1.2.1 脚本任务是一种自动活动。当进程执行到达脚本任务时,将执行相应的脚本。
### 1.2.2 属性:
- **name**:任务属性,用于指示任务的名称
- **type**:任务属性,其值必须为“script”以指示任务的类型
- **scriptFormat**:指示脚本语言的扩展属性(例如,javascript,groovy)
- **script**:要执行的脚本,定义为名为“script”的字段元素中的字符串
- **autoStoreVariables**:可选的任务属性标志(默认值:false),指示脚本中定义的变量是否将存储在执行上下文中(请参阅下面的注释)
- **resultVariableName**:可选的任务属性,当存在时,将在Execution上下文中使用脚本评估结果存储具有指定名称的变量(请参阅下面的注释)
### 1.2.3 flowable支持三种脚本任务类型:Javascript、groovy、juel。
通过指定**脚本**和**scriptFormat**来定义脚本任务类型,其中Groovy脚本引擎与groovy-all JAR捆绑在一起,必须引入相关依赖:
```xml
org.codehaus.groovy
groovy-all
2.5.4
pom
```
### 1.2.4 脚本中的变量:
```javascript
var sum = a+b;
execution.setVariable("sum",sum);
```
也可以简单地调用*execution.setVariable("variableName", variableValue)*,在脚本中设置流程变量。默认情况下,变量不会自动储存(**请注意,在一些早期版本中是会储存的!**)。可以将`scriptTask`的`autoStoreVariables`参数设置为`true`,以自动保存任何在脚本中定义的变量(例如上例中的*sum*)。然而这并不是最佳实践。**最佳实践是显式调用execution.setVariable()**,因为在JDK近期的一些版本中,某些脚本语言不能自动保存变量。查看[这个链接](http://www.jorambarrez.be/blog/2013/03/25/bug-on-jdk-1-7-0_17-when-using-scripttask-in-activiti/)了解更多信息。
```xml
```
此参数的缺省值是`false`,这意味着如果从脚本任务定义中省略该参数,则所有声明的变量将仅在脚本的持续时间内存在。
### 1.2.5 脚本任务的结果
脚本任务的返回值,可以通过为脚本任务定义的*'flowable:resultVariable'*属性设置为流程变量。可以是已经存在的,或者新的流程变量。如果指定为已存在的流程变量,则流程变量的值会被脚本执行的结果值覆盖。如果不指定结果变量名,则脚本结果值将被忽略。
```xml
```
## 1.3 服务任务
### 1.3.1 有四种方法声明如何调用Java逻辑:
- 指定实现了JavaDelegate或ActivityBehavior的类
- 调用解析为委托对象(delegation object)的表达式
- 调用方法表达式(method expression)
- 对值表达式(value expression)求值
### 1.3.2 字段注入与线程安全
通常情况下,在服务任务中使用Java委托与字段注入是线程安全的。然而,有些情况下不能保证线程安全。这取决于设置,或Flowable运行的环境。
当使用*flowable:class*属性时,使用字段注入总是线程安全的(译者注:仍不完全安全,如对于多实例服务任务,使用的是同一个实例)。对于引用了某个类的每一个服务任务,都会实例化新的实例,并且在创建实例时注入一次字段。在不同的任务或流程定义中多次使用同一个类没有问题。
当使用*flowable:expression*属性时,不能使用字段注入。只能通过方法调用传递变量。总是线程安全的。
当使用*flowable:delegateExpression*属性时,委托实例的线程安全性,取决于表达式解析的方式。如果该委托表达式在多个任务或流程定义中重复使用,并且表达式总是返回相同的示例,则字段注入**不是线程安全的**。
### 1.3.3 线程安全最简单的解决方法是:
- 使用表达式代替直接使用Java委托,并将所需数据通过方法参数传递给委托。
- 或者,在每次委托表达式解析时,返回委托类的新实例。这意味着这个bean的scope必须是**prototype(原型)**(例如在委托类上加上@Scope(SCOPE_PROTOTYPE)注解)。
### 1.3.4 异常处理
- 抛出业务异常
可以在服务任务或脚本任务的用户代码中抛出BPMN错误。可以在Java委托、脚本、表达式与委托表达式中,抛出特殊的FlowableException:*BpmnError*。引擎会捕获这个异常,并将其转发至合适的错误处理器,如错误边界事件或错误事件子流程。
默认映射是一个不指定类的映射,可以匹配任何Java异常:
```xml
```
- 发生异常时,将流程执行路由至另一条路径
ThrowingDelegate将会抛出BussinessException业务异常,errorcode为localError,Flowable会将`BussinessException`类,转换为带有指定错误代码的BPMN错误,边界错误捕获事件会捕获到此异常,并继续后续操作。
## 1.4 业务规则任务
业务规则任务(business rule task)用于同步地执行一条或多条规则。Flowable使用名为Drools Expert的Drools规则引擎执行业务规则。目前,业务规则中包含的.drl文件,必须与定义了业务规则服务并执行规则的流程定义一起部署。这意味着流程中使用的所有.drl文件都需要打包在流程BAR文件中,与任务表单等类似。要了解如何为Drools Expert创建业务规则,请访问位于[JBoss Drools](http://www.jboss.org/drools/documentation)的Drools文档。
如果想要使用自己的规则任务实现,比如希望通过不同方法使用Drools,或者想使用完全不同的规则引擎,则可以使用BusinessRuleTask的class或expression属性。这样它会与[服务任务](https://tkjohn.github.io/flowable-userguide/#bpmnJavaServiceTask)的行为完全相同。
要执行与流程定义在同一个BAR文件中部署的一条或多条业务规则,需要定义输入与结果变量。输入变量可以用流程变量的列表定义,使用逗号分隔。输出变量只能有一个变量名,将执行业务规则后的输出对象存储至流程变量。请注意结果变量会包含对象的List。如果没有指定结果变量名,默认为org.flowable.engine.rules.OUTPUT。
## 1.5 邮件任务
Flowable让你可以通过自动的邮件服务任务(email task),增强业务流程。可以向一个或多个收信人发送邮件,支持cc,bcc,HTML文本,等等。请注意邮件任务**不是**BPMN 2.0规范的“官方”任务(所以也没有专用图标)。因此,在Flowable中,邮件任务实现为一种特殊的服务任务。
## 1.6 Http任务
Http任务(Http task)用于**发出HTTP请求**,增强了Flowable的集成能力。请注意Http任务不是BPMN 2.0规范的“官方”任务(所以也没有专用图标)。因此,在Flowable中,Http任务实现为一种特殊的服务任务。
## 1.7 Camel任务
### 1.7.1 Camel任务
Camel任务(Camel task)可以向Camel发送消息,增强Flowable的集成特性。请注意Camel任务**不是**BPMN 2.0规范的“官方”任务(所以也没有专用图标)。因此,在Flowable中,Camel任务实现为一种特殊的服务任务。还请注意,需要在项目中包含Flowable Camel模块才能使用Camel任务。
### 1.7.2 连通性测试示例
将试着从Camel接收与发送消息。我们将发送一个字符串,Camel在其上附加一些文字并返回作为结果。发送部分比较普通,即以变量的格式将信息发送给Camel服务。
## 1.8 手动任务
手动任务(manual task)定义在BPM引擎之外的任务。它用于建模引擎不需要了解,也不需要提供系统或用户界面的工作。对于引擎来说,手动任务将按**直接穿过活动**处理,在流程执行到达手动任务时,自动继续执行流程。
## 1.9 Java接收任务
接收任务(receive task),是等待特定消息到达的简单任务。目前,我们只为这个任务实现了Java语义。当流程执行到达接收任务时,流程状态将提交至持久化存储。这意味着流程将保持等待状态,直到引擎接收到特定的消息,触发流程穿过接收任务继续执行。
要使流程实例从接收任务的等待状态中继续执行,需要使用到达接收任务的执行id,调用*runtimeService.signal(executionId)*。
### 1.10 Shell任务
Shell任务(Shell task)可以运行Shell脚本与命令。
将会运行"cat /etc/passwd | grep root" Shell脚本,等待其结束,并将其结果存入*resultVar*。
## 1.11 执行监听器
执行监听器(execution listener)可以在流程执行中发生特定的事件时,执行外部Java代码或计算表达式。可以被捕获的事件有:
- 流程实例的启动和结束。
- 流程执行转移。
- 活动的启动和结束。
- 网关的启动和结束。
- 中间事件的启动和结束。
- 启动事件的结束,和结束事件的启动。
## 1.12 任务监听器
任务监听器(task listener)用于在特定的任务相关事件发生时,执行自定义的Java逻辑或表达式。
任务监听器只能在流程定义中作为[用户任务](https://tkjohn.github.io/flowable-userguide/#bpmnUserTask)的子元素。请注意,任务监听器是一个Flowable自定义结构,因此也需要作为*BPMN 2.0 extensionElements*,放在*flowable*命名空间下。
*任务监听器*包含下列属性:
- event(事件)
(必填):触发任务监听器的任务事件类型。可用的事件有:
- **create(创建)**:当任务已经创建,并且**所有任务参数都已经设置**时触发。
- **assignment(指派)**:当任务已经指派给某人时触发。请注意:当流程执行到达用户任务时,在触发*create*事件**之前**,会首先触发*assignment*事件。这顺序看起来不太自然,但是有实际原因的:当收到*create*事件时,我们通常希望能看到任务的所有参数,包括办理人。
- **complete(完成)**:当任务已经完成,从运行时数据中删除前触发。
- **delete(删除)**:在任务即将被删除前触发。请注意任务由completeTask正常完成时也会触发。
- **class**:需要调用的委托类。这个类必须实现`org.flowable.engine.delegate.TaskListener`接口。
- **expression**:(不能与*class*属性一起使用):指定在事件发生时要执行的表达式。可以为被调用的对象传递`DelegateTask`对象与事件名(使用`task.eventName`)作为参数。
- **delegateExpression**:指定一个能够解析为`TaskListener`接口实现类的对象的表达式。[与服务任务类似](https://tkjohn.github.io/flowable-userguide/#bpmnJavaServiceTaskXML)。
- 较早之前,我们也引入了新的执行监听器类型,org.flowable.engine.impl.bpmn.listener.ScriptTaskListener。这个脚本任务监听器可以为一个任务监听器事件执行一段脚本代码。
```hxml
def bar = "BAR"; // local variable
foo = "FOO"; // pushes variable to execution context
task.setOwner("kermit"); // test access to task instance
bar // implicit return value
```
## 1.13 多实例
实例活动(multi-instance activity)是在业务流程中,为特定步骤定义重复的方式。在编程概念中,多实例类似**for each**结构:可以为给定集合中的每一条目,**顺序或并行地**,执行特定步骤,甚至是整个子流程。
*多实例*是一个普通活动,加上定义(被称作“*多实例*特性的”)额外参数,会使得活动在运行时被多次执行。下列活动可以成为*多实例活动:*
- [用户任务](https://tkjohn.github.io/flowable-userguide/#bpmnUserTask)
- [脚本任务](https://tkjohn.github.io/flowable-userguide/#bpmnScriptTask)
- [Java服务任务](https://tkjohn.github.io/flowable-userguide/#bpmnJavaServiceTask)
- [Web服务任务](https://tkjohn.github.io/flowable-userguide/#bpmnWebserviceTask)
- [业务规则任务](https://tkjohn.github.io/flowable-userguide/#bpmnBusinessRuleTask)
- [邮件任务](https://tkjohn.github.io/flowable-userguide/#bpmnEmailTask)
- [人工任务](https://tkjohn.github.io/flowable-userguide/#bpmnManualTask)
- [接收任务](https://tkjohn.github.io/flowable-userguide/#bpmnReceiveTask)
- [(嵌入式)子流程](https://tkjohn.github.io/flowable-userguide/#bpmnSubProcess)
- [调用活动](https://tkjohn.github.io/flowable-userguide/#bpmnCallActivity)
[网关](https://tkjohn.github.io/flowable-userguide/#bpmnGateways)与[事件](https://tkjohn.github.io/flowable-userguide/#bpmnEvents)**不能**设置为多实例。
按照BPMN2.0规范的要求,用于为每个实例创建执行的父执行,会提供下列变量:
- **nrOfInstances**:实例总数。
- **nrOfActiveInstances**:当前活动的(即未完成的),实例数量。对于顺序多实例,这个值总为1。
- **nrOfCompletedInstances**:已完成的实例数量。
可以调用`execution.getVariable(x)`方法获取这些值。
另外,每个被创建的执行,都有局部变量(对其他执行不可见,也不存储在流程实例级别):
- **loopCounter**:给定实例在*for-each循环中的index*。可以通过Flowable的**elementIndexVariable**属性为loopCounter变量重命名。
## 1.14 补偿处理器
如果要使用一个活动补偿另一个活动的影响,可以将其声明为*补偿处理器(compensation handler)*。补偿处理器不在正常流程中执行,而只在流程抛出补偿事件时才会执行。
补偿处理器不得有入口或出口顺序流。
补偿处理器必须通过单向的连接,关联一个补偿边界事件。
中间补偿事件抛出**补偿事件**给对应的(activityRef指定的节点)节点的**补偿处理程序**处理。
# 2、流程图
- 用户任务

- 脚本任务

- 服务任务

- 服务任务异常处理

- 业务规则任务

- 邮件任务

- Http任务

- Camel任务

- 手动任务

- Java接收任务

- Shell任务

- 执行监听器

- 任务监听器

- 多实例

- 补偿处理器

# 3、配置
## 3.1 用户任务
**fakeLdapService必须是spring 中的bean**
```xml
```
Groovy脚本引擎与groovy-all JAR捆绑在一起,必须引入相关依赖:
```xml
org.codehaus.groovy
groovy-all
2.5.4
pom
```
## 3.2 脚本任务
## 3.3 服务任务
- spring bean配置详见flowable-context.xml:
```xml
```
- 发生异常时,将流程执行路由至另一条路径:
```xml
```
```java
public class ThrowsExceptionBehavior implements ActivityBehavior {
private static final long serialVersionUID = 1L;
public void execute(DelegateExecution execution) {
System.out.println("====发生异常时,将流程执行路由至另一条出线继续执行==");
String var = (String) execution.getVariable("exception");
String sequenceFlowToTake = null;
try {
executeLogic(var);
sequenceFlowToTake = "no-exception";
} catch (Exception e) {
sequenceFlowToTake = "exception";
}
DelegateHelper.leaveDelegate(execution, sequenceFlowToTake);
}
protected void executeLogic(String value) {
if (value.equals("throw-exception")) {
throw new RuntimeException();
}
}
}
```
## 3.4 业务规则任务
```xml
```
## 3.5 邮件任务
- 在*flowable.cfg.xml*配置文件中设置:
```xml
```
## 3.6 Http任务
### 3.6.1 添加flowable-http依赖
Http任务的默认行为类是org.flowable.http.impl.HttpActivityBehaviorImpl,此类在flowable-http中。
```xml
org.flowable
flowable-http
6.4.0
```
### 3.6.2 创建自定义Http任务行为类
也可以使用自定义的实现,覆盖Http任务的默认行为。 需要扩展org.flowable.http.HttpActivityBehavior,并覆盖perform()方法。
**详见源码DefaultActivityBehaviorFactory中createHttpActivityBehavior方法解析field name为httpActivityBehaviorClass,并利用反射实例化此类。**
```xml
```
### 3.6.2 配置Http客户端
```xml
...
```
## 3.7 Camel任务
### 3.7.1 camelContext配置
详见generic-camel-flowable-context.xml文件
```xml
com.study.demo.camel
```
### 3.7.2 引入camel依赖包
```xml
org.flowable
flowable-camel
6.4.0
```
### 3.7.3 路由及Endpoint 配置
字符串"world"会在结尾连接上名为“input”的参数,并存储在camelBody变量中;
```java
public class SimpleCamelCallRoute extends RouteBuilder{
@Override
public void configure() throws Exception {
from("flowable:camelprocess:simpleCall").transform().simple("${property.input} World");
}
}
```
## 3.8 手动任务
## 3.9 Java接收任务
### 3.10 Shell任务
## 3.11 执行监听器
## 3.12 任务执行器
## 3.13 多实例
- 多实例配置及完成条件动态指定:
```xml
${mulitiInstance.completeTask(execution)}
```
```java
public class MulitiInstanceCompleteCondition {
public boolean completeTask(DelegateExecution execution) {
FlowElement flowElement = execution.getCurrentFlowElement();
System.out.println("总的任务数量:" + execution.getVariable("nrOfInstances") + "当前获取的任务数量:"
+ execution.getVariable("nrOfActiveInstances") + " - " + "已经完成的会签任务数量:"
+ execution.getVariable("nrOfCompletedInstances"));
System.out.println("=======【当前节点:】========" + flowElement.getName());
// isComplete为true,直接完成
String isComplete = execution.getVariable("isComplete").toString();
if ("true".equals(isComplete)) {
return true;
}
return false;
}
}
```
### 3.13.1 流程定义包含了六个执行监听器:
- 流程启动的时候执行监听器,实现接口ExecutionListener:
```java
public class ExampleExecutionListenerOne implements ExecutionListener {
/**
*
*/
private static final long serialVersionUID = 1L;
public void notify(DelegateExecution execution) {
System.out.println("=========================执行器,流程启动时执行=========================");
execution.setVariable("variableSetInExecutionListener", "firstValue");
execution.setVariable("eventNameReceived", execution.getEventName());
execution.setVariable("businessKeyInExecution", execution.getProcessInstanceBusinessKey());
}
}
```
- 第一个人工任务节点,开始的时候执行监听器,实现接口ExecutionListener:
```java
public class ExampleExecutionListenerTwo implements ExecutionListener {
/**
*
*/
private static final long serialVersionUID = 1L;
public void notify(DelegateExecution execution) {
FlowElement currentFlowElement = DelegateHelper.getFlowElement(execution);
System.out.println("========================【执行器,当前节点】:" + currentFlowElement.getName()+ "=========================");
execution.setVariable("variableSetInExecutionListener", "secondValue");
execution.setVariable("eventNameReceived", execution.getEventName());
}
}
```
- 第二个人工任务节点,结束的时候执行监听器,表达式类型:${myExpression.getEventName(execution.eventName)}
```java
public class MyExpressionExecutionListener {
public String getEventName(String name) {
System.out.println("====================表达式执行监听器,eventName:" + name+"========================");
return "expression:" + name ;
}
}
```
- 第三个人工任务节点,开始的时候执行监听器,委托表达式类型(实现JavaDelegate):${myDelegateExpression}
```java
public class MyDelegateExpression implements JavaDelegate {
public void execute(DelegateExecution execution) {
FlowElement currentFlowElement = DelegateHelper.getFlowElement(execution);
System.out.println("=========================【执行器,当前节点】:" + currentFlowElement.getName() + "=========================");
}
}
```
- 第四个人工任务节点,开始的时候执行监听器,脚本类型:ScriptExecutionListener
- 第五个人工任务节点,开始的时候执行监听器,实现接口ExecutionListener:ExampleFieldInjectedExecutionListener
```java
public class ExampleFieldInjectedExecutionListener implements ExecutionListener {
/**
*
*/
private static final long serialVersionUID = 1L;
private Expression fixedValue;
private Expression dynamicValue;
public void notify(DelegateExecution execution) {
FlowElement currentFlowElement = DelegateHelper.getFlowElement(execution);
System.out.println("=========================【执行器,当前节点】:" + currentFlowElement.getName() + "=========================");
execution.setVariable("dynamicValue", fixedValue.getValue(execution).toString() +
dynamicValue.getValue(execution).toString());
}
}
```
## 3.14 补偿处理器
### 3.14.1 补偿处理器配置:
要将一个活动声明为补偿处理器,需要将`isForCompensation`属性设置为true:
```xml
```
### 3.14.2 中间补偿事件配置:
中间补偿事件抛出补偿事件给对应的(activityRef指定的节点)节点的补偿处理程序处理
```xml
```
# 4、测试
## 4.1 用户任务--UserTaskTest
- 运行demo
- 查看数据库表
```sql
SELECT * FROM flowable.ACT_RU_EXECUTION;
SELECT * FROM flowable.ACT_RU_TASK;
SELECT * FROM flowable.ACT_RU_VARIABLE;
```
## 4.2 脚本任务--ScriptTaskTest
- 运行demo
- 查看数据库表
```sql
SELECT * FROM flowable.ACT_RU_EXECUTION;
SELECT * FROM flowable.ACT_RU_TASK;
```
## 4.3 服务任务
### 4.3.1 测试类 ServiceTaskTest
- 线程安全:
在运行上面流程定义的流程实例后,*INSTANCE_COUNT*的值为*2*。这是因为每次解析*${prototypeDelegateExpressionBean}*时,都会创建新实例。可以看到三个*Expression*成员字段的注入没有任何问题。
在对于单例bean,*INSTANCE_COUNT*总是*1*。在这个委托中,没有*Expression*成员字段(使用*MIXED*模式)。而在*COMPATIBILITY*模式下,就会抛出异常,因为需要有成员字段。这个bean也可以使用*DISABLED*模式,但会禁用上面进行了字段注入的原型bean。
单例bean是线程不安全的。
- 发生异常时,将流程执行路由至另一条路径
```xml
```
### 4.3.2 测试类 ExceptionForServiceTaskTest
- 服务任务异常处理
ThrowingDelegate将会抛出BussinessException业务异常,errorcode为localError,Flowable会将`BussinessException`类,转换为带有指定错误代码的BPMN错误,监听在此错误上的边界错误捕获事件会捕获到此异常,并继续后续操作。
## 4.4 业务规则任务--BusinessRuleTaskTest
- 运行demo
- 查看数据库表
## 4.5 邮件任务--MailTaskTest
- 运行demo
- 查看数据库表
## 4.6 Http任务--HttpTaskTest
- 运行demo
- 查看数据库表
## 4.7 Camel任务--CamelTaskTest
- CamelTaskTest测试类说明
在这个例子里,将试着从Camel接收与发送消息。我们将发送一个字符串,Camel在其上附加一些文字并返回作为结果。发送部分比较普通,即以变量的格式将信息发送给Camel服务。这是我们的调用代码:
```java
/**
* 启动流程实例并测试连通性
*
*/
@Test
public void testPingPong() {
Map variables = new HashMap();
variables.put("input", "Hello");
Map outputMap = new HashMap();
variables.put("outputMap", outputMap);
runtimeService.startProcessInstanceByKey("camelprocess", variables);
System.out.println("==============outputMap: " + outputMap);
}
```
“input”变量是实际上是Camel路由的输入,而outputMap用于捕获Camel传回的结果。
请注意SaveOutput服务任务会从上下文中取出“Output”变量,并存储至上面提到的OutputMap。现在需要 了解变量如何发送至Camel,以及如何返回。这就需要了解Camel行为(Behavior)的概念。变量与Camel通信的方式可以通过CamelBehavior配置。在这个例子里使用默认配置。
- 查看数据库表
```sql
SELECT * FROM flowable.act_ru_task;
SELECT * FROM flowable.act_ru_variable;
```
## 4.8 手动任务--ManualTaskTest
- 运行demo
- 查看数据库表
## 4.9 Java接收任务--ReceiveTaskTest
- 运行demo
- 查看数据库表
### 4.10 Shell任务--ShellTaskTest
将会运行"cat /etc/passwd | grep root" Shell脚本,等待其结束,并将其结果存入*resultVar*。
- 运行demo
- 查看数据库表
## 4.11 执行监听器--ExecutionListenerTest
- 运行demo
- 查看数据库表
## 4.12 任务监听器--TaskListenerTest
- 运行demo
- 查看数据库表
## 4.13 多实例--MultilInstanceTest
- 运行demo
- 查看数据库表
## 4.14 补偿处理器--CompensationHandlerTest
- 运行demo
- 查看数据库表