Маркеры задач
|
Этот раздел перенесён из документации 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 (Транзакционный подпроцесс)
Шлюз или событие не могут быть преобразованы в активность с множественным экземпляром.
Если активность является множественным экземпляром, это обозначается тремя короткими линиями в нижней части активности. Три вертикальные линии указывают на то, что экземпляры будут выполняться параллельно, а три горизонтальные линии указывают на последовательное выполнение.
Как предусмотрено спецификацией, каждое родительское выполнение созданных экземпляров будет содержать следующие переменные:
-
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
Атрибуты |
|
Элементы расширений |
— |
Ограничения |
Атрибут |
Граничные события и множественный экземпляр
Поскольку множественный экземпляр является обычной активностью, на её границе можно определить граничное событие. В случае прерывающего граничного события при его срабатывании все ещё активные экземпляры будут уничтожены. Например, рассмотрим следующий подпроцесс с множественным экземпляром:
Здесь все экземпляры подпроцесса будут уничтожены при срабатывании таймера, независимо от того, сколько экземпляров существует и какие внутренние активности ещё не завершены.
Циклы
Маркер цикла пока нативно не поддерживается движком. Для множественного экземпляра количество повторений известно заранее, что в любом случае делает его неподходящим кандидатом для реализации циклов, хотя условие завершения (completionCondition) может оказаться достаточным решением в некоторых случаях.
Чтобы обойти это ограничение, можно явно смоделировать цикл в BPMN-процессе:
Маркер цикла включён в наш бэклог для добавления в движок в будущем.
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() в переменной элемента.
Компенсация
Если активность используется для компенсации эффектов другой активности, её можно объявить обработчиком компенсации. Обработчики компенсации не входят в обычный поток и выполняются только при возникновении события компенсации.
Обратите внимание на значок обработчика компенсации в нижней центральной части сервисной задачи «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