Business Process Modeling with Activiti and BPMN 2.0

September 15th, 2013 by

Having tried a bunch of workflow engines and business processing management platforms I now have given the Activiti framework a try.

I immediately liked the good test support using annotations and jUnit test rules, a straight API and the good Eclipse IDE integration as well as I liked the Activiti Explorer and the Activiti REST Application and the feeling to achieve quick results with less effort when using this framework.

In the following tutorial I’m going to an example BPMN process for an issue request process using different components like Groovy script tasks, service tasks written in Java, E-Mail service tasks and form builders.

Finally I’m showing how to write a full integration test for the process and how to run the process in a web application environment using the Activiti Explorer application.


 

BPMN Model of the issue request process

BPMN Model of the issue request process

Requirements

These are the requirements to run the following examples:

  • Java JDK 6
  • Maven 3
  • Java Web Container e.g. Tomcat 7, Jetty (optional – only needed if you want to try the Activiti Explorer)

Activiti BPMN Designer for Eclipse (optional)

If using Eclipse IDE the Activiti BPMN Designer for Eclipse might be handy but it is not required for the following tutorial.

The plugin allows us to create new Activiti projects with all dependencies needed quick and to model our workflow using a neat designer/editor.

Installing the plugin is easy using the following update site:

http://activiti.org/designer/update

This is how the designer in Eclipse looks like:

Activiti BPMN Designer for Eclipse

Activiti BPMN Designer for Eclipse

Project Setup, Maven, Dependencies

Nevertheless we’re able to set up a simple Maven project by adding the Alfresco Maven repository and the following dependencies to our pom.xml.

Basic Dependencies

Besides the two dependencies for the activiti engine and the spring integration I’m adding groovy here because we’ll be using a Script Task in our process, implemented in Groovy and h2 because I’ll be using an in-memory database in the following example .. so this is the corresponding excerpt from my pom.xml.

<properties>
	<activiti-version>5.11</activiti-version>
</properties>
 
<dependencies>
	<dependency>
		<groupId>org.activiti</groupId>
		<artifactId>activiti-engine</artifactId>
		<version>${activiti-version}</version>
	</dependency>
	<dependency>
		<groupId>org.activiti</groupId>
		<artifactId>activiti-spring</artifactId>
		<version>${activiti-version}</version>
	</dependency>
	<dependency>
		<groupId>org.codehaus.groovy</groupId>
		<artifactId>groovy-all</artifactId>
		<version>2.0.4</version>
	</dependency>
	<dependency>
		<groupId>com.h2database</groupId>
		<artifactId>h2</artifactId>
		<version>1.3.168</version>
	</dependency>
</dependencies>
 
<repositories>
	<repository>
		<id>Activiti</id>
		<url>http://maven.alfresco.com/nexus/content/repositories/activiti</url>
	</repository>
</repositories>

Dependencies for Testing

The following dependencies are needed for testing: jUnit and Hamcrest shouldn’t be a surprise (I never go without them) but I’m also adding greenmail because we need a slim SMTP server to test the MailTasks from out process.

If you’re interested in more detailed information about using GreenMail, please feel free to have a look at my article: “Integration Testing IMAP, SMTP and POP3 with GreenMail“.

<dependencies>
	[..]
	<dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
		<version>4.11</version>
	</dependency>
	<dependency>
		<groupId>org.hamcrest</groupId>
		<artifactId>hamcrest-all</artifactId>
		<version>1.3</version>
		<scope>test</scope>
	</dependency>
	<dependency>
		<groupId>com.icegreen</groupId>
		<artifactId>greenmail</artifactId>
		<version>1.3</version>
		<scope>test</scope>
	</dependency>
</dependencies>

Modeling Issue Request Process in BPMN

We’re going to model the following workflow using our tool of choice .. e.g. Activiti BPMN Designer

Visual Model

This is the visual model of our process

BPMN Model of the issue request process

BPMN Model of the issue request process

Model Description

Now what happens in this workflow?

  • A user starts a new issue request process and is forced to enter some basic information regarding this issue: summary, description, priority and contact e-mail
  • Then a ScriptTask implemented in Groovy filters summary and description for bad words, replacing them with a string “xxx”
  • Then an exclusive or XOR Gateway delegates the flow to a user task depending on the priority:
  • If the priority is set to critical, the flow is delegated to a user task assigned to a candidate group named itsupport-critical to approve or deny the critical request
  • Otherwise the candidate group named itsupport is chosen to approve or deny the noncritical request
  • Then another XOR Gateway directs the flow based on the approval
  • If the issue request is not approved, a denial e-mail is send to the initiator
  • If the request is approved, a confirmation e-mail is send to the initiator and a ServiceTask is executed
  • The ServiceTask is implemented as a Java class and writes the issue request information to a file named issues.txt in the current scope’s temporary directory

Modeling Process Steps

The process is modeled using XML and I’ll explain the different steps in detail – the full process XML file is available on my Bitbucket repository here.

Process Definition

This is the process frame definition, we’re just defining an id for this process and allow users of the group service to initiate the process.

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn"
	xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
	xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema"
	expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
	<process id="issueRequestProcess" name="Issue Request Process"
		isExecutable="true">
		<extensionElements>
			<activiti:potentialStarter>
				<resourceAssignmentExpression>
					<formalExpression>group(service)</formalExpression>
				</resourceAssignmentExpression>
			</activiti:potentialStarter>
		</extensionElements>
	[..]
	</process>
</definitions>

Start Event and Issue Request Form

When the process starts, the initiating user is forced to enter several values to begin the issue request process.

We’re using Activiti extensions to specify form elements, their type and if they are mandatory or not.

In this case, the user is forced to enter a summary, a priority and his e-mail – the description is optional.

<startEvent id="startEvent" name="Start"
	activiti:initiator="initiator" activiti:formKey="newIssueForm">
	<extensionElements>
		<activiti:formProperty id="summary" name="Issue Summary"
			type="string" variable="summary" required="true"></activiti:formProperty>
		<activiti:formProperty id="description"
			name="Detailed Issue Description" type="string" variable="description"></activiti:formProperty>
		<activiti:formProperty id="priority" name="Issue Priority"
			type="enum" variable="priority" required="true">
			<activiti:value id="low" name="low"></activiti:value>
			<activiti:value id="medium" name="medium"></activiti:value>
			<activiti:value id="critical" name="critical"></activiti:value>
		</activiti:formProperty>
		<activiti:formProperty id="email"
			name="Please enter your E-Mail address for feedback" type="string"
			variable="email" required="true"></activiti:formProperty>
	</extensionElements>
</startEvent>

This is the properties view of the start event in the Activiti BPMN Designer

Start event in the Activiti BPMN Designer

Start event in the Activiti BPMN Designer

ScriptTask using Groovy to Filter Bad Words

Now we’re adding a Groovy Script Task to the workflow to filter some badwords in summary and description and replace them with the string “xxx”.

The sequence flow connects the script task with the start event – I’ll omit the sequence flow for the following process steps as long as they do not contain a filter expression.

<scriptTask id="swearWordFilterScriptTask" name="Swear Word Filter Script Task"
	scriptFormat="groovy" activiti:autoStoreVariables="true">
	<script>def badwords = 'fuck|bla'
		def replacement = 'xxx'
 
		def
		filteredSummary = summary.replaceAll(badwords, replacement)
		def
		filteredDescription = description.replaceAll(badwords, replacement)
 
		execution.setVariable('summary', filteredSummary)
		execution.setVariable('description', filteredDescription)
	</script>
</scriptTask>
<sequenceFlow id="processSwearWords" sourceRef="startEvent"
	targetRef="swearWordFilterScriptTask"></sequenceFlow>

This is the script task in the Activiti Designer

Groovy Script Task in the Activiti Designer

Groovy Script Task in the Activiti Designer

XOR-Gateway to segregate critical and noncritical Requests

The following XOR gateway opens two different paths depending on the state of the priority property. The gateway with id=joinPrioritySelectionGateway joins both paths afterwards.

In the next step I’ll demonstrate the implementation of the referenced user task for critical issues.

<exclusiveGateway id="priorityXorGateway"
	name="Exclusive Gateway Filtering by Priority"></exclusiveGateway>
<sequenceFlow id="criticalFlow" name="Flow for critical issues"
	sourceRef="priorityXorGateway" targetRef="approveCriticalIssue">
	<conditionExpression xsi:type="tFormalExpression"><![CDATA[${priority == "critical"}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="noncriticalFlow" name="Flow for noncritical issues"
	sourceRef="priorityXorGateway" targetRef="approveNormalIssue">
	<conditionExpression xsi:type="tFormalExpression"><![CDATA[${priority != "critical"}]]></conditionExpression>
</sequenceFlow>
<exclusiveGateway id="joinPrioritySelectionGateway"
	name="Exclusive Gateway"></exclusiveGateway>

UserTask to Approve Critical Issues

The following user task specifies a candidate group named itsupport-critical whose members are responsible for this task, shows the origin issue request information as read only values in a form to the assigned user and asks him to approve or deny the issue request.

<userTask id="approveCriticalIssue" name="Approve Critical Issue"
	activiti:candidateGroups="itsupport-critical">
	<extensionElements>
		<activiti:formProperty id="summary" name="Issue Summary"
			type="string" expression="${summary}" variable="summary" writable="false"></activiti:formProperty>
		<activiti:formProperty id="description" name="Issue Description"
			type="string" expression="${description}" variable="description"
			writable="false"></activiti:formProperty>
		<activiti:formProperty id="priority" name="Priority"
			type="string" expression="${priority}" variable="priority"
			writable="false"></activiti:formProperty>
		<activiti:formProperty id="approveRequest"
			name="Do you approve the issue request?" type="enum" variable="requestApproved"
			required="true">
			<activiti:value id="true" name="Yes"></activiti:value>
			<activiti:value id="false" name="No"></activiti:value>
		</activiti:formProperty>
	</extensionElements>
</userTask>

This is the user task in the Activiti Designer

UserTask for Request Approval in the Activiti BPMN Designer

UserTask for Request Approval in the Activiti BPMN Designer

XOR-Gateway to handle the Approval / Denial Path

The following exclusive gateway separates two paths depending on the approval or denial in the preceding workflow step.

As the non-approved path only contains one e-mail task, I’m going to describe only the issue-approved path declaration in the next step.

<sequenceFlow id="requestApprovedFlow" name="Request approved"
	sourceRef="exclusivegateway3" targetRef="confirmMailTask">
	<conditionExpression xsi:type="tFormalExpression"><![CDATA[${requestApproved == "true"}]]></conditionExpression>
</sequenceFlow>
<sequenceFlow id="requestDeniedFlow" name="Request Denied"
	sourceRef="exclusivegateway3" targetRef="denialMailTask">
	<conditionExpression xsi:type="tFormalExpression"><![CDATA[${requestApproved == "false"}]]></conditionExpression>
</sequenceFlow>

ServiceTask to  Send a Confirmation E-Mail

The following service task sends an e-mail to the issue request initiator.

<serviceTask id="confirmMailTask" name="Send confirmation mail"
	activiti:type="mail">
	<extensionElements>
		<activiti:field name="to">
			<activiti:expression>${email}</activiti:expression>
		</activiti:field>
		<activiti:field name="from">
			<activiti:string>support@hascode.com</activiti:string>
		</activiti:field>
		<activiti:field name="subject">
			<activiti:expression>Your inquiry regarding ${summary}</activiti:expression>
		</activiti:field>
		<activiti:field name="charset">
			<activiti:string>UTF-8</activiti:string>
		</activiti:field>
		<activiti:field name="text">
			<activiti:expression>Hello ${initiator},
 
your issue regarding ${summary} is being processed right now and you'll receive an update regarding this issue soon from our dev team.
 
Best regards</activiti:expression>
		</activiti:field>
	</extensionElements>
</serviceTask>

This is the mail task in the Activiti Designer

The E-Mail Service Task in the Activiti BPMN Designer

The E-Mail Service Task in the Activiti BPMN Designer

ServiceTask in Java to persist the Issue Request

The following example demonstrates how to implement a service task in Java.

This task simply writes some information from the issue request to a file named issues.txt in the temporary directory of the current scope.

Which directory this is might depend on your environment – when running the workflow in an integration test on my linux machine it is /tmp – running the task in the Activiti Explorer on a Tomcat 7 servlet container resolves to the temporary directory tomcat-directory/temp..

<serviceTask id="storeIssueTask" name="Persist Issue to IssueTracker"
	activiti:class="com.hascode.tutorial.PersistIssueTask"></serviceTask>

This is the Java class – we just need to implement the interface org.activiti.engine.delegate.JavaDelegate here:

package com.hascode.tutorial;
 
import java.io.File;
import java.io.FileWriter;
import java.util.Date;
 
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.JavaDelegate;
 
public class PersistIssueTask implements JavaDelegate {
	private final File issueTracker = new File(new File(
			System.getProperty("java.io.tmpdir")), "issues.txt");
 
	@Override
	public void execute(final DelegateExecution execution) throws Exception {
		String output = String.format(
				"(%s) New issue: %s Description: %s Priority: %s",
				new Date().toString(), execution.getVariable("summary"),
				execution.getVariable("description"),
				execution.getVariable("priority"));
		FileWriter writer = new FileWriter(issueTracker);
		writer.write(output);
		writer.close();
	}
 
}

This is the service task in the Activiti BPMN Designer

Java ServiceTask in the Activiti BPMN Designer

Java ServiceTask in the Activiti BPMN Designer

End Event

This is simply the end event to signal that the process is finished:

<endEvent id="endevent" name="End Event"></endEvent>

Writing Tests

The first thing coming to my mind when I’m playing around with a new framework is “how do I test this stuff?”

Luckily for us, Activiti integrates very nice with jUnit and allows us to bootstrap the rule engine and its environment using a jUnit rule and specifying the process to load for a test using a simple annotation.

I’m going to explain the single steps in detail here but please note that they’re all part of one test method in one test file .. if you’d like to view the complete test file, please feel free to have a look at my Bitbucket repository.

1. Test Setup

The @Rule ActivitiRule allows us to access all important managers, @Deployment(resources = “diagrams/IssueRequestProcess.bpmn”) loads the process definition from the given file.

In addition you might notice that I’m booting an instance of the GreenMail server to emulate an SMTP server running on port 3025.

In the activiti rule I’m referencing a special configuration file where I’ve specified to use an in-memory engine-configuration and to expect the e-mail server at port 3025 in src/test/resources/activiti-test.inmemory-cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans.xsd">
	<bean id="processEngineConfiguration"
		class="org.activiti.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration">
		<property name="databaseSchemaUpdate" value="true" />
		<property name="mailServerPort" value="3025" />
		<property name="jobExecutorActivate" value="false" />
	</bean>
</beans>

This is my quick approach for a test for the issue request workflow:

package it;
 
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.startsWith;
 
import java.io.File;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
import javax.mail.Message;
import javax.mail.internet.MimeMessage;
 
import org.activiti.engine.FormService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.TaskService;
import org.activiti.engine.form.FormProperty;
import org.activiti.engine.history.HistoricDetail;
import org.activiti.engine.history.HistoricFormProperty;
import org.activiti.engine.identity.Group;
import org.activiti.engine.identity.User;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.test.ActivitiRule;
import org.activiti.engine.test.Deployment;
import org.apache.commons.io.FileUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
 
import com.icegreen.greenmail.util.GreenMail;
import com.icegreen.greenmail.util.ServerSetupTest;
 
public class IssueRequestProcessTest {
	private static final String DESCRIPTION_VALUE = "When I'm adding articles to the basket and click on 'buy' I'm getting a 404 error. I hate your fucking shop!";
	private static final String DESCRIPTION_KEY = "description";
	private static final String SUMMARY_VALUE = "Website Error! Shop order failed";
	private static final String SUMMARY_KEY = "summary";
 
	File issueTracker = new File(
			new File(System.getProperty("java.io.tmpdir")), "issues.txt");
 
	GreenMail smtpServer = new GreenMail(ServerSetupTest.SMTP);
 
	@Rule
	public ActivitiRule activitiRule = new ActivitiRule(
			"activiti-test.inmemory-cfg.xml");
 
	@Before
	public void setUp() {
		smtpServer.start();
	}
 
	@After
	public void tearDown() {
		smtpServer.stop();
	}
 
	@Test
	@Deployment(resources = "diagrams/IssueRequestProcess.bpmn")
	public void shouldProcessCriticalIssueRequest() throws Exception {
		[..]
	}
}

2. Creating Users and Groups

Now we need some users and groups and we want to set the current process’ user:

  • One user named “Micha Kops” assigned to the group “service”
  • One user named “itguy” assigned to the group “itsupport-critical”
  • Set “Micha Kops” as the current user
// get a handle on the identity-service
IdentityService identityService = activitiRule.getIdentityService();
 
// create a new user to create a new request
User requester = identityService.newUser("Micha Kops");
identityService.saveUser(requester);
 
// create group service and assign the user to it
Group serviceGroup = identityService.newGroup("service");
identityService.saveGroup(serviceGroup);
identityService.createMembership(requester.getId(),
		serviceGroup.getId());
 
// create a new user for an it-support employee
User itguy = identityService.newUser("itguy");
identityService.saveUser(itguy);
 
// create a group it-support for critical issues
Group itSupportGroup = identityService.newGroup("itsupport-critical");
itSupportGroup.setName("IT Support for Critical Issues");
identityService.saveGroup(itSupportGroup);
 
// assign the user itguy to the group itsupport-critical
identityService.createMembership(itguy.getId(), itSupportGroup.getId());
 
// set requester as current user
identityService.setAuthenticatedUserId(requester.getId());

3. Test Process Definition

We can test the process definition like this:

// assert that the process definition does exist in the current
// environment
ProcessDefinition definition = activitiRule.getRepositoryService()
		.createProcessDefinitionQuery()
		.processDefinitionKey("issueRequestProcess").singleResult();
assertThat(definition, notNullValue());

4. Fill form data and initialize process

// get a handle on the form-service
FormService formService = activitiRule.getFormService();
 
// assert that our start form has four form fields
List<FormProperty> formProps = formService.getStartFormData(
		definition.getId()).getFormProperties();
assertThat(formProps.size(), equalTo(4));
 
// fill out the first form's fields
Map<String, String> requestFormProps = new HashMap<String, String>();
requestFormProps.put(SUMMARY_KEY, SUMMARY_VALUE);
requestFormProps.put(DESCRIPTION_KEY, DESCRIPTION_VALUE);
requestFormProps.put("email", "someguy@hascode.com");
requestFormProps.put("priority", "critical");
 
Date startDate = new Date();
 
// create a new process instance with given form params
ProcessInstance processInstance = formService.submitStartFormData(
		definition.getId(), requestFormProps);
assertThat(processInstance, notNullValue());

5. Test the Audit Module / Historic Data

// test the audit process, fetch historic data
List<HistoricDetail> historicFormProps = activitiRule
		.getHistoryService().createHistoricDetailQuery()
		.formProperties().orderByVariableName().asc().list();
 
// assert that the historic data corresponds to the form data that we've
// entered
assertThat(historicFormProps.size(), equalTo(4));
HistoricFormProperty historicSummary = (HistoricFormProperty) historicFormProps
		.get(0);
assertThat(historicSummary.getPropertyId(), equalTo(DESCRIPTION_KEY));
assertThat(historicSummary.getPropertyValue(),
		equalTo(DESCRIPTION_VALUE));
assertThat(historicSummary.getTime(), greaterThan(startDate));

6. Test the Bad-Words-Filter

// assert that the bad-words filter has filtered one bad word and
// replaced it with 'xxx'
assertThat(
		(String) activitiRule.getRuntimeService()
				.getVariable(processInstance.getProcessInstanceId(),
						DESCRIPTION_KEY),
		endsWith("I hate your xxxing shop!"));

7. Approve the Request

// get a handle on the task service
TaskService taskService = activitiRule.getTaskService();
 
// seach for a task for candidate-group 'itsupport-critical'
Task approveCriticalIssueTask = taskService.createTaskQuery()
		.processInstanceId(processInstance.getProcessInstanceId())
		.taskCandidateGroup(itSupportGroup.getId()).singleResult();
assertThat(approveCriticalIssueTask.getName(),
		equalTo("Approve Critical Issue"));
 
// claim the task for the user 'itguy'
taskService.claim(approveCriticalIssueTask.getId(), itguy.getId());
 
// approve the request and complete the task
Map<String, Object> taskParams = new HashMap<String, Object>();
taskParams.put("requestApproved", "true");
taskService.complete(approveCriticalIssueTask.getId(), taskParams);

8. Test E-Mail Handling

// now we should have received an email..
smtpServer.waitForIncomingEmail(5000L, 1);
MimeMessage[] messages = smtpServer.getReceivedMessages();
assertThat(messages.length, equalTo(1));
MimeMessage mail = messages[0];
 
// verify email content
assertThat(mail.getSubject(), equalTo("Your inquiry regarding "
		+ SUMMARY_VALUE));
assertThat((String) mail.getContent(), startsWith("Hello Micha Kops,"));
assertThat((String) mail.getContent(), containsString(SUMMARY_VALUE));
assertThat(mail.getRecipients(Message.RecipientType.TO)[0].toString(),
		equalTo("\"someguy@hascode.com\" <someguy@hascode.com>"));

9. Test File Write from Java Service Task

// assert that the java service-task has written output to the file
assertThat(issueTracker.exists(), equalTo(true));
assertThat(
		FileUtils.readFileToString(issueTracker),
		endsWith("New issue: Website Error! Shop order failed Description: When I'm adding articles to the basket and click on 'buy' I'm getting a 404 error. I hate your xxxing shop! Priority: critical"));

Setting up an SMTP Server

If you need a quick emulation for an SMTP server other than GreenMail as described above to test a process with an e-mail task there are different ways to achieve this.

If you’ve got Python installed and if you’re using Linux there is a good chance that it is already installed on your system you simply need to run the following command (specify the port according to your needs)

python -m smtpd -n -c DebuggingServer localhost:1025

Otherwise you might want to have a look at projects like Apache James.

Installing and Running the Process using the Activiti Explorer Application

If you want to test the process in a near-real environment you should spend some minutes to give the Activiti Explorer Web Application a try.

To quote the Activiti.org Website:

“Activiti Explorer is a web application that provides access to the Activiti Engine runtime for all users of the system.   It includes task management, process instance inspection, management features and viewing reports based on statistical history data.”

There are only a few steps needed to setup the environment:

Build the JAR/BAR Archive

When using the Eclipse plugin, simply click on “Create deployment artifacts” – this will create two files in a directory named deployment – the .bar file contains the process definition, the jar file contains the generated java classes.

Otherwise run mvn package to create the jar file

Download Activiti Explorer

Download the web application from the Activiti Download Website.

Deploy Activiti Explorer to the Servlet Container

Deploy the war-file to your servlet container e.g. Tomcat/Jetty and add the generated JAR file from the project to the WEB-INF/lib directory in the deployed activiti explorer web application – you might to restart the container afterwards.

Now you should be able to access the application at a location like http://localhost:8080/activiti-explorer/index.html

Create Users and Groups

Log into the application with the useraccount login=kermit, password=kermit – it’s a preconfigured administrator user account.

You need to add two groups: itsupport-critical and service

Add one user to each group.

Creating a new user in the Activiti Explorer

Creating a new user in the Activiti Explorer

Creating a new group in the Activiti Explorer

Creating a new group in the Activiti Explorer

Assigning a user to a group in the Activiti Explorer

Assigning a user to a group in the Activiti Explorer

Deploy the Process

Go to Manage > Deployments > Add new and upload the bar file.

Imported Process Definition in the Activiti Explorer

Imported Process Definition in the Activiti Explorer

Adjust the Mail Settings

To adjust the expected port for the smtp server, simply go to the deployed web application and edit the WEB-INF/activiti-standalone-context.xml and add the following line to the configuration:

<bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
	[..]
	<property name="mailServerPort" value="1025"/>
</bean>

Start SMTP Server

Start an SMTP server as described above and watch out to specify the corresponding port.

Initialize the Process

Log in with a user assigned to the group service, click on Processes > Issue Request Process , fill out the form and submit the request (chose priority=critical or you need to create another group “itsupport”  ;).

Walk through the Process

Now log in with a user assigned to the group itsupport-critical.

Under Tasks > Queued Tasks you should see a task, claim this task for you, approve the request and you should be able to see the outgoing e-mail in your smtp server and in the temporary directory there should be the file issues.txt containing the issue request information.

If you’re unsure, please feel free to have a look at the following screencast:

Screencast Running the Workflow

The following screncast shows the full workflow running in the Activiti Explorer.

  • First a user “Micha Kops” adds a new issue request to the system
  • He has marked the issue with a priority=critical
  • Therefore the task is delegated to the candidate group “itsupport-critical”
  • Now a user from this group logs in and claims the task
  • He approves the issue and an e-mail is send
  • The e-mail is captured running a slim python smtp server on localhost
  • Finally the file written by the service task is printed to the screen

View screencast on YouTube.

Tutorial Sources

Please feel free to download the tutorial sources from my Bitbucket repository, fork it there or clone it using Git:

git clone https://bitbucket.org/hascode/activiti-bpmn2-tutorial.git

Resources

Alternative: JBoss Drools

If you’re interested in a simple rule-engine, please feel free to have a look at the following article of mine: “Integrating the Drools Business Rules Management System in 5 Minutes“.

Article Updates

  • 2018-06-01: Embedded YouTube video removed (GDPR/DSGVO).
  • 2017-03-30: Link to my Drools rule engine article added.

Tags: , , , , , , ,

9 Responses to “Business Process Modeling with Activiti and BPMN 2.0”

  1. dubcio Says:

    Nice article, with lots of details. I was using the workflow foundation and try to find something similar in Java. This is firts article that I get the tool idea without coding. Thanks.

  2. Bao Says:

    This is by far the best activiti example on the web.
    Thank you.

  3. Bruno Says:

    Hy
    Great example.Thanks a lot!!!!

    As I am trying to integrate Activiti and Esper, do you plan to publish a tutorial about an example based on Activiti Eclipse designer, with Esper integration functionality?

  4. micha kops Says:

    Hi,

    thanks for your kind feedback! I had the idea to focus on BAM and CEP with Activiti and Esper but atm I have not written anything yet.

    If you need some basic examples I’d recomment reading Chapter 14.2 “Meeting the Esper framework” from Tijs Rademakers’ “Activiti in Action”.

    Cheers,

    Micha

  5. Antonio Says:

    Excelent example on how to use activiti.

    I have one question if you can help, how do I send an e-mail to the user that starts the process without having to ask the user to fill in the e-mail.

    I would like to send the e-mail to the registered user e-mail.

    Thanks again for the example.

    Best regards,

    António

  6. micha kops Says:

    Hi Antonio,

    thanks for the kind feedback! I think one way to resolve the initiator’s email address is to get the initiator first using the property activiti:initiator and afterwards use the identity service to lookup the user account.

    In a Java delegate you may obtain a reference on the identity service like this:
    public void execute(DelegateExecution execution) throws Exception {
    execution.getEngineServices().getIdentityService()...
    }

    Maybe it is possible to use initiator.properties.email to access the value, but I have not tried this one but it is sometimes referenced on the web.

  7. Hemamalini Says:

    Excellent tutorial

  8. khawla Says:

    Hi
    please i have a probleme that the definition is null all the time and i dont know why ??
    can someone help

  9. micha kops Says:

    Hi,

    is the definition null in your test? or in your runtime environment? could you please provide some more detailed information? :)

Search
Categories