工作流的定时器

作者: 编程应用  发布:2019-10-05

jBPM 定时器实现了以下功能:

图片 1

  • timer 定义在 transition 元素中,当流程处于 state、task、sub-process 类型活动的等待状态时,会开始计算 timer 的时间,当时间耗尽时,就会触发转移。
  • timer 也可以定义在用户代码活动中,负责事件监听的 on 元素支持 timeout 事件。当事件监听器 on 在监听 timeout 事件时,timer 元素必须是 on 元素的第一个子元素。当流程的执行(Execution)进入这个等待活动时,就会激活定时器。如果此活动的持续等待时间超过定时器指定的时间时,就会触发事件;如果流程的执行在定时器指定的时间之内离开此活动,那么就会取消这个定时器。

事件(Event)

事件用来表明流程的生命周期中发生了什么事. 事件总是画成一个圆圈.
在BPMN 2.0中, 事件有两大分类: 捕获(catching)或 触发(throwing) 事件.

捕获(Catching): 当流程执行到事件, 它会等待被触发. 触发的类型是由内部图表或XML中的类型声明来决定的. 捕获事件与触发事件在显示方面是根据内部图表是否被填充来区分的(白色的).

触发(Throwing): 当流程执行到事件, 会触发一个事件. 触发的类型是由内部图表或XML中的类型声明来决定的. 触发事件与捕获事件在显示方面是根据内部图表是否被填充来区分的(被填充为黑色).


timer 元素的属性:

事件定义

事件定义, 决定了事件的语义. 如果没有事件定义, 这个事件就不做什么特别的事情.

没有设置事件定义的开始事件不会在启动流程时做任何事情. 如果给开始事件添加了一个事件定义(比如定时器事件定义)我们就声明了开始流程的事件 "类型 " (这时定时器事件监听器会在某个时间被触发).


属性 类型 默认值 是否必需 描述
duedate 持续时间表达式 必需 指定多长时间以后触发事件。如:15 分钟、2 天等
repeat 持续时间表达式 可选 第一个触发事件后,每隔多长时间再触发事件,这是一个持续的过程。

定时器事件定义

定时器事件是根据指定的时间触发的事件。可以用于开始事件, 中间事件或边界事件.
timeDate: ISO 8601格式指定一个确定的时间, 触发事件的时间.示例:

<timerEventDefinition>
    <timeDate>2011-03-11T12:13:14</timeDate>
</timerEventDefinition>

timeDuration: 指定定时器之前要等待多长时间, timeDuration可以设置为timerEventDefinition的子元素. 使用ISO 8601规定的格式 (由BPMN 2.0规定)。示例(等待10天)。

<timerEventDefinition>
    <timeDuration>P10D</timeDuration>
</timerEventDefinition>

timeCycle: 指定重复执行的间隔, 可以用来定期启动流程实例, 或为超时时间发送多个提醒. timeCycle元素可以使用两种格式, 第一种是
ISO 8601标准的格式. 示例(重复3次,每次间隔10小时):

<timerEventDefinition>
    <timeCycle>R3/PT10H</timeCycle>
</timerEventDefinition>

另外, 你可以使用cron表达式指定timeCycle, 下面的例子是从整点开始, 每5分钟执行一次:

0 0/5 * * * ?

注意: 第一个数字表示秒,而不是像通常Unix cron中那样表示分钟.

你可以在定时器事件定义中使用表达式, 这样你就可以通过流程变量来影响那个定时器定义. 流程定义必须包含ISO 8601(或cron)格式的字符串, 以匹配对应的时间类型.

  <boundaryEvent id="escalationTimer" cancelActivity="true" attachedToRef="firstLineSupport">
     <timerEventDefinition>
      <timeDuration>${duration}</timeDuration>
    </timerEventDefinition>
  </boundaryEvent>

注意: 计时器,只有当启用了异步执行(flowable.cfg.xml中的asyncExecutorActivate需要设置为true, 因为异步执行默认情况下禁用的).


持续时间表达式语法如下:

开始事件

开始事件用来指明流程在哪里开始. 开始事件的类型(消息到达时开始还是等待特定时间间隔 等等), 定义了流程如何启动, 这通过事件中不同的小图表来展示. 在XML中, 这些类型是通过声明不同的子元素来区分的.

开始事件都是捕获事件: 最终这些事件都是(一直)等待着, 直到对应的触发时机出现.

在启动事件中, 可以指定以下Flowable特定属性:
initiator: 当流程启动时, 保存经过身份验证的用户标识的变量名称. 示例如下:

<startEvent id="request" flowable:initiator="initiator" />

经过身份验证的用户必须使用IdentityService.setAuthenticatedUserId(String) try-finally块中的方法进行设置, 如下所示:

try {
  identityService.setAuthenticatedUserId("bono");
  runtimeService.startProcessInstanceByKey("someProcessKey");
} finally {
  identityService.setAuthenticatedUserId(null);
}

quantity [business]{ second | seconds | minute | minutes | hour | hours | day | days | week | weeks | month | months | year | years}

空开始事件

空开始事件技术上意味着没有指定启动流程实例的触发条件. 这就是说引擎不能预计什么时候流程实例会启动. 空开始事件用于, 当流程实例要通过API启动的场景, 通过调用startProcessInstanceByXXX方法.

ProcessInstance processInstance = runtimeService.startProcessInstanceByXXX();

图形表示法
空开始事件显示成一个圆圈,没有内部图表(没有触发类型)

图片 2

XML表示
空开始事件的XML结构是普通的开始事件定义, 没有任何子元素(其他开始事件类型都有一个子元素来声明自己的类型)

<startEvent id="start" name="my start event" />

关键字 是否可选 说明
quantity 正整数,描述时间的值。
business 如果存在,则表示按照工作日历来计算时间;如果没有,则按照正常的公历来计算时间。工作日历会按照预先配置的规则,计算时会去除节假日与休息的时间,只考虑工作时间。

计时器开始事件

定时开始事件用来在指定的时间创建流程实例. 它可以同时用于只启动一次流程和应该在特定时间间隔启动多次的流程.

注意: 子流程不能使用定时开始事件.
注意: 定时开始事件在流程发布后就会开始计算时间. 不需要调用startProcessInstanceByXXX, 虽然也可以调用启动流程的方法, 但是那会导致调用startProcessInstanceByXXX时启动过多的流程
注意: 当包含定时开始事件的新版本流程部署时, 对应的上一个定时器就会被删除. 这是因为通常不希望自动启动旧版本流程的流程实例.

图形表示法
定时开始事件显示为了一个圆圈, 内部是一个表.

图片 3

XML内容
定时开始事件的XML内容是普通开始事件的声明, 包含一个定时定义子元素.请参考定时定义查看配合细节.

示例: 流程会启动4次, 每次间隔5分钟, 从2011年3月11日 12:13开始计时.

<startEvent id="theStart">
  <timerEventDefinition>
    <timeCycle>R4/2011-03-11T12:13/PT5M</timeCycle>
  </timerEventDefinition>
</startEvent>

例如: 进程将在选定的日期开始一次

<startEvent id="theStart">
  <timerEventDefinition>
    <timeDate>2011-03-11T12:13:14</timeDate>
  </timerEventDefinition>
</startEvent>

配好以上关键字之后,加上需要的时间单位即可。

顺序流

顺序流是连接两个流程节点的连线. 流程执行完一个节点后, 会沿着节点的所有外出顺序流继续执行.

图形标记
顺序流显示为从起点到终点的箭头. 箭头总是指向终点.

图片 4

XML内容
顺序流需要流程范围内唯一的id, 以及对起点终点元素的引用.

<sequenceFlow id="flow1" sourceRef="theStart" targetRef="theTask" />

系统默认的工作日历配置文件是 jbpm.businesscalendar.cfg.xml,它存在 jbpm4.jar 的根目录下,内容为:

条件顺序流

可以为顺序流定义一个条件. 离开一个BPMN 2.0节点时, 默认会计算外出顺序流的条件. 如果条件结果为true, 就会选择外出顺序流继续执行. 当多条顺序流被选中时, 就会创建多条分支, 流程会继续以并行方式继续执行.

注意: 上面的讨论仅涉及BPMN 2.0节点(和事件), 不包括网关. 网关会用特定的方式处理顺序流中的条件, 这与网关类型相关.

图形标记
条件顺序流显示为一个正常的顺序流, 不过在起点有一个菱形. 条件表达式也会显示在顺序流上.

图片 5

XML内容
条件顺序流定义为一个正常的顺序流, 包含conditionExpression子元素.

注意目前只支持tFormalExpressions, 如果没有设置xsi:type="", 就会默认值支持目前支持的表达式类型.

<sequenceFlow id="flow" sourceRef="theStart" targetRef="theTask">
  <conditionExpression xsi:type="tFormalExpression">
    <![CDATA[${order.price > 100 && order.price < 250}]]>
  </conditionExpression>
</sequenceFlow>

目前, conditionalExpressions只能与UEL一起使用.
下面的例子引用了流程变量的数据, 通过getter调用JavaBean.

<conditionExpression xsi:type="tFormalExpression">
  <![CDATA[${order.price > 100 && order.price < 250}]]>
</conditionExpression>

这个例子调用一个解析为布尔值的方法

<conditionExpression xsi:type="tFormalExpression">
  <![CDATA[${order.isStandardOrder()}]]>
</conditionExpression>

<?xml version="1.0" encoding="UTF-8"?><jbpm-configuration> <process-engine-context> <business-calendar> <monday hours="9:00-12:00 and 12:30-17:00"/> <tuesday hours="9:00-12:00 and 12:30-17:00"/> <wednesday hours="9:00-12:00 and 12:30-17:00"/> <thursday hours="9:00-12:00 and 12:30-17:00"/> <friday hours="9:00-12:00 and 12:30-17:00"/> <holiday period="01/07/2008 - 31/08/2008"/> </business-calendar> </process-engine-context> </jbpm-configuration>

网关

网关用来控制流程的流向.

图形标记
网关显示成菱形图形, 内部有一个小图标. 图标表示网关的类型.

图片 6


上面配置的工作时间为:周一到周五 9:00-12:00 与 12:30-17:00。01/07/2008 - 31/08/2008 为节假日时间。

独占网关

独占网关(也叫异或(XOR)网关), 用来在流程中实现决策.

当流程执行到这个网关, 所有外出顺序流都会被处理一遍. 其中条件解析为true的顺序流(或者没有设置条件, 概念上在顺序流上定义了一个'true')会被选中,让流程继续运行.

注意这里的外出顺序流与BPMN 2.0通常的概念是不同的. 通常情况下, 所有条件结果为true的顺序流都会被选中, 以并行方式执行, 但独占网关只会选择一条顺序流执行. 就是说, 虽然多个顺序流的条件结果为true, 那么XML中的第一个顺序流(也只有这一条)会被选中, 并用来继续运行流程. 如果没有选中任何顺序流, 会抛出一个异常.

图形标记
独占网关显示成一个普通网关()比如,菱形图形), 内部是一个“X”图标, 表示异或(XOR)语义. 注意, 没有内部图标的网关, 默认为独占网关. BPMN 2.0规范不允许在同一个流程定义中同时使用没有X和有X的菱形图形.

图片 7

XML内容
独占网关的XML内容是很直接的: 用一行定义了网关, 条件表达式定义在外出顺序流中. 参考条件顺序流获得这些表达式的可用配置.
参考下面模型实例

图片 8

它对应的XML内容如下:

<exclusiveGateway id="exclusiveGw" name="Exclusive Gateway" />

<sequenceFlow id="flow2" sourceRef="exclusiveGw" targetRef="theTask1">
  <conditionExpression xsi:type="tFormalExpression">${input == 1}</conditionExpression>
</sequenceFlow>

<sequenceFlow id="flow3" sourceRef="exclusiveGw" targetRef="theTask2">
  <conditionExpression xsi:type="tFormalExpression">${input == 2}</conditionExpression>
</sequenceFlow>

<sequenceFlow id="flow4" sourceRef="exclusiveGw" targetRef="theTask3">
  <conditionExpression xsi:type="tFormalExpression">${input == 3}</conditionExpression>
</sequenceFlow>

工作日历的默认实现类是 BusinessCalendarImpl。

任务

也可以通过实现 org.jbpm.pvm.internal.cal.BusinessCalendar 类来自定义的工作日历,需要实现以下两个方法:

用户任务

用户任务用来设置必须由人员完成的工作. 当流程执行到用户任务, 会创建一个新任务, 并把这个新任务加入到分配人或群组的任务列表中.

图形标记
用户任务显示成一个普通任务(圆角矩形), 左上角有一个小用户图标.

图片 9

XML内容
XML中的用户任务定义如下. id属性是必须的. name属性是可选的.

<userTask id="theTask" name="Important task" />

用户任务也可以设置描述.实际上所有BPMN 2.0元素都可以设置描述.
添加documentation元素可以定义描述.

<userTask id="theTask" name="Schedule meeting" >
  <documentation>
      Schedule an engineering meeting for next week with the new hire.
  </documentation>

...

描述文本可以用标准的Java方式从任务中获取:

task.getDescription()

持续时间
任务可以用一个字段来描述任务的持续时间. 可以使用查询API来对持续时间进行搜索, 根据在时间之前或之后进行搜索.

我们提供了一个节点扩展, 在任务定义中设置一个表达式, 这样在任务创建时就可以为它设置初始持续时间. 表达式应该是java.util.Date, java.util.String (ISO8601格式), ISO8601持续时间(比如PT50M)或null.

例如: 你可以在流程中使用上述格式输入日期, 或在前一个服务任务中计算一个时间. 这里使用了持续时间, 持续时间会基于当前时间进行计算, 再通过给定的时间段累加. 比如, 使用"PT30M"作为持续时间, 任务就会从现在开始持续30分钟.

<userTask id="theTask" name="Important task" flowable:dueDate="${dateVariable}"/>

任务的持续时间也可以通过TaskService修改或在TaskListener中通过传入的DelegateTask参数修改.

用户分配
用户任务可以直接分配给一个用户. 这可以通过humanPerformer元素定义. humanPerformer定义需要一个 resourceAssignmentExpression来实际定义用户. 当前, 只支持formalExpressions.

<process >

  ...

  <userTask id='theTask' name='important task' >
    <humanPerformer>
      <resourceAssignmentExpression>
        <formalExpression>kermit</formalExpression>
      </resourceAssignmentExpression>
    </humanPerformer>
  </userTask>

只有一个用户可以坐拥任务的执行者分配给用户. 在Flowable中, 用户叫做执行者. 拥有执行者的用户不会出现在其他人的任务列表中, 只能出现执行者的个人任务列表中.

直接分配给用户的任务可以通过TaskService像下面这样获取:

List<Task> tasks = taskService.createTaskQuery().taskAssignee("kermit").list();

任务也可以加入到人员的候选任务列表中. 这时, 需要使用potentialOwner 元素. 用法和humanPerformer元素类似. 注意它需要指定表达式中的每个项目是人员还是群组 .

<process >

  ...

  <userTask id='theTask' name='important task' >
    <potentialOwner>
      <resourceAssignmentExpression>
        <formalExpression>user(kermit), group(management)</formalExpression>
      </resourceAssignmentExpression>
    </potentialOwner>
  </userTask>

使用potentialOwner元素定义的任务, 可以像下面这样获取(使用TaskQuery的发那个发与查询设置了执行者的任务类似):

List<Task> tasks = taskService.createTaskQuery().taskCandidateUser("kermit");

任务分配扩展
当分配不复杂时, 用户和组的设置非常麻烦. 为避免复杂性,可以使用用户任务的自定义扩展.
assignee属性: 这个自定义扩展可以直接把用户任务分配给指定用户.

<userTask id="theTask" name="my task" flowable:assignee="kermit" />

它和使用上面定义的humanPerformer效果完全一样.

candidateUsers属性: 这个自定义扩展可以为任务设置候选人.

<userTask id="theTask" name="my task" flowable:candidateUsers="kermit, gonzo" />

它和使用上面定义的potentialOwner效果完全一样. 注意它不需要像使用potentialOwner通过user(kermit)声明, 因为这个属性只能用于人员.

candidateGroups属性: 这个自定义扩展可以为任务设置候选组.

<userTask id="theTask" name="my task" flowable:candidateGroups="management, accountancy" />

它和使用上面定义的potentialOwner效果完全一样. 注意它不需要像使用potentialOwner通过group(management)声明, 因为这个属性只能用于群组.

candidateUsers 和 candidateGroups 可以同时设置在同一个用户任务中.


方法 说明
Date add(Date date, String duration) 计算从一个时间点,经过 duration 后,到达的时间点。
Date subtract(Date date, String duration) 计算从一个时间点,减去 duration 后,到达的时间点。这个方法不支持 business 模式。

Java服务任务

java服务任务用来调用外部java类.

图形标记
服务任务显示为圆角矩形, 左上角有一个齿轮小图标.

图片 10

XML内容
有4钟方法来声明java调用逻辑:

  1. 实现JavaDelegate或ActivityBehavior
  2. 执行解析代理对象的表达式
  3. 调用一个方法表达式
  4. 调用一直值表达式

执行一个在流程执行中调用的类, 需要在'flowable:class'属性中设置全类名.

<serviceTask id="javaService"
             name="My Java Service Task"
             flowable:class="org.flowable.MyJavaDelegate" />

也可以使用表达式调用一个对象. 对象必须遵循一些规则, 并使用flowable:class 属性进行创建.

<serviceTask id="serviceTask" flowable:delegateExpression="${delegateExpressionBean}" />

这里delegateExpressionBean是一个bean, 它实现了JavaDelegate在Spring容器中定义的接口.

要指定应评估的UEL方法表达式, 请使用属性flowable:expression

<serviceTask id="javaService"
             name="My Java Service Task"
             flowable:expression="#{printer.printMessage()}" />

printMessage将在名为的命名对象上调用方法(不带参数)printer.
也可以使用表达式中使用的方法传递参数:

<serviceTask id="javaService"
             name="My Java Service Task"
             flowable:expression="#{printer.printMessage(execution, myVar)}" />

这会调用名为printer对象上的方法printMessage. 第一个参数是DelegateExecution, 在表达式环境中默认名称为execution. 第二个参数传递的是当前流程的名为myVar的变量.
要指定执行的UEL值表达式, 需要使用flowable:expression属性.

<serviceTask id="javaService"
             name="My Java Service Task"
             flowable:expression="#{split.ready}" />

ready属性的getter方法, getReady(无参数), 会作用于名为split的bean上. 这个对象会被解析为流程对象和(如果合适)spring环境中的对象.

这里我只写了一部分, 大家可以参考这两个网址来学习网址1 网址2

字符串 duration 通过 new Duration 来构造出 Duration 对象,它就是持续时间表达式的对象。

在 jbpm.cfg.xml 中配置自定义的工作日历:

<process-engine-context> <object /></process-engine-context>

流程定义:

图片 11

jPDL:

<?xml version="1.0" encoding="UTF-8"?><process name="TimerTransition" xmlns="http://jbpm.org/4.4/jpdl"> <start name="start1" g="124,206,48,48"> <transition to="处理"/> </start> <state name="处理" g="222,203,111,52"> <transition name="超时" to="处理" g="-34,-4"> <timer duedate="15 minutes"/> </transition> <transition name="继续" to="回复" g="-2,-22"/> </state> <state name="处理" g="214,305,124,52"/> <state name="回复" g="422,205,92,52"/></process>

这里假设如果业务员在 15 分钟之内没有进行处理,那么将会发生超时转移,流程将流向 的活动节点。

单元测试:

//发起流程实例Execution processInstance = executionService.startProcessInstanceByKey("TimerTransition");//在 job 中会生成一条信息Job job = managementService.createJobQuery().timers().processInstanceId(processInstance .getId.uniqueResult();//触发定时器managementService.executeJob(job.getId;//断言流程进入活动processInstance = executionService.findExecutionById(processInstance.getId;assertEquals", ((ExecutionImpl) processInstance).getActivityName;

图片 12

jPDL:

<?xml version="1.0" encoding="UTF-8"?><process name="TimerEvent" xmlns="http://jbpm.org/4.4/jpdl"> <start name="start1" g="138,204,48,48"> <transition to="等待"/> </start> <state name="等待" g="228,200,92,52"> <on event="timeout"> <timer duedate="15 minutes"/> <event-listener /> </on> <transition to="下一步操作"/> </state> <state name="下一步操作" g="366,202,92,52"/></process>

timer 元素是 timeout 事件的必需元素,这里表示如果在活动上停留了 15 分钟,那么就会触发 timeout 事件。

自定义 timeout 事件:

public class OvertimeEvent implements EventListener { @Override public void notify(EventListenerExecution execution) throws Exception { //设置流程变量,来表示触发了该事件 execution.setVariable("OvertimeEvent",Boolean.TRUE); }}

单元测试:

//发起流程实例Execution processInstance = executionService.startProcessInstanceByKey("TimerEvent");//在 job 中会生成一条信息Job job = managementService.createJobQuery().timers().processInstanceId(processInstance .getId.uniqueResult();//触发定时器managementService.executeJob(job.getId;//断言流程进入活动(因为我们在事件监听器中并没有发出执行信号)processInstance = executionService.findExecutionById(processInstance.getId;Set<String> expectedActivityNames = new HashSet<>();expectedActivityNames.add;assertEquals(expectedActivityNames, processInstance.findActiveActivityNames;//断言事件被触发assertEquals(Boolean.TRUE, executionService.getVariable(processInstance.getId(), "OvertimeEvent"));assertEquals("等待", ((ExecutionImpl) processInstance).getActivityName;

如果活动在 15 分钟之内被完成,那么就会取消自定义的 timeout 事件,相关的 Job 记录也会被删除。

这个定时转移与我们第 3 节所说的定时转移类似,只不过在配置持续时间表达式时,加入了 business 关键字:

<process name="TimerTransition" xmlns="http://jbpm.org/4.4/jpdl"> ... <state name="处理" g="222,203,111,52"> <transition name="超时" to="处理" g="-34,-4"> <timer duedate="15 business hours"/> </transition> ... </state>...</process>

这里表示如果当前活动停留了超过 15 个 “工作” 小时,那么就会发生超时转移。工作日历中可以配置每天的工作时间段。

利用 timer 的 repeat 属性,可以不断地触发事件。

<process name="TimerRepeatEvent" xmlns="http://jbpm.org/4.4/jpdl"> ... <state name="等待" g="228,200,92,52"> <on event="timeout"> <timer duedate="15 minutes" repeat="15 seconds"/> <event-listener /> </on> <transition to="下一步操作"/> </state> ...</process>

这里的定义表示:事件被触发后,每隔 15 s,会重复触发事件,直到发生转移。

本文由金沙澳门官网送注册58发布于编程应用,转载请注明出处:工作流的定时器

关键词: