Маркеры задач

Этот раздел перенесён из документации Camunda 7 и в дальнейшем будет доработан с учётом особенностей OpenBPM Engine

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

Множественный экземпляр (Multiple Instance)

Активность с множественным экземпляром — это способ определить повторение определённого шага в бизнес-процессе. В программировании это соответствует конструкции for each: она позволяет выполнять определённый шаг или даже целый подпроцесс для каждого элемента заданной коллекции — последовательно или параллельно.

Множественный экземпляр — это обычная активность с дополнительными свойствами (так называемые multi-instance characteristics), которые приводят к многократному выполнению активности во время выполнения процесса. Следующие активности могут стать активностями с множественным экземпляром:

  • Service Task (Сервисная задача)

  • Send Task (Задача отправки)

  • User Task (Пользовательская задача)

  • Business Rule Task (Задача бизнес-правил)

  • Script Task (Скриптовая задача)

  • Receive Task (Задача получения)

  • Manual Task (Ручная задача)

  • (Embedded) Sub-Process (Встроенный подпроцесс)

  • Call Activity (Вызов активности)

  • Transaction Subprocess (Транзакционный подпроцесс)

Шлюз или событие не могут быть преобразованы в активность с множественным экземпляром.

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

multiple instance

Как предусмотрено спецификацией, каждое родительское выполнение созданных экземпляров будет содержать следующие переменные:

  • nrOfInstances — общее количество экземпляров

  • nrOfActiveInstances — количество активных (ещё не завершённых) экземпляров. Для последовательного множественного экземпляра это значение всегда равно 1

  • nrOfCompletedInstances — количество уже завершённых экземпляров

Эти значения можно получить с помощью метода execution.getVariable(x).

Кроме того, каждое из созданных выполнений будет иметь локальную переменную выполнения (то есть невидимую для других выполнений и не хранящуюся на уровне экземпляра процесса):

  • loopCounter — указывает индекс данного экземпляра в цикле for each

Чтобы сделать активность множественным экземпляром, XML-элемент активности должен иметь дочерний элемент multiInstanceLoopCharacteristics.

<multiInstanceLoopCharacteristics isSequential="false|true">
 ...
</multiInstanceLoopCharacteristics>

Атрибут isSequential указывает, выполняются ли экземпляры активности последовательно или параллельно.

Количество экземпляров вычисляется один раз при входе в активность. Существует несколько способов его настройки. Один из них — непосредственно указать число с помощью дочернего элемента loopCardinality.

<multiInstanceLoopCharacteristics isSequential="false|true">
  <loopCardinality>5</loopCardinality>
</multiInstanceLoopCharacteristics>

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

<multiInstanceLoopCharacteristics isSequential="false|true">
  <loopCardinality>${nrOfOrders-nrOfCancellations}</loopCardinality>
</multiInstanceLoopCharacteristics>

Другой способ задать количество экземпляров — указать имя переменной процесса, являющейся коллекцией, с помощью дочернего элемента loopDataInputRef. Для каждого элемента коллекции будет создан один экземпляр. Опционально можно передать конкретный элемент коллекции в экземпляр с помощью дочернего элемента inputDataItem. Это показано в следующем примере XML:

<userTask id="miTasks" name="My Task ${loopCounter}" camunda:assignee="${assignee}">
  <multiInstanceLoopCharacteristics isSequential="false">
    <loopDataInputRef>assigneeList</loopDataInputRef>
    <inputDataItem name="assignee" />
  </multiInstanceLoopCharacteristics>
</userTask>

Предположим, что переменная assigneeList содержит значения [kermit, gonzo, foziee]. В приведённом фрагменте будут созданы три параллельные пользовательские задачи. Каждое из выполнений будет иметь переменную процесса assignee, содержащую одно значение из коллекции, которое в данном примере используется для назначения пользовательской задачи.

Недостаток loopDataInputRef и inputDataItem состоит в том, что 1) их названия довольно сложно запомнить и 2) из-за ограничений схемы BPMN 2.0 они не могут содержать выражения. Эта проблема решена с помощью атрибутов collection и elementVariable в multiInstanceCharacteristics:

<userTask id="miTasks" name="My Task" camunda:assignee="${assignee}">
  <multiInstanceLoopCharacteristics isSequential="true"
     camunda:collection="${myService.resolveUsersForTask()}" camunda:elementVariable="assignee" >
  </multiInstanceLoopCharacteristics>
</userTask>

Активность с множественным экземпляром завершается, когда завершены все экземпляры. Однако можно указать выражение, которое будет вычисляться каждый раз при завершении одного экземпляра. Когда выражение принимает значение true, все оставшиеся экземпляры уничтожаются и активность с множественным экземпляром завершается, продолжая выполнение процесса. Такое выражение должно быть определено в дочернем элементе completionCondition.

<userTask id="miTasks" name="My Task" camunda:assignee="${assignee}">
  <multiInstanceLoopCharacteristics isSequential="false"
     camunda:collection="assigneeList" camunda:elementVariable="assignee" >
    <completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.6 }</completionCondition>
  </multiInstanceLoopCharacteristics>
</userTask>

В этом примере параллельные экземпляры создаются для каждого элемента коллекции assigneeList. Однако когда 60% задач завершены, остальные задачи удаляются и процесс продолжается.

Расширения OpenBPM Engine

Атрибуты

Элементы расширений

Ограничения

Атрибут camunda:exclusive обрабатывается только если атрибут camunda:asyncBefore или camunda:asyncAfter установлен в true

Граничные события и множественный экземпляр

Поскольку множественный экземпляр является обычной активностью, на её границе можно определить граничное событие. В случае прерывающего граничного события при его срабатывании все ещё активные экземпляры будут уничтожены. Например, рассмотрим следующий подпроцесс с множественным экземпляром:

multiple instance boundary

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

Циклы

Маркер цикла пока нативно не поддерживается движком. Для множественного экземпляра количество повторений известно заранее, что в любом случае делает его неподходящим кандидатом для реализации циклов, хотя условие завершения (completionCondition) может оказаться достаточным решением в некоторых случаях.

Чтобы обойти это ограничение, можно явно смоделировать цикл в BPMN-процессе:

loop alternative

Маркер цикла включён в наш бэклог для добавления в движок в будущем.

JSON-коллекции в множественных экземплярах

JSON-массивы, созданные с помощью OpenBPM Engine SPIN, можно использовать в качестве коллекции для активностей с множественным экземпляром. Рассмотрим следующий пример на JavaScript, инициализирующий переменную выполнения collection:

var collection = S('{ "collection" : ["System 1", "System 3"] }');
execution.setVariable("collection", collection);

Этот скрипт можно встроить в модель несколькими способами, например с помощью Script Task (Скриптовая задача).

Теперь можно использовать переменную collection в атрибуте расширения camunda:collection активности с множественным экземпляром:

<multiInstanceLoopCharacteristics
  camunda:collection="${collection.prop('collection').elements()}"
  camunda:elementVariable="collectionElem" />

Здесь используются методы SPIN .prop() и .elements() для получения JSON-массива. Задайте elementVariable активности с множественным экземпляром имя переменной, которая будет содержать элемент массива. Для доступа к значению элемента используйте метод .value() в переменной элемента.

Компенсация

Если активность используется для компенсации эффектов другой активности, её можно объявить обработчиком компенсации. Обработчики компенсации не входят в обычный поток и выполняются только при возникновении события компенсации.

compensation marker

Обратите внимание на значок обработчика компенсации в нижней центральной части сервисной задачи «cancel hotel reservation».

Обработчики компенсации не могут иметь входящих или исходящих потоков управления.

Обработчик компенсации должен быть связан с граничным событием компенсации с помощью направленной ассоциации.

Чтобы объявить активность обработчиком компенсации, необходимо установить атрибут isForCompensation в true:

<serviceTask id="undoBookHotel" isForCompensation="true" camunda:class="..." />

Лицензия и атрибуция

Эта документация была создана на базе материала "Camunda 7 Docs" от Camunda, находится под лицензией Creative Commons Attribution-ShareAlike 3.0 Unported License .

Оригинал документации: https://docs.camunda.org