Activiti BPM Tutorial – Process Engine

Expense Process Diagram

expense_process

Expense Process BPMN20

<?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="expenseProcess" name="The new and shiny expense process" isExecutable="true">
    <startEvent id="startevent1" name="Start"></startEvent>
    <userTask id="usertask1" name="Request Refund" activiti:assignee="kermit">
      <documentation>[Employee:${employee}, 
Amount: ${amount}]</documentation>
    </userTask>
    <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1"></sequenceFlow>
    <userTask id="usertask2" name="Handle Request" activiti:candidateGroups="accountancy">
      <documentation>${employee} has requested a refund amount ${amount}</documentation>
    </userTask>
    <userTask id="usertask3" name="Handle Request" activiti:candidateGroups="management">
      <documentation>${employee} has requested a refund of ${amount}</documentation>
    </userTask>
    <serviceTask id="mailtask1" name="Mail Task" activiti:type="mail">
      <extensionElements>
        <activiti:field name="to">
          <activiti:string><![CDATA[thysmichels@gmail.com]]></activiti:string>
        </activiti:field>
        <activiti:field name="from">
          <activiti:string><![CDATA[lc@lc.com]]></activiti:string>
        </activiti:field>
        <activiti:field name="subject">
          <activiti:string><![CDATA[Activity Mail]]></activiti:string>
        </activiti:field>
        <activiti:field name="html">
          <activiti:expression>${employee} has requested refund</activiti:expression>
        </activiti:field>
      </extensionElements>
    </serviceTask>
    <sequenceFlow id="flow6" sourceRef="mailtask1" targetRef="usertask3"></sequenceFlow>
    <endEvent id="endevent1" name="End"></endEvent>
    <exclusiveGateway id="exclusivegateway1" name="Exclusive Gateway"></exclusiveGateway>
    <exclusiveGateway id="exclusivegateway2" name="Exclusive Gateway"></exclusiveGateway>
    <sequenceFlow id="flow7" sourceRef="usertask3" targetRef="exclusivegateway2"></sequenceFlow>
    <sequenceFlow id="flow8" sourceRef="usertask2" targetRef="exclusivegateway2"></sequenceFlow>
    <sequenceFlow id="flow9" sourceRef="exclusivegateway2" targetRef="endevent1"></sequenceFlow>
    <sequenceFlow id="flow10" sourceRef="usertask1" targetRef="exclusivegateway1"></sequenceFlow>
    <sequenceFlow id="flow11" name="${employeeVerification==&quot;true&quot;}" sourceRef="exclusivegateway1" targetRef="mailtask1">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${employeeVerification=="true"}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow12" name="${employeeVerification==&quot;false&quot;}" sourceRef="exclusivegateway1" targetRef="usertask2">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${employeeVerification=="false"}]]></conditionExpression>
    </sequenceFlow>
    <boundaryEvent id="boundarytimer1" name="Timer" attachedToRef="usertask2" cancelActivity="true">
      <timerEventDefinition>
        <timeDuration>PT1S</timeDuration>
      </timerEventDefinition>
    </boundaryEvent>
    <sequenceFlow id="flow15" sourceRef="boundarytimer1" targetRef="startevent1"></sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_expenseProcess">
    <bpmndi:BPMNPlane bpmnElement="expenseProcess" id="BPMNPlane_expenseProcess">
      <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="132.0" y="290.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
        <omgdc:Bounds height="55.0" width="105.0" x="240.0" y="280.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2">
        <omgdc:Bounds height="55.0" width="105.0" x="462.0" y="390.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="boundarytimer1" id="BPMNShape_boundarytimer1">
        <omgdc:Bounds height="30.0" width="30.0" x="500.0" y="430.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="usertask3" id="BPMNShape_usertask3">
        <omgdc:Bounds height="55.0" width="105.0" x="640.0" y="180.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="mailtask1" id="BPMNShape_mailtask1">
        <omgdc:Bounds height="55.0" width="105.0" x="450.0" y="180.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="890.0" y="297.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="exclusivegateway1" id="BPMNShape_exclusivegateway1">
        <omgdc:Bounds height="40.0" width="40.0" x="413.0" y="287.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="exclusivegateway2" id="BPMNShape_exclusivegateway2">
        <omgdc:Bounds height="40.0" width="40.0" x="810.0" y="294.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
        <omgdi:waypoint x="167.0" y="307.0"></omgdi:waypoint>
        <omgdi:waypoint x="240.0" y="307.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow6" id="BPMNEdge_flow6">
        <omgdi:waypoint x="555.0" y="207.0"></omgdi:waypoint>
        <omgdi:waypoint x="640.0" y="207.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow7" id="BPMNEdge_flow7">
        <omgdi:waypoint x="745.0" y="207.0"></omgdi:waypoint>
        <omgdi:waypoint x="830.0" y="207.0"></omgdi:waypoint>
        <omgdi:waypoint x="830.0" y="294.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow8" id="BPMNEdge_flow8">
        <omgdi:waypoint x="567.0" y="417.0"></omgdi:waypoint>
        <omgdi:waypoint x="830.0" y="417.0"></omgdi:waypoint>
        <omgdi:waypoint x="830.0" y="334.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow9" id="BPMNEdge_flow9">
        <omgdi:waypoint x="850.0" y="314.0"></omgdi:waypoint>
        <omgdi:waypoint x="890.0" y="314.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow10" id="BPMNEdge_flow10">
        <omgdi:waypoint x="345.0" y="307.0"></omgdi:waypoint>
        <omgdi:waypoint x="413.0" y="307.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow11" id="BPMNEdge_flow11">
        <omgdi:waypoint x="433.0" y="287.0"></omgdi:waypoint>
        <omgdi:waypoint x="432.0" y="225.0"></omgdi:waypoint>
        <omgdi:waypoint x="450.0" y="207.0"></omgdi:waypoint>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="36.0" width="100.0" x="443.0" y="287.0"></omgdc:Bounds>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow12" id="BPMNEdge_flow12">
        <omgdi:waypoint x="433.0" y="327.0"></omgdi:waypoint>
        <omgdi:waypoint x="433.0" y="417.0"></omgdi:waypoint>
        <omgdi:waypoint x="462.0" y="417.0"></omgdi:waypoint>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="36.0" width="100.0" x="443.0" y="327.0"></omgdc:Bounds>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow15" id="BPMNEdge_flow15">
        <omgdi:waypoint x="500.0" y="445.0"></omgdi:waypoint>
        <omgdi:waypoint x="149.0" y="444.0"></omgdi:waypoint>
        <omgdi:waypoint x="149.0" y="325.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

Expense Process Test

package org.activiti.designer.test;

import static org.junit.Assert.*;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.io.FileInputStream;

import org.activiti.engine.HistoryService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.test.ActivitiRule;
import org.junit.Rule;
import org.junit.Test;

public class ProcessTestExpenseProcess {

	private String filename = "/Users/tmichels/Documents/workspace-sts-3.5.0.M21/nucleus-business-process/src/main/resources/diagrams/expense_process.bpmn";

	@Rule
	public ActivitiRule activitiRule = new ActivitiRule();
	
	@Test
	public void startProcess() throws Exception {
		ProcessEngine processEngine = ProcessEngineConfiguration
			.createStandaloneInMemProcessEngineConfiguration()
			.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE)
			.setMailServerHost("smtp.gmail.com")
			.setMailServerPort(587)
			.setMailServerUsername("youremail@gmail.com")
			.setMailServerPassword("password")
			.setMailServerUseSSL(true)
			.setMailServerUseTLS(true)
		    .buildProcessEngine();
		
		RuntimeService runtimeService = activitiRule.getRuntimeService();
		
		RepositoryService repositoryService = activitiRule.getRepositoryService();
		repositoryService.createDeployment().addInputStream("expense_process.bpmn",
				new FileInputStream(filename)).deploy();
		
		Map<String, Object> variableMap1 = new HashMap<String, Object>();
		variableMap1.put("employee", "Jack");
		variableMap1.put("amount", 111);
		Map<String, Object> variableMap2 = new HashMap<String, Object>();
		variableMap2.put("employee", "Jonny");
		variableMap2.put("amount", 222);
		Map<String, Object> variableMap3 = new HashMap<String, Object>();
		variableMap3.put("employee", "Pete");
		variableMap3.put("amount", 333);
		
		ProcessInstance processInstance1 = runtimeService.startProcessInstanceByKey("expenseProcess", variableMap1);
		ProcessInstance processInstance2 = runtimeService.startProcessInstanceByKey("expenseProcess", variableMap2);
		ProcessInstance processInstance3 = runtimeService.startProcessInstanceByKey("expenseProcess", variableMap3);
		System.out.println("**** ProcessInstance1 ID *** " + processInstance1.getId());
		
		assertNotNull(processInstance1.getId());
		assertNotNull(processInstance2.getId());
		assertNotNull(processInstance3.getId());
		

		Map<String, Object> processVariables = runtimeService.getVariables(processInstance1.getId());
		for (String processVariable : processVariables.keySet()){
			System.out.println("PROCESS KEY : " + processVariable + " VALUE: " + processVariables.get(processVariable));
		}
		
		TaskService taskService = processEngine.getTaskService();
		
		//Query Task Available for Kermit
		 List<Task> availableTasksForKermit = taskService.createTaskQuery().taskAssignee("kermit").list();
		 for (Task task : availableTasksForKermit) {
		      System.out.println(" *******AVAILABLE TASK**********" + "\n" 
		      		+ 	" Task Name: " + task.getName() + "\n Owner: " + task.getOwner() + "\n" +
		    		  	" Assignee: " + task.getAssignee() +"\n "+ "Description: " + task.getDescription());
		 }
		
		List<Task> tasks = activitiRule.getTaskService().createTaskQuery().list();
		for (Task task : tasks){
			System.out.println("*******LIST TASKS************" + "\n" +
					"Get Task:" + task.getAssignee());
		}

		List<Task> kermitTasks = taskService.createTaskQuery()
		         .taskAssignee("kermit")
		         .processVariableValueEquals("amount", 111)
		         .orderByDueDate().asc()
		         .list();
		
		for (Task kermitTask: kermitTasks){
			System.out.println("*******QUERY FOR ASSIGNEE TASKS************" + "\n" + kermitTask.getAssignee());
		}
		
		 //Can't Claim Task as this task already has an assignee
		 if (availableTasksForKermit.get(0).getAssignee()==null)
			 taskService.claim(availableTasksForKermit.get(0).getId(), "rocky");
	     
	     //Verify Task - No Tasks for Rocky cause Kermit has it
	     tasks = taskService.createTaskQuery().taskAssignee("rocky").list();
	     for (Task task : tasks) {
	       System.out.println("Task for rocky: " + task.getName());
	     }
	     
	     //Ok kermit claims a tasks
	     taskService.claim(availableTasksForKermit.get(0).getId(), "kermit");
	     
	     List<Task> claimedTasks = taskService.createTaskQuery().taskCandidateOrAssigned("kermit").list();
	     for (Task claimedTask : claimedTasks){
	    	 System.out.println("Task claimed by Kermit: " + claimedTask.getDescription());
	     }
	     
	     Map<String, Object> taskCompleteVariables = new HashMap<String, Object>();
	     taskCompleteVariables.put("employeeVerification", false);
	     taskCompleteVariables.put("amountVerificaion", true);
	     taskService.complete(availableTasksForKermit.get(0).getId(),taskCompleteVariables);
	     
	     System.out.println("Number of tasks for kermit: " 
	             + taskService.createTaskQuery().taskAssignee("kermit").count());
	     
	     List<Task> accountancyTasks = taskService.createTaskQuery().taskCandidateGroup("accountancy").list();
	     for (Task accountancyTask : accountancyTasks){
	    	 System.out.println("Accountancy Task: " +accountancyTask.getDescription());
	     }
	     
	     taskService.claim(accountancyTasks.get(0).getId(), "sandy");
	     
	     System.out.println("Number of tasks for Sandy: " 
	             + taskService.createTaskQuery().taskAssignee("sandy").count());
	     
	     System.out.println("BreakPoint Timeout");
	     
	     System.out.println("Number of tasks for Sandy: " 
	             + taskService.createTaskQuery().taskAssignee("sandy").count());
	     
	   //Query Task Available for Kermit
		 List<Task> availableTasksForKermit2 = taskService.createTaskQuery().taskAssignee("kermit").list();
		 for (Task task : availableTasksForKermit2) {
		      System.out.println(" *******AVAILABLE TASK**********" + "\n" 
		      		+ 	" Task Name: " + task.getName() + "\n Owner: " + task.getOwner() + "\n" +
		    		  	" Assignee: " + task.getAssignee() +"\n "+ "Description: " + task.getDescription());
		 }
		 
		 taskService.claim(accountancyTasks.get(0).getId(), "sandy");
		 taskService.complete(accountancyTasks.get(0).getId());
	      
	     HistoryService historyService = processEngine.getHistoryService();
	     HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstance1.getId()).singleResult();
	     System.out.println("Process instance end time: " + historicProcessInstance.getEndTime());
	}
}

Output

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/Users/tmichels/.m2/repository/org/slf4j/slf4j-log4j12/1.7.7/slf4j-log4j12-1.7.7.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/Users/tmichels/.m2/repository/org/slf4j/slf4j-jdk14/1.7.2/slf4j-jdk14-1.7.2.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]
23 May 2014 09:08:57,461 [main]  INFO org.springframework.beans.factory.xml.XmlBeanDefinitionReader:315 - Loading XML bean definitions from class path resource [activiti.cfg.xml]
23 May 2014 09:08:59,874 [main]  INFO org.activiti.engine.impl.db.DbSqlSession:975 - performing create on engine with resource org/activiti/db/create/activiti.h2.create.engine.sql
23 May 2014 09:08:59,955 [main]  INFO org.activiti.engine.impl.db.DbSqlSession:975 - performing create on history with resource org/activiti/db/create/activiti.h2.create.history.sql
23 May 2014 09:08:59,973 [main]  INFO org.activiti.engine.impl.db.DbSqlSession:975 - performing create on identity with resource org/activiti/db/create/activiti.h2.create.identity.sql
23 May 2014 09:08:59,986 [main]  INFO org.activiti.engine.impl.ProcessEngineImpl:79 - ProcessEngine default created
23 May 2014 09:08:59,989 [main]  INFO org.activiti.engine.impl.jobexecutor.JobExecutor:76 - Starting up the JobExecutor[org.activiti.engine.impl.jobexecutor.DefaultJobExecutor].
23 May 2014 09:08:59,994 [Thread-1]  INFO org.activiti.engine.impl.jobexecutor.AcquireJobsRunnable:51 - JobExecutor[org.activiti.engine.impl.jobexecutor.DefaultJobExecutor] starting to acquire jobs
23 May 2014 09:09:00,847 [main]  INFO org.activiti.engine.impl.ProcessEngineImpl:79 - ProcessEngine default created
23 May 2014 09:09:00,853 [main]  INFO org.activiti.engine.impl.bpmn.deployer.BpmnDeployer:81 - Processing resource expense_process.bpmn
**** ProcessInstance1 ID *** 5
PROCESS KEY : amount VALUE: 111
PROCESS KEY : employee VALUE: Jack
 *******AVAILABLE TASK**********
 Task Name: Request Refund
 Owner: null
 Assignee: kermit
 Description: [Employee:Jack, 
Amount: 111]
 *******AVAILABLE TASK**********
 Task Name: Request Refund
 Owner: null
 Assignee: kermit
 Description: [Employee:Jonny, 
Amount: 222]
 *******AVAILABLE TASK**********
 Task Name: Request Refund
 Owner: null
 Assignee: kermit
 Description: [Employee:Pete, 
Amount: 333]
*******LIST TASKS************
Get Task:kermit
*******LIST TASKS************
Get Task:kermit
*******LIST TASKS************
Get Task:kermit
*******QUERY FOR ASSIGNEE TASKS************
kermit
23 May 2014 09:09:02,320 [main]  INFO org.activiti.engine.impl.bpmn.deployer.BpmnDeployer:81 - Processing resource expense_process.bpmn
23 May 2014 09:09:02,442 [main]  INFO org.activiti.engine.impl.bpmn.deployer.BpmnDeployer:81 - Processing resource expense_process.expenseProcess.png
Number of tasks for kermit: 2
Accountancy Task: Jack has requested a refund amount 111
Number of tasks for Sandy: 1
BreakPoint Timeout
Number of tasks for Sandy: 1
 *******AVAILABLE TASK**********
 Task Name: Request Refund
 Owner: null
 Assignee: kermit
 Description: [Employee:Jonny, 
Amount: 222]
 *******AVAILABLE TASK**********
 Task Name: Request Refund
 Owner: null
 Assignee: kermit
 Description: [Employee:Pete, 
Amount: 333]
Process instance end time: Fri May 23 09:09:02 PDT 2014

1 Comment

  1. Anonymous says:

    Your title is misleading – this is not a tutorial !

Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s