Модификация экземпляра процесса

Модель процесса задает потоками управления порядок, в котором выполняются активити. Но иногда нужно гибко запустить активити повторно или отменить выполняющуюся активити. Это пригодится, например, если в модели процесса есть ошибка (скажем, неверное условие потока управления) и выполняющиеся экземпляры процесса нужно исправить. Сценарии использования этого API:

  • исправление экземпляров процесса, в которых нужно повторить или пропустить отдельные шаги

  • перенос экземпляров процесса с одной версии определения процесса на другую

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

Для таких операций процессный движок предоставляет API модификации экземпляра процесса (process instance modification API). Точки входа: RuntimeService.createProcessInstanceModification(…​) и RuntimeService.createModification(…​). С помощью fluent builder за один вызов можно задать несколько инструкций модификации (modification instructions). В частности, можно:

  • начать выполнение перед активити

  • начать выполнение на потоке управления, выходящем из активити

  • отменить выполняющийся экземпляр активности

  • отменить все выполняющиеся экземпляры заданной активити

  • передать переменные с каждой инструкцией

Модификация собственного экземпляра процесса

Не рекомендуется модифицировать экземпляр процесса изнутри этого же экземпляра. Активити, которая пытается модифицировать собственный экземпляр процесса, может вызвать неопределенное поведение.

Модификацию экземпляра процесса на BPMN-диаграмме можно выполнить в интерфейсе OpenBPM Control.

Модификация экземпляра процесса на примере

В качестве примера рассмотрим такую модель процесса:

BPMN-диаграмма примера

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

ProcessInstance
  Decline Loan Application

Теперь сотрудник, выполняющий задачу Decline Loan Application, замечает ошибку в результате оценки и приходит к выводу, что заявку все-таки нужно принять. Такая гибкость в модели процесса не предусмотрена, но модификация экземпляра процесса позволяет исправить выполняющийся экземпляр. Для этого достаточно следующего вызова API:

ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().singleResult();
runtimeService.createProcessInstanceModification(processInstance.getId())
  .startBeforeActivity("acceptLoanApplication")
  .cancelAllForActivity("declineLoanApplication")
  .execute();

Эта команда сначала начинает выполнение перед активити Accept Loan Application и продолжает его до состояния ожидания, в данном случае до создания пользовательской задачи. Затем она отменяет выполняющийся экземпляр активити Decline Loan Application. В списке задач сотрудника задача Decline исчезает и появляется задача Accept. Итоговое дерево экземпляров активности:

ProcessInstance
  Accept Loan Application

Допустим, при одобрении заявки должна существовать переменная approver. Для этого запрос на модификацию можно расширить:

ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().singleResult();
runtimeService.createProcessInstanceModification(processInstance.getId())
  .startBeforeActivity("acceptLoanApplication")
  .setVariable("approver", "joe")
  .cancelAllForActivity("declineLoanApplication")
  .execute();

Добавленный вызов setVariable передает указанную переменную до запуска активити.

Теперь случаи посложнее. Пусть заявка снова не прошла проверку и активна активити Decline Loan Application. Сотрудник понимает, что оценка была проведена с ошибкой, и хочет полностью запустить ее заново. Ниже приведены варианты инструкций модификации для этой задачи.

Запустить активити подпроцесса:

ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().singleResult();
runtimeService.createProcessInstanceModification(processInstance.getId())
  .cancelAllForActivity("declineLoanApplication")
  .startBeforeActivity("assessCreditWorthiness")
  .startBeforeActivity("registerApplication")
  .execute();

Начать со стартового события подпроцесса:

ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().singleResult();
runtimeService.createProcessInstanceModification(processInstance.getId())
  .cancelAllForActivity("declineLoanApplication")
  .startBeforeActivity("subProcessStartEvent")
  .execute();

Запустить сам подпроцесс:

ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().singleResult();
runtimeService.createProcessInstanceModification(processInstance.getId())
  .cancelAllForActivity("declineLoanApplication")
  .startBeforeActivity("evaluateLoanApplication")
  .execute();

Начать со стартового события процесса:

ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().singleResult();
runtimeService.createProcessInstanceModification(processInstance.getId())
  .cancelAllForActivity("declineLoanApplication")
  .startBeforeActivity("processStartEvent")
  .execute();

Модификация экземпляра процесса в тестах JUnit

Модификация экземпляра процесса очень полезна в тестах JUnit. Не нужно проходить весь процесс до проверяемого места: можно сразу перейти к нужной активити или шлюзу.

Для этого экземпляр процесса запускается с модификацией, и токен ставится сразу в нужное место.

Например, чтобы пропустить подпроцесс Evaluate Loan Application и проверить шлюз Application ok? со своей переменной процесса, запустите экземпляр процесса так:

ProcessInstance processInstance = runtimeService.createProcessInstanceByKey("Loan_Application")
  .startBeforeActivity("application_OK")
  .setVariable("approved", true)
  .execute();

После этого в тесте JUnit можно проверить, что экземпляр процесса ожидает в активити Accept Loan Application.

Операционная семантика

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

BPMN-диаграмма примера

Типы инструкций модификации

Fluent builder модификации экземпляра процесса принимает следующие инструкции:

  • startBeforeActivity(String activityId)

  • startBeforeActivity(String activityId, String ancestorActivityInstanceId)

  • startAfterActivity(String activityId)

  • startAfterActivity(String activityId, String ancestorActivityInstanceId)

  • startTransition(String transitionId)

  • startTransition(String transitionId, String ancestorActivityInstanceId)

  • cancelActivityInstance(String activityInstanceId)

  • cancelTransitionInstance(String transitionInstanceId)

  • cancelAllForActivity(String activityId)

Запуск перед активити

ProcessInstanceModificationBuilder#startBeforeActivity(String activityId)
ProcessInstanceModificationBuilder#startBeforeActivity(String activityId, String ancestorActivityInstanceId)

Инструкция startBeforeActivity начинает выполнение перед входом в активити. Она учитывает флаг asyncBefore: если у активити задан asyncBefore, создается задание (job). В общем случае инструкция выполняет модель процесса, начиная с указанной активити, до состояния ожидания. Подробнее о состояниях ожидания см. в разделе Транзакции в процессах.

Запуск после активити

ProcessInstanceModificationBuilder#startAfterActivity(String activityId)
ProcessInstanceModificationBuilder#startAfterActivity(String activityId, String ancestorActivityInstanceId)

Инструкция startAfterActivity начинает выполнение на единственном исходящем потоке управления активити. Флаг asyncAfter этой активити не учитывается. Если исходящих потоков управления больше одного или нет совсем, инструкция завершается ошибкой. При успехе инструкция выполняет модель процесса, начиная с этого потока управления, до состояния ожидания.

Запуск перехода

ProcessInstanceModificationBuilder#startTransition(String transitionId)
ProcessInstanceModificationBuilder#startTransition(String transitionId, String ancestorActivityInstanceId)

Инструкция startTransition начинает выполнение на заданном потоке управления. Ее можно использовать вместо startAfterActivity, когда исходящих потоков управления больше одного. При успехе инструкция выполняет модель процесса, начиная с этого потока управления, до состояния ожидания.

Отмена экземпляра активности

ProcessInstanceModificationBuilder#cancelActivityInstance(String activityInstanceId)

Инструкция cancelActivityInstance отменяет конкретный экземпляр активности. Это может быть как листовой экземпляр активности, например экземпляр пользовательской задачи, так и экземпляр области видимости выше по иерархии, например экземпляр подпроцесса. Как получить экземпляры активности для экземпляра процесса, описано в разделе API на основе экземпляров активности.

Отмена экземпляра перехода

ProcessInstanceModificationBuilder#cancelTransitionInstance(String transitionInstanceId)

Экземпляры перехода (transition instances) представляют потоки выполнения, которые собираются войти в активити или выйти из нее в виде асинхронного продолжения. Задание асинхронного продолжения, которое уже создано, но еще не выполнено, представлено экземпляром перехода. Такие экземпляры отменяет инструкция cancelTransitionInstance. Как получить экземпляры перехода для экземпляра процесса, описано в разделе API на основе экземпляров активности.

Отмена всех экземпляров активности для активити

ProcessInstanceModificationBuilder#cancelAllForActivity(String activityId)

Для удобства есть инструкция cancelAllForActivity: она отменяет все экземпляры активности и экземпляры перехода заданной активити.

Передача переменных

С каждой инструкцией, создающей экземпляры (startBeforeActivity, startAfterActivity или startTransition), можно передать переменные процесса. Для этого в API есть методы:

  • setVariable(String name, Object value)

  • setVariables(Map<String, Object> variables)

  • setVariableLocal(String name, Object value)

  • setVariablesLocal(Map<String, Object> variables)

Переменные устанавливаются после создания областей видимости, нужных для запуска (см. Вложенный запуск), и до начала выполнения указанного элемента. Поэтому для инструкций startBefore и startAfter в истории процессного движка эти переменные не выглядят так, будто их установили во время выполнения указанной активити. Локальные переменные устанавливаются на том выполнении, которое выполняет инструкцию, то есть входит в активити и т. п.

Подробнее о переменных и областях видимости см. в разделе Переменные.

API на основе экземпляров активности

API модификации экземпляра процесса построен на экземплярах активности (activity instances). Дерево экземпляров активности для экземпляра процесса возвращает следующий метод:

ProcessInstance processInstance = ...;
ActivityInstance activityInstance = runtimeService.getActivityInstance(processInstance.getId());

ActivityInstance это рекурсивная структура данных, и экземпляр активности, который возвращает этот метод, представляет сам экземпляр процесса. Идентификаторы объектов ActivityInstance используются для отмены конкретных экземпляров (см. Отмена экземпляра активности) и для выбора предка при запуске (см. Выбор предка при запуске).

Для спуска по дереву экземпляров активности в интерфейсе ActivityInstance есть методы getChildActivityInstances и getChildTransitionInstances. Пусть, например, активны активити Assess Credit Worthiness и Register Application. Тогда дерево экземпляров активности выглядит так:

ProcessInstance
  Evaluate Loan Application
    Assess Credit Worthiness
    Register Application

В коде экземпляры активности Assess и Register можно получить так:

ProcessInstance processInstance = ...;
ActivityInstance activityInstance = runtimeService.getActivityInstance(processInstance.getId());
ActivityInstance subProcessInstance = activityInstance.getChildActivityInstances()[0];
ActivityInstance[] leafActivityInstances = subProcessInstance.getChildActivityInstances();
// в leafActivityInstances два элемента, по одному на каждую активити

Можно также сразу получить все экземпляры активности для заданной активити:

ProcessInstance processInstance = ...;
ActivityInstance activityInstance = runtimeService.getActivityInstance(processInstance.getId());
ActivityInstance assessCreditWorthinessInstances = activityInstance.getActivityInstances("assessCreditWorthiness")[0];

В отличие от экземпляров активности, экземпляры перехода представляют не активные активити, а активити, в которые выполнение вот-вот войдет или из которых вот-вот выйдет. Так бывает, когда задания асинхронных продолжений уже существуют, но еще не выполнены. Дочерние экземпляры перехода у экземпляра активности возвращает метод getChildTransitionInstances. API для экземпляров перехода похож на API для экземпляров активности.

Вложенный запуск

Возьмем экземпляр процесса из примера выше, в котором активна активити Decline Loan Application. Отправим инструкцию запуска перед активити Assess Credit Worthiness. При ее применении процессный движок создает экземпляры всех родительских областей видимости, которые еще не активны. В данном случае перед запуском активити движок создает экземпляр подпроцесса Evaluate Loan Application. Дерево экземпляров активности было таким:

ProcessInstance
  Decline Loan Application

а стало таким:

ProcessInstance
  Decline Loan Application
  Evaluate Loan Application
    Assess Credit Worthiness

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

BPMN-диаграмма примера

При запуске активити Assess Credit Worthiness регистрируется и подписка на граничное событие сообщения Cancelation Notice Received, так что подпроцесс можно отменить этим событием.

Выбор предка при запуске

По умолчанию запуск активити создает экземпляры всех родительских областей видимости, у которых еще нет экземпляров. Если дерево экземпляров активности такое:

ProcessInstance
  Decline Loan Application

то запуск Assess Credit Worthiness дает следующее дерево:

ProcessInstance
  Decline Loan Application
  Evaluate Loan Application
    Assess Credit Worthiness

Экземпляр области видимости подпроцесса тоже создан. Теперь допустим, что экземпляр подпроцесса уже существует, как в этом дереве:

ProcessInstance
  Evaluate Loan Application
    Assess Credit Worthiness

Повторный запуск Assess Credit Worthiness выполнится в контексте существующего экземпляра подпроцесса, и дерево станет таким:

ProcessInstance
  Evaluate Loan Application
    Assess Credit Worthiness
    Assess Credit Worthiness

Если нужно другое поведение и подпроцесс должен получить второй экземпляр, передайте идентификатор экземпляра активности предка в метод startBeforeActivity(String activityId, String ancestorActivityInstanceId). Аналогичные методы есть для запуска после активити и для запуска перехода. Параметр ancestorActivityInstanceId принимает идентификатор экземпляра активности, который сейчас активен и относится к активити-предку запускаемой активити. Активити считается допустимым предком, если она содержит запускаемую активити напрямую или через другие вложенные активити.

Если идентификатор экземпляра активности предка задан, создаются экземпляры всех областей видимости между предком и запускаемой активити, даже если экземпляры у них уже есть. В примере следующий код запускает активити Assess Credit Worthiness, указывая предком экземпляр процесса (он же корневой экземпляр активности):

ProcessInstance processInstance = ...;
ActivityInstance activityInstanceTree = runtimeService.getActivityInstance(processInstance.getId());
runtimeService.createProcessInstanceModification(activityInstanceTree.getId())
  .startBeforeActivity("assessCreditWorthiness", processInstance.getId())
  .execute();

Итоговое дерево экземпляров активности:

ProcessInstance
  Evaluate Loan Application
    Assess Credit Worthiness
  Evaluate Loan Application
    Assess Credit Worthiness

Подпроцесс запущен второй раз.

Распространение отмены

Отмена экземпляра активности распространяется на родительские экземпляры активности, в которых не остается других экземпляров активности. Так экземпляр процесса не остается в бессмысленном состоянии выполнения. Например, если в подпроцессе активна одна активити и ее экземпляр отменяется, подпроцесс тоже отменяется. Рассмотрим такое дерево экземпляров активности:

ProcessInstance
  Decline Loan Application
  Evaluate Loan Application
    Assess Credit Worthiness

После отмены экземпляра активности Assess Credit Worthiness дерево становится таким:

ProcessInstance
  Decline Loan Application

Если после выполнения всех инструкций не осталось ни одного активного экземпляра активности, отменяется весь экземпляр процесса. В примере выше так случилось бы, если бы отменили оба экземпляра активности: Assess Credit Worthiness и Decline Loan Application.

Экземпляр процесса отменяется только после выполнения всех инструкций. Если между двумя инструкциями в экземпляре процесса нет активных экземпляров активности, экземпляр процесса не отменяется сразу. Допустим, активна активити Decline Loan Application. Дерево экземпляров активности:

ProcessInstance
  Decline Loan Application

Следующая модификация выполнится успешно, хотя сразу после инструкции отмены в экземпляре процесса нет активных экземпляров активности:

ProcessInstance processInstance = ...;
runtimeService.createProcessInstanceModification(processInstance.getId())
  .cancelAllForActivity("declineLoanApplication")
  .startBeforeActivity("acceptLoanApplication")
  .execute();

Порядок выполнения инструкций

Инструкции модификации всегда выполняются в том порядке, в котором их передали. Поэтому одни и те же инструкции в разном порядке могут дать разный результат. Рассмотрим такое дерево экземпляров активности:

ProcessInstance
  Evaluate Loan Application
    Assess Credit Worthiness

Допустим, нужно отменить экземпляр Assess Credit Worthiness и запустить активити Register Application. Эти две инструкции можно упорядочить двумя способами: сначала отмена или сначала запуск. В первом случае код такой:

ProcessInstance processInstance = ...;
runtimeService.createProcessInstanceModification(processInstance.getId())
  .cancelAllForActivity("assessCreditWorthiness")
  .startBeforeActivity("registerApplication")
  .execute();

Из-за распространения отмены экземпляр подпроцесса отменяется при выполнении инструкции отмены, а при выполнении инструкции запуска создается заново. После модификации получается другой экземпляр подпроцесса Evaluate Loan Application. Все сущности, связанные с прежним экземпляром, например переменные и подписки на события, удалены.

Теперь рассмотрим случай, когда сначала выполняется запуск:

ProcessInstance processInstance = ...;
runtimeService.createProcessInstanceModification(processInstance.getId())
  .startBeforeActivity("registerApplication")
  .cancelAllForActivity("assessCreditWorthiness")
  .execute();

Из-за выбора предка по умолчанию при запуске и того, что отмена в этом случае не распространяется на экземпляр подпроцесса, после модификации экземпляр подпроцесса остается прежним. Связанные с ним сущности, например переменные и подписки на события, сохраняются.

Запуск активити с прерывающей или отменяющей семантикой

Модификация экземпляра процесса учитывает прерывающую или отменяющую семантику запускаемых активити. В частности, запуск прерывающего граничного события или прерывающего событийного подпроцесса отменяет (прерывает) активити, к которой привязано событие или в которой определен подпроцесс. Рассмотрим такой процесс:

BPMN-диаграмма примера

Допустим, сейчас активна активити Assess Credit Worthiness. Событийный подпроцесс можно запустить следующим кодом:

ProcessInstance processInstance = ...;
runtimeService.createProcessInstanceModification(processInstance.getId())
  .startBeforeActivity("cancelEvaluation")
  .execute();

Стартовое событие подпроцесса Cancel Evaluation прерывающее, поэтому выполняющийся экземпляр Assess Credit Worthiness будет отменен. То же произойдет, если запустить стартовое событие событийного подпроцесса:

ProcessInstance processInstance = ...;
runtimeService.createProcessInstanceModification(processInstance.getId())
  .startBeforeActivity("eventSubProcessStartEvent")
  .execute();

Но если напрямую запустить активити внутри событийного подпроцесса, прерывание не выполняется. Рассмотрим такой код:

ProcessInstance processInstance = ...;
runtimeService.createProcessInstanceModification(processInstance.getId())
  .startBeforeActivity("notifyAccountant")
  .execute();

Итоговое дерево экземпляров активности:

ProcessInstance
  Evaluate Loan Application
    Assess Credit Worthiness
    Cancel Evaluation
      Notify Accountant

Модификация экземпляров активити с множественным экземпляром

Модификация работает и для активити с множественным экземпляром (multi-instance). Ниже различаются тело множественного экземпляра (multi-instance body) и внутренняя активити (inner activity). Внутренняя активити это сама активити с идентификатором из модели процесса. Тело множественного экземпляра представляет собой область видимости вокруг этой активити, которая не отображается в модели процесса отдельным элементом. Для активити с идентификатором anActivityId тело множественного экземпляра по соглашению получает идентификатор anActivityId#multiInstanceBody.

Благодаря этому разделению можно запустить как тело множественного экземпляра целиком, так и отдельный экземпляр внутренней активити у выполняющейся параллельной активити с множественным экземпляром. Рассмотрим такую модель процесса:

BPMN-диаграмма примера

Допустим, активити с множественным экземпляром активна и у нее три экземпляра:

ProcessInstance
  Contact Customer - Multi-Instance Body
    Contact Customer
    Contact Customer
    Contact Customer

Следующая модификация запускает четвертый экземпляр активити Contact Customer в том же теле множественного экземпляра:

ProcessInstance processInstance = ...;
runtimeService.createProcessInstanceModification(processInstance.getId())
  .startBeforeActivity("contactCustomer")
  .execute();

Итоговое дерево экземпляров активности:

ProcessInstance
  Contact Customer - Multi-Instance Body
    Contact Customer
    Contact Customer
    Contact Customer
    Contact Customer

Процессный движок корректно обновляет переменные множественного экземпляра nrOfInstances, nrOfActiveInstances и loopCounter. Если активити с множественным экземпляром настроена по коллекции, при выполнении инструкции коллекция не учитывается и переменная элемента коллекции для дополнительного экземпляра не заполняется. Чтобы ее заполнить, передайте переменную элемента коллекции вместе с инструкцией запуска через метод #setVariableLocal.

Теперь рассмотрим такой запрос:

ProcessInstance processInstance = ...;
runtimeService.createProcessInstanceModification(processInstance.getId())
  .startBeforeActivity("contactCustomer#multiInstanceBody")
  .execute();

Он запускает тело множественного экземпляра целиком второй раз, и дерево экземпляров активности становится таким:

ProcessInstance
  Contact Customer - Multi-Instance Body
    Contact Customer
    Contact Customer
    Contact Customer
    Contact Customer
  Contact Customer - Multi-Instance Body
    Contact Customer
    Contact Customer
    Contact Customer

Асинхронная модификация экземпляра процесса

Модификацию одного экземпляра процесса можно выполнить асинхронно. Инструкции модификации те же, что и при синхронной модификации, а вызов fluent builder выглядит так:

Batch modificationBatch = runtimeService.createProcessInstanceModification(processInstanceId)
        .cancelActivityInstance("exampleActivityId:1")
        .startBeforeActivity("exampleActivityId:2")
        .executeAsync();

Этот вызов создает пакетную операцию (batch) модификации, которая выполняется асинхронно. При асинхронной модификации одного экземпляра процесса передавать переменные нельзя.

Модификация нескольких экземпляров процесса

Если условию удовлетворяют несколько экземпляров процесса, их можно модифицировать за один раз с помощью RuntimeService.createModification(…​). Этот метод позволяет задать инструкции модификации и идентификаторы экземпляров процесса, которые нужно модифицировать. Экземпляры процесса должны относиться к заданному определению процесса.

Fluent builder модификации принимает следующие инструкции:

  • startBeforeActivity(String activityId)

  • startAfterActivity(String activityId)

  • startTransition(String transitionId)

  • cancelAllForActivity(String activityId)

Экземпляры процесса для модификации выбираются по набору идентификаторов или по запросу экземпляров процесса. Можно указать и то и другое: список идентификаторов и запрос. Тогда модифицируется объединение полученных множеств.

ProcessInstanceQuery processInstanceQuery = runtimeService.createProcessInstanceQuery();

runtimeService.createModification("exampleProcessDefinitionId")
  .cancelAllForActivity("exampleActivityId:1")
  .startBeforeActivity("exampleActivityId:2")
  .processInstanceQuery(processInstanceQuery)
  .processInstanceIds("processInstanceId:1", "processInstanceId:2")
  .execute();

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

Пример синхронного выполнения:

runtimeService.createModification("exampleProcessDefinitionId")
  .cancelAllForActivity("exampleActivityId:1")
  .startBeforeActivity("exampleActivityId:2")
  .processInstanceIds("processInstanceId:1", "processInstanceId:2")
  .execute();

Пример асинхронного выполнения:

Batch batch = runtimeService.createModification("exampleProcessDefinitionId")
  .cancelAllForActivity("exampleActivityId:1")
  .startBeforeActivity("exampleActivityId:2")
  .processInstanceIds("processInstanceId:1", "processInstanceId:2", "processInstanceId:100")
  .executeAsync();

Пропуск слушателей и маппинга входных/выходных параметров

В транзакции, которая выполняет модификацию, можно не вызывать слушатели выполнения и задач, а также маппинг входных/выходных параметров. Это полезно, если модификация выполняется в системе, у которой нет доступа к развертываниям затронутых процессных приложений и их классам. Для одного экземпляра процесса используйте метод execute(boolean skipCustomListeners, boolean skipIoMappings) (или executeAsync с теми же параметрами), для нескольких экземпляров процесса методы skipCustomListeners() и skipIoMappings() builder’а модификации.

Аннотация

Метод setAnnotation передает произвольный текст аннотации для аудита.

runtimeService.createProcessInstanceModification(processInstanceId)
  .cancelAllForActivity("declineLoanApplication")
  .startBeforeActivity("processStartEvent")
  .setAnnotation("Modified to resolve an error.")
  .execute();

Аннотация отображается в журнале пользовательских операций для выполненной модификации.

Проверка корректности

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

BPMN-диаграмма примера

Допустим, активна активити Decline Loan Application. С помощью модификации можно запустить активити Assess Credit Worthiness. Когда она завершится, выполнение застрянет на объединяющем параллельном шлюзе: на второй входящий поток управления токен не придет никогда, и шлюз не сработает. Это одна из самых очевидных ситуаций, в которых экземпляр процесса не может продолжить выполнение. В зависимости от модели процесса таких ситуаций может быть много.

Процессный движок не распознает модификации, которые приводят к таким ситуациям. Пользователь API сам отвечает за то, чтобы модификация не оставила экземпляр процесса в нежелательном состоянии. Впрочем, исправлять такие ситуации можно той же модификацией экземпляра процесса.

Документация создана на основе Camunda 7 Docs (Camunda, docs.camunda.org) и распространяется по лицензии Creative Commons Attribution-ShareAlike 3.0 Unported.