Getting Started with Activiti 7: A Comprehensive Guide

Activiti 7 Tutorial

Table of Contents

    1. Activiti Environment Setup
    1. IDE Plugin Installation
    1. Database Configuration
    1. Creating a Maven Project
    1. Activiti Fundamentals
    1. Process Definition Deployment
    1. Process Execution, Task Query, and Task Completion
    1. ZIP-Based Deployment
    1. Process Query and Deletion
    1. Process Resource Download
    1. Viewing Historical Data
    1. Task Suspension and Activation
    1. Using UEL Expressions for Assignee Assignment
    1. Using Task Listeners for Assignee Assignment
    1. Conditional Branching
    1. Group Tasks
    1. Exclusive Gateway
    1. Parallel Gateway
    1. Inclusive Gateway
  1. Activiti Environment Setup

2. IDE Plugin Installation

BPMN diagrams are represented as XML files (BPMN files can be viewed in any text editor). To install the Activiti plugin in IntelliJ IDEA:

  • Navigate to File | Settings | Plugins
  • Search for actiBPM (Activiti BPMN visualizer)
  • Install and restart IDEA

3. Database Support

Activiti supports various databases including MySQL, PostgreSQL, Oracle, and more. For MySQL:

  • Create a new database: CREATE DATABASE activiti DEFAULT CHARACTER SET utf8;
  • Ensure the database user has appropriate permissions
  • Configure the connection in the Activiti configuration file

4. Creating a Maven Project

Add the following dependencies to your pom.xml:

<properties>
    <slf4j.version>1.6.6</slf4j.version>
    <log4j.version>1.2.12</log4j.version>
    <activiti.version>7.0.0.Beta1</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.activiti</groupId>
        <artifactId>activiti-bpmn-model</artifactId>
        <version>${activiti.version}</version>
    </dependency>
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-bpmn-converter</artifactId>
        <version>${activiti.version}</version>
    </dependency>
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-json-converter</artifactId>
        <version>${activiti.version}</version>
    </dependency>
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-bpmn-layout</artifactId>
        <version>${activiti.version}</version>
    </dependency>
    <dependency>
        <groupId>org.activiti.cloud</groupId>
        <artifactId>activiti-cloud-services-api</artifactId>
        <version>${activiti.version}</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.40</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.4.5</version>
    </dependency>
    <dependency>
        <groupId>commons-dbcp</groupId>
        <artifactId>commons-dbcp</artifactId>
        <version>1.4</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>${log4j.version}</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>${slf4j.version}</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>${slf4j.version}</version>
    </dependency>
</dependencies>

Create log4j.properties in src/main/resources:

log4j.rootCategory=debug, CONSOLE, LOGFILE
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r[%15.15t] %-5p %30.30c %x - %m%n
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=activiti.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r[%15.15t] %-5p %30.30c %x - %m%n

Create activiti.cfg.xml in src/main/resources:

<?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.StandaloneProcessEngineConfiguration">
        <property name="jdbcDriver" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql:///activiti"/>
        <property name="jdbcUsername" value="root"/>
        <property name="jdbcPassword" value="password"/>
        <property name="databaseSchemaUpdate" value="true"/>
    </bean>
</beans>

Test database table creation:

public class ActivitiInitializationTest {
    @Test
    public void initializeDatabase() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        System.out.println("Process Engine: " + processEngine);
    }
}

Activiti Database Tables Overview

Category Table Name Description
General Data
ACT_GE_BYTEARRAY General process definitions and resources
ACT_GE_PROPERTY System-related properties
Process History
ACT_HI_ACTINST Historical process instances
ACT_HI_ATTACHMENT Historical process attachments
ACT_HI_COMMENT Historical comments
ACT_HI_DETAIL Historical process run details
ACT_HI_IDENTITYLINK Historical user relationships
ACT_HI_PROCINST Historical process instances
ACT_HI_TASKINST Historical task instances
ACT_HI_VARINST Historical process variables
Process Definition
ACT_RE_DEPLOYMENT Deployment information
ACT_RE_MODEL Model information
ACT_RE_PROCDEF Deployed process definitions
Runtime Data
ACT_RU_EVENT_SUBSCR Runtime events
ACT_RU_EXECUTION Runtime process executions
ACT_RU_IDENTITYLINK Runtime user relationships
ACT_RU_JOB Runtime jobs
ACT_RU_TASK Runtime tasks
ACT_RU_VARIABLE Runtime variables

Core Activiti Services

Service Purpose
RepositoryService Manages process definitions and resources
RuntimeService Manages process execution
TaskService Manages tasks
HistoryService Manages historical data
ManagementService Engine management operations
  1. Activiti Fundamentals

BPMN Process Symbols

  • Events
    • Start Event
    • Intermediate Event
    • End Event
  • Activities
    • User Task
    • Service Task
    • Subprocess
  • Gateways
    • Exclusive Gateway (X)
    • Parallel Gateway (+)
    • Inclusive Gateway
    • Event Gateway
  • Flows
    • Sequence Flow
    • Message Flow
    • Association
    • Data Association

Using the Process Designer

  • Right-click on project → New → New Activiti 6.x BPMN 2.0 File
  • Right-click on XML editor → View BPMN (Activiti) Diagram
  • Right-click on canvas:
    • Connection: Create flows between elements
    • Event: Add events (start, end, intermediate)
    • Task: Add task types
    • Gateway: Add gateways
    • Container: Add containers
    • Boundary Event: Add boundary events
    • Intermediate Event: Add intermediate events
  1. Process Definition Deployment

public class ProcessDeploymentExample {
    @Test
    public void deployProcessDefinition() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        
        Deployment deployment = repositoryService
            .createDeployment()
            .name("Business Trip Request")
            .addClasspathResource("bpmn/travel-request.bpmn20.xml")
            .addClasspathResource("bpmn/travel-request.png")
            .deploy();
            
        System.out.println("Deployment ID: " + deployment.getId());
        System.out.println("Deployment Name: " + deployment.getName());
    }
}

  1. Process Execution, Task Query, and Task Completion

public class ProcessExecutionExample {
    @Test
    public void startProcessInstance() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RuntimeService runtimeService = processEngine.getRuntimeService();
        
        ProcessInstance instance = runtimeService.startProcessInstanceByKey("travelRequest");
        
        System.out.println("Process Definition ID: " + instance.getProcessDefinitionId());
        System.out.println("Process Instance ID: " + instance.getId());
        System.out.println("Activity ID: " + instance.getActivityId());
    }

    @Test
    public void queryTasks() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        
        List<Task> tasks = taskService
            .createTaskQuery()
            .processDefinitionKey("travelRequest")
            .taskAssignee("alice")
            .list();
            
        for (Task task : tasks) {
            System.out.println("Process Instance ID: " + task.getProcessInstanceId());
            System.out.println("Task ID: " + task.getId());
            System.out.println("Assignee: " + task.getAssignee());
            System.out.println("Task Name: " + task.getName());
        }
    }

    @Test
    public void completeTask() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        
        Task task = taskService
            .createTaskQuery()
            .processDefinitionKey("travelRequest")
            .taskAssignee("bob")
            .singleResult();
            
        if (task != null) {
            System.out.println("Task ID: " + task.getId());
            System.out.println("Assignee: " + task.getAssignee());
            taskService.complete(task.getId());
        }
    }
}

  1. ZIP-Based Deployment

public class ZipDeploymentExample {
    @Test
    public void deployFromZip() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        
        InputStream inputStream = getClass().getClassLoader()
            .getResourceAsStream("bpmn/process-bundle.zip");
        ZipInputStream zipInputStream = new ZipInputStream(inputStream);
        
        Deployment deployment = repositoryService
            .createDeployment()
            .addZipInputStream(zipInputStream)
            .deploy();
            
        System.out.println("Deployment ID: " + deployment.getId());
        System.out.println("Deployment Name: " + deployment.getName());
    }
}

  1. Process Query and Deletion

public class ProcessManagementExample {
    @Test
    public void queryProcessDefinitions() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        
        List<ProcessDefinition> definitions = repositoryService
            .createProcessDefinitionQuery()
            .processDefinitionKey("myProcess")
            .orderByProcessDefinitionVersion()
            .desc()
            .list();
            
        for (ProcessDefinition definition : definitions) {
            System.out.println("Definition ID: " + definition.getId());
            System.out.println("Definition Name: " + definition.getName());
            System.out.println("Definition Key: " + definition.getKey());
            System.out.println("Version: " + definition.getVersion());
            System.out.println("Deployment ID: " + definition.getDeploymentId());
        }
    }

    @Test
    public void deleteDeployment() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        
        String deploymentId = "1001";
        repositoryService.deleteDeployment(deploymentId, true);
    }
}

  1. Process Resource Download

public class ResourceDownloadExample {
    @Test
    public void downloadProcessResources() throws IOException {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        
        ProcessDefinition processDefinition = repositoryService
            .createProcessDefinitionQuery()
            .processDefinitionKey("myProcess")
            .singleResult();
            
        String deploymentId = processDefinition.getDeploymentId();
        String diagramName = processDefinition.getDiagramResourceName();
        String bpmnName = processDefinition.getResourceName();
        
        InputStream diagramStream = repositoryService.getResourceAsStream(deploymentId, diagramName);
        InputStream bpmnStream = repositoryService.getResourceAsStream(deploymentId, bpmnName);
        
        File diagramFile = new File("output/diagram.png");
        File bpmnFile = new File("output/process.bpmn20.xml");
        
        FileOutputStream diagramOut = new FileOutputStream(diagramFile);
        FileOutputStream bpmnOut = new FileOutputStream(bpmnFile);
        
        IOUtils.copy(diagramStream, diagramOut);
        IOUtils.copy(bpmnStream, bpmnOut);
        
        diagramOut.close();
        bpmnOut.close();
        diagramStream.close();
        bpmnStream.close();
    }
}

  1. Viewing Historical Data

public class HistoryQueryExample {
    @Test
    public void queryHistoricalData() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        HistoryService historyService = processEngine.getHistoryService();
        
        List<HistoricActivityInstance> activities = historyService
            .createHistoricActivityInstanceQuery()
            .processDefinitionId("myProcess:1:10004")
            .orderByHistoricActivityInstanceStartTime()
            .asc()
            .list();
            
        for (HistoricActivityInstance activity : activities) {
            System.out.println("Activity ID: " + activity.getActivityId());
            System.out.println("Activity Name: " + activity.getActivityName());
            System.out.println("Process Definition ID: " + activity.getProcessDefinitionId());
            System.out.println("Process Instance ID: " + activity.getProcessInstanceId());
            System.out.println("=========================");
        }
    }
}

  1. Task Suspension and Activation

public class SuspensionExample {
    @Test
    public void addBusinessKey() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RuntimeService runtimeService = processEngine.getRuntimeService();
        
        ProcessInstance instance = runtimeService
            .startProcessInstanceByKey("travelRequest", "ORDER-001");
            
        System.out.println("Business Key: " + instance.getBusinessKey());
    }

    @Test
    public void suspendProcessDefinition() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        
        ProcessDefinition definition = repositoryService
            .createProcessDefinitionQuery()
            .processDefinitionKey("travelRequest")
            .singleResult();
            
        boolean isSuspended = definition.isSuspended();
        String definitionId = definition.getId();
        
        if (isSuspended) {
            repositoryService.activateProcessDefinitionById(definitionId, true, null);
            System.out.println("Process definition activated: " + definitionId);
        } else {
            repositoryService.suspendProcessDefinitionById(definitionId, true, null);
            System.out.println("Process definition suspended: " + definitionId);
        }
    }

    @Test
    public void suspendProcessInstance() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RuntimeService runtimeService = processEngine.getRuntimeService();
        
        ProcessInstance instance = runtimeService
            .createProcessInstanceQuery()
            .processInstanceId("15001")
            .singleResult();
            
        boolean isSuspended = instance.isSuspended();
        String instanceId = instance.getId();
        
        if (isSuspended) {
            runtimeService.activateProcessInstanceById(instanceId);
            System.out.println("Process instance activated: " + instanceId);
        } else {
            runtimeService.suspendProcessInstanceById(instanceId);
            System.out.println("Process instance suspended: " + instanceId);
        }
    }
}

  1. Using UEL Expressions for Assignee Assignment

BPMN process with UEL expressions:

<process id="travelRequestUel" name="Travel Request - UEL" isExecutable="true">
  <startEvent id="startEvent"/>
  <userTask id="createTask" name="Create Request" 
            activiti:assignee="${initiator}"/>
  <userTask id="managerApproval" name="Manager Approval" 
            activiti:assignee="${manager}"/>
  <userTask id="directorApproval" name="Director Approval" 
            activiti:assignee="${director}"/>
  <userTask id="financeApproval" name="Finance Approval" 
            activiti:assignee="${finance}"/>
  <endEvent id="endEvent"/>
  <sequenceFlow sourceRef="startEvent" targetRef="createTask"/>
  <sequenceFlow sourceRef="createTask" targetRef="managerApproval"/>
  <sequenceFlow sourceRef="managerApproval" targetRef="directorApproval"/>
  <sequenceFlow sourceRef="directorApproval" targetRef="financeApproval"/>
  <sequenceFlow sourceRef="financeApproval" targetRef="endEvent"/>
</process>

public class UelAssignmentExample {
    @Test
    public void deployWithUel() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        
        Deployment deployment = repositoryService
            .createDeployment()
            .name("Travel Request - UEL")
            .addClasspathResource("bpmn/travel-uel.bpmn20.xml")
            .deploy();
            
        System.out.println("Deployment ID: " + deployment.getId());
    }

    @Test
    public void startWithUelVariables() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RuntimeService runtimeService = processEngine.getRuntimeService();
        
        Map<String, Object> variables = new HashMap<>();
        variables.put("initiator", "Alice");
        variables.put("manager", "Bob Manager");
        variables.put("director", "Carol Director");
        variables.put("finance", "Dave Finance");
        
        runtimeService.startProcessInstanceByKey("travelRequestUel", variables);
    }
}

  1. Using Task Listeners for Assignee Assigment

BPMN process with task listener:

<process id="listenerProcess" name="Process with Listener" isExecutable="true">
  <startEvent id="start"/>
  <userTask id="createRequest" name="Create Request">
    <extensionElements>
      <activiti:taskListener event="all" 
                  class="com.example.listener.CustomTaskListener"/>
    </extensionElements>
  </userTask>
  <userTask id="reviewRequest" name="Review Request"/>
  <endEvent id="end"/>
  <sequenceFlow sourceRef="start" targetRef="createRequest"/>
  <sequenceFlow sourceRef="createRequest" targetRef="reviewRequest"/>
  <sequenceFlow sourceRef="reviewRequest" targetRef="end"/>
</process>

public class CustomTaskListener implements TaskListener {
    @Override
    public void notify(DelegateTask delegateTask) {
        if ("Create Request".equals(delegateTask.getName()) 
            && "create".equals(delegateTask.getEventName())) {
            delegateTask.setAssignee("SystemUser");
        }
    }
}

public class ListenerExample {
    @Test
    public void deployWithListener() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        
        Deployment deployment = repositoryService
            .createDeployment()
            .name("Process with Listener")
            .addClasspathResource("bpmn/listener-process.bpmn20.xml")
            .deploy();
            
        System.out.println("Deployment ID: " + deployment.getId());
    }

    @Test
    public void startWithListener() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RuntimeService runtimeService = processEngine.getRuntimeService();
        
        runtimeService.startProcessInstanceByKey("listenerProcess");
    }
}

  1. Conditional Branching

Process with conditional flows:

<process id="travelWithCondition" isExecutable="true">
  <startEvent id="start"/>
  <userTask id="createTask" name="Create Request" 
            activiti:assignee="${assignee0}"/>
  <userTask id="deptApproval" name="Department Approval" 
            activiti:assignee="${assignee1}"/>
  <userTask id="directorApproval" name="Director Approval" 
            activiti:assignee="${assignee2}"/>
  <userTask id="financeApproval" name="Finance Approval" 
            activiti:assignee="${assignee3}"/>
  <endEvent id="end"/>
  <sequenceFlow sourceRef="start" targetRef="createTask"/>
  <sequenceFlow sourceRef="createTask" targetRef="deptApproval"/>
  <sequenceFlow sourceRef="deptApproval" targetRef="directorApproval">
    <conditionExpression><![CDATA[${travelRequest.days < 3}]]></conditionExpression>
  </sequenceFlow>
  <sequenceFlow sourceRef="deptApproval" targetRef="financeApproval">
    <conditionExpression><![CDATA[${travelRequest.days >= 3}]]></conditionExpression>
  </sequenceFlow>
  <sequenceFlow sourceRef="directorApproval" targetRef="financeApproval"/>
  <sequenceFlow sourceRef="financeApproval" targetRef="end"/>
</process>

public class ConditionalFlowExample {
    @Test
    public void deployConditionalProcess() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        
        Deployment deployment = repositoryService
            .createDeployment()
            .name("Conditional Process")
            .addClasspathResource("bpmn/conditional-process.bpmn20.xml")
            .deploy();
    }

    @Test
    public void startWithVariables() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RuntimeService runtimeService = processEngine.getRuntimeService();
        
        Map<String, Object> variables = new HashMap<>();
        variables.put("assignee0", "User1");
        variables.put("assignee1", "Manager1");
        variables.put("assignee2", "Director1");
        variables.put("assignee3", "Finance1");
        
        runtimeService.startProcessInstanceByKey("travelWithCondition", variables);
    }

    @Test
    public void completeWithCondition() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        
        TravelRequest request = new TravelRequest();
        request.setDays(2);
        
        Map<String, Object> variables = new HashMap<>();
        variables.put("travelRequest", request);
        
        Task task = taskService
            .createTaskQuery()
            .processDefinitionKey("travelWithCondition")
            .taskAssignee("Manager1")
            .singleResult();
            
        if (task != null) {
            taskService.complete(task.getId(), variables);
        }
    }
}

  1. Group Tasks

Process with candidate users:

<process id="groupTaskProcess" name="Group Task Process" isExecutable="true">
  <startEvent id="start"/>
  <userTask id="createTask" name="Create Request" 
            activiti:assignee="alice"/>
  <userTask id="groupApproval" name="Group Approval" 
            activiti:candidateUsers="user1,user2"/>
  <userTask id="managerApproval" name="Manager Approval" 
            activiti:assignee="bob"/>
  <userTask id="financeApproval" name="Finance Approval" 
            activiti:assignee="charlie"/>
  <endEvent id="end"/>
  <sequenceFlow sourceRef="start" targetRef="createTask"/>
  <sequenceFlow sourceRef="createTask" targetRef="groupApproval"/>
  <sequenceFlow sourceRef="groupApproval" targetRef="managerApproval"/>
  <sequenceFlow sourceRef="managerApproval" targetRef="financeApproval"/>
  <sequenceFlow sourceRef="financeApproval" targetRef="end"/>
</process>

public class GroupTaskExample {
    @Test
    public void deployGroupTaskProcess() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        
        Deployment deployment = repositoryService
            .createDeployment()
            .name("Group Task Process")
            .addClasspathResource("bpmn/group-task.bpmn20.xml")
            .deploy();
    }

    @Test
    public void startGroupProcess() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RuntimeService runtimeService = processEngine.getRuntimeService();
        
        runtimeService.startProcessInstanceByKey("groupTaskProcess");
    }

    @Test
    public void queryGroupTasks() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        
        List<Task> tasks = taskService
            .createTaskQuery()
            .processDefinitionKey("groupTaskProcess")
            .taskCandidateUser("user2")
            .list();
            
        for (Task task : tasks) {
            System.out.println("Task ID: " + task.getId());
            System.out.println("Process Instance ID: " + task.getProcessInstanceId());
        }
    }

    @Test
    public void claimGroupTask() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        
        String taskId = "40001";
        String candidateUser = "user1";
        
        Task task = taskService
            .createTaskQuery()
            .taskId(taskId)
            .taskCandidateUser(candidateUser)
            .singleResult();
            
        if (task != null) {
            taskService.claim(taskId, candidateUser);
        }
    }

    @Test
    public void returnToGroup() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        
        String taskId = "40001";
        taskService.setAssignee(taskId, null);
    }

    @Test
    public void transferToUser() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        
        String taskId = "40001";
        String newAssignee = "user2";
        
        taskService.setAssignee(taskId, newAssignee);
    }
}

  1. Exclusive Gateway

Process with exclusive gateway for conditional routing:

<process id="exclusiveGatewayProcess" name="Exclusive Gateway Process" 
         isExecutable="true">
  <startEvent id="start"/>
  <userTask id="fillForm" name="Fill Form" 
            activiti:assignee="employee"/>
  <userTask id="deptReview" name="Department Review" 
            activiti:assignee="deptManager"/>
  <userTask id="directorReview" name="Director Review" 
            activiti:assignee="director"/>
  <userTask id="financeReview" name="Finance Review" 
            activiti:assignee="finance"/>
  <endEvent id="end"/>
  <exclusiveGateway id="decisionGateway" name="Days Decision"/>
  <sequenceFlow sourceRef="start" targetRef="fillForm"/>
  <sequenceFlow sourceRef="fillForm" targetRef="deptReview"/>
  <sequenceFlow sourceRef="deptReview" targetRef="decisionGateway"/>
  <sequenceFlow sourceRef="decisionGateway" targetRef="directorReview">
    <conditionExpression><![CDATA[${request.days >= 3}]]></conditionExpression>
  </sequenceFlow>
  <sequenceFlow sourceRef="decisionGateway" targetRef="financeReview">
    <conditionExpression><![CDATA[${request.days < 3}]]></conditionExpression>
  </sequenceFlow>
  <sequenceFlow sourceRef="directorReview" targetRef="financeReview"/>
  <sequenceFlow sourceRef="financeReview" targetRef="end"/>
</process>

public class ExclusiveGatewayExample {
    @Test
    public void deployExclusiveGateway() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        
        repositoryService
            .createDeployment()
            .addClasspathResource("bpmn/exclusive-gateway.bpmn20.xml")
            .name("Exclusive Gateway Process")
            .deploy();
    }

    @Test
    public void startWithCondition() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RuntimeService runtimeService = processEngine.getRuntimeService();
        
        BusinessRequest request = new BusinessRequest();
        request.setDays(2);
        
        Map<String, Object> variables = new HashMap<>();
        variables.put("request", request);
        
        ProcessInstance instance = runtimeService
            .startProcessInstanceByKey("exclusiveGatewayProcess", variables);
            
        System.out.println("Process Instance ID: " + instance.getId());
    }
}

  1. Parallel Gateway

Process with parallel gateway for concurrent execution:

<process id="parallelGatewayProcess" name="Parallel Gateway Process" 
         isExecutable="true">
  <startEvent id="start"/>
  <userTask id="createTask" name="Create Request" 
            activiti:assignee="employee"/>
  <parallelGateway id="splitGateway" name="Parallel Split"/>
  <userTask id="techReview" name="Technical Review" 
            activiti:assignee="techLead"/>
  <userTask id="projectReview" name="Project Review" 
            activiti:assignee="projectMgr"/>
  <parallelGateway id="joinGateway" name="Parallel Join"/>
  <exclusiveGateway id="decisionGate" name="Decision"/>
  <userTask id="finalApproval" name="Final Approval" 
            activiti:assignee="executive"/>
  <endEvent id="end"/>
  <sequenceFlow sourceRef="start" targetRef="createTask"/>
  <sequenceFlow sourceRef="createTask" targetRef="splitGateway"/>
  <sequenceFlow sourceRef="splitGateway" targetRef="techReview">
    <conditionExpression><![CDATA[${request.days >= 3}]]></conditionExpression>
  </sequenceFlow>
  <sequenceFlow sourceRef="splitGateway" targetRef="projectReview">
    <conditionExpression><![CDATA[${request.days < 3}]]></conditionExpression>
  </sequenceFlow>
  <sequenceFlow sourceRef="techReview" targetRef="joinGateway"/>
  <sequenceFlow sourceRef="projectReview" targetRef="joinGateway"/>
  <sequenceFlow sourceRef="joinGateway" targetRef="decisionGate"/>
  <sequenceFlow sourceRef="decisionGate" targetRef="finalApproval">
    <conditionExpression><![CDATA[${request.days >= 3}]]></conditionExpression>
  </sequenceFlow>
  <sequenceFlow sourceRef="decisionGate" targetRef="end">
    <conditionExpression><![CDATA[${request.days < 3}]]></conditionExpression>
  </sequenceFlow>
  <sequenceFlow sourceRef="finalApproval" targetRef="end"/>
</process>

public class ParallelGatewayExample {
    @Test
    public void deployParallelGateway() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        
        repositoryService
            .createDeployment()
            .addClasspathResource("bpmn/parallel-gateway.bpmn20.xml")
            .name("Parallel Gateway Process")
            .deploy();
    }

    @Test
    public void startParallelProcess() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RuntimeService runtimeService = processEngine.getRuntimeService();
        
        BusinessRequest request = new BusinessRequest();
        request.setDays(4);
        
        Map<String, Object> variables = new HashMap<>();
        variables.put("request", request);
        
        ProcessInstance instance = runtimeService
            .startProcessInstanceByKey("parallelGatewayProcess", variables);
            
        System.out.println("Process Definition ID: " + instance.getProcessDefinitionId());
        System.out.println("Process Instance ID: " + instance.getId());
    }
}

  1. Inclusive Gateway

Process with inclusive gateway for complex conditional branching:

<process id="inclusiveGatewayProcess" name="Inclusive Gateway Process" 
         isExecutable="true">
  <startEvent id="start"/>
  <userTask id="submitRequest" name="Submit Request" 
            activiti:assignee="employee"/>
  <inclusiveGateway id="splitInclusive"/>
  <userTask id="techTask" name="Technical Review" 
            activiti:assignee="techLead"/>
  <userTask id="hrTask" name="HR Review" 
            activiti:assignee="hrManager"/>
  <userTask id="projectTask" name="Project Review" 
            activiti:assignee="projectMgr"/>
  <inclusiveGateway id="joinInclusive"/>
  <exclusiveGateway id="finalDecision"/>
  <userTask id="executiveTask" name="Executive Approval" 
            activiti:assignee="executive"/>
  <endEvent id="end"/>
  <sequenceFlow sourceRef="start" targetRef="submitRequest"/>
  <sequenceFlow sourceRef="submitRequest" targetRef="splitInclusive"/>
  <sequenceFlow sourceRef="splitInclusive" targetRef="techTask">
    <conditionExpression><![CDATA[${request.days < 3}]]></conditionExpression>
  </sequenceFlow>
  <sequenceFlow sourceRef="splitInclusive" targetRef="hrTask"/>
  <sequenceFlow sourceRef="splitInclusive" targetRef="projectTask">
    <conditionExpression><![CDATA[${request.days >= 3}]]></conditionExpression>
  </sequenceFlow>
  <sequenceFlow sourceRef="techTask" targetRef="joinInclusive"/>
  <sequenceFlow sourceRef="hrTask" targetRef="joinInclusive"/>
  <sequenceFlow sourceRef="projectTask" targetRef="joinInclusive"/>
  <sequenceFlow sourceRef="joinInclusive" targetRef="finalDecision"/>
  <sequenceFlow sourceRef="finalDecision" targetRef="executiveTask">
    <conditionExpression><![CDATA[${request.days >= 3}]]></conditionExpression>
  </sequenceFlow>
  <sequenceFlow sourceRef="finalDecision" targetRef="end"/>
  <sequenceFlow sourceRef="executiveTask" targetRef="end"/>
</process>

public class InclusiveGatewayExample {
    @Test
    public void deployInclusiveGateway() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        
        repositoryService
            .createDeployment()
            .addClasspathResource("bpmn/inclusive-gateway.bpmn20.xml")
            .name("Inclusive Gateway Process")
            .deploy();
    }

    @Test
    public void startInclusiveProcess() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        RuntimeService runtimeService = processEngine.getRuntimeService();
        
        BusinessRequest request = new BusinessRequest();
        request.setDays(4);
        
        Map<String, Object> variables = new HashMap<>();
        variables.put("request", request);
        
        ProcessInstance instance = runtimeService
            .startProcessInstanceByKey("inclusiveGatewayProcess", variables);
            
        System.out.println("Process Definition ID: " + instance.getProcessDefinitionId());
    }

    @Test
    public void completeTask() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        
        Task task = taskService
            .createTaskQuery()
            .processDefinitionKey("inclusiveGatewayProcess")
            .taskAssignee("projectMgr")
            .singleResult();
            
        if (task != null) {
            taskService.complete(task.getId());
        }
    }
}

Tags: activiti Workflow bpmn java process-engine

Posted on Mon, 17 Aug 2026 16:35:48 +0000 by engkeb0i