Добро пожаловать в проект "Camunda BPM для разработчика"! Наш проект - это материалы с воркшопов и вебинаров, посвященные разработке Java-приложений с использованием процессного движка Camunda BPM.
По результатам каждого занятия публикуется полная запись (для участников), сокращенная запись (для размещения на Youtube) и исходный код проекта на GitHub.
Что необходимо иметь участнику workshop'а или разработчику, желающему самостоятельно повторить занятие?
- Компьютер с правами администратора 🖥
- Java JDK
- Camunda Modeler последней версии: https://camunda.com/download/modeler/
- Java IDE: https://www.jetbrains.com/idea/download/
- Пиво (или любой другой бодрящий напиток, опционально) 🍺
Занятие 1: Печенеги vs Половцы
Цели - научиться:
- Настраивать проект Camunda при помощи Camunda Boot Starter
- Создавать и разворачивать процесс
- Создавать Java код и связывать его с Camunda
- Работать с данными в процессе
- Обрабатывать события BPMN
Шаг 1: Сгенерировать проект Camunda с использованием Camunda BPM Initialzr
Необходимо открыть страницу по ссылке: https://start.camunda.com. Задать настройки проекта и нажать Generate Project.
Полученный архив с именем приложения распаковать и открыть в IDE. Например в IDEA - Import project - as Maven project. После открытия проекта - дождаться, пока Maven скачает зависимости. После, запустить приложение:
src/main/java/имя_приложения/Application.java > нажать правой кнопкой > >
После запуска приложения открыть браузер и перейти по ссылке: 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.
Усложним процесс, добавив в него больше вариативности. Добавим возможность ввести количество наших воинов, которых мы отправим в бой, модифицируем 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} - для поражения.
После чего можем протестировать процесс. Обратите внимание, что если мы при старте процесса не зададим переменную warriors - количество наших воинов, то выработается exeption "The process could not be started. : Unsuccessful HTTP response". По логам видно, что это null pointer exection.
Шаг 3: Добавить в описание процесса boundary error event и шлюз
Теперь добавим обработку событий в наш процесс. Для этого на сервисную задачу прикрепим 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:
https://www.youtube.com/watch?time_continue=4&v=BHxYtmJK6MQ&feature=emb_logo