«1. Обзор

Activiti API — это система управления рабочими процессами и бизнес-процессами. Мы можем определить в нем процесс, выполнить его и манипулировать им по-разному, используя сервисы, предоставляемые API. Требуется JDK 7+.

Разработку с использованием API можно вести в любой IDE, но для использования Activiti Designer нам понадобится Eclipse.

Мы можем определить в нем процесс, используя стандарт BPMN 2.0. Есть и другой, менее популярный способ — использование классов Java, таких как StartEvent, EndEvent, UserTask, SequenceFlow и т. д.

Если мы хотим запустить процесс или получить доступ к какой-либо из служб, нам нужно создать ProcessEngineConfiguration.

Мы можем получить ProcessEngine с помощью ProcessEngineConfiguration некоторыми способами, которые мы обсудим далее в этой статье. Через ProcessEngine мы можем выполнять операции Workflow и BPMN.

2. Зависимости Maven

Чтобы использовать этот API, нам нужно включить зависимость Activiti:

<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-engine</artifactId>
</dependency>

3. Создание ProcessEngine

ProcessEngine в Activiti, как правило, настраивается с помощью файла XML, activiti .cfg.xml. Пример этого файла конфигурации:

<beans xmlns="...">
    <bean id="processEngineConfiguration" class=
      "org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
        <property name="jdbcUrl" 
          value="jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000" />
        <property name="jdbcDriver" value="org.h2.Driver" />
        <property name="jdbcUsername" value="root" />
        <property name="jdbcPassword" value="" />
        <property name="databaseSchemaUpdate" value="true" />
    </bean>
</beans>

Теперь мы можем получить ProcessEngine, используя класс ProcessEngines:

ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();

Этот оператор будет искать файл activiti.cfg.xml в пути к классам и создавать ProcessEngine на основе конфигурации в файле.

Пример кода для файла конфигурации показывает, что это просто конфигурация на основе Spring. Но это не означает, что мы можем использовать Activiti только в среде Spring. Возможности Spring используются только внутри для создания ProcessEngine.

Давайте напишем тестовый пример JUnit, который создаст ProcessEngine, используя показанный выше файл конфигурации:

@Test
public void givenXMLConfig_whenGetDefault_thenGotProcessEngine() {
    ProcessEngine processEngine 
      = ProcessEngines.getDefaultProcessEngine();
    assertNotNull(processEngine);
    assertEquals("root", processEngine.getProcessEngineConfiguration()
      .getJdbcUsername());
}

4. API и службы Activiti Process Engine

Точкой входа для взаимодействия с API является ProcessEngine. Через ProcessEngine мы можем получить доступ к различным службам, которые предоставляют методы рабочего процесса/BPMN. ProcessEngine и все сервисные объекты являются потокобезопасными.

Taken from https://www.activiti.org/userguide/images/api.services.png

Класс ProcessEngines просканирует файлы activiti.cfg.xml и activiti-context.xml. Как упоминалось ранее, для всех файлов activiti.cfg.xml ProcessEngine будет создан стандартным образом.

Принимая во внимание, что для всех файлов activiti-context.xml он будет создан способом Spring — я создам контекст приложения Spring и получу из него ProcessEngine. Во время выполнения процесса все шаги будут посещены в порядке, определенном в файле BPMN.

Во время выполнения процесса все шаги будут посещены в порядке, определенном в файле BPMN.

4.1. Определение процесса и родственные термины

ProcessDefinition представляет бизнес-процесс. Он используется для определения структуры и поведения различных этапов процесса. Развертывание определения процесса означает загрузку определения процесса в базу данных Activiti.

Определения процессов в основном определяются стандартом BPMN 2.0. Их также можно определить с помощью кода Java. Все термины, определенные в этом разделе, также доступны в виде классов Java.

Когда мы запускаем определение процесса, его можно называть процессом

ProcessInstance — это одно выполнение ProcessDefinition.

StartEvent связан с каждым бизнес-процессом. Он указывает точку входа в процесс. Точно так же существует EndEvent, указывающий на конец процесса. Мы можем определить условия для этих событий.

Все шаги (или элементы) между началом и концом называются задачами. Задания могут быть разных типов. Наиболее часто используемые задачи — UserTasks и ServiceTasks.

Пользовательские задачи, как следует из названия, таковы, что пользователь должен выполнять их вручную.

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

«SequenceFlows соединяют Задачи. Мы можем определить SequenceFlows по исходным и целевым элементам, которые они будут соединять. Опять же, мы также можем определить условия для SequenceFlows, чтобы создать условные пути в процессе.

4.2. Службы

Мы кратко обсудим службы, предоставляемые Activiti:

    RepositoryService помогает нам манипулировать развертыванием определений процессов. Эта служба работает со статическими данными, связанными с определением процесса. RuntimeService управляет экземплярами ProcessInstances (текущими запущенными процессами), а также переменными процесса. TaskService отслеживает пользовательские задачи. Задачи, которые пользователь должен выполнять вручную, лежат в основе API Activiti. Мы можем создать задачу, запросить и выполнить задачу, манипулировать исполнителем задачи и т. д., используя эту услугу. FormService является дополнительной услугой. API можно использовать без него и без ущерба для каких-либо его функций. Он используется для определения начальной формы и формы задачи в процессе. IdentityService управляет пользователями и группами. HistoryService отслеживает историю Activiti Engine. Мы также можем установить различные уровни истории. ManagementService связан с метаданными и обычно не требуется при создании приложения. DynamicBpmnService помогает нам что-либо изменить в процессе без его повторного развертывания

5. Работа со службами Activiti

Чтобы узнать, как мы можем работать с различными службами и Рассмотрим пример процесса «Запрос на отпуск сотрудника»:

В файле BPMN 2.0, VacationRequest.bpmn20.xml, для этого процесса стартовое событие будет определено как:

<startEvent id="startEvent" name="request" 
  activiti:initiator="employeeName">
    <extensionElements>
        <activiti:formProperty id="numberOfDays" 
          name="Number of days" type="long" required="true"/>
        <activiti:formProperty id="startDate" 
          name="Vacation start date (MM-dd-yyyy)" type="date" 
          datePattern="MM-dd-yyyy hh:mm" required="true"/>
        <activiti:formProperty id="reason" name="Reason for leave" 
          type="string"/>
     </extensionElements>
</startEvent>

Аналогично, первая пользовательская задача, назначенная группе пользователей «management», будет выглядеть следующим образом:

<userTask id="handle_vacation_request" name=
  "Handle Request for Vacation">
    <documentation>${employeeName} would like to take ${numberOfDays} day(s)
      of vacation (Motivation: ${reason}).</documentation>
    <extensionElements>
        <activiti:formProperty id="vacationApproved" name="Do you approve
          this vacation request?" type="enum" required="true"/>
        <activiti:formProperty id="comments" name="Comments from Manager"
          type="string"/>
    </extensionElements>
    <potentialOwner>
      <resourceAssignmentExpression>
        <formalExpression>management</formalExpression>
      </resourceAssignmentExpression>
    </potentialOwner>
</userTask>

В ServiceTask нам нужно определить фрагмент кода, который необходимо выполнить. У нас есть этот фрагмент кода в виде класса Java:

<serviceTask id="send-email-confirmation" name="Send email confirmation" 
  activiti:class=
  "com.example.activiti.servicetasks.SendEmailServiceTask.java">
</serviceTask>

Условный поток будет показан путем добавления тега «conditionExpression» в «sequenceFlow»:

<sequenceFlow id="flow3" name="approved" 
  sourceRef="sid-12A577AE-5227-4918-8DE1-DC077D70967C" 
  targetRef="send-email-confirmation">
    <conditionExpression xsi:type="tFormalExpression">
      <![CDATA[${vacationApproved == 'true'}]]>
    </conditionExpression>
</sequenceFlow>

Здесь VacationApproved — это formProperty пользовательской задачи, показанной выше.

Как видно на диаграмме, это очень простой процесс. Сотрудник составляет заявление на отпуск, указывая количество дней и дату начала отпуска. Заявка поступает к менеджеру. Они могут одобрить/отклонить запрос.

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

Служебные задачи снабжены некоторым фрагментом кода для выполнения (здесь в виде класса Java). Мы дали класс SendEmailServiceTask.java.

Эти типы классов должны расширять JavaDelegate. Также нам нужно переопределить его метод execute(), который будет выполняться, когда выполнение процесса достигнет этого шага.

5.1. Развертывание процесса

Чтобы сделать наш процесс известным движку Activiti, нам нужно развернуть процесс. Мы можем сделать это программно, используя RepositoryService. Давайте напишем тест JUnit, чтобы показать это:

@Test 
public void givenBPMN_whenDeployProcess_thenDeployed() {
    ProcessEngine processEngine 
      = ProcessEngines.getDefaultProcessEngine();
    RepositoryService repositoryService 
      = processEngine.getRepositoryService();
    repositoryService.createDeployment()
      .addClasspathResource(
      "org/activiti/test/vacationRequest.bpmn20.xml")
      .deploy();
    Long count=repositoryService.createProcessDefinitionQuery().count();
    assertEquals("1", count.toString());
}

Развертывание означает, что движок будет анализировать файл BPMN и преобразовывать его во что-то исполняемое. Кроме того, запись будет добавлена ​​в таблицу репозитория для каждого развертывания.

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

5.2. Запуск ProcessInstance

После развертывания ProcessDefinition в Activiti Engine мы можем выполнить процесс, создав ProcessInstances. ProcessDefinition — это план, а ProcessInstance — его выполнение во время выполнения.

Для одного ProcessDefinition может быть несколько ProcessInstances.

Все детали, связанные с ProcessInstances, доступны через RuntimeService.

В нашем примере в стартовом событии нужно передать количество дней отпуска, дату начала и причину. Мы будем использовать переменные процесса и передавать их при создании ProcessInstance.

Давайте напишем тестовый пример JUnit, чтобы лучше понять:

@Test
public void givenDeployedProcess_whenStartProcessInstance_thenRunning() {
    //deploy the process definition    
    Map<String, Object> variables = new HashMap>();
    variables.put("employeeName", "John");
    variables.put("numberOfDays", 4);
    variables.put("vacationMotivation", "I need a break!");
    
    RuntimeService runtimeService = processEngine.getRuntimeService();
    ProcessInstance processInstance = runtimeService
      .startProcessInstanceByKey("vacationRequest", variables);
    Long count=runtimeService.createProcessInstanceQuery().count();
 
    assertEquals("1", count.toString());
}

«

«Несколько экземпляров одного определения процесса будут отличаться переменными процесса.

Существует несколько способов запустить экземпляр процесса. Здесь мы используем ключ процесса. После запуска экземпляра процесса мы можем получить информацию о нем, запросив RuntimeService.

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

Когда наш экземпляр процесса запускается, первым шагом является пользовательская задача, назначенная группе пользователей «management».

У пользователя может быть почтовый ящик со списком задач, которые он должен выполнить. Теперь, если мы хотим продолжить выполнение процесса, пользователю необходимо завершить эту задачу. Для Activiti Engine это называется «выполнение задачи».

Мы можем запросить TaskService, чтобы получить объект задачи, а затем выполнить ее.

@Test 
public void givenProcessInstance_whenCompleteTask_thenGotNextTask() {
    // deploy process and start process instance   
    TaskService taskService = processEngine.getTaskService();
    List<Task> tasks = taskService.createTaskQuery()
      .taskCandidateGroup("management").list();
    Task task = tasks.get(0);
    
    Map<String, Object> taskVariables = new HashMap<>();
    taskVariables.put("vacationApproved", "false");
    taskVariables.put("comments", "We have a tight deadline!");
    taskService.complete(task.getId(), taskVariables);

    Task currentTask = taskService.createTaskQuery()
      .taskName("Modify vacation request").singleResult();
    assertNotNull(currentTask);
}

Код, который нам нужно написать для этого, выглядит так:

Обратите внимание, что метод complete() службы TaskService также принимает необходимые переменные процесса. Передаем ответ от менеджера.

После этого движок процесса перейдет к следующему шагу. Здесь следующий шаг спрашивает сотрудника, следует ли повторно отправить запрос на отпуск или нет.

Итак, наш ProcessInstance теперь ожидает в этой UserTask, которая имеет имя «Modify leave request».

5.4. Приостановка и активация процесса

@Test(expected = ActivitiException.class)
public void givenDeployedProcess_whenSuspend_thenNoProcessInstance() {
    // deploy the process definition
    repositoryService.suspendProcessDefinitionByKey("vacationRequest");
    runtimeService.startProcessInstanceByKey("vacationRequest");
}	

Мы можем приостановить ProcessDefinition, а также ProcessInstance. Если мы приостановим ProcessDefinition, мы не сможем создать его экземпляр, пока он приостановлен. Мы можем сделать это с помощью RepositoryService:

Чтобы активировать его снова, нам просто нужно вызвать один из методов relayService.activateProcessDefinitionXXX.

Точно так же мы можем приостановить ProcessInstance, используя RuntimeService.

6. Заключение

В этой статье мы увидели, как можно использовать Activiti с Java. Мы создали образец файла ProcessEngineCofiguration, который помогает нам создать ProcessEngine.

С его помощью мы получили доступ к различным сервисам, предоставляемым API. Эти сервисы помогают нам управлять и отслеживать ProcessDefinitions, ProcessInstances, UserTasks и т. д.