Building your BPM applications with Bonita Runtime – Part 3

ttoine's picture
Blog Categories: 

As you may know, process based applications generated with Bonita Open Solution Studio and Bonita Open Solution User Experience use Bonita Runtime. Bonita Runtime is an open source BPM engine written in Java. It is a very powerful and lightweight library that allows you to define, deploy, execute and manage your processes.

If you don't want to leverage the existing interfaces of the process based applications and User Experience to interact with the Bonita Runtime and prefer to do it the hard way :-) to and implement your own application and interfaces directly, this series of articles will give you basic knowledge to get started quickly.

Bonita Runtime has been designed to provide a lot of flexibility through service injection. It is completely configurable using an XML file called "environment". This configuration describes all services used. You can change all of them or replace them with your own implementation.

In addition, Bonita Runtime is non intrusive, meaning it requires only Java. You can install it in any JVM of your choice, in any web/JEE container or use it as a simple Java library.

Another cool feature of Bonita Runtime is support for command injection that allows you to create your own operations in the Runtime.

In this series of articles, I'm going to deal with the following topics:

  1. Understanding object models
  2. Using APIs
  3. Configuring the Runtime
  4. Using EJBs
  5. Using commands
  6. Setting up your maven project and getting started with an example

If you missed Part 1, you can read it here. If you missed Part 2, you can read it here.

In Part 3, I'm going to deal with:

  1. Understanding object models
  2. Using APIs
  3. Configuring the Runtime
  4. Using EJBs
  5. Using commands
  6. Setting up your maven project and getting started with an example

5 - Using commands

Bonita Runtime is providing is very useful mechanism called 'Commands' which allows developers to write their own commands inside the Bonita Runtime Server. A command is a Java class implementing org.ow2.bonita.util.Command interface. There is only one operation to implement:

T execute(org.ow2.bonita.env.Environment environment) throws Exception;

To execute a command, you have to make it available in both client and server side classpath. You can do that either by

  • having the jar file containing the command available in the classpath (Env property or common lib folder in a web container for example)
  • including the command compiled class in the deployed bar file
  • deploying the jar file containing the command inside bonita class laoder using ManagementAPI

A command has many benefits and can be used for different purposes. A command is executed on the server side and you have access to the entire runtime inside (I mean not only APIs but all internal objects, services...). Let me give you the reasons why you could use Commands:

  1. Transaction: you want that a sequence of APIs calls to be executed in the same transaction. e.g. you want to create a new ProcessInstance and then set some variables. If setVariable call fails and you are doing that outside of a command, then your ProcessInstance remains "created". If the behaviour you want is "Do no create the ProcessInstance if something fails" then you have to write your own command with these 2 calls inside.
  2. Bug fixing: you noticed a bug in an API operation and you want to fix it without waiting for the next maintenance release. You can use all internal Object model and services to implement the needed feature without bug :-)
  3. Missing operation: APIs are offering a wide scope of operations but maybe you miss one important for your application? In that case, you can use the commands mechanism to implement your own feature just as explained in point 2.

You can find an example of commands in the community source explorer here.

6 - Setting up your maven project and getting started with an example

In this chapter, I am going to help you to get started with your first Bonita project using maven. In this example I am going to setup a very simple architecture without any container (neither Web neither EJB). Bonita Runtime is going to be used as a simple library. This example will illustrate basic operations of the APIs such as

  • deploy process
  • instantiate process
  • get task list
  • execute task
  • ...

This example will use a very simple process inside Bonita Open Solution Studio. This process contains only 2 human tasks, both assigned to the process instance initiator:

Let's start with our 5 minutes setup using my favourite IDE: Eclipse.

First of all, you can create a new simple maven project with the following parameters:

  • Group ID: org.bonitasoft.example
  • Artifact ID: runtime
  • Version: 1.0

I also created an additional source folder: src/main/resources in which I copied the exported process and 'jaas-standard.cfg' file (available in runtime distribution).

Once this maven project is created, we can add a maven dependency to server side libraries. You only have to a a new dependency to bonita-server artifact (notice I added a parent to get pre configured OW2 maven repository):

Our maven project is now configured, we can start coding! In this application, we are going to do the following operations:

  1. login
  2. deploy the bar file
  3. create a new process instance
  4. get task list and execute 'task1'
  5. assign 'task2' to another user and then back to the logged user
  6. execute 'task2'
  7. check created process instance is finished
  8. clean the database (delete all processes)
  9. logout
/** * Copyright (C) 2009 BonitaSoft S.A. * BonitaSoft, 31 rue Gustave Eiffel - 38000 Grenoble * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2.0 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program.  If not, see <>. */ package org.bonitasoft.example.runtime; import; import java.util.Collection; import; import org.ow2.bonita.facade.ManagementAPI; import org.ow2.bonita.facade.QueryRuntimeAPI; import org.ow2.bonita.facade.RuntimeAPI; import org.ow2.bonita.facade.def.element.BusinessArchive; import org.ow2.bonita.facade.def.majorElement.ProcessDefinition; import org.ow2.bonita.facade.runtime.ActivityState; import org.ow2.bonita.facade.runtime.InstanceState; import org.ow2.bonita.facade.runtime.TaskInstance; import org.ow2.bonita.facade.uuid.ProcessDefinitionUUID; import org.ow2.bonita.facade.uuid.ProcessInstanceUUID; import org.ow2.bonita.util.AccessorUtil; import org.ow2.bonita.util.BonitaConstants; import org.ow2.bonita.util.BusinessArchiveFactory; import org.ow2.bonita.util.SimpleCallbackHandler; /** * @author Charles Souillard */ public class App { private static final String LOGIN = "admin"; private static final String PASSWORD = "bpm"; private static final String BAR_FILE_PATH = "src/main/resources/"; private static final String JAAS_FILE_PATH = "src/main/resources/jaas-standard.cfg"; static { System.setProperty(BonitaConstants.JAAS_PROPERTY, JAAS_FILE_PATH); } public static void main( String[] args ) throws Exception { //get all used APIs final ManagementAPI managementAPI = AccessorUtil.getManagementAPI(); final RuntimeAPI runtimeAPI = AccessorUtil.getRuntimeAPI(); final QueryRuntimeAPI queryRuntimeAPI = AccessorUtil.getQueryRuntimeAPI(); //define non final fields Collection<TaskInstance> tasks = null; //login final LoginContext loginContext = new LoginContext("Bonita", new SimpleCallbackHandler(LOGIN, PASSWORD)); loginContext.login(); try { //deploy the bar file final File barFile = new File(BAR_FILE_PATH); final BusinessArchive businessArchive = BusinessArchiveFactory.getBusinessArchive(barFile); final ProcessDefinition process = managementAPI.deploy(businessArchive); final ProcessDefinitionUUID processUUID = process.getUUID(); //we can use the QueryDefinition API to get the process definition based on the process UUID //final QueryDefinitionAPI queryDefinitionAPI = AccessorUtil.getQueryDefinitionAPI(); //final ProcessDefinition deployedProcess = queryDefinitionAPI.getProcess(processUUID); //create a new process instance without initial variables, attachments... final ProcessInstanceUUID instanceUUID = runtimeAPI.instantiateProcess(processUUID); //get the task list and check it is not empty tasks = queryRuntimeAPI.getTaskList(ActivityState.READY); if (tasks.size() != 1) { throw new RuntimeException("Incorrect task list size! Actual size:" + tasks.size()); } //get the first task in the list which must be "task1" final TaskInstance task1 = tasks.iterator().next(); if (!"task1".equals(task1.getActivityName())) { throw new RuntimeException("Incorrect task name! Actual name:" + task1.getActivityName()); } //execute task1 and assign it to me runtimeAPI.executeTask(task1.getUUID(), true); //check we have a new task in the task list tasks = queryRuntimeAPI.getTaskList(ActivityState.READY); if (tasks.size() != 1) { throw new RuntimeException("Incorrect task list size! Actual size:" + tasks.size()); } //get the first task in the list which must be "task1" final TaskInstance task2 = tasks.iterator().next(); if (!"task2".equals(task2.getActivityName())) { throw new RuntimeException("Incorrect task name! Actual name:" + task2.getActivityName()); } //assign task2 to another user runtimeAPI.assignTask(task2.getUUID(), "john"); //check my tasklist is empty tasks = queryRuntimeAPI.getTaskList(ActivityState.READY); if (tasks.size() != 0) { throw new RuntimeException("Incorrect task list size! Actual size:" + tasks.size()); } //assign back task2 to admin and execute it runtimeAPI.assignTask(task2.getUUID(), LOGIN); runtimeAPI.executeTask(task2.getUUID(), true); //check process instance is finished final InstanceState instanceState = queryRuntimeAPI.getProcessInstance(instanceUUID).getInstanceState(); if (!InstanceState.FINISHED.equals(instanceState)) { throw new RuntimeException("Incorrect state for process instance! Actual state:" + instanceState); } System.err.println("\n**********\n"); System.err.println("Application executed successfully!"); System.err.println("\n**********\n"); } finally { //delete all processes to be able to run this App many times without getting "Process already deployed" exception //this MUST NOT be done in production environment! managementAPI.deleteAllProcesses(); //logout loginContext.logout(); } } }

You can now launch this application like a Java Main: right click on the backgroung and 'Run As -> Java Application'.

If you want to use this example as a basis for your examples, you can download it here:

Compressed example

I hope this series of articles help you to get started with Bonita Runtime ... And remember have fun with Bonita!