流程引擎 API
服务API
Java API是与引擎互动的最常见方式。中心起点是ProcessEngine,它可以通过几种方式创建,如配置部分所述。从ProcessEngine中,你可以获得包含工作流/BPM方法的各种服务。ProcessEngine和服务对象是线程安全的。所以你可以为整个服务器保留对其中一个对象的引用。
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
RuntimeService runtimeService = processEngine.getRuntimeService();
TaskService taskService = processEngine.getTaskService();
IdentityService identityService = processEngine.getIdentityService();
FormService formService = processEngine.getFormService();
HistoryService historyService = processEngine.getHistoryService();
ManagementService managementService = processEngine.getManagementService();
FilterService filterService = processEngine.getFilterService();
ExternalTaskService externalTaskService = processEngine.getExternalTaskService();
CaseService caseService = processEngine.getCaseService();
DecisionService decisionService = processEngine.getDecisionService();
ProcessEngines.getDefaultProcessEngine()
将在第一次调用时初始化并建立一个流程引擎,此后总是返回相同的流程引擎。所有流程引擎的正确创建和关闭可以通过ProcessEngines.init()
和ProcessEngines.destroy()
完成。
ProcessEngines类将扫描所有camunda.cfg.xml
和activiti.cfg.xml
文件。
对于所有camunda.cfg.xml
文件,流程引擎将以典型的方式建立。
ProcessEngineConfiguration
.createProcessEngineConfigurationFromInputStream(inputStream)
.buildProcessEngine()
对于所有的activiti.cfg.xml
文件,流程引擎将以Spring的方式构建:首先创建Spring应用上下文,然后从该应用上下文中获得流程引擎。
所有的服务都是无状态的。这意味着你可以很容易地在一个集群的多个节点上运行Camunda平台,每个节点都去同一个数据库,而不必担心哪个机器实际执行了以前的调用。对任何服务的任何调用都是无状态的,无论它在哪里执行。
当使用Camunda引擎时,仓库服务(RepositoryService)可能是第一个需要的服务。这个服务提供了管理和操纵部署和流程定义的操作。流程定义是BPMN 2.0流程的Java对应物,这里就不多说了。它是一个流程的每个步骤的结构和行为的表示。部署是引擎中的包装单元。一个部署可以包含多个BPMN 2.0 XML文件和任何其他资源。一个部署中包含的内容的选择由开发者决定。它的范围可以从一个单一的流程BPMN 2.0 XML文件到整个流程和相关资源包(例如,部署 “hr-processes “可以包含与hr流程相关的一切内容)。RepositoryService允许部署这种包。进行一次部署意味着部署内容被上传到引擎,在那里所有的流程都被检查和解析,然后被存储在数据库中。从那时起,系统就知道该部署了,并且任何包含在部署中的流程现在都可以被启动。
此外,这项仓库服务(RepositoryService)允许:
- 查询引擎已知的部署和流程定义。
- 失效和激活流程定义。失效意味着不能对它们做进一步的操作,而激活则是相反的操作。
- 检索各种资源,如部署中包含的文件或由引擎自动生成的流程图。
RepositoryService是关于静态信息的(即,不改变的数据,或者至少不会改变很多的),而RuntimeService则完全相反。它处理的是启动流程定义的新流程实例。如上所述,一个流程定义定义了流程中不同步骤的结构和行为。一个流程实例是这样流程定义的一次执行。对于每个流程定义,通常有许多实例在同时运行。RuntimeService也是用于检索和存储流程变量的服务。这是某个特定流程实例的数据,可以被流程中的各种构造使用(例如,排他网关经常使用流程变量来确定选择哪条路径继续流程)。RuntimeService还允许对流程实例和执行进行查询。执行(Executions)是BPMN 2.0中’token’的概念。一般来说,执行是一个指针,指向流程实例当前所在的位置。最后,当流程实例在等待外部触发并且流程需要继续时,RuntimeService就会被使用。流程实例可以有各种等待状态,这个服务包含各种操作,以 “通知” 实例已经收到外部触发,流程实例可以继续执行。
需要由系统的实际人类用户执行的任务是流程引擎的核心。围绕任务的一切都被归入TaskService,例如:
- 查询分配给用户或组的任务。
- 创建新的独立任务。这些是与流程实例无关的任务。
- 操纵一个任务被分配给哪个用户,或者哪个用户以某种方式参与到任务中。
- 声称并完成一项任务。声称意味着有人决定成为该任务的受让人,意味着这个用户将完成该任务。完成意味着 “完成任务的工作”。一般来说,这就是填写一个类似的表格。
身份服务(IdentityService)是非常简单的。它允许对组和用户进行管理(创建、更新、删除、查询…)。重要的是要理解,核心引擎实际上在运行时并不对用户进行任何检查。例如,一个任务可以被分配给任何用户,但引擎并不验证该用户是否为系统所知。这是因为该引擎也可以与LDAP、活动目录等服务结合使用。
表单服务(FormService)是一个可选的服务。这意味着没有它,Camunda引擎也可以完美地使用,不会缺少任何功能。这项服务引入了起始表单和任务表单的概念。开始表单是在流程实例启动前显示给用户的表单,而任务表单是当用户想完成一项任务时显示的表单。你可以在BPMN 2.0流程定义中定义这些表单。这个服务以一种简单的方式暴露了这些数据,以便于工作。但同样,这是可选的,因为表单不需要嵌入流程定义中。
历史服务(HistoryService)暴露了引擎收集的所有历史数据。当执行流程时,引擎可以保留很多数据(这是可配置的),如流程实例的开始时间、谁做了哪些任务、完成任务花了多长时间、每个流程实例遵循的路径等。该服务主要暴露了访问这些数据的查询功能。
管理服务(ManagementService)在编码自定义应用程序时通常不需要。它允许检索关于数据库表和表元数据的信息。此外,它暴露了查询功能和Job的管理操作。Job在引擎中被用于各种事情,如定时器、异步延续、延迟暂停/激活等。稍后,这些主题将被更详细地讨论。
过滤器服务(FilterService)允许创建和管理过滤器。过滤器是像任务查询一样的存储查询。例如,过滤器被任务列表用来过滤用户任务。
外部任务服务(ExternalTaskService)提供对外部任务实例的访问。外部任务代表在外部处理的工作项目,独立于流程引擎。
案例服务(CaseService)与运行时服务(RuntimeService)类似,但用于案例实例。它处理启动案例定义的新案例实例并管理案例执行的生命周期。该服务也被用来检索和更新案例实例的流程变量。
决策服务 (DecisionService) 允许评估部署在引擎中的决策。它是评估独立于流程定义的业务规则任务中的决策的一种选择。
Java Docs
关于服务操作和引擎API的更多详细信息,见 Java Docs .
查询 API
要从引擎中查询数据,有多种可能性。
- Java查询API。流利的Java API可以查询引擎实体(如ProcessInstances, Tasks, …)。
- REST查询API。REST API来查询引擎实体(如ProcessInstances、Tasks…)。
- 本地查询。提供自己的SQL查询,以检索引擎实体(如ProcessInstances,Tasks,…),如果查询API缺乏你需要的可能性(例如,OR条件)。
- 自定义查询。使用完全定制的查询和自己的MyBatis映射来检索自己的价值对象,或将引擎与领域数据连接起来。
- SQL查询。使用数据库的SQL查询,如报告。
推荐的方法是使用其中一个查询API。
Java查询API允许使用流畅的API来编写完全类型安全的查询。你可以在你的查询中添加各种条件(所有这些条件都作为一个逻辑AND一起应用),并精确地进行排序。下面的代码显示了一个例子:
List<Task> tasks = taskService.createTaskQuery()
.taskAssignee("kermit")
.processVariableValueEquals("orderId", "0815")
.orderByDueDate().asc()
.list();
你可以在Java Docs 中找到更多信息。
查询最大结果限制
在没有限制最大结果数量的情况下查询结果,或者查询大量的 的结果会导致高内存消耗,甚至出现内存不足的异常。借助于查询最大结果限制的帮助下,你可以限制最大结果数。
此限制仅在以下情况下执行:
- 经过身份验证的用户执行查询
- 查询API被直接调用,例如通过REST API(在过程中没有通过delegation code强制执行)。
禁忌
建议
- 使用
Query#unlimitedList
方法执行一个查询。 - 执行一个分页查询,其最大结果数小于或等于最大结果限制
- 执行一个本地查询,因为它不能通过REST API或Webapps来访问 因此不太可能被利用
限制
- 通过REST API进行统计查询
- 通过Webapps(私有API)执行被调用的实例查询
自定义身份服务查询
当你提供…
- 通过实现
ReadOnlyIdentityProvider
或WritableIdentityProvider
接口,实现一个自定义的身份提供者 - 和一个专门的身份服务查询的实现(例如:
GroupQuery',
TenantQuery’, `UserQuery’)
在调用Query#unlimitedList
时,确保不受任何限制地返回所有结果。
检索无限列表的可能性对于确保REST API的正常工作很重要,因为有几个接口依赖于检索无限的结果。
分页查询
分页允许配置一个查询所检索的最大结果数以及第一个索引的位置。
请参阅以下示例:
List<Task> tasks = taskService.createTaskQuery()
.taskAssignee("kermit")
.processVariableValueEquals("orderId", "0815")
.orderByDueDate().asc()
.listPage(20, 50);
上面所示的查询检索了50个结果,从索引为20的结果开始。
或(OR)查询
查询API的默认行为是将过滤条件与一个AND表达式联系在一起。 OR查询可以建立查询,其中过滤条件与OR表达式联系在一起。
小心!
- 这个功能只适用于任务和流程实例查询(runtime & history)
- 以下方法不能应用于OR查询:orderBy…(), initializeFormKeys(), withCandidateGroups(), withoutCandidateGroups(), withCandidateUsers(), withoutCandidateUsers().
在调用or()
之后,可以有一个由几个过滤标准组成的链式查询。每个过滤条件都被连接在一起
用一个OR表达式连接起来。调用endOr()
标志着OR查询的结束。调用这两个方法就相当于
就像把过滤条件放在括号里一样。
List<Task> tasks = taskService.createTaskQuery()
.taskAssignee("John Munda")
.or()
.taskName("Approve Invoice")
.taskPriority(5)
.endOr()
.list();
上面的查询检索了所有分配给 “John Munda “的任务,这些任务同时被命名为 “Approve Invoice
或被赋予5级优先权(assignee = "John Munda" AND (name = "Approve Invoice" OR priority = 5)
, Conjunctive Normal Form).
在内部,该查询被翻译成如下SQL查询(稍微简化):
SELECT DISTINCT *
FROM act_ru_task RES
WHERE RES.assignee_ = 'John Munda'
AND ( Upper(RES.name_) = Upper('Approve Invoice')
OR RES.priority_ = 5 );
一次可以使用任意数量的OR查询。当建立一个查询时,不仅包括一个单一的OR查询,还包括用AND表达式连接起来的过滤标准,OR查询被附加到标准链上作为一个AND表达式。一个与变量有关的过滤条件可以在同一个OR查询中多次应用。
List<Task> tasks = taskService.createTaskQuery()
.or()
.processVariableValueEquals("orderId", "0815")
.processVariableValueEquals("orderId", "4711")
.processVariableValueEquals("orderId", "4712")
.endOr()
.list();
除了与变量有关的过滤标准外,这种行为是不同的。 每当一个non-variable-filter-criterion在查询中被使用多次时,只有最后应用的值才会被使用。
List<Task> tasks = taskService.createTaskQuery()
.or()
.taskCandidateGroup("sales")
.taskCandidateGroup("controlling")
.endOr()
.list();
小心!
在上面的查询中,过滤标准taskCandidateGroup
的值 “sales”被替换为
“controlling”。为了避免这种行为,可以使用带有…In结尾的过滤条件,例如:
- taskCandidateGroupIn()
- tenantIdIn()
- processDefinitionKeyIn()
REST Query API
java查询api也被暴露为REST服务,请参阅REST文档 。
本机查询
有时你需要更强大的查询,例如,使用OR运算符的查询或你无法使用查询API表达的限制。对于这些情况,我们引入了本地查询,它允许你编写你自己的SQL查询。返回类型由你使用的查询对象定义,数据被映射到正确的对象中,例如,任务、ProcessInstance、Execution等。由于查询将在数据库中进行,你必须使用数据库模式中定义的表和列名。这需要一些关于内部数据结构的知识,建议谨慎使用本地查询。表名可以通过API检索,以保持尽可能小的依赖性。
List<Task> tasks = taskService.createNativeTaskQuery()
.sql("SELECT count(*) FROM " + managementService.getTableName(Task.class) + " T WHERE T.NAME_ = #{taskName}")
.parameter("taskName", "aOpenTask")
.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();
自定义查询
由于性能的原因,有时可能不希望查询引擎对象,而是查询一些自己的值或DTO对象,从不同的表中收集数据–也许包括你自己的类。
教程
SQL Queries
表结构是非常直接的–我们专注于使其易于理解。因此,在报告等用例中进行SQL查询是可以的。只需确保你不会在不清楚自己在做什么的情况下,通过更新表来弄乱引擎的数据。