流程引擎的API和服务

  • 流程引擎API(ProcessEngine API)是与Activiti打交道的最常用方式
  • Activiti从ProcessEngine开始.在ProcessEngine中,可以获得很多包括工作流或者BPM方法的服务
  • ProcessEngine和服务类都是线程安全的.可以在整个服务器中仅保持它们的一个引用就可以

ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

RuntimeService runtimeService = processEngine.getRuntimeService();
RepositoryService repositoryService = processEngine.getRepositoryService();
TaskService taskService = processEngine.getTaskService();
ManagementService managementService = processEngine.getManagementService();
IdentityService identityService = processEngine.getIdentityService();
HistoryService historyService = processEngine.getHistoryService();
FormService formService = processEngine.getFormService();
ProcessEngines.getDefaultProcessEngine():
- 会在第一次调用时,初始化并创建一个流程引擎,以后再调用就会返回相同的流程引擎
- 使用对应的方法可以创建和关闭所有流程引擎:ProcessEngines.init()和ProcessEngines.destroy()
- ProcessEngines会扫描所有activiti.cfg.xml 和 activiti-context.xml 文件
- 对于activiti.cfg.xml文件,流程引擎会使用Activiti的经典方式构建:
- ProcessEngineConfiguration.createProcessEngineConfigurationFromInputStream(inputStream).buildProcessEngine()
- 对于activiti-context.xml文件,流程引擎会使用Spring方法构建:先创建一个Spring的环境,然后通过环境获得流程引擎
- 所有服务都是无状态的.这意味着可以在多节点集群环境下运行Activiti,每个节点都指向同一个数据库,不用担心哪个机器实际执行前端的调用.无论在哪里执行服务都没有问题
RepositoryService
- 负责静态信息
- 是使用Activiti引擎时最先接触的服务,提供了管理和控制发布包和流程定义的操作
- 流程定义是BPMN 2.0流程的java实现.它包含了一个流程每个环节的结构和行为
- 发布包是Activiti引擎的打包单位.一个发布包可以包含多个BPMN 2.0 xml文件和其他资源
- 开发者可以自由选择把任意资源包含到发布包中
- 既可以把一个单独的BPMN 2.0 xml文件放到发布包里,也可以把整个流程和相关资源都放在一起
- 可以通过RepositoryService来部署这种发布包.发布一个发布包,意味着把它上传到引擎中,所有流程都会在保存进数据库之前分析解析好
- 从这点来说,系统知道这个发布包的存在,发布包中包含的流程就已经可以启动了
- RepositoryService可以查询引擎中的发布包和流程定义
- RepositoryService暂停或激活发布包,对应全部和特定流程定义.暂停意味着它们不能再执行任何操作了,激活是对应的反向操作
- RepositoryService获得多种资源,例如包含在发布包里的文件,引擎自动生成的流程图
- RepositoryService获得流程定义的pojo版本,可以用来通过java解析流程,而不必通过xml
RuntimeService
- 负责启动一个流程定义的新实例
- 流程定义定义了流程各个节点的结构和行为
- 流程实例就是这样一个流程定义的实例
- 对每个流程定义来说,同一时间会有很多实例在执行
- RuntimeService可以用来获取和保存流程变量,这些数据是特定于某个流程实例的,并会被很多流程中的节点使用
- Runtimeservice可以查询流程实例和执行,执行对应BPMN 2.0中的'token',基本上执行指向流程实例当前在哪里
- RuntimeService可以在流程实例等待外部触发时使用,可以用来继续流程实例.流程实例可以有很多暂停状态,而服务提供了多种方法来'触发'实例, 接受外部触发后,流程实例就会继续向下执行
TaskService
- 任务是由系统中真实人员执行的,它是Activiti这类BPMN引擎的核心功能之一, 所有与任务有关的功能都包含在TaskService中
- 在TaskService中,查询分配给用户或组的任务
- 在TaskService中,创建独立运行任务,这些任务与流程实例无关
- 在TaskService中,手工设置任务的执行者,或者这些用户通过何种方式与任务关联
- 在TaskService中,认领并完成一个任务:
- 认领意味着一个人期望成为任务的执行者,即这个用户会完成这个任务
- 完成意味着“做这个任务要求的事情”,通常来说会有很多种处理形式
IdentityService
- 可以管理,创建,更新,删除,查询..群组和用户
- Activiti执行时并没有对用户进行检查.任务可以分配给任何人,但是引擎不会校验系统中是否存在这个用户.这是Activiti引擎也可以使用外部服务:ldap,活动目录...
HistoryService
- HistoryService提供了Activiti引擎的所有历史数据
- 在执行流程时,引擎会根据配置保存很多数据:流程实例启动时间,任务的参与者,完成任务的时间,每个流程实例的执行路径..., 这个服务主要通过查询功能来获得这些数据
FormService
- FormService是一个可选服务,即使不使用它,Activiti也可以完美运行,不会损失任何功能
- FormService提供了启动表单和任务表单两个概念
- 启动表单会在流程实例启动之前展示给用户
- 任务表单会在用户完成任务时展示
- Activiti支持在BPMN 2.0流程定义中设置这些表单.这个服务以一种简单的方式将数据暴露出来,是可选的,表单也不一定要嵌入到流程定义中
ManagementService
- 在使用Activiti的定制环境中基本上不会用到
- ManagementService可以查询数据库的表和表的元数据
- ManagementService提供了查询和管理异步操作的功能
- Activiti的异步操作用途很多:定时器,异步操作,延迟暂停,激活..

异常策略

  • Activiti中的基础异常为org.activiti.engine.ActivitiException, 一个非检查异常
  • 这个异常可以在任何时候被API抛出,特定方法抛出的特定的异常
/**
* Called when the task is successfully executed.
* @param taskId the id of the task to complete, cannot be null.
* @throws ActivitiObjectNotFoundException when no task exists with the given id.
*/
void complete(String taskId);

当传入一个不存在的任务的id时,就会抛出异常.taskId不能为null,如果传入null,就会抛出ActivitiIllegalArgumentException

  • 应该避免过多的异常继承,子类只用于特定的场合
  • 流程引擎和API调用的其他场合不使用子类异常,抛出一个普通的ActivitiExceptions
ActivitiWrongDbException: 				当Activiti引擎发现数据库版本号和引擎版本号不一致时抛出
ActivitiOptimisticLockingException: 对同一数据进行并发方法并出现乐观锁时抛出
ActivitiClassLoadingException: 当无法找到需要加载的类或在加载类时出现了错误-JavaDelegate,TaskListener
ActivitiObjectNotFoundException: 当请求或操作的对应不存在时抛出
ActivitiIllegalArgumentException: 这个异常表示调用Activiti API时传入了一个非法的参数,可能是引擎配置中的非法值,或提供了一个非法值,或流程定义中使用的非法值
ActivitiTaskAlreadyClaimedException: 当任务已经被认领了,再调用taskService.claim(...)就会抛出

查询 API

  • 在Activiti流程引擎中查询数据有两种方式:

    • 查询API
    • 原生查询
  • 查询API: 查询API提供了完全类型安全的API,可以自定义添加查询条件和精确的排序条件,所有条件都以AND组合
 	 List<Task> tasks = taskService.createTaskQuery()
.taskAssignee("kermit")
.processVariableValueEquals("orderId", "0815")
.orderByDueDate().asc()
.list();
  • 原生查询:

    • 需要更强大的查询时:使用OR条件或者能使用查询API实现的条件.
    • 可以编写自己的SQL查询. 返回类型由你使用的查询对象决定,数据会映射到正确的对象上:任务,流程实例,执行..
    • 查询作用在数据库上,必须使用数据库中定义的表名和列名,要了解内部数据结构
    • 使用原生查询时,表名可以通过API获得,可以尽量减少对数据库的依赖
	List<Task> tasks = taskService.createNativeTaskQuery()
.sql("SELECT count(*) FROM " + managementService.getTableName(Task.class) + " T WHERE T.NAME_ = #{taskName}")
.parameter("taskName", "gonzoTask")
.list(); long count = taskService.createNativeTaskQuery()
.sql("SELECT count(*) FROM " + managementService.getTableName(Task.class) + " T1, "
+ managementService.getTableName(VariableInstanceEntity.class) + " V1 WHERE V1.TASK_ID_ = T1.ID_")
.count();

表达式

  • Activiti使用UEL处理表达式.UEL即统一表达式语言, 是EE6规范的一部分.为了在所有运行环境都支持最新UEL的所有功能,使用JUEL的修改版本
  • 表达式可以用在很多场景下:
    • Java服务任务
    • 执行监听器
    • 任务监听器
    • 条件流
  • 虽然有两重表达式:值表达式和方法表达式, Activiti进行了抽象,所以两者可以同样使用在需要表达式的场景中
  • Value expression: 解析为值,默认
${myVar}
${myBean.myProperty}

所有流程变量都可以使用,所有spring bean(spring环境中)也可以使用在表达式中

  • Method expression: 调用一个方法,使用或不使用参数
${printer.print()}
${myBean.addNewOrder('orderName')}
${myBean.doSomething(myVar, execution)}

当调用一个无参数的方法时,记得在方法名后添加空的括号,以区分值表达式

传递的参数可以是字符串也可以是表达式,它们会被自动解析

  • 这些表达式支持解析原始类型:

    • bean
    • list
    • 数组
    • map
    • 包括比较
  • 在流程实例中,表达式中可以使用一些默认对象:
    • execution: DelegateExecution,提供外出执行的额外信息
    • task: DelegateTask,提供当前任务的额外信息 ,只对任务监听器的表达式有效
    • authenticatedUserId: 当前登录的用户id.如果没有用户登录,这个变量就不可用

单元测试

  • 业务流程是软件项目的一部分,它也应该和普通的业务流程一样进行测试:使用单元测试
  • 因为Activiti是一个嵌入式的java引擎,所以为业务流程编写单元测试和写普通单元测试完全一样
  • Activiti支持JUnit 3和4进行单元测试
    • 使用JUnit 3时, 必须集成org.activiti.engine.test.ActivitiTestCase. 它通过保护的成员变量提供ProcessEngine和服务,
    • 在测试的setup()中,默认会使用classpath下的activiti.cfg.xml初始化流程引擎
    • 要使用不同的配置文件,可以重写getConfigurationResource() 方法
    • 如果配置文件相同的话,对应的流程引擎会被静态缓存,就可以用于多个单元测试
  • 继承了ActivitiTestCase, 可以在测试方法上使用org.activiti.engine.test.Deployment注解.测试执行前,与测试类在同一个包下的,格式为testClassName.testMethod.bpmn20.xml的资源文件,会被部署.测试结束后,发布包也会被删除,包括所有相关的流程实例,任务...Deployment注解也可以直接设置资源的位置
public class MyBusinessProcessTest extends ActivitiTestCase {

  @Deployment
public void testSimpleProcess() {
runtimeService.startProcessInstanceByKey("simpleProcess"); Task task = taskService.createTaskQuery().singleResult();
assertEquals("My Task", task.getName()); taskService.complete(task.getId());
assertEquals(0, runtimeService.createProcessInstanceQuery().count());
}
}
  • 要想在使用JUnit 4编写单元测试时获得同样的功能

    • 可以使用org.activiti.engine.test.ActivitiRule. 通过它,可以通过getter方法获得流程引擎和各种服务
    • 使用这个Rule也会启用org.activiti.engine.test.Deployment注解
    • 它会在classpath下查找默认的配置文件,如果配置文件相同的话,对应的流程引擎会被静态缓存,就可以用于多个单元测试
public class MyBusinessProcessTest {

  @Rule
public ActivitiRule activitiRule = new ActivitiRule(); @Test
@Deployment
public void ruleUsageExample() {
RuntimeService runtimeService = activitiRule.getRuntimeService();
runtimeService.startProcessInstanceByKey("ruleUsage"); TaskService taskService = activitiRule.getTaskService();
Task task = taskService.createTaskQuery().singleResult();
assertEquals("My Task", task.getName()); taskService.complete(task.getId());
assertEquals(0, runtimeService.createProcessInstanceQuery().count());
}
}

调试单元测试

  • 使用内存数据库H2进行单元测试,在调试环境监视Activiti的数据库:
  • 在单元测试里设置了一个断点:

  • 用调试模式运行单元测试,右击单元测试,选择[运行为]和[单元测试],测试会停在我们的断点上, 然后我们就可以监视测试的变量,它们显示在调试面板里

  • 要监视Activiti的数据,打开[显示]窗口(如果找不到,打开[窗口]-[显示视图]-[其他],选择[显示]并点击[代码已完成],org.h2.tools.Server.createWebServer("-web").start()

  • 选择你点击的行,右击.然后选择[显示]

  • 打开一个浏览器,输入http://localhost:8082, 输入内存数据库的JDBC URL(默认为jdbc:h2:mem:activiti),点击连接按钮

  • 可以看到Activiti的数据,通过它们可以了解单元测试时,如何以及为什么这样运行的

Web中的流程引擎

  • ProcessEngine是线程安全的,可以在多线程下共享
  • 在web应用中, 意味着可以在容器启动时创建流程引擎, 在容器关闭时关闭流程引擎
  • 编写一个ServletContextListener 在普通的Servlet环境下初始化和销毁流程引擎:
public class ProcessEnginesServletContextListener implements ServletContextListener {

  public void contextInitialized(ServletContextEvent servletContextEvent) {
ProcessEngines.init();
} public void contextDestroyed(ServletContextEvent servletContextEvent) {
ProcessEngines.destroy();
} }

contextInitialized方法会执行ProcessEngines.init() 这会查找classpath下的activiti.cfg.xml文件,根据配置文件创建一个ProcessEngine(比如,多个jar中都包含配置文件)如果classpath中包含多个配置文件,确认它们有不同的名字

  • 需要使用流程引擎时,可以通过
ProcessEngines.getDefaultProcessEngine()

ProcessEngines.getProcessEngine("myName");
  • ContextListener中的contextDestroyed方法会执行ProcessEngines.destroy().这会关闭所有初始化的流程引擎

项目实践之工作流引擎基本文档!Activiti工作流框架中流程引擎API和服务详解的更多相关文章

  1. 使用DOM进行xml文档的crud(增删改查)操作<操作详解>

    很多朋友对DOM有感冒,这里我花了一些时间写了一个小小的教程,这个能看懂,会操作了,我相信基于DOM的其它API(如JDOM,DOM4J等)一般不会有什么问题. 后附java代码,也可以下载(可点击这 ...

  2. Atitit 项目的主体设计与结构文档 v3

    Atitit 项目的主体设计与结构文档 v3 1. 实现的目标2 1.1. cross device跨设备(pc 手机 平板)作为规划2 1.2. 企业级Java体系与开发语言2 1.3. 高扩展性, ...

  3. Atitit 项目的主体设计与结构文档 v5

    Atitit 项目的主体设计与结构文档 v5 1. 版本历史说明2 2. 功能大概说明2 3. 实现的目标3 3.1. cross device跨设备(pc 手机 平板)3 3.2. cross sc ...

  4. swagger:API在线文档自动生成框架

    传统的API从开发测试开始我们经常借用类似Postman.fiddle等等去做接口测试等等工具:Swagger 为API的在线测试.在线文档提供了一个新的简便的解决方案: NET 使用Swagger ...

  5. 随时发布:REST API文档的代码仓库中的持续集成与协作

    本文主要内容:API文档提供了预测客户成功的关键路径:在代码附近的文档上进行协作可以更好地检查代码和文档文件,提高自动化效率,并专门针对文档进行质量测试:提供通用文档框架,标准,自动化和工具,以提高团 ...

  6. VS2010-MFC(利用MFC向导生成单文档应用程序框架)

    一.VC++与MFC 讲VC++免不了要提MFC,MFC全称Microsoft Foundation Classes,也就是微软基础类库.它是VC++的核心,是C++与Windows API的结合,很 ...

  7. VS2010/MFC编程入门之二(利用MFC向导生成单文档应用程序框架)

    VS2010/MFC编程入门之二(利用MFC向导生成单文档应用程序框架)-软件开发-鸡啄米 http://www.jizhuomi.com/software/141.html   上一讲中讲了VS20 ...

  8. ElasticSearch 学习记录之 分布式文档存储往ES中存数据和取数据的原理

    分布式文档存储 ES分布式特性 屏蔽了分布式系统的复杂性 集群内的原理 垂直扩容和水平扩容 真正的扩容能力是来自于水平扩容–为集群添加更多的节点,并且将负载压力和稳定性分散到这些节点中 ES集群特点 ...

  9. Xml学习笔记(3)利用递归解析Xml文档添加到TreeView中

    利用递归解析Xml文档添加到TreeView中 private void Form1_Load(object sender, EventArgs e) { XmlDocument doc = new ...

随机推荐

  1. IP Networks UVA - 1590

     Alex is administrator of IP networks. His clients have a bunch of individual IP addresses and he de ...

  2. Windows下反(反)调试技术汇总

    反调试技术,恶意代码用它识别是否被调试,或者让调试器失效.恶意代码编写者意识到分析人员经常使用调试器来观察恶意代码的操作,因此他们使用反调试技术尽可能地延长恶意代码的分析时间.为了阻止调试器的分析,当 ...

  3. python 自动化审计

    基于python的自动化代码审计 代码审计 逢魔安全实验室   2018-02-11  10,539   本文通过介绍在python开发中经常出现的常规web漏洞,然后通过静态和动态两种方式对pyth ...

  4. 使用EasySYS搭建驱动开发基本框架

    提供EasySYS的下载地址:http://bbs.pediy.com/showthread.php?p=956643,看雪上有提供下载,自行百度. EasySYS你能够帮我们快速的搭建驱动的开发框架 ...

  5. UVA11039

    题意:      给你一个序列,由n个数字组成,每个数字的绝对值都不相同,然后让你从这n个数中拿出一些数,组成一个绝对值递增并且正负交替的最大序列,问组成的最大序列的最大长度是多少? 思路:     ...

  6. Python中Selenium模块的使用

    目录 Selenium的介绍.配置和调用 Selenium的配置 Selenium的调用 Selenium的使用 定位 定位元素的使用 定位下拉标签元素 在iframe框架之间切换 上传文件 Webd ...

  7. Linux提权之利用 /etc/passwd 文件

    当我们获得了某个Linux服务器的低权限之后,我们想要对该低权限账号进行提权,以执行更多的操作. 接下来我们的提权是利用 /etc/passwd 文件的可写入权限,导致我们写入一个其他用户进去. 首先 ...

  8. Portswigger web security academy:Clickjacking (UI redressing)

    Portswigger web security academy:Clickjacking (UI redressing) 目录 Portswigger web security academy:Cl ...

  9. <JVM中篇:字节码与类的加载篇>01-Class字节码文件结构

    笔记来源:尚硅谷JVM全套教程,百万播放,全网巅峰(宋红康详解java虚拟机) 同步更新:https://gitee.com/vectorx/NOTE_JVM https://codechina.cs ...

  10. linux当前运行进程

    一:linux查询服务器服务进程 inux中的ps命令是Process Status的缩写.ps命令用来列出系统中当前运行的那些进程.ps命令列出的是当前那些进程的快照, 就是执行ps命令的那个时刻的 ...