Recently I came accross JBPM and wanted to start with some small application. It was easy to download latest JBPM package, install it and import examples into Eclipse to check the stuff. However, I am more to Maven so I wanted to create the application based with this setup. The following paragraphs are the way how to build mavenized standalone application that makes use of JBPM.

 

Step 1: Make simple Maven project

First of all, create simple maven application:

 

mvn -DgroupId=com.example -DartifactId=jbpm-standalone -Dversion=1.0 archetype:generate

 

Select archetype named maven-archetype-quickstart and confirm creation. By this you'll get basic maven project. Now, import the project as the existing Maven project into Eclipse and see what's in. Pretty simple and boring so far

 

 

Step 2: Update dependencies

To make use of JBPM in this maven project, we need to import dependencies that are necessary for the application to compile and run. I finished with the bellow listed POM.

 

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>jbpm-standalone</artifactId>
    <version>1.0</version>
    <packaging>jar</packaging>

    <name>jbpm-standalone</name>
    <url>http://jboss.org</url>

    <properties>
        <repository.jboss.org.url>http://repository.jboss.org/maven2</repository.jboss.org.url>
        <repository.maven2.dev.java.net.url>http://download.java.net/maven/2/</repository.maven2.dev.java.net.url>
        <repository.ibiblio.url>http://www.ibiblio.org/maven2</repository.ibiblio.url>
        <repository.jboss.org.nexus.url>https://repository.jboss.org/nexus/content/groups/public-jboss</repository.jboss.org.nexus.url>

        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <jdk.debug>true</jdk.debug>
        <jdk.optimize>false</jdk.optimize>
        <jdk.source>1.6</jdk.source>
        <jdk.target>1.6</jdk.target>

        <version.jbpm>4.4</version.jbpm>
        <version.xerces>2.9.1</version.xerces>
        <version.javax.mail>1.4.1</version.javax.mail>
        <version.slf4j>1.6.1</version.slf4j>
        <version.testng>5.14</version.testng>
        <version.javassist>3.4.GA</version.javassist>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.jbpm.jbpm4</groupId>
            <artifactId>jbpm-api</artifactId>
            <version>${version.jbpm}</version>
        </dependency>
        <dependency>
            <groupId>org.jbpm.jbpm4</groupId>
            <artifactId>jbpm-jpdl</artifactId>
            <version>${version.jbpm}</version>
        </dependency>
        <dependency>
            <groupId>org.jbpm.jbpm4</groupId>
            <artifactId>jbpm-pvm</artifactId>
            <version>${version.jbpm}</version>
        </dependency>
        <dependency>
            <groupId>org.jbpm.jbpm4</groupId>
            <artifactId>jbpm-log</artifactId>
            <version>${version.jbpm}</version>
        </dependency>
        <dependency>
            <groupId>org.jbpm.jbpm4</groupId>
            <artifactId>jbpm-db</artifactId>
            <version>${version.jbpm}</version>
        </dependency>
        <dependency>
            <groupId>xerces</groupId>
            <artifactId>xercesImpl</artifactId>
            <version>${version.xerces}</version>
        </dependency>
        <dependency>
            <groupId>javax.mail</groupId>
            <artifactId>mail</artifactId>
            <version>${version.javax.mail}</version>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>${version.testng}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${version.slf4j}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-jdk14</artifactId>
            <version>${version.slf4j}</version>
        </dependency>
        <dependency>
            <groupId>javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>${version.javassist}</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.1</version>
                <configuration>
                    <source>${jdk.source}</source>
                    <target>${jdk.target}</target>
                    <encoding>utf-8</encoding>
                    <debug>${jdk.debug}</debug>
                    <optimize>${jdk.optimize}</optimize>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.1</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>java</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <mainClass>com.example.App</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <repositories>
        <repository>
            <id>repository.jboss.org</id>
            <name>JBoss Repository</name>
            <url>${repository.jboss.org.url}</url>
            <layout>default</layout>
        </repository>
        <repository>
            <id>repository.maven2.dev.java.net</id>
            <name>Java.net Repository for Maven</name>
            <url>${repository.maven2.dev.java.net.url}</url>
            <layout>default</layout>
        </repository>
        <repository>
            <id>repository.ibiblio</id>
            <name>IBiblio Maven Repository</name>
            <url>${repository.ibiblio.url}</url>
            <layout>default</layout>
        </repository>
    </repositories>
</project> 

 

Step 3: Define simple workflow

For JBPM to run, you must have some process to execute. It is not any super extensive super complex process, but is just sufficient for the starter app. Bellow are the steps of the process:

  1. Starts ...
  2. Greets ...
  3. Waits in intermediate state ...
  4. Prints that it is ending ...
  5. Ends.

 

... and the the process file itself ...

 

 

<?xml version="1.0" encoding="UTF-8"?> 
<process name="Activity" xmlns="http://jbpm.org/4.4/jpdl"> 
    <start g="27,51,80,40">
        <transition g="-18,4" name="greet" to="say hello" />
    </start>
 
    <java class="com.example.OutputActivity" g="141,49,87,50" method="say"
        name="say hello">
        <arg>
            <string value="Hello!" />
        </arg>
        <transition g="-28,4" name="wait for end" to="proceed to end" />
    </java>

    <state g="312,47,92,52" name="proceed to end">
        <transition g="-23,4" name="to ending" to="ending" />
    </state>

    <java class="com.example.OutputActivity" g="465,48,92,52" method="say"
        name="ending">
        <arg>
            <string value="Bye!" />
        </arg>
        <transition g="-19,4" name="to end" to="end" />
    </java>

    <end g="619,51,80,40" name="end" />

</process>

 

The class that is executed by the process is shipped in the archive attached to this post. It is very simple...

 

Step 4: Configuration files

 

Each JBPM-enabled application needs specific configuration file in the classpath. By default, the name is jbpm.cfg.xml, and we will keep this name as well. The goal of this configuration file is to set up various services that JBPM makes use of during execution. The details of this configuration are described in JBPM configuration. Here we focus only on setting up the basic app. So, the configuration file, that gets placed into src/main/resources looks like this:

 

<?xml version="1.0" encoding="UTF-8"?>
<jbpm-configuration>
 
 <import resource="jbpm.default.cfg.xml" />
 <import resource="jbpm.tx.hibernate.cfg.xml" />
 <import resource="jbpm.jpdl.cfg.xml" />
 <import resource="jbpm.identity.cfg.xml" />
 
</jbpm-configuration>

 

In addition, as JBPM makes use of Hibernate to offload data about processes into database, so the hibernate configuration is necessary as well. Here, we stick to the simplest possible configuration - put data into HSQLDB. For this we need file jbpm.hibernate.cfg.xml in the same location mentioned above. The contents is the following:

 

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
          "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
          "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
  <session-factory>
  
     <property name="hibernate.dialect">org.hibernate.dialect.HSQLDialect</property>
     <property name="hibernate.connection.driver_class">org.hsqldb.jdbcDriver</property>
     <property name="hibernate.connection.url">jdbc:hsqldb:mem:.</property>
     <property name="hibernate.connection.username">sa</property>
     <property name="hibernate.connection.password"></property>
     <property name="hibernate.hbm2ddl.auto">create-drop</property>
     <property name="hibernate.format_sql">true</property>
     
     <mapping resource="jbpm.repository.hbm.xml" />
     <mapping resource="jbpm.execution.hbm.xml" />
     <mapping resource="jbpm.history.hbm.xml" />
     <mapping resource="jbpm.task.hbm.xml" />
     <mapping resource="jbpm.identity.hbm.xml" />
     
  </session-factory>
</hibernate-configuration>

 

Step 5: The code

The last step is to setup process engine, command it to execute and go through states. This is done in couple simple steps, that are listed and explained in the code snippet bellow:

 

package com.example;
 
import org.jbpm.api.Configuration;
import org.jbpm.api.Execution;
import org.jbpm.api.ExecutionService;
import org.jbpm.api.ProcessEngine;
import org.jbpm.api.ProcessInstance;
import org.jbpm.api.RepositoryService;
 
public class App {
 
    public static void main(String[] args) {
        /*
         * Create process engine from configuration. Here the configuration
         * object represents default jbpm.cfg.xml file.
         */
        ProcessEngine processEngine = Configuration.getProcessEngine();
 
        /*
         * Repository service provides access to resources that are known to
         * JBPM, including loaded processes.
         */
        RepositoryService repositoryService = processEngine.getRepositoryService();
 
        /*
         * Execution service is the handle to execute state transitions in
         * process engine.
         */
        ExecutionService executionService = processEngine.getExecutionService();
 
        /*
         * Now query repository service for the process that we have created and
         * deploy it.
         */
        repositoryService.createDeployment().addResourceFromClasspath(
                "process.jpdl.xml").deploy();
 
        /*
         * Start the process instance with the name of the process. By starting
         * process, new ProcessInstance object is created. In our example flow,
         * after the initial state, there is 'java' state that gets executed and
         * the transition continues to next 'state' where it waits to be
         * commanded.
         */
        ProcessInstance processInstance = executionService
                .startProcessInstanceByKey("Activity");
 
        /*
         * We are now in 'state' named 'proceed to end'. We must ask process
         * instance to find active execution in this particular state.
         */
        Execution execution = processInstance
                .findActiveExecutionIn("proceed to end");
 
        /*
         * Finally we ask execution service to signal that transition from the
         * state shall occur. By this, the rest of the process is executed and
         * the flow ends.
         */
        processInstance = executionService.signalExecutionById(execution
                .getId());
    }
} 

 

Step 6: Package and execute

At this point, it is possible to compile the whole application and execute it. You can do that by running following command (it both build the app and executes it). Note that some of the files were not listed here, but are included in the takeaway archive bellow this post..

 

mvn clean package exec:java

 

exec:java is execution of the exec-maven-plugin that runs com.example.App class. If you change the name of the main class, you shall change also the configuration of the plugin in pom.xml.

 

After executing it, you shall see lots of listing of various messages with text Hello! and Bye! at the very end. If you see that, you've succeeded!

 

Takeaway

Attached to this post, there is archive with project just described. It is ready Eclipse project that you just need to execute. In addition, there is pair of launchers, one that build the application and one that executes it. You will find them in the "Run configurations".