Статьи

Обработка событий в верблюжьей слюне

В предыдущем посте о camel-drools я представил компонент camel-drools и реализовал некоторый простой процесс, ориентированный на выполнение задач, используя правила внутри маршрута Camel. Сегодня я покажу, как расширить этот пример, добавив обработку событий .

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

Предположим, что наша система должна контролировать выполнение задач, назначенных пользователям. После создания задачи у пользователя есть 10 дней на ее выполнение. Когда он этого не делает — следует отправить остаток по электронной почте.

Определение правила может выглядеть так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
import org.apache.camel.component.drools.stateful.model.*
global org.apache.camel.component.drools.CamelDroolsHelper helper
 
declare TaskCreated
    @role( event )
    @expires( 365d )
end
 
declare TaskCompleted
    @role( event )
    @expires( 365d )
end
 
rule "Task not completed after 10 days"
    when
       $t : TaskCreated()
       not(TaskCompleted(name==$t.name, this after [-*, 10d] $t))
    then
       helper.send("direct:escalation", $t.getName());
end

Как видите, есть два типа событий: TaskCreated — когда система назначает задачу пользователям, и TaskCompleted — когда пользователь завершает задачу. Мы коррелируем эти два по свойству name. Во-первых, нам нужно объявить наши классы моделей как события, добавив аннотации @role (event) и @expires. Затем мы опишем правило: «если после 10 дней события TaskCreated нет события TaskCompleted, отправьте имя задачи по адресу direct: escalation route». Опять же, это может быть примером декларативного программирования — нам не нужно указывать фактические имена задач, просто соотносим TaskCreated с событиями TaskCompleted по имени.

В этом примере я использовал временной оператор «после». Описание других — см. Документацию Drools Fusion .

И, наконец, вот фрагмент кода теста JUnit:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class TaskEventsTest extends GenericTest {
 
    DefaultCamelContext ctx;
 
    @Test
    public void testCompleted() throws Exception {
        insertAdvanceDays(new TaskCreated("Task1"), 4);
        assertContains(0);
        insertAdvanceDays(new TaskCompleted("Task1"), 4);
        advanceDays(5);
        assertContains(0);
    }
 
    @Test
    public void testNotCompleted() throws Exception {
        insertAdvanceDays(new TaskCreated("Task1"), 5);
        assertContains(0);
        advanceDays(5);
        assertContains("Task1");
    }
 
    @Test
    public void testOneNotCompleted() throws Exception {
        ksession.insert(new TaskCreated("Task1"));
        insertAdvanceDays(new TaskCreated("Task2"), 5);
        assertContains(0);
        insertAdvanceDays(new TaskCompleted("Task1"), 4);
        assertContains(0);
        advanceDays(1);
        assertContains("Task2");
        advanceDays(10);
        assertContains("Task2");
    }
 
    @Override
    protected void setUpResources(KnowledgeBuilder kbuilder) throws Exception {
        kbuilder.add(new ReaderResource(new StringReader(
                IOUtils.toString(getClass()
                 .getResourceAsStream("/stateful/task-event.drl")))),
                 ResourceType.DRL);
    }
 
    @Override
    public void setUpInternal() throws Exception {
        this.ctx = new DefaultCamelContext();
        CamelDroolsHelper helper = new CamelDroolsHelper(ctx,
                new DefaultExchange(ctx)) {
            public Object send(String uri, Object body) {
                sentStuff.add(body.toString());
                return null;
            };
        };
        ksession.setGlobal("helper", helper);
    }
}
  • Вы можете найти исходный код для этого примера здесь .