JBoss.orgCommunity Documentation

Chapter 20. Integration

20.1. Maven
20.1.1. Maven artifacts as deployment units
20.1.2. Use Maven for dependency management
20.2. CDI
20.2.1. Overview
20.2.2. Configuring CDI integration
20.2.3. RuntimeManager as CDI bean
20.3. Spring
20.3.1. Direct use of Runtime Manager API
20.3.2. jBPM services with Spring
20.4. Ejb
20.4.1. Ejb services implementation
20.4.2. Local interface
20.4.3. Remote interface
20.5. OSGi

Apache Maven is used by jBPM for two main purposes:

Since version 6, jBPM provides simplified and complete deployment mechanism that is based entirely on Apache Maven artifacts. These artifacts also known as kjars are simple JAR files that include a descriptor for KIE system to produce KieBase and KieSession. Descriptor of the kjar is represented as XML file named kmodule.xml and it can be:

<kmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.drools.org/xsd/kmodule">
</kmodule>

Empty kmodule.xml that provides all defaults for the kjar:

All these and more can be configured manually via kmodule.xml when defaults are not enough. The complete set of elements can be found in xsd schema of kmodule.xml.

<kmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xmlns="http://www.drools.org/xsd/kmodule">
  <kbase name="defaultKieBase" default="true" eventProcessingMode="cloud" equalsBehavior="identity" declarativeAgenda="disabled" scope="javax.enterprise.context.ApplicationScoped" packages="*">
    <ksession name="defaultKieSession" type="stateful" default="true" clockType="realtime" scope="javax.enterprise.context.ApplicationScoped">
        <workItemHandlers>
            <workItemHandler name="CustomTask" type="FQCN_OF_HANDLER" />
        </workItemHandlers>
        <listeners>
            <listener type="FQCN_OF_EVENT_LISTENER" />
        </listeners>
    </ksession>
    <ksession name="defaultStatelessKieSession" type="stateless" default="true" clockType="realtime" scope="javax.enterprise.context.ApplicationScoped"/>
  </kbase>
</kmodule>

As illustrated in the listing above the kmodule.xml provides flexible way of instructing the runtime engine on what should be configured and how. The example above does not present all available options, but these are the most common when working with processes.

Note

Important to note is that when using RuntimeManager, KieSession instances are created by the RuntimeManager instead of by KieContainer but kmodule.xml (or model in general) is aways used as a base of the construction process. KieBase although is always taken from KieContainer.

Kjars are represented same way as any other Maven artifact - by Group Artifact Version which is then represented as ReleaseId in KIE API. This the the only thing required to deploy kjar into runtime environment such as KIE Workbeanch.

When building systems that embed jBPM as workflow engine the simplest way is to configure all dependencies required by jBPM via Apache Maven. jBPM provides set of BOMs (Bill Of Material) to simplify what artifacts needs to be declared. Common way to start with integration of custom application and jBPM is to define dependency management:

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <version.org.drools>6.0.0.Final</version.org.drools>
    <version.org.jbpm>6.0.0.Final</version.org.jbpm>
    <hibernate.version>4.2.0.Final</hibernate.version>
    <hibernate.core.version>4.2.0.Final</hibernate.core.version>
    <slf4j.version>1.6.4</slf4j.version>
    <jboss.javaee.version>1.0.0.Final</jboss.javaee.version>
    <logback.version>1.0.9</logback.version>
    <h2.version>1.3.161</h2.version>
    <btm.version>2.1.4</btm.version>
    <junit.version>4.8.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 drools BOM -->
      <dependency>
        <groupId>org.jbpm</groupId>
        <artifactId>jbpm-bom</artifactId>
        <type>pom</type>
        <version>${version.org.jbpm}</version>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

Above should be declared in top level pom.xml so all modules that need to use KIE (drools and jBPM) API can access it.

Next, module(s) that would operate on KIE API should declare following dependencies:

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

Above are the main runtime dependencies, regardless of where the application is deployed (application server, servlet container, standalone app). A good practice is to test the workflow components to ensure they work properly before actual deployment and thus following test dependencies should be defined:

    <!-- test dependencies -->
    <dependency>
      <groupId>org.jbpm</groupId>
      <artifactId>jbpm-shared-services</artifactId>
      <classifier>btm</classifier>
      <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>org.codehaus.btm</groupId>
      <artifactId>btm</artifactId>
      <version>${btm.version}</version>
      <scope>test</scope>
    </dependency>

Last but not least, define the JBoss Maven repository for artifacts resolution:

  <repositories>
    <repository>
      <id>jboss-public-repository-group</id>
      <name>JBoss Public Repository Group</name>
      <url>http://repository.jboss.org/nexus/content/groups/public/</url>
      <releases>
        <updatePolicy>never</updatePolicy>
      </releases>
      <snapshots>
        <updatePolicy>daily</updatePolicy>
      </snapshots>
    </repository>
  </repositories>

That should allow to configure jBPM in your application and provide access to KIE API to operate on processes, rules, events.

jBPM 6 comes with out of the box integration with CDI (Contexts and Dependency Injection). Although most of the API can be used in CDI world there are some dedicated modules that are designed especially for CDI containers. The most important one is jbpm-services-cdi that provides cdi wrappers on top of jbpm services, these shall be used in most of the cases were CDI is available for jBPM integration. It provides following set of services:

These services are first class citizens for CDI world so they are available for injection in any other CDI bean.

Service responsible for deploying DeploymentUnits into runtime environment. By deploying given deployment unit becomes ready for execution and has RuntimeManager created for it.DeploymentService can next be used to retrieve:

Deployment service stores the deployed units by default in memory and thus in case of a need to restore all previously deployed units, component that uses deployment service needs to store that information itself. Common places for such a store are database, file system, repository of some sort, etc. Deployment service will fire CDI events on deployment and undeployment to allow application components to react real time to these events to be able to store deployments or remove them from the store when they are undeployed.

use CDI observer mechanism to get notification on above events. First to save deployments in the store of your choice:

    public void saveDeployment(@Observes @Deploy DeploymentEvent event) {
        // store deployed unit info for further needs 
        DeployedUnit deployedUnit = event.getDeployedUnit();
    }

next to remove it when it was undeployed

    public void removeDeployment(@Observes @Undeploy DeploymentEvent event) {
        // remove deployment with id event.getDeploymentId()
    }

Due to the fact that there might be several implementation of DeploymentService use of qualifiers is needed to instruct CDI container which one shall be injected. jBPM comes with two out of the box:

The general practice is that every implementation of DeploymentService should come with dedicated implementation of DeploymentUnit as these two provided out of the box.

FormProviderService provides access to form representations usually displayed on UI for both process forms and user task forms. It is built on concept of isolated FormProviders that can provide different capabilities and be backed by different technologies. FormProvider interface describes contract for the implementations

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 FormProvider interface should always define priority as this is the main driver for the FormProviderService to ask for the content of the form of a given provider. FormProviderService will collect all available providers and iterate over them asking for the form content (rendered) in their priority order. The lower the number the higher priority it gets during evaluation, e.g. provider with priority 5 will be evaluated before provider with priority 10. FormProviderService will iterate over available providers as long as one delivers the content. In the worse case scenario, simple text based forms will be returned.

jBPM comes with following FormProviders out of the box:

To make use of jbpm-services-cdi in your system you'll need to provide some beans for the out of the box services to satisfy all dependencies they have. There are several beans that depends on actual scenario

When running in JEE environment like an JBoss Application Server following producer bean should satisfy all requirements of the jbpm-services-cdi

public class EnvironmentProducer { 
   
    @PersistenceUnit(unitName = "org.jbpm.domain")
    private EntityManagerFactory emf;

    @Inject
    @Selectable
    private UserGroupInfoProducer userGroupInfoProducer;

    @Inject
    @Kjar
    private DeploymentService deploymentService;

    @Produces
    public EntityManagerFactory getEntityManagerFactory() {
        return this.emf;
    }

    @Produces
    public org.kie.api.task.UserGroupCallback produceSelectedUserGroupCalback() {
        return userGroupInfoProducer.produceCallback();
    }

    @Produces
    public UserInfo produceUserInfo() {
        return userGroupInfoProducer.produceUserInfo();
    }

    @Produces
    @Named("Logs")
    public TaskLifeCycleEventListener produceTaskAuditListener() {
        return new JPATaskLifeCycleEventListener(true);
    }

    @Produces
    public DeploymentService getDeploymentService() {
        return this.deploymentService;
    }

    @Produces
    public IdentityProvider produceIdentityProvider {
        return new IdentityProvider() {
             // implement IdentityProvider
        };
    }
}

Then beans.xml for the application should enable proper alternative for user group callback (that will be taken based on @Selectable qualifier)

<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 http://docs.jboss.org/cdi/beans_1_0.xsd">

  <alternatives>
    <class>org.jbpm.kie.services.cdi.producer.JAASUserGroupInfoProducer</class>
  </alternatives>

</beans>

Optionally there can be several other producers provided to deliver:

These components can be provided by implementing following interfaces

/**
 * Allows to provide custom implementations to deliver WorkItem name and WorkItemHandler instance pairs
 * for the runtime.
 * <br/>
 * It will be invoked by RegisterableItemsFactory implementation (especially InjectableRegisterableItemsFactory 
 * in CDI world) for every KieSession. Recommendation is to always produce new instances to avoid unexpected 
 * results. 
 *
 */
public interface WorkItemHandlerProducer {

    /**
     * Returns map of (key = work item name, value work item handler instance) of work items 
     * to be registered on KieSession
     * <br/>
     * Parameters that might be given are as follows:
     * <ul>
     *  <li>ksession</li>
     *  <li>taskService</li>
     *  <li>runtimeManager</li>
     * </ul>
     * 
     * @param identifier - identifier of the owner - usually RuntimeManager that allows the producer to filter out
     * and provide valid instances for given owner
     * @param params - owner might provide some parameters, usually KieSession, TaskService, RuntimeManager instances
     * @return map of work item handler instances (recommendation is to always return new instances when this method is invoked)
     */
    Map<String, WorkItemHandler> getWorkItemHandlers(String identifier, Map<String, Object> params);
}

and

/**
 * Allows do define custom producers for know EventListeners. Intention of this is that there might be several 
 * implementations that might provide different listener instance based on the context they are executed in. 
 * <br/>
 * It will be invoked by RegisterableItemsFactory implementation (especially InjectableRegisterableItemsFactory 
 * in CDI world) for every KieSession. Recommendation is to always produce new instances 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 as follows:
     * <ul>
     *  <li>ksession</li>
     *  <li>taskService</li>
     *  <li>runtimeManager</li>
     * </ul>
     * @param identifier - identifier of the owner - usually RuntimeManager that allows the producer to filter out
     * and provide valid instances for given owner
     * @param params - owner might provide some parameters, usually KieSession, TaskService, RuntimeManager instances
     * @return list of listener instances (recommendation is to always return new instances when this method is invoked)
     */
    List<T> getEventListeners(String identifier, Map<String, Object>  params);
}

Beans implementing these two interfaces will be collected on runtime and consulted when building KieSession by RuntimeManager. See RuntimeManager section for more details on this.

A complete runnable example of application built with CDI can be found here.

RuntimeManager itself can be injected as CDI bean into any other CDI bean within the application. It has then requirement to get RungimeEnvironment properly produces to allow RuntimeManager to be correctly initialized. RuntimeManager comes with three predefined strategies and each of them gets CDI qualifier so it can be referenced:

Producer that was defined in Configuration section should be now enhanced with producer methods to provide RuntimeEnvironment

public class EnvironmentProducer { 
   
    //add 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 single producer method is capable of providing RuntimeEnvironment for all strategies of RuntimeManager by specifying all qualifiers on the method level.

Once complete producer is available, RuntimeManager can be injected into application's CDi bean

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

That's all what needs to be configured to make use of CDI power with jBPM.

As an alternative to DeploymentService, RuntimeManagerFactory can be injected and then RuntimeManager instance can be created manually by the application. In such case EnvironmentProducer stays same as for DeploymentService and following is an example of simple 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();     
    }

}

jBPM can be configured in many ways with Spring though the two most frequenlty used approaches are:

While both approaches are tested and valid, which one to chose is a matter of the system functionaltiy. Before selecting one of the approache the most important question to ask is:

Will my system run multiple runtime managers at the same time?

If the asnwer to this question is no, then go ahead with direct Runtime Manager API as it will be the simplest way to use jBPM within your application. But when answer is yes, then go ahead with jbpm services as they encapsulate runtime manager API with best practices by providing dynamic runtime environment for your BPM logic - also known as execution server.

This is the standard (and the simplest) way to get up and running with jBPM in your application. You only configure it once and run as part of the application. With the RuntimeManager usage, both process engine and task service will be managed in complete synchronization, meaning there is no need from end user to deal with "plumbing" code to make these two work together.

To provide spring based way of setting up jBPM, few factory beans where added:

FactoryBeans provide standard way to configure Spring application spring xml though there are not custom spring xml tags equivalent for them.

Following section aims at giving complete spring configuration for single runtime manager wihtin spring application context.

  1. Setup entity manager factory and transaction manager:

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

    with this we have ready persistence configuration that gives us:

  2. Configure resource that we are going to use - business process

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

    this configures single process that will be available for execution - sample.bpmn that will be taken from class path. This is the simplest way to get your processes included when trying out jbpm.

  3. Configure RuntimeEnvironment with our infrastructure (entity manager, transaction manager, 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>
    

    that gives us default runtime environment ready to be used to create instance of a RuntimeManager.

  4. Create RuntimeManager with the environment we just setup

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

    with just four steps you are ready to execute your processes with Spring and jBPM 6, utilizing EntityManagerFactory and JTA transaction manager.

    Complete spring configuration file can be found here.

This is just one configuration setup that jBPM 6 supports - JTA transaction manager and EntityManagerFactory, others are:

  • JTA and SharedEntityManager

  • Local Persistence Unit and EntityManagerFactory

  • Local Persistence Unit and SharedEntityManager

For more details about difference configuration options look at the example configuration files and test cases.

In case more dynamic nature is required in your Spring application then more appropriate could be to build up so called execution server based on jbpm services. jBPM services has been designed in a way to make them framework agnostic and in case framework specific addons are required they will be brought by additional module. So the code logic of the services is embedded in jbpm-kie-services. These are pure java services and by that can be easily consumed by Spring application.

Dynamic nature means that processes (And other assets like data model, rules, forms, etc) can be added and removed without restarting application.

There is almost no code involved to completely configure jBPM services in spring besides single interface that needs to be implemented - IdentityProvider that depends on your security configuration. One built with Spring Security can be like following though it might not cover all features one can have for Spring application.


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

}

As usual, first thing to start with is transaction configuration:


<context:annotation-config />
<tx:annotation-driven />
<tx:jta-transaction-manager />                

<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" />

Next configuration of JPA and persistence follows:


<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/group information 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"/>

Configure runtime manager factory that is Spring context aware and by that can interact with spring container in correct way and supporting services (transactional command service and task service)


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

And this is all is needed to build fully featured execution server with Spring and jBPM services. A complete Spring web application with this setup can be found here.

jBPM since version 6.2 provides out of the box integration layer with Enterprise Java Beans (EJB) for both local and remote interaction.

Ejb services are brought by following modules:

EJB layer is based on jbpm services and thus provides almost same capabilities as the core module though there are some imiliations when it comes to remote interfaces. Main difference is for the DeploymentService that has been limited for remote ejb service to following methods:

Main rationale behind is to avoid returning runtime objects such as RuntimeManager over EJB remote as it won't bring any value because it will be "disconnected" state.

All other services do provide exact same set of functionality as core module.

Ejb services as an extension of core services provide EJB based execution semantic and based on various EJB specific features.

Transactions

Transaction is managed by EJB container thus there is no need to setup any sort of transaction manager or user transaction within application code.

Identity provider

Identity provider by default is backed by EJBContext and will rely on caller principal information for both name and roles. When inspecting IdentityProvider interface there are two methods related to roles:

This means that ejb must be secured according to JEE security practices to authentiate and authorize users so valid information will be available. In case no authentication/authorization is configured for EJB services an anonymous user is always assumed.

In addition to that, EJB services acept CDI sytly injection for IdentityProvider in case another (non ejb) security model is used. Simply create valid CDI bean that implements org.kie.internal.identity.IdentityProvider and make it available for injection with application and such implementation will take precedence over EJBContext based identity provider.

Deployment synchronization

Deployment synchronization is enabled by default and will attempt to synchronize any deployments every 3 seconds. It is implemented as ejb singleton with container managed concurrency and lock type set to write. Under the covers it utilizes EJB TimerService to schedule the synchronization jobs.

EJB Scheduler Service

jBPM uses scheduler service to deal with time based activities such as timer events, deadlines, etc. When running in EJB environment and EJB Timer Service based scheduler will be used. It will be automatically registered for all instances of RuntimeManager. When it comes to cluster support application server specific configuration might be required.

UserGroupCallback and UserInfo selection

UserGroupCallback and UserInfo might differ for various applications and thus should be sort of pluggable. With EJB we could not make it directly available for injections as they could not be injected with common type so there is another mechanism that allows to select one of provided out of the box implementation or to give a custom one. This mechanism is based on ssytem properties:

System properties can either be added to the startup configuration of the server (jvm) which is recommended or be set programmatically before services will be used - for example with custom @Startup bean that will configure it properly for selected callback and user info.

A example application that utilizes EJB services can be found here.

Remote EJB services are defined as dedicated remote interfaces that extends core services:

These can be used similar way as local interfaces except for handling custom types. Custom types can be defined:

Globally available types do not require any special handling as they will be available for EJB container when remote requests are handled - marshalling of incoming data. Though local custom types won't be visible by default to EJB container as they are not on application classpath. Thus special handling of such types is required.

EJB services provides easy yet rather powerful mechanism to resolve the issue - it comes with two additional types:

These special objects will perform eager serialization to bytes using ObjectInputStream to remove the need of serialization from the EJB client/container. Though it might be worse in case of performance it does overcome much more complecated handling of class loaders on EJB container side to allow use of custom types defined in the project.

Here is an example code needed to work with local types and remote EJB:

// 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 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);

Similar way RemoteObject can be used for example to send evnet to process instance:


// send event with 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);

These illustrates how to wrap custom data when interacting with remote EJB services. Next section will introduce how to make a connection to a remote service vai client code.

Remote client support is provided by implemetation of ClientServiceFactory interface that is facede for application server specific code:


/**
 * Generic service factory used for remote look ups 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;
}

Implementations can be dynamically registered using ServiceLoader mechanism and by default there is only one available for JBoss AS/EAP/Wildfly. Each ClientServiceFactory must provide name which will be used to register it within the client registry so it can be then easily looked up.

Here is a code used to get hold of default JBoss based remote client:


// get hold of valid client service factory
ClientServiceFactory factory = ServiceFactoryProvider.getProvider("JBoss");

// application is the name known to application server aka module name
String application = "sample-war-ejb-app";

// get given service out of the factory
DeploymentServiceEJBRemote deploymentService = factory.getService(application, DeploymentServiceEJBRemote.class);

With service available all know to its interface methods are ready to be used.

When working with JBoss AS and remote client you can add 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.2.0.Final</version> <!-- use valid version for the server you run on -->
  <optional>true</optional>
  <type>pom</type>
</dependency>

All core jBPM JARs (and core dependencies) are OSGi-enabled. That means that they contain MANIFEST.MF files (in the META-INF directory) that describe their dependencies etc. These manifest files are automatically generated by the build. You can plug these JARs directly into an OSGi environment.

OSGi is a dynamic module system for declarative services. So what does that mean? Each JAR in OSGi is called a bundle and has its own Classloader. Each bundle specifies the packages it exports (makes publicly available) and which packages it imports (external dependencies). OSGi will use this information to wire the classloaders of different bundles together; the key distinction is you don't specify what bundle you depend on, or have a single monolithic classpath, instead you specify your package import and version and OSGi attempts to satisfy this from available bundles.

It also supports side by side versioning, so you can have multiple versions of a bundle installed and it'll wire up the correct one. Further to this Bundles can register services for other bundles to use. These services need initialisation, which can cause ordering problems - how do you make sure you don't consume a service before its registered? OSGi has a number of features to help with service composition and ordering. The two main ones are the programmatic ServiceTracker and the XML based Declarative Services. There are also other projects that help with this; Spring DM, iPOJO, Gravity.

The following jBPM JARs are OSGi-enabled: