Перейти к основному содержимому

Введение в Activiti со Spring

· 5 мин. чтения

1. Обзор

Проще говоря, Activiti — это платформа управления рабочими процессами и бизнес-процессами.

Мы можем быстро приступить к работе, создав ProcessEngineConfiguration (обычно на основе файла конфигурации). Отсюда мы можем получить ProcessEngine , а через ProcessEngine мы можем выполнять рабочие процессы и операции BPM.

API предоставляет различные службы, которые можно использовать для доступа к процессам и управления ими. Эти сервисы могут предоставить нам информацию об истории процессов, о том, что запущено в данный момент, и о процессах, которые развернуты, но еще не запущены.

Службы также можно использовать для определения структуры процесса и управления состоянием процесса, т. е. запуска, приостановки, отмены и т. д.

Если вы не знакомы с API, ознакомьтесь с нашим Введением в API Activiti с Java . В этой статье мы обсудим, как настроить Activiti API в приложении Spring Boot.

2. Настройка с помощью Spring Boot

Давайте посмотрим, как мы можем настроить Activiti как приложение Spring Boot Maven и начать его использовать.

2.1. Начальная настройка

Как обычно, нам нужно добавить зависимость maven:

<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-basic</artifactId>
</dependency>

Последнюю стабильную версию API можно найти здесь . Он работает с Spring Boot до версии 1.5.4. С v2.0.0.M1 пока не работает.

Мы также можем создать проект Spring Boot, используя https://start.spring.io , и выбрать Activiti в качестве зависимости.

Просто добавив эту зависимость и аннотацию @EnableAutoConfiguration в приложение Spring Boot, оно выполнит первоначальную настройку:

  • Создать источник данных (для API требуется база данных для создания ProcessEngine )
  • Создание и предоставление bean -компонента ProcessEngine
  • Создание и предоставление компонентов служб Activiti
  • Создайте исполнителя заданий Spring

2.2. Создание и запуск процесса

Построим пример создания и запуска бизнес-процесса. Чтобы определить процесс, нам нужно создать файл BPMN.

Затем просто скачайте файл BPMN. Нам нужно будет поместить этот файл в папку src/main/resources/processes . По умолчанию Spring Boot будет искать в этой папке развертывание определения процесса.

Мы создадим демонстрационный процесс, содержащий одну пользовательскую задачу:

./e928901a99ae7bcac06d4eabf5f44fd1.jpg

Инициатором процесса назначается исполнитель пользовательской задачи. Файл BPMN для этого определения процесса выглядит так:

<process id="my-process" name="say-hello-process" isExecutable="true">
<startEvent id="startEvent" name="startEvent">
</startEvent>
<sequenceFlow id="sequence-flow-1" sourceRef="startEvent" targetRef="A">
</sequenceFlow>
<userTask id="A" name="A" activiti:assignee="$INITIATOR">
</userTask>
<sequenceFlow id="sequence-flow-2" sourceRef="A" targetRef="endEvent">
</sequenceFlow>
<endEvent id="endEvent" name="endEvent">
</endEvent>
</process>

Теперь мы создадим контроллер REST для обработки запросов на запуск этого процесса:

@Autowired
private RuntimeService runtimeService;

@GetMapping("/start-process")
public String startProcess() {

runtimeService.startProcessInstanceByKey("my-process");
return "Process started. Number of currently running"
+ "process instances = "
+ runtimeService.createProcessInstanceQuery().count();
}

Здесь runtimeService.startProcessInstanceByKey("my-process") запускает выполнение процесса, ключ которого "my-process" . runtimeService.createProcessInstanceQuery().count() даст нам количество экземпляров процесса.

Каждый раз, когда мы переходим по пути «/start-process» , будет создаваться новый экземпляр ProcessInstance , и мы увидим увеличение количества запущенных в данный момент процессов.

Тестовый пример JUnit показывает нам это поведение:

@Test
public void givenProcess_whenStartProcess_thenIncreaseInProcessInstanceCount()
throws Exception {

String responseBody = this.mockMvc
.perform(MockMvcRequestBuilders.get("/start-process"))
.andReturn().getResponse().getContentAsString();

assertEquals("Process started. Number of currently running"
+ " process instances = 1", responseBody);

responseBody = this.mockMvc
.perform(MockMvcRequestBuilders.get("/start-process"))
.andReturn().getResponse().getContentAsString();

assertEquals("Process started. Number of currently running"
+ " process instances = 2", responseBody);

responseBody = this.mockMvc
.perform(MockMvcRequestBuilders.get("/start-process"))
.andReturn().getResponse().getContentAsString();

assertEquals("Process started. Number of currently running"
+ " process instances = 3", responseBody);
}

3. Игра с процессами

Теперь, когда у нас есть запущенный процесс в Activiti с использованием Spring Boot, давайте расширим приведенный выше пример, чтобы продемонстрировать, как мы можем получить доступ к процессу и управлять им.

3.1. Получить список задач для данного ProcessInstance

У нас есть две пользовательские задачи A и B . Когда мы запускаем процесс, он будет ждать завершения первой задачи A , а затем выполнит задачу B. Давайте создадим метод-обработчик, который принимает запросы на просмотр задач, связанных с данным processInstance .

Такие объекты, как Task , не могут быть отправлены в качестве ответа напрямую, поэтому нам нужно создать пользовательский объект и преобразовать Task в наш пользовательский объект. Мы назовем этот класс TaskRepresentation :

class TaskRepresentation {
private String id;
private String name;
private String processInstanceId;

// standard constructors
}

Метод обработчика будет выглядеть так:

@GetMapping("/get-tasks/{processInstanceId}")
public List<TaskRepresentation> getTasks(
@PathVariable String processInstanceId) {

List<Task> usertasks = taskService.createTaskQuery()
.processInstanceId(processInstanceId)
.list();

return usertasks.stream()
.map(task -> new TaskRepresentation(
task.getId(), task.getName(), task.getProcessInstanceId()))
.collect(Collectors.toList());
}

Здесь taskService.createTaskQuery().processInstanceId(processInstanceId).list() использует TaskService и получает список задач, связанных с данным processInstanceId . Мы видим, что когда мы запустим созданный нами процесс, мы получим задачу A , сделав запрос к только что определенному нами методу:

@Test
public void givenProcess_whenProcessInstance_thenReceivedRunningTask()
throws Exception {

this.mockMvc.perform(MockMvcRequestBuilders.get("/start-process"))
.andReturn()
.getResponse();
ProcessInstance pi = runtimeService.createProcessInstanceQuery()
.orderByProcessInstanceId()
.desc()
.list()
.get(0);
String responseBody = this.mockMvc
.perform(MockMvcRequestBuilders.get("/get-tasks/" + pi.getId()))
.andReturn()
.getResponse()
.getContentAsString();

ObjectMapper mapper = new ObjectMapper();
List<TaskRepresentation> tasks = Arrays.asList(mapper
.readValue(responseBody, TaskRepresentation[].class));

assertEquals(1, tasks.size());
assertEquals("A", tasks.get(0).getName());
}

3.2. Завершение задачи

Теперь посмотрим, что произойдет, когда мы выполним задачу А. Мы создаем метод-обработчик, который будет обрабатывать запросы на выполнение задачи A для данного processInstance :

@GetMapping("/complete-task-A/{processInstanceId}")
public void completeTaskA(@PathVariable String processInstanceId) {
Task task = taskService.createTaskQuery()
.processInstanceId(processInstanceId)
.singleResult();
taskService.complete(task.getId());
}

taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult() создает запрос в службе задач и дает нам задачу данного processInstance . Это UserTask A . Следующая строка taskService.complete(task.getId) завершает эту задачу.

Следовательно, теперь процесс достиг конца, и RuntimeService не содержит ProcessInstances . Мы можем увидеть это, используя тестовый пример JUnit:

@Test
public void givenProcess_whenCompleteTaskA_thenNoProcessInstance()
throws Exception {

this.mockMvc.perform(MockMvcRequestBuilders.get("/start-process"))
.andReturn()
.getResponse();
ProcessInstance pi = runtimeService.createProcessInstanceQuery()
.orderByProcessInstanceId()
.desc()
.list()
.get(0);
this.mockMvc.perform(MockMvcRequestBuilders.get("/complete-task-A/" + pi.getId()))
.andReturn()
.getResponse()
.getContentAsString();
List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().list();
assertEquals(0, list.size());
}

Вот как мы можем использовать сервисы Activiti для работы с процессами.

4. Вывод

В этой статье мы прошли обзор использования Activiti API с Spring Boot . Более подробную информацию об API можно найти в руководстве пользователя . Мы также увидели, как создать процесс и выполнять над ним различные операции с помощью сервисов Activiti.

Spring Boot упрощает использование, поскольку нам не нужно беспокоиться о создании базы данных, развертывании процессов или создании ProcessEngine .

Имейте в виду, что интеграция Activiti с Spring Boot все еще находится на экспериментальной стадии и еще не поддерживается Spring Boot 2.

Как всегда, реализацию всех увиденных нами примеров можно найти на GitHub .