Chapter 14. Integration with Spring

14.1. Configuring Red Hat JBoss BPM Suite with Spring

The jboss-bpms-engine.zip file contains the Spring module, which is called kie-spring-VERSION-redhat-MINOR_VERSION.jar.

You can configure the Spring modules:

As a Self Managed Process Engine
If you require a single runtime manager instance, use the RuntimeManager API. The RuntimeManager API synchronizes the process engine and task service internally.
As a Shared Task Service
If you require multiple runtime manager instances, use the jBPM services.
Note

Do not use the shared task service if you use Spring and your process is instantiated using the per process or per request runtime strategy.

14.1.1. Integrating Spring with Runtime Manager API

To integrate Spring with the Runtime Manager API, include the following factory beans:

  • org.kie.spring.factorybeans.RuntimeEnvironmentFactoryBean
  • org.kie.spring.factorybeans.RuntimeManagerFactoryBean
  • org.kie.spring.factorybeans.TaskServiceFactoryBean
Note

TaskServiceFactoryBean is required only for shared task service.

RuntimeEnvironmentFactoryBean

RuntimeEnvironmentFactoryBean produces RuntimeEnvironment instances consumed by RuntimeManager.

You can create the following types of RuntimeEnvironment instances:

  • DEFAULT: The default type.
  • EMPTY: An empty environment.
  • DEFAULT_IN_MEMORY: Same as DEFAULT with no persistence of the runtime engine.
  • DEFAULT_KJAR: Same as DEFAULT with knowledge assets taken from kJAR and identified by releaseID or GAV (Group, Artifact, Version).
  • DEFAULT_KJAR_CL: Built from class path that consists of a kmodule.xml descriptor.

Knowledge information is required for all the RuntimeEnvironment types. Provide one or more of the following:

  • knowledgeBase
  • assets
  • releaseId
  • groupId, artifactId, version

For the DEFAULT, DEFAULT_KJAR, DEFAULT_KJAR_CL types, configure persistence using entity manager factory or transaction manager.

RuntimeManagerFactoryBean

RuntimeManagerFactoryBean creates RuntimeManager instances based on runtimeEnvironment. You can create the following runtimeEnvironment instances:

  • SINGLETON (default)
  • PER_REQUEST
  • PER_PROCESS_INSTANCE

Every RuntimeManager instance must have a unique ID. You can dispose of any RuntimeManager instance created by RuntimeManagerFactoryBean by calling the close() method.

TaskServiceFactoryBean

TaskServiceFactoryBean creates TaskService instance based on the given properties. Creates a single instance only.

Properties required:

  • entity manager factory
  • transaction manager

When using the TaskServiceFactoryBean, provide the Spring transaction manager. When using a shared entity manager from Spring, you can also provide EntityManager instance instead of entity manager factory.

Optional properties:

  • userGroupCallback: MVELUserGroupCallbackImpl by default.
  • userInfo: DefaultUserInfo by default.
  • listener: List of TaskLifeCycleEventListener instances.
Sample RuntimeManager Configuration with Spring

To create a single runtime manager Spring configuration:

  1. Configure the entity manager factory and the transaction manager, for example:

    <bean id="jbpmEMF" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
      <property name="persistenceUnitName" value="org.jbpm.persistence.spring.jta"/>
    </bean>
    
    <bean id="btmConfig" factory-method="getConfiguration" class="bitronix.tm.TransactionManagerServices"></bean>
    
    <bean id="BitronixTransactionManager" factory-method="getTransactionManager"
          class="bitronix.tm.TransactionManagerServices" depends-on="btmConfig" destroy-method="shutdown" />
    
    <bean id="jbpmTxManager" class="org.springframework.transaction.jta.JtaTransactionManager">
      <property name="transactionManager" ref="BitronixTransactionManager" />
      <property name="userTransaction" ref="BitronixTransactionManager" />
    </bean>

    This configuration provides:

    • JTA transaction manager, backed by Bitronix for unit tests or servlet containers.
    • The org.jbpm.persistence.spring.jta entity manager factory for persistence unit.
  2. Configure resources you use, for example a business process:

    <bean id="process" factory-method="newclass pathResource" class="org.kie.internal.io.ResourceFactory">
      <constructor-arg>
        <value>jbpm/processes/sample.bpmn</value>
      </constructor-arg>
    </bean>

    The sample.bpmn process is included from the class path.

  3. Configure RuntimeEnvironment using your entity manager, transaction manager, and resources:

    <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>
  4. Create RuntimeManager:

    <bean id="runtimeManager" class="org.kie.spring.factorybeans.RuntimeManagerFactoryBean" destroy-method="close">
      <property name="identifier" value="spring-rm"/>
      <property name="runtimeEnvironment" ref="runtimeEnvironment"/>
    </bean>

An example of complete configuration:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                          http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">

  <import resource="classpath:jbpm/configuration-template/assets.xml" />

  <bean id="jbpmEMF" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="persistenceUnitName" value="org.jbpm.persistence.spring.jta"/>
    <property name="persistenceXmlLocation" value="classpath:jbpm/persistence-jta.xml"/>
  </bean>

  <bean id="btmConfig" factory-method="getConfiguration" class="bitronix.tm.TransactionManagerServices"/>

  <bean id="BitronixTransactionManager" factory-method="getTransactionManager"
        class="bitronix.tm.TransactionManagerServices" depends-on="btmConfig" destroy-method="shutdown" />

  <bean id="jbpmTxManager" class="org.springframework.transaction.jta.JtaTransactionManager">
    <property name="transactionManager" ref="BitronixTransactionManager" />
    <property name="userTransaction" ref="BitronixTransactionManager" />
  </bean>

  <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" ref="assets"/>
  </bean>

  <bean id="logService" class="org.jbpm.process.audit.JPAAuditLogService" depends-on="runtimeEnvironment">
    <constructor-arg value="#{runtimeEnvironment.environment}" />
    <constructor-arg value="STANDALONE_JTA" />
  </bean>
</beans>

14.1.2. Spring and jBPM Services

If you require multiple runtime managers, you can use jBPM services directly in your application. Due to the dynamic nature of jBPM services, processes and other assets can be added and removed without restarting your application.

To configure jBPM services:

  1. Add the kie-spring Maven dependency into your pom.xml:

    <dependencies>
      ...
      <dependency>
          <groupId>org.kie</groupId>
          <artifactId>kie-spring</artifactId>
          <version>6.5.0.Final-redhat-2</version>
        </dependency>
        ...
    </dependencies>

    For the current Maven artifact version, see chapter Supported Component Versions of the Red Hat JBoss BPM Suite Installation Guide.

Note

Depending on your implementation, other dependencies may be necessary, for example spring-security.

  1. Implement the IdentityProvider interface:

    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;
    	}
    
    }

To configure jBPM services in a Spring application:

  1. Configure the transaction manager:

    <context:annotation-config />
    <tx:annotation-driven />
    <tx:jta-transaction-manager />
    
    <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" />
  2. Configure JPA and persistence:

    <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 providers:

    <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:

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

    The runtime manager factory is Spring context aware and can interact with Spring containers.

  5. Configure jBPM services as Spring beans:

    <!-- 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 runtime data service as listener on 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>