25.05.20

Camunda BPM для разработчика

20 мин · Обучающие

Добро пожаловать в проект "Camunda BPM для разработчика"! Наш проект - это материалы с воркшопов и вебинаров, посвященные разработке Java-приложений с использованием процессного движка Camunda BPM.

По результатам каждого занятия публикуется полная запись (для участников), сокращенная запись (для размещения на Youtube) и исходный код проекта на GitHub.

Что необходимо иметь участнику workshop'а или разработчику, желающему самостоятельно повторить занятие?

Занятие 1: Печенеги vs Половцы

Camunda для разработчика: Занятие 1

Цели - научиться:

  • Настраивать проект Camunda при помощи Camunda Boot Starter
  • Создавать и разворачивать процесс
  • Создавать Java код и связывать его с Camunda
  • Работать с данными в процессе
  • Обрабатывать события BPMN

Шаг 1: Сгенерировать проект Camunda с использованием Camunda BPM Initialzr

Необходимо открыть страницу по ссылке: https://start.camunda.com. Задать настройки проекта и нажать Generate Project.

Camunda для разработчика: Camunda BPM Initialzr

Полученный архив с именем приложения распаковать и открыть в IDE. Например в IDEA - Import project - as Maven project. После открытия проекта - дождаться, пока Maven скачает зависимости. После, запустить приложение:

src/main/java/имя_приложения/Application.java > нажать правой кнопкой > >
Camunda для разработчика: IDEA

После запуска приложения открыть браузер и перейти по ссылке: http://localhost:8080/.

Войти в веб-приложение Camunda, используя учетную запись:

Имя пользователя: demo
Пароль: demo

При необходимости, используемые логин и пароль можно заменить в файле resources/application.yaml. Полный перечень настроек application.yaml доступен по ссылке: https://docs.camunda.org/manual/latest/user-guide/spring-boot-integration/configuration/#camunda-engine-properties

💪 Поздравляем! Вы развернули процессное приложение Camunda!

Шаг 2: Внести изменения в описание процесса и реализовать Java Delegate

Спроектируем процесс под названием "Половцы vs Печенеги" в котором смоделируем битву. Для начала создадим новый delegate класс под названием PrepareToBattle. Взаимодействие процессного движка Camunda с кодом осуществляется посредством делегатного когда, другими словами наш класс должен имплементировать интерфейс JavaDelegate. Для корректной имплементации необходимо реализовать метод execute, кроме этого к классу добавим аннотацию Component. После чего можем наполнять процесс данными - количество вражеских воинов и статус битвы. Чтобы присвоить эти переменные переменным процесса используем метод setVariable():

package com.reunico.demo;
 
import org.camunda.bpm.engine.delegate.DelegateExecution;
import org.camunda.bpm.engine.delegate.JavaDelegate;
import org.springframework.stereotype.Component;
 
@Component
public class PrepareToBattle implements JavaDelegate {
 
    @Override
    public void execute(DelegateExecution delegateExecution) throws Exception {
        int enemyWarriors = (int) (Math.random() * 100);
        String battleStatus = "Undefined";
         
        delegateExecution.setVariable("enemyWarriors", enemyWarriors);
        delegateExecution.setVariable("battleStatus", battleStatus);
    }
}

Теперь немного поменяем наш процесс. В первую очередь поменяем его имя, для этого нажимаем на пустое пространство и видим id процесса - app-process, который выступает в роли ключа процесса и name - "Печенеги vs Половцы". Задача "Perform battle!" - это сервисная задача, которая наполнит процесс данными. Из этой задачи вызовем созданный нами класс. Нажимаем два раза на сервисный таск, после чего в поле Implementation выбираем Delegate Expression. В качестве Delegate Expression записываем название созданного класса (spring bean), но с маленькой буквой с использованием синтаксиса JUEL. После этого можем перезапустить приложение и убедиться в работоспособности процесса в Camunda Task List.

Camunda для разработчика: Процесс Camunda для разработчика: Service Task Delegate Expression

Усложним процесс, добавив в него больше вариативности. Добавим возможность ввести количество наших воинов, которых мы отправим в бой, модифицируем PrepareToBattle. Чтобы получить переменную из процесса достаточно использовать метод getVariable("warriors"). Также добавим условие победы или поражения битвы. Не забываем поместить обратно в процесс переменную, содержащую эту информацию:

package com.reunico.demo;
 
import org.camunda.bpm.engine.delegate.DelegateExecution;
import org.camunda.bpm.engine.delegate.JavaDelegate;
import org.springframework.stereotype.Component;
 
@Component
public class PrepareToBattle implements JavaDelegate {
 
    @Override
    public void execute(DelegateExecution delegateExecution) throws Exception {
        int warriors = (int) delegateExecution.getVariable("warriors");
        int enemyWarriors = (int) (Math.random() * 100);
        String battleStatus = "Undefined";
        boolean isWin = false;
 
 
        if ((warriors - enemyWarriors) > 0) {
            isWin = true;
            battleStatus = "Victory!";
        } else {
            battleStatus = "Fail :(";
        }
 
 
        delegateExecution.setVariable("enemyWarriors", enemyWarriors);
        delegateExecution.setVariable("battleStatus", battleStatus);
        delegateExectution.setVariable("isWin", isWin);
    }
}

Что касается самого процесса - удалим пользовательскую задачу и добавим шлюз. Чтобы определить по какому маршруту пойдет процесс необходимо кликнуть на стрелочку, соединяющую шлюз и пользовательскую задачу, и в Condition Type выбрать пункт Expression. В самом поле Expression в зависимости от пути написать ${isWin} - для маршрута "Celebrate victory" и ${!isWin} - для поражения.

Camunda для разработчика: Service Task Delegate Expression

После чего можем протестировать процесс. Обратите внимание, что если мы при старте процесса не зададим переменную warriors - количество наших воинов, то выработается exeption "The process could not be started. : Unsuccessful HTTP response". По логам видно, что это null pointer exection.

Camunda для разработчика: Unsuccessfull HTTP Response

Шаг 3: Добавить в описание процесса boundary error event и шлюз

Теперь добавим обработку событий в наш процесс. Для этого на сервисную задачу прикрепим Error Boundary Event, заполним его параметры. Добавим пользовательскую задачу, которая будет вызвана при срабатывании ошибки, чтобы заставить пользователя ввести число воинов:

Camunda для разработчика: Error boundary event

Чтобы обработать ошибку в код делегата допишем условие:

package com.reunico.demo;
 
import org.camunda.bpm.engine.delegate.DelegateExecution;
import org.camunda.bpm.engine.delegate.BpmnError;
import org.camunda.bpm.engine.delegate.JavaDelegate;
import org.springframework.stereotype.Component;
 
@Component
public class PrepareToBattle implements JavaDelegate {
 
    @Override
    public void execute(DelegateExecution delegateExecution) throws Exception {
        int warriors = (int) delegateExecution.getVariable("warriors");
        int enemyWarriors = (int) (Math.random() * 100);
        String battleStatus = "Undefined";
        boolean isWin = false;
 
 
        if ((warriors - enemyWarriors) > 0) {
            isWin = true;
 
            battleStatus = "Victory!";
 
        } else {
            battleStatus = "Fail :(";
 
        }
 
 
        if (warriors < 1 || warriors > 100) {
            throw new BpmnError("warriorsError");
        }
 
 
        delegateExecution.setVariable("enemyWarriors", enemyWarriors);
        delegateExecution.setVariable("battleStatus", battleStatus);
        delegateExectution.setVariable("isWin", isWin);
    }
}
Исходный код проекта на GitHub: https://github.com/mstislavm/camundaBattle.

Полное описание в видеоматериале на YouTube: