一、ManagementService
-
相关API
-
管理服务【ManagementService】
-
工作的产生
-
异步任务的产生【ACT RU_JOB】
-
Java Service Task
<serviceTask id="someTask" activiti:class="com.laogoubi.delegate.MDCErrorDelegate"></serviceTask>
以上的配置中定义了一个serviceTask,并且为该serviceTask设置了对应的Delegate(activiti:class)。如果需要定义异步的serviceTask,则需要为serviceTask节点加入activiti:async属性,并将该属性值设置为true。自定义的JavaDelegate 类,JavaDelegate需要实现JavaDelegate接口。
public class MDCErrorDelegate implements JavaDelegate { private static final Logger logger = LoggerFactory.getLogger(MDCErrorDelegate.class); @Override public void execute(DelegateExecution execution) { logger.info("run MDCErrorDelegate"); throw new RuntimeException("only test"); } }
-
-
定时中间事件产生的工作【ACT_RU_TIME_JOB】
-
如果在流程定义了定时器相关节点,那么这些节点不会马上执行,有可能在某个定义的时间点上执行,这些节点会被转化为工作保存到定时器工作表【ACT_RU_TIME_JOB】中,流程引擎会定时查询该表的数据,然后将符合执行条件(时间条件)的工作取出来交由线程池执行。在流程中可以加入定时器的节点中间事件节点、流程开始事件节点和边界事件节点。
<intermediateCatchEvent id="timerintermediatecatchevent1" name=”TimerCatchEvent”> <timerEventDefinition> <timeDuration>PT1M</timeDuration> </timerEventDefinition> </intermediateCatchEvent>
表示定时器将会在1分钟后触发,即1分钟后会跳过该流程节点,执行下一个节点。
-
-
定时边界事件产生的工作
-
定时边界事件( Timer Boundary Event )同样会产生定时工作。
<boundaryEvent id="boundaryEvent1" cancelActivity="false" attachedToRef="usertask1"> <timerEventDefinition> <timeDuration>PT1M</timeDuration> </timerEventDefinition> </boundaryEvent>
该边界事件成为了定时边界事件,并且该节点会在1分钟后被触发。
-
-
定时开始事件产生的工作
-
在开始事件中同样可以加入定时器,让其变为定时开始事件。
<startEvent id="start"> <timerEventDefinition> <timeCycle>R5/PT10S</timeCycle> </timerEventDefinition> </startEvent>
在开始事件中加入定时器,并且设置了开始事件会在10秒后被触发。
-
-
流程抛出事件产生的工作
-
当执行流遇到信号Catching事件时,会停留在该事件节点,一直等待信号,并且会在事件描述表(ACT_RU_EVENT_SUBSCR)中加入相应的事件描述数据。
<intermediateCatchEvent id="signal-received"> <signalEventDefinition signalRef="signalStart"></signalEventDefinition> </intermediateCatchEvent>
-
-
暂停工作的产生
- 流程定义与流程实例可以被中断,中断后,与这些流程定义、流程实例有关的工作,将会被保存到中断工作表(ACT_RU_SUSPENED_JOB)。
- 调用流程中断方法
- suspendProcessInstanceById
-
无法执行的工作
- 如果工作在执行时发生异常, 则可以重新执行, 默认情况下,工作最大执行次数为3次,可以使用ManagementService 的setJobRetries方法来设置重试次数。如果一个工作执行多次,仍然是失败的,那么Activiti就会将其写到ACT_RU_DEADLETTER_JOB表中,该表主要用来保存一些无法执行的工作。
-
-
工作管理
-
Job任务查询
工作查询对象 描述 对应的表 JobQuery 查询一般工作 ACT_RU_JOB TimerJobQuery 查询定时工作 ACT_RU_TIMER_JOB SuspendedJobQuery 查询中断工作 ACT_RU_SUSPENDED_JOB DeadLetterJobQuery 查询无法执行的工作 ACT_RU_DEADLETTER_JOB -
获取工作异常信息
- 当工作执行中出现异常时,各个工作表的EXCEPTION_STACK_ID_ 和 EXCEPTION_MSG_ 字段会保存工作执行的异常信息,使用ManagementService的几个getXXXJobExceptionStacktrace方法可以获取这些异常信息,该方法根据EXCEPTION_STACK_ID_字段所保存的值,到ACT_GE_BYTEARRAY表中查询相应的详细异常信息。
-
转移与删除工作
- 几个保存工作的数据表分别代表不同状态的工作,如果想要改变工作的状态,可以调用几个move方法来进行工作的转移。
- 可以调用几个delete方法来删除工作,可以删除一般工作、定时器工作和无法执行的工作,但要注意并没有提供删除中断工作的方法。
-
-
数据库管理
-
查询引擎属性
- Activiti会将流程引擎相关的属性配置保存到ACT_GE_PROPERTY表中,一些全局的、可能会发生改变的属性配置均会被放到该表中保存。使用ManagementService的getProperties方法可以返回这些属性及其值。
-
数据表信息查询
-
查询表结构元数据(TableMetaData)
- 如果需要知道一个数据表有哪些字段以及这些字段的类型,则可以使用ManagementService的getTableMetaData方法来获取某一个数据表的基础信息,该方法返回一个TableMetaData对象,通过该对象可以得到数据表的名称、表的全部宇段名称以及各个字段的类型信息。
- 如果需要知道一个数据表有哪些字段以及这些字段的类型,则可以使用 ManagementService的getTableMetaData方法来获取某一个数据表的基础信息,该方法返回一个TableMetaData对象,通过该对象可以得到数据表的名称、表的全部宇段名称以及各个字段的类型信息。
-
通用表查询(TablePageQuery)
- 对于一些非 Activiti自带的数据表,如果想对其进行数据查询,则可以使用ManagementService的 createTablePageQuery方法创建一个TablePageQuery对象,使用该对象的tableName方法可以设置到哪个表进行数据查询,再使用listPage方法返回一个TablePage对象,使用listPage方法时需要传入查询的开始索引值和查询的最大数据量。
-
数据库操作
-
使用ManagementService的databaseSchemaUpgrade方法可以实现对数据库的原始操作,这些操作可以由调用人自己决定,可以创建schema、创建表、删除schema和删除表等,调用该方法需要提供Connection、数据库catalog 和数据库schema参数,其中可以使用Connection的createStatement方法得到Statement对象,然后可以执行各种 SQL语句。
managementService.databaseSchemaUpgrade();
-
-
-
-
二、相关代码
-
测试代码
package com.laogoubi.coreapi; import com.laogoubi.mapper.MyCustomMapper; import org.activiti.engine.ManagementService; import org.activiti.engine.impl.cmd.AbstractCustomSqlExecution; import org.activiti.engine.impl.interceptor.Command; import org.activiti.engine.impl.interceptor.CommandContext; import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity; import org.activiti.engine.management.TablePage; import org.activiti.engine.runtime.*; import org.activiti.engine.test.ActivitiRule; import org.activiti.engine.test.Deployment; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import org.junit.Rule; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; import java.util.Map; /** * @ClassName ManagementServiceTest * @Description TODO * @Author eastern * @Date 2020/7/4 上午11:34 * @Version 1.0 **/ public class ManagementServiceTest { private static final Logger logger = LoggerFactory.getLogger(ManagementServiceTest.class); @Rule public ActivitiRule activitiRule = new ActivitiRule("activiti_job.cfg.xml"); @Test @Deployment(resources = {"diagrams/my-process-job.bpmn20.xml"}) public void testJobQry(){ ManagementService managementService = activitiRule.getManagementService(); List<Job> jobs = managementService.createTimerJobQuery().listPage(0, 100); for (Job job : jobs) { logger.info("job = {}", ToStringBuilder.reflectionToString(job, ToStringStyle.JSON_STYLE)); } JobQuery jobQuery = managementService.createJobQuery(); SuspendedJobQuery suspendedJobQuery = managementService.createSuspendedJobQuery(); DeadLetterJobQuery deadLetterJobQuery = managementService.createDeadLetterJobQuery(); } @Test @Deployment(resources = {"diagrams/my-process-job.bpmn20.xml"}) public void testTablePageQry(){ ManagementService managementService = activitiRule.getManagementService(); TablePage tablePage = managementService.createTablePageQuery().tableName(managementService.getTableName(ProcessDefinitionEntity.class)).listPage(0, 100); List<Map<String, Object>> rows = tablePage.getRows(); for (Map<String, Object> row : rows) { logger.info("row = {}", row); } } @Test @Deployment(resources = {"diagrams/my-process-job.bpmn20.xml"}) public void testCustomSql(){ activitiRule.getRuntimeService().startProcessInstanceByKey("my-process"); ManagementService managementService = activitiRule.getManagementService(); List<Map<String, Object>> mapList = managementService.executeCustomSql(new AbstractCustomSqlExecution<MyCustomMapper, List<Map<String, Object>>>(MyCustomMapper.class) { @Override public List<Map<String, Object>> execute(MyCustomMapper mapper) { return mapper.findAll(); } }); logger.info("mapList.size = {}", mapList); for (Map<String, Object> map : mapList) { logger.info("map = {}", map); } } @Test @Deployment(resources = {"diagrams/my-process-job.bpmn20.xml"}) public void testCommand(){ activitiRule.getRuntimeService().startProcessInstanceByKey("my-process"); ManagementService managementService = activitiRule.getManagementService(); ProcessDefinitionEntity processDefinitionEntity = managementService.executeCommand(new Command<ProcessDefinitionEntity>() { @Override public ProcessDefinitionEntity execute(CommandContext commandContext) { ProcessDefinitionEntity processDefinitionEntity = commandContext.getProcessDefinitionEntityManager().findLatestProcessDefinitionByKey("my" + "-process"); return processDefinitionEntity; } }); logger.info("processDefinitionEntity = {}", processDefinitionEntity); } }
-
MyCustomMapper
package com.laogoubi.mapper; import org.apache.ibatis.annotations.Select; import java.util.List; import java.util.Map; /** * @ClassName MyCustomMapper * @Description TODO * @Author eastern * @Date 2020/7/5 上午10:02 * @Version 1.0 **/ public interface MyCustomMapper { @Select("SELECT * FROM ACT_RU_TASK") public List<Map<String, Object>> findAll(); }
-
资源
-
my-process-job.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: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/test"> <process id="my-process"> <startEvent id="start"> <timerEventDefinition> <timeCycle>R5/PT10S</timeCycle> </timerEventDefinition> </startEvent> <sequenceFlow id="flow1" sourceRef="start" targetRef="someTask" /> <userTask id="someTask" name="Activiti is awesome!" /> <sequenceFlow id="flow2" sourceRef="someTask" targetRef="end" /> <endEvent id="end" /> </process> </definitions>
-