Search

Chapter 68. Integration with Java frameworks

download PDF

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>

Note

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.

Note

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>

Note

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”.

Note

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 the kmodule.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 of UserGroupCallback that the task service must use, the default value is MVELUserGroupCallbackImpl
  • userInfo: The implementation of UserInfo that the task service must use, the default value is DefaultUserInfo
  • listener: A list of TaskLifeCycleEventListener 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

  1. 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
  2. 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.

  3. Configure the RuntimeEnvironment instance with the entity manager, transaction manager, and resources:

    Configuring the RuntimeEnvironment instance in the spring.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.

  4. 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

  1. 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" />

  2. 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>

  3. 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"/>

  4. 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>

  5. 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 the jbpm-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 to write.
  • DefinitionServiceEJBImpl is implemented as an EJB singleton with container-managed concurrency. Its overall lock type is set to read and for the buildProcessDefinition() method the lock type is set to write.
  • 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 to read. For the following methods the lock type is set to write:

    • 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 the EJBContext interface does not provide an option to fetch all roles for a particular user
  • hasRole() delegates to the isCallerInRole() 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 the UserGroupCallback interface:

    • mvel: The default implementation, typically used for testing.
    • ldap: The LDAP-based implementation. This implementation requires additional configuration in the jbpm.usergroup.callback.properties file.
    • db: The database-based implementation. This implementation requires additional configuration in the jbpm.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 the org.jbpm.ht.custom.callback system property.
  • org.jbpm.ht.userinfo: This property selects the implementation for the UserInfo interface:

    • ldap: The LDAP-based implementation. This implementation requires additional configuration in the jbpm-user.info.properties file.
    • db: The database-based implementation. This implementation requires additional configuration in the jbpm-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 the org.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 dedicated java.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.

Note

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.

Red Hat logoGithubRedditYoutubeTwitter

Learn

Try, buy, & sell

Communities

About Red Hat Documentation

We help Red Hat users innovate and achieve their goals with our products and services with content they can trust.

Making open source more inclusive

Red Hat is committed to replacing problematic language in our code, documentation, and web properties. For more details, see the Red Hat Blog.

About Red Hat

We deliver hardened solutions that make it easier for enterprises to work across platforms and environments, from the core datacenter to the network edge.

© 2024 Red Hat, Inc.