68.2. 与 CDI 集成
流程引擎支持自动与 CDI 集成。您可以在 CDI 框架中使用大多数 API,而无需任何修改。
进程引擎还提供一些专门用于 CDI 容器的专用模块。最重要的模块是 jbpm-services-cdi,它为处理引擎服务提供 CDI 封装器。您可以使用这些打包程序在 CDI 应用程序中集成进程引擎。模块提供以下一组服务:
-
DeploymentService -
ProcessService -
UserTaskService -
RuntimeDataService -
DefinitionService
这些服务可用于在任何其他 CDI Bean 中注入。
68.2.1. CDI 部署服务 复制链接链接已复制到粘贴板!
DeploymentService 服务在运行时环境中部署并取消部署部署单元。当您使用这个服务部署单元时,部署单元就可以执行,并创建一个 RuntimeManager 实例。您还可以使用 DeploymentService 检索以下对象:
-
给定部署 ID 的
RuntimeManager实例 -
代表给定部署 ID 的完整部署单元的
DeployedUnit实例 - 部署服务已知的所有部署单元列表
默认情况下,部署服务不会将部署单元的信息保存到任何持久性存储。在 CDI 框架中,使用该服务的组件可以保存和恢复部署单元信息,例如使用数据库、文件系统或存储库。
部署服务会在部署和取消部署中触发 CDI 事件。使用服务的组件可以处理这些事件来存储部署,并在取消部署时将其从存储中移除。
-
部署单元时会触发带有
@Deployqualifier 的DeploymentEvent -
带有
@Undeployqualifier 的DeploymentEvent在单元的 undeployment 上触发
您可以使用 CDI 观察程序机制来获取这些事件的通知。
以下示例收到单元部署中的通知,并可以保存部署:
部署事件处理示例
public void saveDeployment(@Observes @Deploy DeploymentEvent event) {
// Store deployed unit information
DeployedUnit deployedUnit = event.getDeployedUnit();
}
以下示例在部署单元时收到通知,并可从存储中删除部署:
处理非部署事件的示例
public void removeDeployment(@Observes @Undeploy DeploymentEvent event) {
// Remove deployment with the ID event.getDeploymentId()
}
DeploymentService 服务有几个实现,因此您必须使用限定符来指示 CDI 容器注入特定的实施。每一实施部署都必须存在 DeploymentUnit 的 匹配。
流程引擎提供 KmoduleDeploymentService 实施。这种实现旨在与 KmoduleDeploymentUnits 合作,它们是 KJAR 文件中所含的小描述符。这个实现是大多数用例中典型的解决方案。此实施的限定符是 @Kjar。
68.2.2. CDI 的表单供应商服务 复制链接链接已复制到粘贴板!
FormProviderService 服务提供对表单的访问,通常显示在用户界面中用于进程表单和用户任务表单的用户界面。
该服务依赖于隔离形式的供应商的概念,可提供不同功能并由不同的技术提供支持。FormProvider 接口描述了表单供应商的合同。
FormProvider 接口的定义
public interface FormProvider {
int getPriority();
String render(String name, ProcessDesc process, Map<String, Object> renderContext);
String render(String name, Task task, ProcessDesc process, Map<String, Object> renderContext);
}
FormProvider 接口的实现必须定义优先级值。当 FormProviderService 服务需要呈现表单时,它会按优先级顺序调用可用提供程序。
优先级越低,提供商获得的优先级越高。例如,在优先级为 10 的供应商之前,评估优先级为 5 的供应商。对于每个所需表单,服务按照其优先级顺序迭代可用的提供程序,直至其中之一来提供内容。在最糟糟的情况中,返回一个简单的基于文本的表单。
进程引擎提供 FormProvider 的以下实现:
- 提供 Form Modeller 工具中创建的表单的供应商,优先级为 2
- 基于 FreeMarker 的实施,它支持流程和任务表单,优先级为 3
- 默认表单供应商返回简单的基于文本的表单,如果不存在其他供应商提供任何内容,其使用方法为 1000
68.2.3. CDI 运行时数据服务 复制链接链接已复制到粘贴板!
RuntimeDataService 服务提供对在运行时可用的数据的访问,包括以下数据:
- 要执行的可用进程,带有各种过滤器
- 活跃的进程实例,带有不同的过滤器
- 进程实例历史记录
- 进程实例变量
- 进程实例的活跃和完成节点
RuntimeDataService 的默认实现会观察部署事件,并索引所有部署的进程以将其公开给调用组件。
68.2.4. CDI 定义服务 复制链接链接已复制到粘贴板!
DefinitionService 服务提供对作为 BPMN2 XML 定义一部分存储的进程详细信息的访问。
在使用提供信息的任何方法前,调用 buildProcessDefinition() 方法,以使用从 BPMN2 内容检索的进程信息填充存储库。
BPMN2DataService 实现提供对以下数据的访问:
- 给定进程定义过程的整体描述
- 在进程定义中找到的所有用户任务的集合
- 有关用户任务节点定义的输入的信息
- 有关为用户任务节点定义的输出的信息
- 在给定进程定义中定义的可重用进程的 ID(call 活动)
- 有关在给定进程定义中定义的进程变量的信息
有关包含在进程定义中的所有组织实体(用户和组)的信息。根据具体进程定义,用户和组返回的值可包含以下信息:
- 实际用户或组名称
-
用于在运行时获取实际用户或组名称的进程变量,例如:
#{manager}
68.2.5. CDI 集成配置 复制链接链接已复制到粘贴板!
要在 CDI 框架中使用 jbpm-services-cdi 模块,您必须提供一些 bean 来满足所含服务实施的依赖项。
根据使用场景,需要几个 Bean:
- 实体管理器和实体管理器工厂
- 用于人工任务的用户组回调
- 身份提供程序将经过身份验证的用户信息传递给服务
在 JEE 环境中运行(如红帽 JBoss EAP)时,以下制作者 bean 满足 jbpm-services-cdi 模块的所有要求。
制作者 bean 满足 JEE 环境中的 jbpm-services-cdi 模块的所有要求
public class EnvironmentProducer {
@PersistenceUnit(unitName = "org.jbpm.domain")
private EntityManagerFactory emf;
@Inject
@Selectable
private UserGroupInfoProducer userGroupInfoProducer;
@Inject
@Kjar
private DeploymentService deploymentService;
@Produces
public EntityManagerFactory getEntityManagerFactory() {
return this.emf;
}
@Produces
public org.kie.api.task.UserGroupCallback produceSelectedUserGroupCalback() {
return userGroupInfoProducer.produceCallback();
}
@Produces
public UserInfo produceUserInfo() {
return userGroupInfoProducer.produceUserInfo();
}
@Produces
@Named("Logs")
public TaskLifeCycleEventListener produceTaskAuditListener() {
return new JPATaskLifeCycleEventListener(true);
}
@Produces
public DeploymentService getDeploymentService() {
return this.deploymentService;
}
@Produces
public IdentityProvider produceIdentityProvider {
return new IdentityProvider() {
// implement IdentityProvider
};
}
}
应用的 Bean.xml 文件必须为用户组 info 回调启用正确的替代选择。这种替代选择基于 @Selectable qualifier。
在 Bean.xml 文件'中,为用户组信息回调的定义
<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://docs.jboss.org/cdi/beans_1_0.xsd">
<alternatives>
<class>org.jbpm.kie.services.cdi.producer.JAASUserGroupInfoProducer</class>
</alternatives>
</beans>
org.jbpm.kie.services.cdi.producer.JAASUserGroupInfoProducer 是一个示例值。这个值通常适合于红帽 JBoss EAP,因为它可重复利用应用服务器上的安全设置,无论服务器使用哪种安全方法,如 LDAP 或数据库。
另外,您还可以提供一些其他制作者来提供 WorkItemHandlers 和 Process、Agenda、WorkingMemory 事件监听程序。您可以通过实现以下接口来提供这些组件:
处理引擎与 CDI 集成的工作项目处理程序制作界面
/**
* Enables providing custom implementations to deliver WorkItem name and WorkItemHandler instance pairs
* for the runtime.
* <br/>
* This interface is invoked by the RegisterableItemsFactory implementation (in particular InjectableRegisterableItemsFactory
* in the CDI framework) for every KieSession. Always return new instances of objects to avoid unexpected
* results.
*
*/
public interface WorkItemHandlerProducer {
/**
* Returns map of work items(key = work item name, value = work item handler instance)
* to be registered on KieSession
* <br/>
* The following parameters might be given:
* <ul>
* <li>ksession</li>
* <li>taskService</li>
* <li>runtimeManager</li>
* </ul>
*
* @param identifier - identifier of the owner - usually the RuntimeManager. This parameter allows the producer to filter out
* and provide valid instances for a given owner
* @param params - the owner might provide some parameters, usually KieSession, TaskService, RuntimeManager instances
* @return map of work item handler instances (always return new instances when this method is invoked)
*/
Map<String, WorkItemHandler> getWorkItemHandlers(String identifier, Map<String, Object> params);
}
用于处理引擎与 CDI 集成的事件监听程序制作者接口
/**
* Enables defining custom producers for known EventListeners. There might be several
* implementations that might provide a different listener instance based on the context in which they are executed.
* <br/>
* This interface is invoked by the RegisterableItemsFactory implementation (in particular, InjectableRegisterableItemsFactory
* in the CDI framework) for every KieSession. Always return new instances of objects to avoid unexpected results.
*
* @param <T> type of the event listener - ProcessEventListener, AgendaEventListener, WorkingMemoryEventListener
*/
public interface EventListenerProducer<T> {
/**
* Returns list of instances for given (T) type of listeners
* <br/>
* Parameters that might be given are:
* <ul>
* <li>ksession</li>
* <li>taskService</li>
* <li>runtimeManager</li>
* </ul>
* @param identifier - identifier of the owner - usually RuntimeManager. This parameter allows the producer to filter out
* and provide valid instances for given owner
* @param params - the owner might provide some parameters, usually KieSession, TaskService, RuntimeManager instances
* @return list of listener instances (always return new instances when this method is invoked)
*/
List<T> getEventListeners(String identifier, Map<String, Object> params);
}
实施这两个接口的 Bean 会在运行时收集并在 RuntimeManager 类构建 KieSession 实例时调用。
68.2.5.1. 作为 CDI Bean 的运行时管理器 复制链接链接已复制到粘贴板!
您可以将 RuntimeManager 类作为 CDI Bean 注入应用程序中的任何其他 CDI Bean。必须正确生成 RuntimeEnvironment 类,以启用 RuntimeManager 实例的正确初始化。
以下 CDI 限定符引用现有的运行时管理器策略:
-
@Singleton -
@PerRequest -
@PerProcessInstance
有关运行时管理器的详情,请参考 第 66.2 节 “运行时管理器”。
虽然您可以直接注入 RuntimeManager 类,但对于大多数框架用例(如 CDI、EJB 或 Spring)的解决方案正在使用服务。流程引擎服务使用运行时管理器实施许多最佳实践。
要使用运行时管理器,您必须将 RuntimeEnvironment 类添加到 第 68.2.5 节 “CDI 集成配置” 部分中定义的 producer 中。
提供 RuntimeEnvironment 类的 producer bean
public class EnvironmentProducer {
//Add the same producers as for services
@Produces
@Singleton
@PerRequest
@PerProcessInstance
public RuntimeEnvironment produceEnvironment(EntityManagerFactory emf) {
RuntimeEnvironment environment = RuntimeEnvironmentBuilder.Factory.get()
.newDefaultBuilder()
.entityManagerFactory(emf)
.userGroupCallback(getUserGroupCallback())
.registerableItemsFactory(InjectableRegisterableItemsFactory.getFactory(beanManager, null))
.addAsset(ResourceFactory.newClassPathResource("BPMN2-ScriptTask.bpmn2"), ResourceType.BPMN2)
.addAsset(ResourceFactory.newClassPathResource("BPMN2-UserTask.bpmn2"), ResourceType.BPMN2)
.get();
return environment;
}
}
在本例中,单一制作者方法可以通过在方法级别上指定所有运行时管理器策略,为所有运行时管理器策略提供 RuntimeEnvironment 类。
当完整的制作者可用时,RuntimeManager 类可以注入到应用程序的 CDI bean 中:
注入 RuntimeManager 类
public class ProcessEngine {
@Inject
@Singleton
private RuntimeManager singletonManager;
public void startProcess() {
RuntimeEngine runtime = singletonManager.getRuntimeEngine(EmptyContext.get());
KieSession ksession = runtime.getKieSession();
ProcessInstance processInstance = ksession.startProcess("UserTask");
singletonManager.disposeRuntimeEngine(runtime);
}
}
如果您注入 RuntimeManager 类,则应用程序中只能有一个 RuntimeManager 实例。在典型的情形中,使用 DeploymentService 服务根据需要创建 RuntimeManager 实例。
作为 DeploymentService 的替代方案,您可以注入 RuntimeManageronnectionFactory y 类,然后应用程序就可以使用它来创建 RuntimeManager 实例。在这种情况下,仍需要 EnvironmentProducer 定义。以下示例显示了一个简单的 ProcessEngine bean。
ProcessEngine bean 示例
public class ProcessEngine {
@Inject
private RuntimeManagerFactory managerFactory;
@Inject
private EntityManagerFactory emf;
@Inject
private BeanManager beanManager;
public void startProcess() {
RuntimeEnvironment environment = RuntimeEnvironmentBuilder.Factory.get()
.newDefaultBuilder()
.entityManagerFactory(emf)
.addAsset(ResourceFactory.newClassPathResource("BPMN2-ScriptTask.bpmn2"), ResourceType.BPMN2)
.addAsset(ResourceFactory.newClassPathResource("BPMN2-UserTask.bpmn2"), ResourceType.BPMN2)
.registerableItemsFactory(InjectableRegisterableItemsFactory.getFactory(beanManager, null))
.get();
RuntimeManager manager = managerFactory.newSingletonRuntimeManager(environment);
RuntimeEngine runtime = manager.getRuntimeEngine(EmptyContext.get());
KieSession ksession = runtime.getKieSession();
ProcessInstance processInstance = ksession.startProcess("UserTask");
manager.disposeRuntimeEngine(runtime);
manager.close();
}
}