Chapter 68. Integration with Java frameworks
You can integrate the process engine with several industry-standard Java frameworks, such as Apache Maven, CDI, Spring, and EJB..
68.1. Integration with Apache Maven
The process engine uses Maven for two main purposes:
- To create KJAR artifacts, which are deployment units that the process engine can install into a runtime environment for execution
- To manage dependencies for building applications that embed the process engine
68.1.1. Maven artifacts as deployment units
The process engine provides a mechanism to deploy processes from Apache Maven artifacts. These artifacts are in the JAR file format and are known as KJAR files, or informally KJARs. A KJAR file includes a descriptor that defines a KIE base and KIE session. It also contains the business assets, including process definitions, that the process engine can load into the KIE base.
The descriptor of a KJAR file is represented by an XML file named kie-deployment-descriptor.xml
. The descriptor can be empty, in which case the default configuration applies. It can also provide custom configuration for the KIE base and KIE session.
An empty kie-deployment-descriptor.xml
descriptor
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <deployment-descriptor xsi:schemaLocation="http://www.jboss.org/jbpm deployment-descriptor.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <persistence-unit>org.jbpm.domain</persistence-unit> <audit-persistence-unit>org.jbpm.domain</audit-persistence-unit> <audit-mode>JPA</audit-mode> <persistence-mode>JPA</persistence-mode> <runtime-strategy>SINGLETON</runtime-strategy> <marshalling-strategies/> <event-listeners/> <task-event-listeners/> <globals/> <work-item-handlers /> <environment-entries/> <configurations/> <required-roles/> <remoteable-classes/> </deployment-descriptor>
With an empty kie-deployment-descriptor.xml
descriptor, the following default configuration applies:
A single default KIE base is created with the following characteristics:
- It contains all assets from all packages in the KJAR file
-
Its event processing mode is set to
cloud
-
Its equality behaviour is set to
identity
- Its declarative agenda is disabled
-
For CDI applications, its scope is set to
ApplicationScope
A single default stateless KIE session is created with the following characteristics:
- It is bound to the single KIE base
-
Its clock type is set to
real time
-
For CDI applications, its scope is set to
ApplicationScope
A single default stateful KIE session is created with the following characteristics:
- It is bound to the single KIE base
-
Its clock type is set to
real time
-
For CDI applications, its scope is set to
ApplicationScope
If you do not want to use the defaults, you can change all configuration settings using the kie-deployment-descriptor.xml
file. You can find the complete specification of all elements for this file in the XSD schema.
The following sample shows a custom kie-deployment-descriptor.xml
file that configures the runtime engine. This example configures the most common options and includes a single work item handler. You can also use the kie-deployment-descriptor.xml
file to configure other options.
Sample custom kie-deployment-descriptor.xml
file
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <deployment-descriptor xsi:schemaLocation="http://www.jboss.org/jbpm deployment-descriptor.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <persistence-unit>org.jbpm.domain</persistence-unit> <audit-persistence-unit>org.jbpm.domain</audit-persistence-unit> <audit-mode>JPA</audit-mode> <persistence-mode>JPA</persistence-mode> <runtime-strategy>SINGLETON</runtime-strategy> <marshalling-strategies/> <event-listeners/> <task-event-listeners/> <globals/> <work-item-handlers> <work-item-handler> <resolver>mvel</resolver> <identifier>new org.jbpm.process.workitem.bpmn2.ServiceTaskHandler(ksession, classLoader)</identifier> <parameters/> <name>Service Task</name> </work-item-handler> </work-item-handlers> <environment-entries/> <configurations/> <required-roles/> <remoteable-classes/> </deployment-descriptor>
If you use the RuntimeManager
class, this class creates KieSession
instances, not the KieContainer
class. However, the kie-deployment-descriptor.xml
model is always used as a base of the construction process. The KieContainer
class always creates the KieBase
instance.
You can reference KJAR artifacts, like any other Maven artifacts, using the GAV (group, artifact, version) value. When deploying units from KJAR files, the process engine uses the GAV value as the release ID in the KIE API. You can use the GAV value to deploy KJAR artifacts into a runtime environment, for example, a KIE Server.
68.1.2. Dependency management with Maven
When you build projects that embed the process engine, use Apache Maven to configure all dependencies required by the process engine.
The process engine provides a set of BOMs (Bills of Material) to simplify declaring artifact dependencies.
Use the top-level pom.xml
file of your project to define dependency management for embedding the process engine, as shown in the following example. The example includes the main runtime dependencies, which are applicable whether the application is deployed on an application server, in a servlet container, or as a standalone application.
This example also includes version properties for components that applications using the process engine commonly need. Adjust the list of components and versions as necessary. You can view the third-party dependency versions that the product team tests in the parent pom.xml
file in the Github repository.
Maven dependency management settings for embedding the process engine
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <version.org.drools> </version.org.drools> <version.org.jbpm>7.52.0.Final-redhat-00007</version.org.jbpm> <hibernate.version>5.3.17.Final</hibernate.version> <hibernate.core.version>5.3.17.Final</hibernate.core.version> <slf4j.version>1.7.26</slf4j.version> <jboss.javaee.version>1.0.0.Final</jboss.javaee.version> <logback.version>1.2.9</logback.version> <h2.version>1.3.173</h2.version> <narayana.version>5.9.0.Final</narayana.version> <jta.version>1.0.1.Final</jta.version> <junit.version>4.13.1</junit.version> </properties> <dependencyManagement> <dependencies> <!-- define Drools BOM --> <dependency> <groupId>org.drools</groupId> <artifactId>drools-bom</artifactId> <type>pom</type> <version>${version.org.drools}</version> <scope>import</scope> </dependency> <!-- define jBPM BOM --> <dependency> <groupId>org.jbpm</groupId> <artifactId>jbpm-bom</artifactId> <type>pom</type> <version>${version.org.jbpm}</version> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
In modules that use the process engine Java API (KIE API), declare the necessary process engine dependencies and other components that the modules require, as in the following example:
Dependencies for modules that use the KIE API
<dependency> <groupId>org.jbpm</groupId> <artifactId>jbpm-flow</artifactId> </dependency> <dependency> <groupId>org.jbpm</groupId> <artifactId>jbpm-flow-builder</artifactId> </dependency> <dependency> <groupId>org.jbpm</groupId> <artifactId>jbpm-bpmn2</artifactId> </dependency> <dependency> <groupId>org.jbpm</groupId> <artifactId>jbpm-persistence-jpa</artifactId> </dependency> <dependency> <groupId>org.jbpm</groupId> <artifactId>jbpm-human-task-core</artifactId> </dependency> <dependency> <groupId>org.jbpm</groupId> <artifactId>jbpm-runtime-manager</artifactId> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> </dependency>
If your application uses persistence and transactions, you must add artifacts that implement the JTA and JPA frameworks. Additional dependencies are required for testing the workflow components before actual deployment.
The following example defines the dependencies that include Hibernate for JPA, the H2 database for persistence, Narayana for JTA, and the components needed for testing. This example uses the test
scope. Adjust this example as necessary for your application. For production use, remove the test
scope.
Example test module dependencies for the process engine
<!-- test dependencies --> <dependency> <groupId>org.jbpm</groupId> <artifactId>jbpm-shared-services</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>${logback.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>${hibernate.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>${hibernate.core.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>${h2.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>jboss-transaction-api_1.2_spec</groupId> <artifactId>org.jboss.spec.javax.transaction</artifactId> <version>${jta.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.jboss.narayana.jta</groupId> <artifactId>narayana-jta</artifactId> <version>${narayana.version}</version> <scope>test</scope> </dependency>
With this configuration you can embed the process engine in your application and use the KIE API to interact with processes, rules, and events.
Maven repositories
To use Red Hat product versions of Maven dependencies, you must configure the Red Hat JBoss Enterprise Maven repository in the top-level pom.xml
file. For information about this repository, see JBoss Enterprise Maven Repository.
Alternatively, download the rhpam-7.11.0-maven-repository.zip
product deliverable file from the Software Downloads page of the Red Hat Customer Portal and make the contents of this file available as a local Maven repository.
68.2. Integration with CDI
The process engine supports integration with CDI automatically. You can use most of its API in the CDI framework without any modification.
The process engine also provides some dedicated modules that are designed specifically for CDI containers. The most important module is jbpm-services-cdi
, which provides CDI wrappers for process engine services. You can use these wrappers to integrate the process engine in CDI applications. The module provides the following set of services:
-
DeploymentService
-
ProcessService
-
UserTaskService
-
RuntimeDataService
-
DefinitionService
These services are available for injection in any other CDI bean.
68.2.1. Deployment service for CDI
The DeploymentService
service deploys and undeploys deployment units in the runtime environment. When you deploy a unit using this service, the deployment unit becomes ready for execution and a RuntimeManager
instance is created for it. You can also use the DeploymentService
to retrieve the following objects:
-
The
RuntimeManager
instance for a given deployment ID -
The
DeployedUnit
instance that represents the complete deployment unit for the given deployment ID - The list of all deployed units known to the deployment service
By default, the deployment service does not save information about deployed units to any persistent storage. In the CDI framework, the component that uses the service can save and restore deployment unit information, for example, using a database, file, system, or repository.
The deployment service fires CDI events on deployment and undeployment. The component that uses the service can process these events to store deployments and remove them from the store when they are undeployed.
-
A
DeploymentEvent
with the@Deploy
qualifier is fired on deployment of a unit -
A
DeploymentEvent
with the@Undeploy
qualifier is fired on undeployment of a unit
You can use the CDI observer mechanism to get notification on these events.
The following example receives notification on deployment of a unit and can save the deployment:
Example of processing of a deployment event
public void saveDeployment(@Observes @Deploy DeploymentEvent event) { // Store deployed unit information DeployedUnit deployedUnit = event.getDeployedUnit(); }
The following example receives notification on deployment of a unit and can remove the deployment from storage:
Example of processing of an undeployment event
public void removeDeployment(@Observes @Undeploy DeploymentEvent event) { // Remove deployment with the ID event.getDeploymentId() }
Several implementations of the DeploymentService
service are possible, so you must use qualifiers to instruct the CDI container to inject a particular implementation. A matching implementation of DeploymentUnit
must exist for every implementation of DeploymentService
.
The process engine provides the KmoduleDeploymentService
implementation. This implementation is designed to work with KmoduleDeploymentUnits
, which are small descriptors that are included in a KJAR file. This implementation is the typical solution for most use cases. The qualifier for this implementation is @Kjar
.
68.2.2. Form provider service for CDI
The FormProviderService
service provides access to form representations, which are usually displayed on the user interface for both process forms and user task forms.
The service relies on the concept of isolated form providers that can provide different capabilities and be backed by different technologies. The FormProvider
interface describes the contract for implementations of form providers.
Definition of the FormProvider
interface
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); }
Implementations of the FormProvider
interface must define a priority value. When the FormProviderService
service needs to render a form, it calls the available providers in their priority order.
The lower the priority value, the higher priority the provider gets. For example, a provider with a priority of 5 is evaluated before a provider with a priority of 10. For each required form, the service iterates over the available providers in the order of their priority, until one of them delivers the content. In the worst-case scenario, a simple text-based form is returned.
The process engine provides the following implementations of FormProvider
:
- A provider that delivers forms created in the Form Modeller tool, with a priority of 2
- A FreeMarker-based implementation that supports process and task forms, with a priority of 3
- The default forms provider, returning a simple text-based form, used as a last resort if no other provider delivers any content, with a priority of 1000
68.2.3. Runtime data service for CDI
The RuntimeDataService
service provides access to data that is available at runtime, including the following data:
- The available processes to be executed, with various filters
- The active process instances, with various filters
- The process instance history
- The process instance variables
- The active and completed nodes of process instance
The default implementation of RuntimeDataService
observes deployment events and indexes all deployed processes to expose them to the calling components.
68.2.4. Definition service for CDI
The DefinitionService
service provides access to process details that are stored as part of BPMN2 XML definitions.
Before using any method that provides information, invoke the buildProcessDefinition()
method to populate the repository with process information that is retrieved from the BPMN2 content.
The BPMN2DataService
implementation provides access to the following data:
- The overall description of the process for the given process definition
- The collection of all user tasks found in the process definition
- The information about the defined inputs for a user task node
- The information about defined outputs for a user task node
- The IDs of reusable processes (call activity) that are defined within a given process definition
- The information about process variables that are defined within a given process definition
The information about all organizational entities (users and groups) that are included in the process definition. Depending on the particular process definition, the returned values for users and groups can contain the following information:
- The actual user or group name
-
The process variable that is used to get the actual user or group name on runtime, for example,
#{manager}
68.2.5. CDI integration configuration
To use the jbpm-services-cdi
module in your CDI framework, you must provide some beans to satisfy the dependencies of the included service implementations.
Several beans can be required, depending on the usage scenario:
- The entity manager and entity manager factory
- The user group callback for human tasks
- The identity provider to pass authenticated user information to the services
When running in a JEE environment, such as Red Hat JBoss EAP, the following producer bean satisfies all requirements of the jbpm-services-cdi
module.
The producer bean that satisfies all requirements of the jbpm-services-cdi
module in a JEE environment
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 }; } }
The beans.xml
file for the application must enable a proper alternative for user group info callback. This alternative is taken based on the @Selectable
qualifier.
Definition of the alternative for user group info callback in the beans.xml
file`
<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
is an example value. This value is usually a good fit for Red Hat JBoss EAP, as it reuses security settings on the application server, regardless of which security method the server uses, for example, LDAP or database.
Optionally, you can provide several other producers to deliver WorkItemHandlers
and Process
, Agenda
, WorkingMemory
event listeners. You can provide these components by implementing the following interfaces:
Work item handler producer interface for process engine integration with 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); }
Event listener producer interface for process engine integration with 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); }
The beans implementing these two interfaces are collected at runtime and invoked when the RuntimeManager
class builds a KieSession
instance.
68.2.5.1. Runtime manager as a CDI bean
You can inject the RuntimeManager
class as a CDI bean into any other CDI bean within your application. The RuntimeEnvironment
class must be properly produced to enable correct initialization of the RuntimeManager
instance.
The following CDI qualifiers reference the existing runtime manager strategies:
-
@Singleton
-
@PerRequest
-
@PerProcessInstance
For more information about the runtime manager, see Section 66.2, “Runtime manager”.
Though you can inject the RuntimeManager
class directly, the solution for most use cases for frameworks such as CDI, EJB, or Spring is using services. The process engine services implement many best practices for using the runtime manager.
To use the runtime manager, you must add the RuntimeEnvironment
class to the producer that is defined in the Section 68.2.5, “CDI integration configuration” section.
The producer bean that provides the RuntimeEnvironment
class
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; } }
In this example, a single producer method is capable of providing the RuntimeEnvironment
class for all runtime manager strategies by specifying all qualifiers on the method level.
When the complete producer is available, the RuntimeManager
class can be injected into a CDI bean in the application:
Injecting the RuntimeManager
class
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); } }
If you inject the RuntimeManager
class, only one instance of RuntimeManager
might exist in the application. In typical cases, use the DeploymentService
service, which creates RuntimeManager
instances as necessary.
As an alternative to DeploymentService
, you can inject the RuntimeManagerFactory
class and then the application can use it to create RuntimeManager
instances. In this case, the EnvironmentProducer
definition is still required. The following example shows a simple ProcessEngine bean.
Example 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(); } }
68.3. Integration with Spring
While there are several ways to use the process engine with the Spring framework, two approaches are most frequently used
- Direct use of the Runtime Manager API
- Use of process engine services
Both approaches are tested and valid.
If your application needs to use only one runtime manager, use the direct Runtime Manager API, because it is the simplest way to use the process engine within a Spring application.
If your application needs to use multiple instances of the runtime manager, use process engine services, which encapsulate best practices by providing a dynamic runtime environment.
68.3.1. Direct use of the runtime manager API in Spring
The runtime manager manages the process engine and task service in synchronization. For more information about the runtime manager, see Section 66.2, “Runtime manager”.
To set up the runtime manager in the Spring framework, use the following factory beans:
-
org.kie.spring.factorybeans.RuntimeEnvironmentFactoryBean
-
org.kie.spring.factorybeans.RuntimeManagerFactoryBean
-
org.kie.spring.factorybeans.TaskServiceFactoryBean
These factory beans provide a standard way to configure the spring.xml
file for your Spring application.
68.3.1.1. RuntimeEnvironmentFactoryBean
bean
The RuntimeEnvironmentFactoryBean
factory bean produces instances of RuntimeEnvironment
. These instances are required for creating RuntimeManager
instances.
The bean supports creating the following types of RuntimeEnvironment
instances with different default configurations:
-
DEFAULT
: The default, or most common, configuration for the runtime manager -
EMPTY
: A completely empty environment that you can configure manually -
DEFAULT_IN_MEMORY
: The same configuration as DEFAULT, but without persistence of the runtime engine -
DEFAULT_KJAR
: The same configuration as DEFAULT, but assets are loaded from KJAR artifacts, which are identified by the release ID or the GAV value -
DEFAULT_KJAR_CL
: The configuration is built from thekmodule.xml
descriptor in a KJAR artifact
Mandatory properties depend on the selected type. However, knowledge information must be present for all types. This requirement means that one of the following kinds of information must be provided:
-
knowledgeBase
-
assets
-
releaseId
-
groupId, artifactId, version
For the DEFAULT
, DEFAULT_KJAR
, and DEFAULT_KJAR_CL
types, you must also configure persistence by providing the following parameters:
- Entity manager factory
- Transaction manager
The transaction manager must be the Spring transaction manager, because persistence and transaction support is configured based on this transaction manager.
Optionally, you can provide an EntityManager
instance instead of creating a new instance from EntityManagerFactory
, for example, you might use a shared entity manager from Spring.
All other properties are optional. They can override defaults that are determined by the selected type of the runtime environment.
68.3.1.2. RuntimeManagerFactoryBean
bean
The RuntimeManagerFactoryBean
factory bean produces RuntimeManager
instances of a given type, based on the provided RuntimeEnvironment
instance.
The supported types correspond to runtime manager strategies:
-
SINGLETON
-
PER_REQUEST
-
PER_PROCESS_INSTANCE
The default type, when no type is specified, is SINGLETON
.
The identifier is a mandatory property, because every runtime manager must be uniquely identified. All instances created by this factory are cached, so they can be properly disposed using the destroy method (close()
).
68.3.1.3. TaskServiceFactoryBean
bean
The TaskServiceFactoryBean
factory bean produces an instance of TaskService
based on given properties. You must provide the following mandatory properties:
- Entity manager factory
- Transaction manager
The transaction manager must be the Spring transaction manager, because persistence and transaction support is configured based on this transaction manager.
Optionally, you can provide an EntityManager
instance instead of creating a new instance from EntityManagerFactory
, for example, you might use a shared entity manager from Spring.
You can also set additional optional properties for the task service instance:
-
userGroupCallback
: The implementation ofUserGroupCallback
that the task service must use, the default value isMVELUserGroupCallbackImpl
-
userInfo
: The implementation ofUserInfo
that the task service must use, the default value isDefaultUserInfo
-
listener
: A list ofTaskLifeCycleEventListener
listeners which must be notified upon various operations on tasks
This factory bean creates a single instance of the task service. By design, this instance must be shared across all beans in the Spring environment.
68.3.1.4. Configuring a sample runtime manager with a Spring application
The following procedure is an example of complete configuration for a single runtime manager within a Spring application.
Procedure
Configure the entity manager factory and the transaction manager:
Configuring the entity manager factory and the transaction manager in the
spring.xml
file<bean id="jbpmEMF" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="persistenceUnitName" value="org.jbpm.persistence.spring.jta"/> </bean> <bean id="jbpmEM" class="org.springframework.orm.jpa.support.SharedEntityManagerBean"> <property name="entityManagerFactory" ref="jbpmEMF"/> </bean> <bean id="narayanaUserTransaction" factory-method="userTransaction" class="com.arjuna.ats.jta.UserTransaction" /> <bean id="narayanaTransactionManager" factory-method="transactionManager" class="com.arjuna.ats.jta.TransactionManager" /> <bean id="jbpmTxManager" class="org.springframework.transaction.jta.JtaTransactionManager"> <property name="transactionManager" ref="narayanaTransactionManager" /> <property name="userTransaction" ref="narayanaUserTransaction" /> </bean>
These settings define the following persistence configuration:
- JTA transaction manager (backed by Narayana JTA - for unit tests or servlet containers)
-
Entity manager factory for the
org.jbpm.persistence.spring.jta
persistence unit
Configure the business process resource:
Configuring the business process resource in the
spring.xml
file<bean id="process" factory-method="newClassPathResource" class="org.kie.internal.io.ResourceFactory"> <constructor-arg> <value>jbpm/processes/sample.bpmn</value> </constructor-arg> </bean>
These settings define a single process that is to be available for execution. The name of the resource is
sample.bpmn
and it must be available on the class path. You can use the class path as a simple way to include resources for trying out the process engine.Configure the
RuntimeEnvironment
instance with the entity manager, transaction manager, and resources:Configuring the
RuntimeEnvironment
instance in thespring.xml
file<bean id="runtimeEnvironment" class="org.kie.spring.factorybeans.RuntimeEnvironmentFactoryBean"> <property name="type" value="DEFAULT"/> <property name="entityManagerFactory" ref="jbpmEMF"/> <property name="transactionManager" ref="jbpmTxManager"/> <property name="assets"> <map> <entry key-ref="process"><util:constant static-field="org.kie.api.io.ResourceType.BPMN2"/></entry> </map> </property> </bean>
These settings define a default runtime environment for the runtime manager.
Create a
RuntimeManager
instance based on the environment:<bean id="runtimeManager" class="org.kie.spring.factorybeans.RuntimeManagerFactoryBean" destroy-method="close"> <property name="identifier" value="spring-rm"/> <property name="runtimeEnvironment" ref="runtimeEnvironment"/> </bean>
Result
After these steps you can use the runtime manager to execute processes in the Spring environment, using the EntityManagerFactory
class and the JTA transaction manager.
You can find complete Spring configuration files for different strategies in the repository.
68.3.1.5. Additional configuration options for the runtime manager in the Spring framework
In addition to the configuration with the EntityManagerFactory
class and the JTA transaction manager, as described in Section 68.3.1.4, “Configuring a sample runtime manager with a Spring application”, you can use other configuration options for the runtime manager in the Spring framework:
-
JTA and the
SharedEntityManager
class -
Local Persistence Unit and the
EntityManagerFactory
class -
Local Persistence Unit and
SharedEntityManager
class
If your application is configured with a Local Persistence Unit and uses the AuditService
service to query process engine history data, you must add the org.kie.api.runtime.EnvironmentName.USE_LOCAL_TRANSACTIONS
environment entry to the RuntimeEnvironment
instance configuration:
RuntimeEnvironment
instance configuration for a Local Persistence Unit in the spring.xml
file
<bean id="runtimeEnvironment" class="org.kie.spring.factorybeans.RuntimeEnvironmentFactoryBean"> ... <property name="environmentEntries" ref="env" /> </bean> ... <util:map id="env" key-type="java.lang.String" value-type="java.lang.Object"> <entry> <key> <util:constant static-field="org.kie.api.runtime.EnvironmentName.USE_LOCAL_TRANSACTIONS" /> </key> <value>true</value> </entry> </util:map>
You can find more examples of configuration options in the repository: configuration files and test cases.
68.3.2. Process engine services with Spring
You might want to create a dynamic Spring application, where you can add and remove business assets such as process definitions, data models, rules, and forms without restarting the application.
In this case, use process engine services. Process engine services are designed as framework-agnostic, and separate modules bring in the required framework-specific addons.
The jbpm-kie-services
module contains the code logic of the services. A Spring application can consume these pure Java services.
The only code you must add to your Spring application to configure process engine services is the implementation of the IdentityProvider
interface. This implementation depends on your security configuration. The following example implementation uses Spring Security, though it might not cover all available security features for a Spring application.
Implementation of the IdentityProvider
interface using Spring Security
import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.kie.internal.identity.IdentityProvider; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; public class SpringSecurityIdentityProvider implements IdentityProvider { public String getName() { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null && auth.isAuthenticated()) { return auth.getName(); } return "system"; } public List<String> getRoles() { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null && auth.isAuthenticated()) { List<String> roles = new ArrayList<String>(); for (GrantedAuthority ga : auth.getAuthorities()) { roles.add(ga.getAuthority()); } return roles; } return Collections.emptyList(); } public boolean hasRole(String role) { return false; } }
68.3.2.1. Configuring process engine services with a Spring application
The following procedure is an example of complete configuration for process engine services within a Spring application.
Procedure
Configure transactons:
Configuring transactions in the
spring.xml
file<context:annotation-config /> <tx:annotation-driven /> <tx:jta-transaction-manager /> <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" />
Configure JPA and persistence:
Configuring JPA and persistence in the
spring.xml
file<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" depends-on="transactionManager"> <property name="persistenceXmlLocation" value="classpath:/META-INF/jbpm-persistence.xml" /> </bean>
Configure security and user and group information providers:
Configuring security and user and group information providers in the
spring.xml
file<util:properties id="roleProperties" location="classpath:/roles.properties" /> <bean id="userGroupCallback" class="org.jbpm.services.task.identity.JBossUserGroupCallbackImpl"> <constructor-arg name="userGroups" ref="roleProperties"></constructor-arg> </bean> <bean id="identityProvider" class="org.jbpm.spring.SpringSecurityIdentityProvider"/>
Configure the runtime manager factory. This factory is Spring context aware, so it can interact with the Spring container in the correct way and support the necessary services, including the transactional command service and the task service:
Configuring the runtime manager factory in the
spring.xml
file<bean id="runtimeManagerFactory" class="org.kie.spring.manager.SpringRuntimeManagerFactoryImpl"> <property name="transactionManager" ref="transactionManager"/> <property name="userGroupCallback" ref="userGroupCallback"/> </bean> <bean id="transactionCmdService" class="org.jbpm.shared.services.impl.TransactionalCommandService"> <constructor-arg name="emf" ref="entityManagerFactory"></constructor-arg> </bean> <bean id="taskService" class="org.kie.spring.factorybeans.TaskServiceFactoryBean" destroy-method="close"> <property name="entityManagerFactory" ref="entityManagerFactory"/> <property name="transactionManager" ref="transactionManager"/> <property name="userGroupCallback" ref="userGroupCallback"/> <property name="listeners"> <list> <bean class="org.jbpm.services.task.audit.JPATaskLifeCycleEventListener"> <constructor-arg value="true"/> </bean> </list> </property> </bean>
Configure process engine services as Spring beans:
Configuring process engine services as Spring beans in the
spring.xml
file<!-- Definition service --> <bean id="definitionService" class="org.jbpm.kie.services.impl.bpmn2.BPMN2DataServiceImpl"/> <!-- Runtime data service --> <bean id="runtimeDataService" class="org.jbpm.kie.services.impl.RuntimeDataServiceImpl"> <property name="commandService" ref="transactionCmdService"/> <property name="identityProvider" ref="identityProvider"/> <property name="taskService" ref="taskService"/> </bean> <!-- Deployment service --> <bean id="deploymentService" class="org.jbpm.kie.services.impl.KModuleDeploymentService" depends-on="entityManagerFactory" init-method="onInit"> <property name="bpmn2Service" ref="definitionService"/> <property name="emf" ref="entityManagerFactory"/> <property name="managerFactory" ref="runtimeManagerFactory"/> <property name="identityProvider" ref="identityProvider"/> <property name="runtimeDataService" ref="runtimeDataService"/> </bean> <!-- Process service --> <bean id="processService" class="org.jbpm.kie.services.impl.ProcessServiceImpl" depends-on="deploymentService"> <property name="dataService" ref="runtimeDataService"/> <property name="deploymentService" ref="deploymentService"/> </bean> <!-- User task service --> <bean id="userTaskService" class="org.jbpm.kie.services.impl.UserTaskServiceImpl" depends-on="deploymentService"> <property name="dataService" ref="runtimeDataService"/> <property name="deploymentService" ref="deploymentService"/> </bean> <!-- Register the runtime data service as a listener on the deployment service so it can receive notification about deployed and undeployed units --> <bean id="data" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean" depends-on="deploymentService"> <property name="targetObject" ref="deploymentService"></property> <property name="targetMethod"><value>addListener</value></property> <property name="arguments"> <list> <ref bean="runtimeDataService"/> </list> </property> </bean>
Result
Your Spring application can use process engine services.
68.4. Integration with EJB
The process engine provides a complete integration layer for Enterprise Java Beans (EJB). This layer supports both local and remote EJB interaction.
The following modules provide EJB services:
-
jbpm-services-ejb-api
: The API module that extends thejbpm-services-api
module with EJB-specific interfaces and objects -
jbpm-services-ejb-impl
: An EJB extension for core services -
jbpm-services-ejb-timer
: A process engine Scheduler Service implementation based on the EJB Timer Service -
jbpm-services-ejb-client
: An EJB remote client implementation for remote interaction, which supports Red Hat JBoss EAP by default
The EJB layer is based on process engine services. It provides almost the same capabilities as the core module, though some limitations exist if you use the remote interface.
The main limitation affects the deployment service, which, if it is used as a remote EJB service, supports only the following methods:
-
deploy()
-
undeploy()
-
activate()
-
deactivate()
-
isDeployed()
Other methods are excluded because they return instances of runtime objects, such as RuntimeManager
, which can not be used over the remote interface.
All other services provide the same functionality over EJB as the versions included in the core module.
68.4.1. Implementations for EJB services
As an extension of process engine core services, EJB services provide EJB-based execution semantics and are based on various EJB-specific features.
-
DeploymentServiceEJBImpl
is implemented as an EJB singleton with container-managed concurrency. Its lock type is set towrite
. -
DefinitionServiceEJBImpl
is implemented as an EJB singleton with container-managed concurrency. Its overall lock type is set toread
and for thebuildProcessDefinition()
method the lock type is set towrite
. -
ProcessServiceEJBImpl
is implemented as a stateless session bean. RuntimeDataServiceEJBImpl
is implemented as an EJB singleton. For the majority of methods the lock type is set toread
. For the following methods the lock type is set towrite
:-
onDeploy()
-
onUnDeploy()
-
onActivate()
-
onDeactivate()
-
-
UserTaskServiceEJBImpl
is implemented as a stateless session bean.
Transactions
The EJB container manages transactions in EJB services. For this reason, you do not need to set up any transaction manager or user transaction within your application code.
Identity provider
The default identity provider is based on the EJBContext
interface and relies on caller principal information for both name and roles. The IdentityProvider
interface provides two methods related to roles:
-
getRoles()
returns an empty list, because theEJBContext
interface does not provide an option to fetch all roles for a particular user -
hasRole()
delegates to theisCallerInRole()
method of the context
To ensure that valid information is available to the EJB environment, you must follow standard JEE security practices to authenticate and authorize users. If no authentication or authorization is configured for EJB services, an anonymous user is always assumed.
If you use a different security model, you can use CDI-style injection for the IdentityProvider
object for EJB services. In this case, create a valid CDI bean that implements the org.kie.internal.identity.IdentityProvider
interface and make this bean available for injection with your application. This implementation will take precedence over the EJBContext
-based identity provider.
Deployment synchronization
Deployment synchronization is enabled by default and attempts to synchronize any deployments every 3 seconds. It is implemented as an EJB singleton with container-managed concurrency. Its lock type is set to write
. It uses the EJB timer service to schedule synchronization jobs.
EJB scheduler service
The process engine uses the scheduler service to handle time-based activities such as timer events and deadlines. When running in an EJB environment, the process engine uses a scheduler based on the EJB timer service. It registers this scheduler for all RuntimeManager
instances.
You might need to use a configuration specific to an application server to support cluster operation.
UserGroupCallback
and UserInfo
implementation selection
The required implementation of UserGroupCallback
and UserInfo
interfaces might differ for various applications. These interfaces can not be injected with EJB directly. You can use the following system properties to select existing implementations or use custom implementations of these interfaces for the process engine:
org.jbpm.ht.callback
: This property selects the implementation for theUserGroupCallback
interface:-
mvel
: The default implementation, typically used for testing. -
ldap
: The LDAP-based implementation. This implementation requires additional configuration in thejbpm.usergroup.callback.properties
file. -
db
: The database-based implementation. This implementation requires additional configuration in thejbpm.usergroup.callback.properties
file. -
jaas
: An implementation that requests user information from the container. -
props
: A simple property-based callback. This implementation requires an additional properties file that contains all users and groups. -
custom
: A custom implementation. You must provide the fully-qualified class name of the implementation in theorg.jbpm.ht.custom.callback
system property.
-
org.jbpm.ht.userinfo
: This property selects the implementation for theUserInfo
interface:-
ldap
: The LDAP-based implementation. This implementation requires additional configuration in thejbpm-user.info.properties
file. -
db
: The database-based implementation. This implementation requires additional configuration in thejbpm-user.info.properties
file. -
props
: A simple property-based implementation. This implementation requires an additional properties file that contains all user information. -
custom
: A custom implementation. You must provide the fully-qualified class name of the implementation in theorg.jbpm.ht.custom.userinfo
system property.
-
Typically, set the system properties in the startup configuration of the application server or JVM. You can also set the properties in the code before using the services. For example, you can provide a custom @Startup
bean that configures these system properties.
68.4.2. Local EJB interfaces
The following local EJB service interfaces extend core services:
-
org.jbpm.services.ejb.api.DefinitionServiceEJBLocal
-
org.jbpm.services.ejb.api.DeploymentServiceEJBLocal
-
org.jbpm.services.ejb.api.ProcessServiceEJBLocal
-
org.jbpm.services.ejb.api.RuntimeDataServiceEJBLocal
-
org.jbpm.services.ejb.api.UserTaskServiceEJBLocal
You must use these interfaces as injection points and annotate them with @EJB
:
Using local EJB service interfaces
@EJB private DefinitionServiceEJBLocal bpmn2Service; @EJB private DeploymentServiceEJBLocal deploymentService; @EJB private ProcessServiceEJBLocal processService; @EJB private RuntimeDataServiceEJBLocal runtimeDataService;
After injecting these interfaces, invoke operations on them in the same way as on core modules. No restrictions exist for using local interfaces.
68.4.3. Remote EJB interfaces
The following dedicated remote EJB interfaces extend core services:
-
org.jbpm.services.ejb.api.DefinitionServiceEJBRemote
-
org.jbpm.services.ejb.api.DeploymentServiceEJBRemote
-
org.jbpm.services.ejb.api.ProcessServiceEJBRemote
-
org.jbpm.services.ejb.api.RuntimeDataServiceEJBRemote
-
org.jbpm.services.ejb.api.UserTaskServiceEJBRemote
You can use these interfaces in the same way as local interfaces, with the exception of handling custom types.
You can define custom types in two ways. Globally defined types are available on application classpath and included in the enterprise application. If you define a type locally to the deployment unit, the type is declared in a project dependency (for example, in a KJAR file) and is resolved at deployment time.
Globally available types do not require any special handling. The EJB container automatically marshalls the data when handling remote requests. However, local custom types are not visible to the EJB container by default.
The process engine EJB services provide a mechanism to work with custom types. They provide the following two additional types:
-
org.jbpm.services.ejb.remote.api.RemoteObject
: A serializable wrapper class for single-value parameters org.jbpm.services.ejb.remote.api.RemoteMap
: A dedicatedjava.util.Map
implementation to simplify remote invocation of service methods that accept custom object input. The internal implementation of the map holds content that is already serialized, in order to avoid additional serialization at sending time.This implementation does not include some of the methods of
java.util.Map
that are usually not used when sending data.
These special objects perform eager serialization to bytes using an ObjectInputStream
object. They remove the need for serialization of data in the EJB client/container. Because no serialization is needed, it is not necessary to share the custom data model with the EJB container.
The following example code works with local types and remote EJB services:
Using local types with remote EJB services
// Start a process with custom types via remote EJB Map<String, Object> parameters = new RemoteMap(); Person person = new org.jbpm.test.Person("john", 25, true); parameters.put("person", person); Long processInstanceId = processService.startProcess(deploymentUnit.getIdentifier(), "custom-data-project.work-on-custom-data", parameters); // Fetch task data and complete a task with custom types via remote EJB Map<String, Object> data = userTaskService.getTaskInputContentByTaskId(taskId); Person fromTaskPerson = data.get("_person"); fromTaskPerson.setName("John Doe"); RemoteMap outcome = new RemoteMap(); outcome.put("person_", fromTaskPerson); userTaskService.complete(taskId, "john", outcome);
In a similar way, you can use the RemoteObject
class to send an event to a process instance:
// Send an event with a custom type via remote EJB Person person = new org.jbpm.test.Person("john", 25, true); RemoteObject myObject = new RemoteObject(person); processService.signalProcessInstance(processInstanceId, "MySignal", myObject);
68.4.4. Remote EJB client
Remote client support is provided by implementation of the ClientServiceFactory
interface that is a facade for application server specific code:
Definition of the ClientServiceFactory
interface
/** * Generic service factory used for remote lookups that are usually container specific. * */ public interface ClientServiceFactory { /** * Returns unique name of given factory implementation * @return */ String getName(); /** * Returns remote view of given service interface from selected application * @param application application identifier on the container * @param serviceInterface remote service interface to be found * @return * @throws NamingException */ <T> T getService(String application, Class<T> serviceInterface) throws NamingException; }
You can dynamically register implementations using the ServiceLoader
mechanism. By default, only one implementation is available in Red Hat JBoss EAP.
Each ClientServiceFactory
implementation must provide a name. This name is used to register it within the client registry. You can look up implementations by name.
The following code gets the default Red Hat JBoss EAP remote client:
Getting the default Red Hat JBoss EAP remote client
// Retrieve a valid client service factory ClientServiceFactory factory = ServiceFactoryProvider.getProvider("JBoss"); // Set the application variable to the module name String application = "sample-war-ejb-app"; // Retrieve the required service from the factory DeploymentServiceEJBRemote deploymentService = factory.getService(application, DeploymentServiceEJBRemote.class);
After retrieving a service you can use its methods.
When working with Red Hat JBoss EAP and the remote client you can add the following Maven dependency to bring in all EJB client libraries:
<dependency> <groupId>org.jboss.as</groupId> <artifactId>jboss-as-ejb-client-bom</artifactId> <version>7.3.0.Final</version> <!-- use the valid version for the server you run on --> <optional>true</optional> <type>pom</type> </dependency>
68.5. Integration with OSGi
All core process engine JAR files and core dependencies are OSGi-enabled. The following additional process engine JAR files are also OSGI-enabled:
- jbpm-flow
- jbpm-flow-builder
- jbpm-bpmn2
OSGi-enabled JAR files contain MANIFEST.MF
files in the META-INF
directory. These files contain data such as the required dependencies. You can add such JAR files to an OSGi environment.
For additional information about the OSGi infrastructure, see the OSGI documentation.
Support for integration with the OSGi framework is deprecated. It does not receive any new enhancements or features and will be removed in a future release.