Declaring CEP-based Rules

Previously, we've discussed how rules should attempt to be atomic and work together to achieve management of complex scenarios. This is very well aligned with CEP as each different rule can deal with one aspect of the aggregation, composition, or abstraction of other events. They can work together to achieve real-time resolution of very complex event situations. We will still need a few added features of Drools to be able to do so, as follows:

  • How to instruct Drools that an object is to be treated as an event
  • How to compare two events in time

In the next subsections, we will see how to achieve this using the DRL syntax.

Semantics of events

Before we get into the detail about how to define an event, we need to understand a few characteristics of the events. The first characteristic marks a difference between two main types of events—punctual and interval events—as shown in the following:

  • Punctual events: They are the events that occurred at a specific instance in time. They may represent the exact moment when a change in the reality of our domain model appeared, or they may have a lifespan that is too short to be considered. An example of punctual events is sensor readings, which will associate a specific value from the sensor with a specific time of the reading. Transactions can also be considered punctual events if they are so short lived that we can ignore their duration.
  • Interval events: They are events that have two distinctive moments in time: the moment they started and the moment they ended. This makes the interval events a bit more complex to compare than the punctual events; if you have two punctual events, you can only compare whether they happened at the same time, before, or after each other. For interval events on the other hand, you might compare cases where one event started and ended during another one, just to name a scenario.

Also, regardless of being punctual or interval events, they share a set of conceptual characteristics worth mentioning before we look into the code:

  • They are usually immutable: Events are supposed to be a record of the state of our domain model at a particular moment in time. You cannot change the past, therefore, you shouldn't have to change your event information. The engine doesn't force this feature, but it is something to keep in mind when designing our events. They might be decorated by adding extra information to them, but you shouldn't have to modify their internal data with which they were inserted in the Kie Session.
  • They have a managed lifecycle: As the engine understands all the events as mere objects with a time relation, the Kie Session can determine, based on the rules defined in it, when an event will no longer trigger the rules (as it is too old for the relations considered in these rules) and can automatically delete it from a session.

Declaring time-based-events in Drools

The first thing we'll need to do in order to create the CEP rules is to specify the type of objects that need to be treated as events to the engine. That is, the objects that should have the time metadata. This will allow the Kie Session to apply temporal reasoning to these types. There are a few ways to define that a specific type should be treated as an event, but they all define the same set of metadata. The properties to be defined are as shown in the following:

  • Role of the type: This is the only mandatory piece of metadata used to define a type as an event. It will have two specific types: fact and event.
  • Timestamp: This is an optional property to define the attribute of the type that will define the moment when the event took place. If not present, the timestamp of each event instance will be the moment it was inserted in the Kie Session.
  • Duration: This is an optional property to define the attribute of the type that will specify the duration of the event. If not present, the event will be treated as a punctual event. This property is required for interval events.
  • Expires: This is an optional character string to determine how long this type of event should be present in the Kie Session before automatic deletion.

Now that we understand the properties, let's see the different ways to apply them to our types. This metadata can be directly defined as class-level annotations in our Java beans, as follows:

@org.kie.api.definition.type.Role(Role.Type.EVENT)
@org.kie.api.definition.type.Duration("durationAttr")
@org.kie.api.definition.type.Timestamp("executionTime")
@org.kie.api.definition.type.Expires("2h30m")
public class TransactionEvent implements Serializable {
    private Date executionTime;
    private Long durationAttr;
    /* class content skipped */
}

As you can see in the previous code section, we can define the annotations for the role, duration, timestamp, and expiration properties of an event type. Duration should identify an attribute of the Long type and Timestamp should identify an attribute of the Date type. This way, the Kie Session will be able to understand the inserted objects of said type as events.

Another way to define these properties is in the declared types. Similar annotations can be used to define a declared type as an event, as follows:

declare PhoneCallEvent
    @role(event)
    @timestamp(whenDidWeReceiveTheCall)
    @duration(howLongWasTheCall)
    @expires(2h30m)
    whenDidWeReceiveTheCall: Date
    howLongWasTheCall: Long
    callInfo: String
end

The previous code section shows that we can create our own declared type and annotate it accordingly in order to make it an event.

Another way to declare the events is to grab an existing class and declare it as an event inside the DRL. This is very common when events are created to be shared between different applications and we cannot directly modify them in order to have the annotations on the Java bean. We can do something as shown in the following code section to declare the existing Java beans as events:

import path.to.my.shared.ExternalEvent;
...
declare ExternalEvent
    @role(event)
end

Just as the previous code section shows, we can redeclare this non-annotated Java bean inside the DRL to be treated as an event. As previously stated, all the annotations are optional. The only necessary annotation to treat the declared type as an event type is to have the @role(event) annotation. You can see the examples of events like these in the chapter-06/chapter-06-events project of the code bundle.

Now that we've seen how to declare our event types, we need to start seeing how to compare them. To do so, we will review the existing temporal operators.

Temporal operators

Once we define our event types, we need a way to compare the events based on their timestamp. To do so, there are 13 temporal operators that we can use in Drools. Some of these operators only make sense for comparing interval events, but given two events, they can compare them as the following code snippet shows:

declare MyEvent
    @role(event)
    @timestamp(executionTime)
End
rule "my first time operators example"
when
    $e1: MyEvent()
    $e2: MyEvent(this after[5m] $e1)
Then
    System.out.println("We have two events" + " 5 minutes apart");
end

In the previous example, we make use of the after operator to determine whether an event is at least five minutes newer than another event. As you can see, the comparison is done on the specific event instances. Internally, the time comparison will happen against the timestamp attribute called executionTime, but we can disregard that fact when dealing with events. This provides an advantage if we need to modify the timestamp nature of an event type in the future as we don't have to change the CEP rules where it is used.

Also, we can notice the use of a parameter in the operator, passed inside the square brackets. Each temporal operator will be prepared to receive between zero and four parameters to make use of the operator in a more specific way. In the previous scenario, we pass a 5m parameter to specify that an event should be at least five minutes after the other.

There are many temporal operators with which we can work. Here's a list of them and what they mean:

Temporal operators

The previous diagram shows the different temporal operators and how they will compare between different events. They all share certain qualities, as shown in the following:

  • They operate against two events. They are prepared to compare two events against each other.
  • They can also be used to compare the Date objects as dates are, by definition, the most minimalistic representation of an event (only the temporal information without any extra data).
  • They can receive parameters to specify their internal work. The operation of these parameters is thoroughly explained at the product documentation at https://docs.jboss.org/drools/release/latest/drools-docs/html/ under the Temporal Operators title.

One more thing worth mentioning about events is that they are still facts too. The engine will add the temporal features to the event types, but we can still compare any of their internal attributes and methods to create conditions and constraints on the rules, like we have done in the previous chapters.

In order to get familiar with a CEP rule, let's analyze one of the rules that we can find in the chapter-06/chapter-06-rules project of the code bundle and aim to detect fraud attempts, as follows:

rule "More than 10 transactions in an hour from one client"
    when
        $t1: TransactionEvent($cId: customerId)
        Number(intValue >= 10) from accumulate($t2: TransactionEvent(this != $t1, customerId == $cId, this meets[1h] $t1),count($t2) )
        not (SuspiciousCustomerEvent(customerId == $cId, reason == "Many transactions"))
    then
        insert(new SuspiciousCustomerEvent($cId, "Many transactions"));
end

This example DRL file can be found at chapter-06-rules/src/main/resources/chapter06/cep/cep-rules.drl. In order to run this example, we start with our previously defined TransactionEvent event type. We will check two main things in our rule: whether we have 10 transactions from the same customer within an hour, and that we still don't have a complex event to reflect this situation.

The first condition is written inside an accumulate. We count the number of TransactionEvent objects we have that contain the same customer ID and we also check whether they happened within an hour of the original reference transaction using this meets [1h] $t1.

The consequence of this rule is not a particular action against the outside. Instead, we just detect a complex event called SuspiciousCustomerEvent (a declared type in our example). This will represent an aggregation of our transaction events.

The second condition is a simple not clause, where we just check whether we haven't already fired this rule for the specific customer by checking the SuspiciousCustomerEvent object, which we need to add in the consequence if in case it hasn't been already added.

This rule will only detect the situation as that's the smallest responsibility we can break it down to. We could do a lot with suspicious customers, but this rule only has the responsibility of understanding a specific situation where a customer acts suspiciously. We need to remember to always keep our rules as atomic as possible. Other rules might detect a suspicious activity from a customer by other means.

Once the suspicious customer is detected, another rule can take care of deciding what to do when we detect a few suspicious customer events. For that case, we will create a different rule:

rule "More than 3 suspicious cases: warn the owner"
  when
    SuspiciousCustomerEvent($cId: customerId)
    not (AlarmTriggered(customerId == $cId))
    Number(intValue >= 2) from accumulate($s: SuspiciousCustomerEvent(customerId==$cId),count($s)
    )
  then //warn the owner
    System.out.println("WARNING: Suspicious fraud" +" case. Client " + $cId);
    insert(new AlarmTriggered($cId));
end

As we previously stated, we can have multiple rules detecting suspicious customer activities. This rule will trigger when two or more of these rules get triggered for the same customer. Once this happens, we send a warning to the owner. In this example, it is represented as a system output for simplicity, but it could just as easily be a helper method or global variable method programmed to send an e-mail or SMS.

As we can see from the previous examples, we can break down our complex event processing cases into multiple rules, each one connected to the rest of the CEP scenario by the events it consumes or produces. These aggregations of events lead to a special kind of architecture for our systems, where events and their relation with isolated application components allow us to create very decoupled, highly extensible components. This architecture is known as event driven architecture, and we'll describe it in the next subsection.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.142.255.140