Обработка сообщений и сигналов в Activiti в Alfresco

Здесь я хотела бы описать, как реализовать этот удобный механизм в Activiti внутри Alfresco.

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

Например, вам надо подать некое заявление для разрешения на некую деятельность. Заявление - электронный документ - отправляется по такому каналу в некую госструктуру. После чего податель заявления в течении определенного времени ожидает ответ на свое заявление. Ответ - это также электронный документ - сбрасывается по каналу в репозиторий 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());
 


137