同步操作将从 我气你/demo09 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
环境:
一个用户任务被用来模拟需要由人来完成的工作。当流程执行到达此类用户任务时,将在分配给该任务的任何用户或组的任务列表中创建新任务。
通过指定脚本和scriptFormat来定义脚本任务类型,其中Groovy脚本引擎与groovy-all JAR捆绑在一起,必须引入相关依赖:
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.5.4</version>
<type>pom</type>
</dependency>
var sum = a+b;
execution.setVariable("sum",sum);
也可以简单地调用execution.setVariable("variableName", variableValue),在脚本中设置流程变量。默认情况下,变量不会自动储存(请注意,在一些早期版本中是会储存的!)。可以将scriptTask
的autoStoreVariables
参数设置为true
,以自动保存任何在脚本中定义的变量(例如上例中的sum)。然而这并不是最佳实践。最佳实践是显式调用execution.setVariable(),因为在JDK近期的一些版本中,某些脚本语言不能自动保存变量。查看这个链接了解更多信息。
<scriptTask id="script" scriptFormat="JavaScript" flowable:autoStoreVariables="false">
此参数的缺省值是false
,这意味着如果从脚本任务定义中省略该参数,则所有声明的变量将仅在脚本的持续时间内存在。
脚本任务的返回值,可以通过为脚本任务定义的*'flowable:resultVariable'*属性设置为流程变量。可以是已经存在的,或者新的流程变量。如果指定为已存在的流程变量,则流程变量的值会被脚本执行的结果值覆盖。如果不指定结果变量名,则脚本结果值将被忽略。
<scriptTask id="juel" name="juel脚本服务" scriptFormat="juel" flowable:autoStoreVariables="false" flowable:resultVariable="myVar">
<script><![CDATA[#{juelvar}]]></script>
</scriptTask>
通常情况下,在服务任务中使用Java委托与字段注入是线程安全的。然而,有些情况下不能保证线程安全。这取决于设置,或Flowable运行的环境。
当使用flowable:class属性时,使用字段注入总是线程安全的(译者注:仍不完全安全,如对于多实例服务任务,使用的是同一个实例)。对于引用了某个类的每一个服务任务,都会实例化新的实例,并且在创建实例时注入一次字段。在不同的任务或流程定义中多次使用同一个类没有问题。
当使用flowable:expression属性时,不能使用字段注入。只能通过方法调用传递变量。总是线程安全的。
当使用flowable:delegateExpression属性时,委托实例的线程安全性,取决于表达式解析的方式。如果该委托表达式在多个任务或流程定义中重复使用,并且表达式总是返回相同的示例,则字段注入不是线程安全的。
可以在服务任务或脚本任务的用户代码中抛出BPMN错误。可以在Java委托、脚本、表达式与委托表达式中,抛出特殊的FlowableException:BpmnError。引擎会捕获这个异常,并将其转发至合适的错误处理器,如错误边界事件或错误事件子流程。
默认映射是一个不指定类的映射,可以匹配任何Java异常:
<serviceTask id="exceptiondeal" name="抛出业务异常" flowable:class="com.study.demo.delegate.ThrowingDelegate">
<extensionElements>
<flowable:mapException errorCode="localError"/>
</extensionElements>
</serviceTask>
发生异常时,将流程执行路由至另一条路径
ThrowingDelegate将会抛出BussinessException业务异常,errorcode为localError,Flowable会将BussinessException
类,转换为带有指定错误代码的BPMN错误,边界错误捕获事件会捕获到此异常,并继续后续操作。
业务规则任务(business rule task)用于同步地执行一条或多条规则。Flowable使用名为Drools Expert的Drools规则引擎执行业务规则。目前,业务规则中包含的.drl文件,必须与定义了业务规则服务并执行规则的流程定义一起部署。这意味着流程中使用的所有.drl文件都需要打包在流程BAR文件中,与任务表单等类似。要了解如何为Drools Expert创建业务规则,请访问位于JBoss Drools的Drools文档。
如果想要使用自己的规则任务实现,比如希望通过不同方法使用Drools,或者想使用完全不同的规则引擎,则可以使用BusinessRuleTask的class或expression属性。这样它会与服务任务的行为完全相同。
要执行与流程定义在同一个BAR文件中部署的一条或多条业务规则,需要定义输入与结果变量。输入变量可以用流程变量的列表定义,使用逗号分隔。输出变量只能有一个变量名,将执行业务规则后的输出对象存储至流程变量。请注意结果变量会包含对象的List。如果没有指定结果变量名,默认为org.flowable.engine.rules.OUTPUT。
Flowable让你可以通过自动的邮件服务任务(email task),增强业务流程。可以向一个或多个收信人发送邮件,支持cc,bcc,HTML文本,等等。请注意邮件任务不是BPMN 2.0规范的“官方”任务(所以也没有专用图标)。因此,在Flowable中,邮件任务实现为一种特殊的服务任务。
Http任务(Http task)用于发出HTTP请求,增强了Flowable的集成能力。请注意Http任务不是BPMN 2.0规范的“官方”任务(所以也没有专用图标)。因此,在Flowable中,Http任务实现为一种特殊的服务任务。
Camel任务(Camel task)可以向Camel发送消息,增强Flowable的集成特性。请注意Camel任务不是BPMN 2.0规范的“官方”任务(所以也没有专用图标)。因此,在Flowable中,Camel任务实现为一种特殊的服务任务。还请注意,需要在项目中包含Flowable Camel模块才能使用Camel任务。
将试着从Camel接收与发送消息。我们将发送一个字符串,Camel在其上附加一些文字并返回作为结果。发送部分比较普通,即以变量的格式将信息发送给Camel服务。
手动任务(manual task)定义在BPM引擎之外的任务。它用于建模引擎不需要了解,也不需要提供系统或用户界面的工作。对于引擎来说,手动任务将按直接穿过活动处理,在流程执行到达手动任务时,自动继续执行流程。
接收任务(receive task),是等待特定消息到达的简单任务。目前,我们只为这个任务实现了Java语义。当流程执行到达接收任务时,流程状态将提交至持久化存储。这意味着流程将保持等待状态,直到引擎接收到特定的消息,触发流程穿过接收任务继续执行。
要使流程实例从接收任务的等待状态中继续执行,需要使用到达接收任务的执行id,调用runtimeService.signal(executionId)。
Shell任务(Shell task)可以运行Shell脚本与命令。
将会运行"cat /etc/passwd | grep root" Shell脚本,等待其结束,并将其结果存入resultVar。
执行监听器(execution listener)可以在流程执行中发生特定的事件时,执行外部Java代码或计算表达式。可以被捕获的事件有:
任务监听器(task listener)用于在特定的任务相关事件发生时,执行自定义的Java逻辑或表达式。
任务监听器只能在流程定义中作为用户任务的子元素。请注意,任务监听器是一个Flowable自定义结构,因此也需要作为BPMN 2.0 extensionElements,放在flowable命名空间下。
任务监听器包含下列属性:
event(事件)
(必填):触发任务监听器的任务事件类型。可用的事件有:
class:需要调用的委托类。这个类必须实现org.flowable.engine.delegate.TaskListener
接口。
expression:(不能与class属性一起使用):指定在事件发生时要执行的表达式。可以为被调用的对象传递DelegateTask
对象与事件名(使用task.eventName
)作为参数。
delegateExpression:指定一个能够解析为TaskListener
接口实现类的对象的表达式。与服务任务类似。
较早之前,我们也引入了新的执行监听器类型,org.flowable.engine.impl.bpmn.listener.ScriptTaskListener。这个脚本任务监听器可以为一个任务监听器事件执行一段脚本代码。
<flowable:taskListener event="complete" class="org.flowable.engine.impl.bpmn.listener.ScriptTaskListener" >
<flowable:field name="script">
<flowable:string>
def bar = "BAR"; // local variable
foo = "FOO"; // pushes variable to execution context
task.setOwner("kermit"); // test access to task instance
bar // implicit return value
</flowable:string>
</flowable:field>
<flowable:field name="language" stringValue="groovy" />
<flowable:field name="resultVariable" stringValue="myVar" />
</flowable:taskListener>
实例活动(multi-instance activity)是在业务流程中,为特定步骤定义重复的方式。在编程概念中,多实例类似for each结构:可以为给定集合中的每一条目,顺序或并行地,执行特定步骤,甚至是整个子流程。
多实例是一个普通活动,加上定义(被称作“多实例特性的”)额外参数,会使得活动在运行时被多次执行。下列活动可以成为多实例活动:
按照BPMN2.0规范的要求,用于为每个实例创建执行的父执行,会提供下列变量:
可以调用execution.getVariable(x)
方法获取这些值。
另外,每个被创建的执行,都有局部变量(对其他执行不可见,也不存储在流程实例级别):
如果要使用一个活动补偿另一个活动的影响,可以将其声明为补偿处理器(compensation handler)。补偿处理器不在正常流程中执行,而只在流程抛出补偿事件时才会执行。
补偿处理器不得有入口或出口顺序流。
补偿处理器必须通过单向的连接,关联一个补偿边界事件。
中间补偿事件抛出补偿事件给对应的(activityRef指定的节点)节点的补偿处理程序处理。
fakeLdapService必须是spring 中的bean
<userTask id="usertask" name="用户任务" flowable:assignee="${fakeLdapService.findManagerForEmployee(emp)}">
</userTask>
Groovy脚本引擎与groovy-all JAR捆绑在一起,必须引入相关依赖:
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.5.4</version>
<type>pom</type>
</dependency>
<bean id="delegateExpressionBean" class="com.study.demo.delegate.DelegateExpressionBean"/>
<bean id="printer" class="com.study.demo.delegate.Printer" />
<bean id="split" class="com.study.demo.delegate.Split" />
<bean id="genderBean" class="com.study.demo.delegate.Gender"/>
<bean id="singletonDelegateExpressionBean" class="com.study.demo.delegate.SingletonDelegateExpressionBean" scope="singleton"/>
<bean id="prototypeDelegateExpressionBean" class="com.study.demo.delegate.PrototypeDelegateExpressionBean" scope="prototype"/>
<serviceTask id="exceptiondeal" name="异常处理" flowable:class="com.study.demo.exception.ThrowsExceptionBehavior"></serviceTask>
<sequenceFlow id="sid-748CA641-57F3-46F8-8D1F-1C409A509F83" sourceRef="serviceTask4" targetRef="exceptiondeal"></sequenceFlow>
<sequenceFlow id="exception" name="exception" sourceRef="exceptiondeal" targetRef="user"></sequenceFlow>
<sequenceFlow id="no-exception" name="no-exception" sourceRef="exceptiondeal" targetRef="sid-FB3C07EC-B784-4C3D-BC1B-294C5155FBF0"></sequenceFlow>
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();
}
}
}
<businessRuleTask id="businessruletask" name="业务规则任务" flowable:ruleVariablesInput="${order}" flowable:resultVariable="rulesOutput"
flowable:class="com.study.demo.delegate.MyRuleServiceDelegate"></businessRuleTask>
<!--邮件服务配置 -->
<property name="mailServerHost" value="smtp.qq.com" />
<property name="mailServerPort" value="587"/>
<property name="mailServerDefaultFrom" value="465243573@qq.com" />
<property name="mailServerUsername" value="465243573@qq.com" />
<property name="mailServerPassword" value="POP3/SMTP服务的授权码(qq邮箱-->设置-->账户-->开启服务-->POP3/SMTP服务-->开启)" />
<property name="mailServerUseSSL" value="true" />
Http任务的默认行为类是org.flowable.http.impl.HttpActivityBehaviorImpl,此类在flowable-http中。
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-http</artifactId>
<version>6.4.0</version>
</dependency>
也可以使用自定义的实现,覆盖Http任务的默认行为。 需要扩展org.flowable.http.HttpActivityBehavior,并覆盖perform()方法。
详见源码DefaultActivityBehaviorFactory中createHttpActivityBehavior方法解析field name为httpActivityBehaviorClass,并利用反射实例化此类。
<serviceTask id="httpGet" flowable:type="http">
<extensionElements>
<flowable:field name="httpActivityBehaviorClass">
<flowable:string>
<![CDATA[org.example.flowable.HttpActivityBehaviorCustomImpl]]>
</flowable:string>
</flowable:field>
</extensionElements>
</sericeTask>
<bean id="processEngineConfiguration" class="org.flowable.spring.SpringProcessEngineConfiguration">
...
<!--http任务配置 -->
<property name="httpClientConfig" ref="httpClientConfig" />
<bean id="httpClientConfig" class="org.flowable.engine.cfg.HttpClientConfig">
<property name="connectTimeout" value="5000"/>
<property name="socketTimeout" value="5000"/>
<property name="connectionRequestTimeout" value="5000"/>
<property name="requestRetryLimit" value="5"/>
</bean>
详见generic-camel-flowable-context.xml文件
<camelContext id="camelContext" xmlns="http://camel.apache.org/schema/spring" >
<packageScan>
<package>com.study.demo.camel</package>
</packageScan>
</camelContext>
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-camel</artifactId>
<version>6.4.0</version>
</dependency>
字符串"world"会在结尾连接上名为“input”的参数,并存储在camelBody变量中;
public class SimpleCamelCallRoute extends RouteBuilder{
@Override
public void configure() throws Exception {
from("flowable:camelprocess:simpleCall").transform().simple("${property.input} World");
}
}
<userTask id="user" name="多实例">
<multiInstanceLoopCharacteristics isSequential="false" flowable:collection="assigneeList" flowable:elementVariable="assignee">
<completionCondition>${mulitiInstance.completeTask(execution)}</completionCondition>
</multiInstanceLoopCharacteristics>
</userTask>
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;
}
}
流程启动的时候执行监听器,实现接口ExecutionListener:
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:
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)}
public class MyExpressionExecutionListener {
public String getEventName(String name) {
System.out.println("====================表达式执行监听器,eventName:" + name+"========================");
return "expression:" + name ;
}
}
第三个人工任务节点,开始的时候执行监听器,委托表达式类型(实现JavaDelegate):${myDelegateExpression}
public class MyDelegateExpression implements JavaDelegate {
public void execute(DelegateExecution execution) {
FlowElement currentFlowElement = DelegateHelper.getFlowElement(execution);
System.out.println("=========================【执行器,当前节点】:" + currentFlowElement.getName() + "=========================");
}
}
第四个人工任务节点,开始的时候执行监听器,脚本类型:ScriptExecutionListener
第五个人工任务节点,开始的时候执行监听器,实现接口ExecutionListener:ExampleFieldInjectedExecutionListener
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());
}
}
要将一个活动声明为补偿处理器,需要将isForCompensation
属性设置为true:
<serviceTask id="compensationHandler" name="补偿处理程序" isForCompensation="true" flowable:class="com.study.demo.compensation.CompensationProcessorJavaDelegate"></serviceTask>
中间补偿事件抛出补偿事件给对应的(activityRef指定的节点)节点的补偿处理程序处理
<intermediateThrowEvent id="intermediateCompensation" name="中间补偿事件">
<compensateEventDefinition activityRef="servicetask1" />
</intermediateThrowEvent>
SELECT * FROM flowable.ACT_RU_EXECUTION;
SELECT * FROM flowable.ACT_RU_TASK;
SELECT * FROM flowable.ACT_RU_VARIABLE;
SELECT * FROM flowable.ACT_RU_EXECUTION;
SELECT * FROM flowable.ACT_RU_TASK;
在运行上面流程定义的流程实例后,INSTANCE_COUNT的值为2。这是因为每次解析*${prototypeDelegateExpressionBean}时,都会创建新实例。可以看到三个Expression*成员字段的注入没有任何问题。
在对于单例bean,INSTANCE_COUNT总是1。在这个委托中,没有Expression成员字段(使用MIXED模式)。而在COMPATIBILITY模式下,就会抛出异常,因为需要有成员字段。这个bean也可以使用DISABLED模式,但会禁用上面进行了字段注入的原型bean。
单例bean是线程不安全的。
<serviceTask id="exceptiondeal" name="异常处理" flowable:class="com.study.demo.exception.ThrowsExceptionBehavior"></serviceTask>
<sequenceFlow id="sid-748CA641-57F3-46F8-8D1F-1C409A509F83" sourceRef="serviceTask4" targetRef="exceptiondeal"></sequenceFlow>
<sequenceFlow id="exception" name="exception" sourceRef="exceptiondeal" targetRef="user"></sequenceFlow>
<sequenceFlow id="no-exception" name="no-exception" sourceRef="exceptiondeal" targetRef="sid-FB3C07EC-B784-4C3D-BC1B-294C5155FBF0"></sequenceFlow>
服务任务异常处理
ThrowingDelegate将会抛出BussinessException业务异常,errorcode为localError,Flowable会将BussinessException
类,转换为带有指定错误代码的BPMN错误,监听在此错误上的边界错误捕获事件会捕获到此异常,并继续后续操作。
运行demo
查看数据库表
CamelTaskTest测试类说明
在这个例子里,将试着从Camel接收与发送消息。我们将发送一个字符串,Camel在其上附加一些文字并返回作为结果。发送部分比较普通,即以变量的格式将信息发送给Camel服务。这是我们的调用代码:
/**
* 启动流程实例并测试连通性
*
*/
@Test
public void testPingPong() {
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("input", "Hello");
Map<String, String> outputMap = new HashMap<String, String>();
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配置。在这个例子里使用默认配置。
SELECT * FROM flowable.act_ru_task;
SELECT * FROM flowable.act_ru_variable;
将会运行"cat /etc/passwd | grep root" Shell脚本,等待其结束,并将其结果存入resultVar。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。