Обработка сообщений и сигналов в Activiti в Alfresco
Здесь я хотела бы описать, как реализовать этот удобный механизм в Activiti внутри Alfresco.
Когда используют сообщения и сигналы? К примеру, в процессах организаций, сообщающихся между собой по некоему общему электронному каналу. Или же в системе происходят различные события, не входящие в описание процесса, но влияющие на его поведение.
Например, вам надо подать некое заявление для разрешения на некую деятельность. Заявление - электронный документ - отправляется по такому каналу в некую госструктуру. После чего податель заявления в течении определенного времени ожидает ответ на свое заявление. Ответ - это также электронный документ - сбрасывается по каналу в репозиторий Alfresco. После чего срабатывает сообщение о приходе документа и процесс продолжает свою работу в одном направлении. Если же ответ по истечении установленного времени не приходит (документ не появился в Alfresco), то означает это отказ подателю, и процесс идет уже по другому пути.
Сигналы так же используются в процессах, когда некие совершенно различные действия и процессы должны каким-то образом пересечься. Классический пример тому - процесс заказа пиццы, когда надо постоянно выяснять, был ли заказ удовлетворен, и сигнализировать об этом процессу.
В данной теме рассмотрим имплементацию обработки сообщений. Поскольку сигналы и сообщения обрабатываются практически аналогично за малым исключением.
Представьте ситуацию: в процессе было инициировано некое заявление на разрешение, заявление было отправлено на ревизию, и теперь ваш процесс ждет 5 минут: если в течении 5 минут разрешение не поступило в систему, то процесс переходит на следующий шаг (например, "Корректировка"), если же сообщение пришло, то процесс завершается.
Вот пример описания такого сообщения:
Главное понять, что в процессе мы всего лишь описываем сообщение, устанавливаем его имя и идентификационный номер, а также описываем шаги на случай прихода сообщения или же его отсутствия по истечению установленного времени. Сам же механизм сообщения процессу о срабатывании сигнала описывается вне процесса и сообщается с ним всего лишь по идентификационному номеру самого процесса.
ID процесса в java script-е находится очень просто:
Все что вам надо - это как-то где-то записать, увязать номер процесса с номером заявления. Таким образом, как
только разрешение на заявление упадет в репозиторий, вы сумеете связать пришедшее разрешение с номером исходного заявляния, а по нему уже - связать с номером процесса. И все, что вам останется сделать - это просигналить ему о сообщении.
А вот для того, чтобы просигналить, вам нужно использовать Activiti API
В моем случае был написан js local scoped object на Java, имплеметриющий метод notify (который и сигналит процессу о пришедшем сообщении). Затем пишется JS скрипт. Скрипт выполняется по рулю на папке, в которую падает сигнальный документ (то самое разрешение). Скрипт находит номер процесса, соответствующий номеру заявления, на которое пришло разрешение и вызывает метод notify объекта, передавая ему execution id процесса и имя сообщения процесса (Важно передавать именно имя, а не id сообщения!!!):
Код объекта на Java:
А вот параметр Runtime Service передается сеттеру в бине так:
Сигналы в процессах обрабатываются точно так же, за исключением того, что аргументами метода notify является имя сигнала, а строчки
Когда используют сообщения и сигналы? К примеру, в процессах организаций, сообщающихся между собой по некоему общему электронному каналу. Или же в системе происходят различные события, не входящие в описание процесса, но влияющие на его поведение.
Например, вам надо подать некое заявление для разрешения на некую деятельность. Заявление - электронный документ - отправляется по такому каналу в некую госструктуру. После чего податель заявления в течении определенного времени ожидает ответ на свое заявление. Ответ - это также электронный документ - сбрасывается по каналу в репозиторий Alfresco. После чего срабатывает сообщение о приходе документа и процесс продолжает свою работу в одном направлении. Если же ответ по истечении установленного времени не приходит (документ не появился в Alfresco), то означает это отказ подателю, и процесс идет уже по другому пути.
Сигналы так же используются в процессах, когда некие совершенно различные действия и процессы должны каким-то образом пересечься. Классический пример тому - процесс заказа пиццы, когда надо постоянно выяснять, был ли заказ удовлетворен, и сигнализировать об этом процессу.
В данной теме рассмотрим имплементацию обработки сообщений. Поскольку сигналы и сообщения обрабатываются практически аналогично за малым исключением.
Представьте ситуацию: в процессе было инициировано некое заявление на разрешение, заявление было отправлено на ревизию, и теперь ваш процесс ждет 5 минут: если в течении 5 минут разрешение не поступило в систему, то процесс переходит на следующий шаг (например, "Корректировка"), если же сообщение пришло, то процесс завершается.
Вот пример описания такого сообщения:
<message id="resolution" name="resolution" /> ... <sequenceFlow sourceRef="sendWay" targetRef="waitForResponse" /> <eventBasedGateway id="waitForResponse" /> <sequenceFlow sourceRef="waitForResponse" targetRef="messageEvent" /> <sequenceFlow sourceRef="waitForResponse" targetRef="timerEvent" /> <intermediateCatchEvent id="messageEvent" name="Событие для разрешения" > <messageEventDefinition messageRef="resolution" /> </intermediateCatchEvent> <intermediateCatchEvent id="timerEvent" name="Ожидание разрешения" > <timerEventDefinition> <timeDuration>PT5M</timeDuration> </timerEventDefinition> </intermediateCatchEvent> <sequenceFlow sourceRef="timerEvent" targetRef="edit" /> <sequenceFlow sourceRef="messageEvent" targetRef="end" /> <userTask id="edit" name="Корректировка заявления" activiti:formKey="abrw:edit" activiti:candidateGroups="${abrw_manager}" >
Главное понять, что в процессе мы всего лишь описываем сообщение, устанавливаем его имя и идентификационный номер, а также описываем шаги на случай прихода сообщения или же его отсутствия по истечению установленного времени. Сам же механизм сообщения процессу о срабатывании сигнала описывается вне процесса и сообщается с ним всего лишь по идентификационному номеру самого процесса.
ID процесса в java script-е находится очень просто:
var id = execution.id
Все что вам надо - это как-то где-то записать, увязать номер процесса с номером заявления. Таким образом, как
только разрешение на заявление упадет в репозиторий, вы сумеете связать пришедшее разрешение с номером исходного заявляния, а по нему уже - связать с номером процесса. И все, что вам останется сделать - это просигналить ему о сообщении.
А вот для того, чтобы просигналить, вам нужно использовать Activiti API
В моем случае был написан js local scoped object на Java, имплеметриющий метод notify (который и сигналит процессу о пришедшем сообщении). Затем пишется JS скрипт. Скрипт выполняется по рулю на папке, в которую падает сигнальный документ (то самое разрешение). Скрипт находит номер процесса, соответствующий номеру заявления, на которое пришло разрешение и вызывает метод notify объекта, передавая ему execution id процесса и имя сообщения процесса (Важно передавать именно имя, а не id сообщения!!!):
mycompanyMessageNotifier.notify("resolution", processID);
Код объекта на Java:
package org.mycompany.repo.jsobject; import org.alfresco.repo.processor.BaseProcessorExtension; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.repo.jscript.ScriptLogger; import org.activiti.engine.*; import org.activiti.engine.runtime.*; public class MessageNotifier extends BaseProcessorExtension { private RuntimeService rsimpl; private ScriptLogger logger = new ScriptLogger(); public void setRuntimeService(final RuntimeService rsimpl) { this.rsimpl = rsimpl; } public void notify(String messageName, String executionId) { try { Execution execution = rsimpl.createExecutionQuery().messageEventSubscriptionName(messageName).singleResult(); rsimpl.messageEventReceived(messageName, execution.getId()); } catch(ActivitiObjectNotFoundException aonfe) { logger.error("Such execution doesn't exist. Execution id: "+executionId); } catch(ActivitiException ae) { logger.error("Execution (id="+executionId+") doesn't subscribe to singal."); } } }
А вот параметр Runtime Service передается сеттеру в бине так:
<!-- Message notifier js local scoped object --> <bean id="mycompanyMessageNotifier" parent="baseJavaScriptExtension" class="org.mycompany.repo.jsobject.MessageNotifier"> <property name="runtimeService" ref="activitiRuntimeService" /> <property name="extensionName"> <value>mycompanyMessageNotifier</value> </property> </bean>
Сигналы в процессах обрабатываются точно так же, за исключением того, что аргументами метода notify является имя сигнала, а строчки
Execution execution = rsimpl.createExecutionQuery().messageEventSubscriptionName(messageName).singleResult(); rsimpl.messageEventReceived(messageName, execution.getId());меняются на строчки
Execution execution = rsimpl.createExecutionQuery().signalEventSubscriptionName(signalName).singleResult(); rsimpl.signalEventReceived(signalName, execution.getId());