Chapter 7. Complex Event Processing
7.1. Introduction to Complex Event Processing
JBoss BRMS Complex Event Processing provides the JBoss Enterprise BRMS Platform with complex event processing capabilities.
For the purpose of this guide, Complex Event Processing, or CEP, refers to the ability to process multiple events and detect interesting events from within a collection of events, uncover relationships that exist between events, and infer new data from the events and their relationships.
An event can best be described as a record of a significant change of state in the application domain. Depending on how the domain is modeled, the change of state may be represented by a single event, multiple atomic events, or even hierarchies of correlated events. Using a stock broker application as an example, a change in security prices, a change in ownership from seller to buyer, or a change in an account holder’s balance are all considered to be events as a change has occurred in the state of the application domain.
Event processing use cases, in general, share several requirements and goals with business rules use cases.
From a business perspective, business rule definitions are often defined based on the occurrence of scenarios triggered by events. For example:
On an algorithmic trading application: Take an action if the security price increases X% above the day’s opening price.
The price increases are denoted by events on a stock trade application.
On a monitoring application: Take an action if the temperature in the server room increases X degrees in Y minutes.
The sensor readings are denoted by events.
Both business rules and event processing queries change frequently and require an immediate response for the business to adapt to new market conditions, regulations, and corporate policies.
From a technical perspective:
- Both business rules and event processing require seamless integration with the enterprise infrastructure and applications. This is particularly important with regard to life-cycle management, auditing, and security.
- Both business rules and event processing have functional requirements like pattern matching and non-functional requirements like response time limits and query/rule explanations.
JBoss BRMS Complex Event Processing provides the complex event processing capabilities of JBoss Business Rules Management System. The Business Rules Management and Business Process Management capabilities are provided by other modules.
Complex event processing scenarios share these distinguishing characteristics:
- They usually process large numbers of events, but only a small percentage of the events are of interest.
- The events are usually immutable, as they represent a record of change in state.
- The rules and queries run against events and must react to detected event patterns.
- There are usually strong temporal relationships between related events.
- Individual events are not important. The system is concerned with patterns of related events and the relationships between them.
- It is often necessary to perform composition and aggregation of events.
As such, JBoss BRMS Complex Event Processing supports the following behaviors:
- Support events, with their proper semantics, as first class citizens.
- Allow detection, correlation, aggregation, and composition of events.
- Support processing streams of events.
- Support temporal constraints in order to model the temporal relationships between events.
- Support sliding windows of interesting events.
- Support a session-scoped unified clock.
- Support the required volumes of events for complex event processing use cases.
- Support reactive rules.
- Support adapters for event input into the engine (pipeline).
7.2. Events
Events are a record of significant change of state in the application domain. From a complex event processing perspective, an event is a special type of fact or object. A fact is a known piece of data. For instance, a fact could be a stock’s opening price. A rule is a definition of how to react to the data. For instance, if a stock price reaches $X, sell the stock.
The defining characteristics of events are the following:
- Events are immutable
An event is a record of change which has occurred at some time in the past, and as such it cannot be changed.
NoteThe rules engine does not enforce immutability on the Java objects representing events; this makes event data enrichment possible.
The application should be able to populate un-populated event attributes, which can be used to enrich the event with inferred data; however, event attributes that have already been populated should not be changed.
- Events have strong temporal constraints
- Rules involving events usually require the correlation of multiple events that occur at different points in time relative to each other.
- Events have managed life-cycles
- Because events are immutable and have temporal constraints, they are usually only of interest for a specified period of time. This means the engine can automatically manage the life-cycle of events.
- Events can use sliding windows
- It is possible to define and use sliding windows with events since all events have timestamps associated with them. Therefore, sliding windows allow the creation of rules on aggregations of values over a time period.
Events can be declared as either interval-based events or point-in-time events. Interval-based events have a duration time and persist in working memory until their duration time has lapsed. Point-in-time events have no duration and can be thought of as interval-based events with a duration of zero.
7.2.1. Event Declaration
To declare a fact type as an event, assign the @role
metadata tag to the fact with the event
parameter. The @role
metadata tag can accept two possible values:
-
fact
: assigning the fact role declares the type is to be handled as a regular fact. Fact is the default role. -
event
: assigning the event role declares the type is to be handled as an event.
This example declares that a stock broker application’s StockTick
fact type will be handled as an event:
Example 7.1. Declaring Fact Type as Event
import some.package.StockTick declare StockTick @role( event ) end
Facts can also be declared inline. If StockTick
was a fact type declared in the DRL instead of in a pre-existing class, the code would be as follows:
Example 7.2. Declaring Fact Type and Assigning it to Event Role
declare StockTick @role(event) datetime : java.util.Date symbol : String price : double end
For more information about type declarations, see Section 8.9, “Type Declaration”.
7.2.2. Event Metadata
Every event has associated metadata. Typically, the metadata is automatically added as each event is inserted into working memory. The metadata defaults can be changed on an event-type basis using the metadata tags:
-
@role
-
@timestamp
-
@duration
-
@expires
The following examples assume the application domain model includes the following class:
Example 7.3. The VoiceCall Fact Class
/** * A class that represents a voice call in a Telecom domain model. */ public class VoiceCall { private String originNumber; private String destinationNumber; private Date callDateTime; private long callDuration; // in milliseconds // Constructors, getters, and setters. }
- @role
The
@role
metadata tag indicates whether a given fact type is either a regular fact or an event. It accepts eitherfact
orevent
as a parameter. The default isfact
.@role(<fact|event>)
Example 7.4. Declaring VoiceCall as Event Type
declare VoiceCall @role(event) end
- @timestamp
A timestamp is automatically assigned to every event. By default, the time is provided by the session clock and assigned to the event at insertion into the working memory. Events can have their own timestamp attribute, which can be included by telling the engine to use the attribute’s timestamp instead of the session clock.
To use the attribute’s timestamp, use the attribute name as the parameter for the
@timestamp
tag.@timestamp(<attributeName>)
Example 7.5. Declaring VoiceCall Timestamp Attribute
declare VoiceCall @role(event) @timestamp(callDateTime) end
- @duration
JBoss BRMS Complex Event Processing supports both point-in-time and interval-based events. A point-in-time event is represented as an interval-based event with a duration of zero time units. By default, every event has a duration of zero. To assign a different duration to an event, use the attribute name as the parameter for the
@duration
tag.@duration(<attributeName>)
Example 7.6. Declaring VoiceCall Duration Attribute
declare VoiceCall @role(event) @timestamp(callDateTime) @duration(callDuration) end
- @expires
Events may be set to expire automatically after a specific duration in the working memory. By default, this happens when the event can no longer match and activate any of the current rules. You can also explicitly define when an event should expire. The
@expires
tag is only used when the engine is running in stream mode.@expires(<timeOffset>)
The value of
timeOffset
is a temporal interval that sets the relative duration of the event.[#d][#h][#m][#s][#[ms]]
All parameters are optional and the
#
parameter should be replaced by the appropriate value.To declare that the
VoiceCall
facts should expire one hour and thirty-five minutes after insertion into the working memory, use the following:Example 7.7. Declaring Expiration Offset for VoiceCall Events
declare VoiceCall @role(event) @timestamp(callDateTime) @duration(callDuration) @expires(1h35m) end
7.3. Clock Implementation in Complex Event Processing
7.3.1. Session Clock
Events have strong temporal constraints making it is necessary to use a reference clock. If a rule needs to determine the average price of a given stock over the last sixty minutes, it is necessary to compare the stock price event’s timestamp with the current time. The reference clock provides the current time.
Because the rules engine can simultaneously run an array of different scenarios that require different clocks, multiple clock implementations can be used by the engine.
Scenarios that require different clocks include the following:
- Rules testing: Testing always requires a controlled environment, and when the tests include rules with temporal constraints, it is necessary to control the input rules, facts, and the flow of time.
- Regular execution: A rules engine that reacts to events in real time needs a real-time clock.
- Special environments: Specific environments may have specific time control requirements. For instance, clustered environments may require clock synchronization or JEE environments may require you to use an application server-provided clock.
- Rules replay or simulation: In order to replay or simulate scenarios, it is necessary that the application controls the flow of time.
7.3.2. Available Clock Implementations
JBoss BRMS Complex Event Processing comes equipped with two clock implementations:
- Real-Time Clock
The real-time clock is the default implementation based on the system clock. The real-time clock uses the system clock to determine the current time for timestamps.
To explicitly configure the engine to use the real-time clock, set the session configuration parameter to
realtime
:import org.kie.api.KieServices.Factory; import org.kie.api.runtime.conf.ClockTypeOption; import org.kie.api.runtime.KieSessionConfiguration; KieSessionConfiguration config = KieServices.Factory.get().newKieSessionConfiguration(); config.setOption(ClockTypeOption.get("realtime"));
- Pseudo-Clock
The pseudo-clock is useful for testing temporal rules since it can be controlled by the application.
To explicitly configure the engine to use the pseudo-clock, set the session configuration parameter to
pseudo
:import org.kie.api.runtime.conf.ClockTypeOption; import org.kie.api.runtime.KieSessionConfiguration; import org.kie.api.KieServices.Factory; KieSessionConfiguration config = KieServices.Factory.get().newKieSessionConfiguration(); config.setOption(ClockTypeOption.get("pseudo"));
This example shows how to control the pseudo-clock:
import java.util.concurrent.TimeUnit; import org.kie.api.runtime.KieSessionConfiguration; import org.kie.api.KieServices.Factory; import org.kie.api.runtime.KieSession; import org.drools.core.time.SessionPseudoClock; import org.kie.api.runtime.rule.FactHandle; import org.kie.api.runtime.conf.ClockTypeOption; KieSessionConfiguration conf = KieServices.Factory.get().newKieSessionConfiguration(); conf.setOption( ClockTypeOption.get("pseudo")); KieSession session = kbase.newKieSession(conf, null); SessionPseudoClock clock = session.getSessionClock(); // Then, while inserting facts, advance the clock as necessary: FactHandle handle1 = session.insert(tick1); clock.advanceTime(10, TimeUnit.SECONDS); FactHandle handle2 = session.insert(tick2); clock.advanceTime(30, TimeUnit.SECONDS); FactHandle handle3 = session.insert(tick3);
For a list of Maven dependencies, see example Embedded jBPM Engine Dependencies. If you use Red Hat JBoss BRMS, see Embedded Drools Engine Dependencies.
7.4. Event Processing Modes
Rules engines process facts and rules to provide applications with results. Regular facts (facts with no temporal constraints) are processed independent of time and in no particular order. Red Hat JBoss BRMS processes facts of this type in cloud mode. Events (facts which have strong temporal constraints) must be processed in real-time or near real-time. Red Hat JBoss BRMS processes these events in stream mode. Stream mode deals with synchronization and makes it possible for Red Hat JBoss BRMS to process events.
7.4.1. Cloud Mode
Cloud mode is the default operating mode of Red Hat JBoss Business Rules Management System.
Running in Cloud mode, the engine applies a many-to-many pattern matching algorithm, which treats the events as an unordered cloud. Events still have timestamps, but there is no way for the rules engine running in Cloud mode to draw relevance from the timestamp because Cloud mode is unaware of the present time.
This mode uses the rules constraints to find the matching tuples, activate, and fire rules.
Cloud mode does not impose any kind of additional requirements on facts; however, because it has no concept of time, it cannot take advantage of temporal features such as sliding windows or automatic life-cycle management. In Cloud mode, it is necessary to explicitly retract events when they are no longer needed.
Certain requirements that are not imposed include the following:
- No need for clock synchronization since there is no notion of time.
- No requirement on ordering events since the engine looks at the events as an unordered cloud against which the engine tries to match rules.
Cloud mode can be specified either by setting a system property, using configuration property files, or using the API.
The API call follows:
import org.kie.api.conf.EventProcessingOption; import org.kie.api.KieBaseConfiguration; import org.kie.api.KieServices.Factory; KieBaseConfiguration config = KieServices.Factory.get().newKieBaseConfiguration(); config.setOption(EventProcessingOption.CLOUD);
The equivalent property follows:
drools.eventProcessingMode = cloud
For a list of Maven dependencies, see example Embedded jBPM Engine Dependencies. If you use Red Hat JBoss BRMS, see Embedded Drools Engine Dependencies.
7.4.2. Stream Mode
Stream mode processes events chronologically as they are inserted into the rules engine. Stream mode uses a session clock that enables the rules engine to process events as they occur in time. The session clock enables processing events as they occur based on the age of the events. Stream mode also synchronizes streams of events (so events in different streams can be processed in chronological order), implements sliding windows of interest, and enables automatic life-cycle management.
The requirements for using stream mode are the following:
- Events in each stream must be ordered chronologically.
- A session clock must be present to synchronize event streams.
The application does not need to enforce ordering events between streams, but the use of event streams that have not been synchronized may cause unexpected results.
Stream mode can be enabled by setting a system property, using configuration property files, or using the API.
The API call follows:
import org.kie.api.conf.EventProcessingOption; import org.kie.api.KieBaseConfiguration; import org.kie.api.KieServices.Factory; KieBaseConfiguration config = KieServices.Factory.get().newKieBaseConfiguration(); config.setOption(EventProcessingOption.STREAM);
The equivalent property follows:
drools.eventProcessingMode = stream
For a list of Maven dependencies, see example Embedded jBPM Engine Dependencies. If you use Red Hat JBoss BRMS, see Embedded Drools Engine Dependencies.
7.5. Event Streams
Complex event processing use cases deal with streams of events. The streams can be provided to the application using JMS queues, flat text files, database tables, raw sockets, or even web service calls.
Streams share a common set of characteristics:
- Events in the stream are ordered by timestamp. The timestamps may have different semantics for different streams, but they are always ordered internally.
- There is usually a high volume of events in the stream.
- Atomic events contained in the streams are rarely useful by themselves.
- Streams are either homogeneous (they contain a single type of event) or heterogeneous (they contain events of different types).
A stream is also known as an entry point.
Facts from one entry point, or stream, may join with facts from any other entry point in addition to facts already in working memory. Facts always remain associated with the entry point through which they entered the engine. Facts of the same type may enter the engine through several entry points, but facts that enter the engine through entry point A will never match a pattern from entry point B.
7.5.1. Declaring and Using Entry Points
Entry points are declared implicitly by making direct use of them in rules. Referencing an entry point in a rule will make the engine, at compile time, identify and create the proper internal structures to support that entry point.
For example, a banking application that has transactions fed into the engine using streams could have one stream for all of the transactions executed at ATMs. A rule for this scenario could state, "A withdrawal is only allowed if the account balance is greater than the withdrawal amount the customer has requested."
Example 7.8. ATM Rule
rule "Authorize Withdraw" when WithdrawRequest($ai : accountId, $am : amount) from entry-point "ATM Stream" CheckingAccount(accountId == $ai, balance > $am) then // authorize withdraw end
When the engine compiles this rule, it will identify that the pattern is tied to the entry point ATM Stream. The engine will create all the necessary structures for the rule-base to support the ATM Stream, and this rule will only match WithdrawRequest
events coming from the ATM Stream.
Note the ATM example rule joins the event (WithdrawalRequest
) from the stream with a fact from the main working memory (CheckingAccount
).
The banking application may have a second rule that states, "A fee of $2 must be applied to a withdraw request made using a branch teller."
Example 7.9. Using Multiple Streams
rule "Apply Fee on Withdraws on Branches" when WithdrawRequest($ai : accountId, processed == true) from entry-point "Branch Stream" CheckingAccount(accountId == $ai) then // apply a $2 fee on the account end
This rule matches events of the same type (WithdrawRequest
) as the example ATM rule but from a different stream. Events inserted into the ATM Stream will never match the pattern on the second rule, which is tied to the Branch Stream; accordingly, events inserted into the Branch Stream will never match the pattern on the example ATM rule, which is tied to the ATM Stream.
Declaring the stream in a rule states that the rule is only interested in events coming from that stream.
Events can be inserted manually into an entry point instead of directly into the working memory.
Example 7.10. Inserting Facts into Entry Point
import org.kie.api.runtime.KieSession; import org.kie.api.runtime.rule.EntryPoint; // Create your rulebase and your session as usual: KieSession session = ... // Get a reference to the entry point: EntryPoint atmStream = session.getEntryPoint("ATM Stream"); // ...and start inserting your facts into the entry point: atmStream.insert(aWithdrawRequest);
For a list of Maven dependencies, see example Embedded jBPM Engine Dependencies. If you use Red Hat JBoss BRMS, see Embedded Drools Engine Dependencies.
7.5.2. Negative Pattern in Stream Mode
A negative pattern is concerned with conditions that are not met. Negative patterns make reasoning in the absence of events possible. For instance, a safety system could have a rule that states "If a fire is detected and the sprinkler is not activated, sound the alarm."
In Cloud mode, the engine assumes all facts (regular facts and events) are known in advance and evaluates negative patterns immediately.
Example 7.11. Rule with Negative Pattern
rule "Sound the Alarm" when $f : FireDetected() not(SprinklerActivated()) then // sound the alarm end
An example in stream mode is displayed below. This rule keeps consistency when dealing with negative patterns and temporal constraints at the same time interval.
Example 7.12. Rule with Negative Pattern, Temporal Constraints, and Explicit Duration Parameter
rule "Sound the Alarm" duration(10s) when $f : FireDetected() not(SprinklerActivated(this after[0s,10s] $f)) then // sound the alarm end
In stream mode, negative patterns with temporal constraints may force the engine to wait for a set time before activating a rule. A rule may be written for an alarm system that states, "If a fire is detected and the sprinkler is not activated after 10 seconds, sound the alarm." Unlike the previous stream mode example, this one does not require the user to calculate and write the duration parameter.
Example 7.13. Rule with Negative Pattern with Temporal Constraints
rule "Sound the Alarm" when $f : FireDetected() not(SprinklerActivated(this after[0s,10s] $f)) then // sound the alarm end
The rule depicted below expects one "Heartbeat" event to occur every 10 seconds; if not, the rule fires. What is special about this rule is that it uses the same type of object in the first pattern and in the negative pattern. The negative pattern has the temporal constraint to wait between 0 to 10 seconds before firing, and it excludes the Heartbeat bound to $h. Excluding the bound Heartbeat is important since the temporal constraint [0s, …] does not exclude by itself the bound event $h from being matched again, thus preventing the rule to fire.
Example 7.14. Excluding Bound Events in Negative Patterns
rule "Sound the Alarm" when $h: Heartbeat() from entry-point "MonitoringStream" not(Heartbeat(this != $h, this after[0s,10s] $h) from entry-point "MonitoringStream") then // sound the alarm end
7.6. Temporal Operations
7.6.1. Temporal Reasoning
Complex Event Processing requires the rules engine to engage in temporal reasoning. Events have strong temporal constraints so it is vital the rules engine can determine and interpret an event’s temporal attributes, both as they relate to other events and the 'flow of time' as it appears to the rules engine. This makes it possible for rules to take time into account; for instance, a rule could state "Calculate the average price of a stock over the last 60 minutes."
JBoss BRMS Complex Event Processing implements interval-based time events, which have a duration attribute that is used to indicate how long an event is of interest. Point-in-time events are also supported and treated as interval-based events with a duration of 0 (zero).
7.6.2. Temporal Operations
JBoss BRMS Complex Event Processing implements the following temporal operators and their logical complements (negation):
-
after
-
before
-
coincides
-
during
-
finishes
-
finishes by
-
includes
-
meets
-
met by
-
overlaps
-
overlapped by
-
starts
-
started by
7.6.3. After
The after
operator correlates two events and matches when the temporal distance (the time between the two events) from the current event to the event being correlated falls into the distance range declared for the operator.
For example:
$eventA : EventA(this after[3m30s, 4m] $eventB)
This pattern only matches if the temporal distance between the time when $eventB
finished and the time when $eventA
started is between the lower limit of three minutes and thirty seconds and the upper limit of four minutes.
This can also be represented as follows:
3m30s <= $eventA.startTimestamp - $eventB.endTimeStamp <= 4m
The after
operator accepts one or two optional parameters:
- If two values are defined, the interval starts on the first value (3 minutes and 30 seconds in the example) and ends on the second value (4 minutes in the example).
- If only one value is defined, the interval starts on the provided value and runs indefinitely with no end time.
- If no value is defined, the interval starts at one millisecond and runs indefinitely with no end time.
The after
operator also accepts negative temporal distances.
For example:
$eventA : EventA(this after[-3m30s, -2m] $eventB)
If the first value is greater than the second value, the engine will automatically reverse them.
The following two patterns are equivalent to each other:
$eventA : EventA(this after[-3m30s, -2m] $eventB) $eventA : EventA(this after[-2m, -3m30s] $eventB)
7.6.4. Before
The before
operator correlates two events and matches when the temporal distance (time between the two events) from the event being correlated to the current event falls within the distance range declared for the operator.
For example:
$eventA : EventA(this before[3m30s, 4m] $eventB)
This pattern only matches if the temporal distance between the time when $eventA
finished and the time when $eventB
started is between the lower limit of three minutes and thirty seconds and the upper limit of four minutes.
This can also be represented as follows:
3m30s <= $eventB.startTimestamp - $eventA.endTimeStamp <= 4m
The before
operator accepts one or two optional parameters:
- If two values are defined, the interval starts on the first value (3 minutes and 30 seconds in the example) and ends on the second value (4 minutes in the example).
- If only one value is defined, the interval starts on the provided value and runs indefinitely with no end time.
- If no value is defined, the interval starts at one millisecond and runs indefinitely with no end time.
The before
operator also accepts negative temporal distances.
For example:
$eventA : EventA(this before[-3m30s, -2m] $eventB)
If the first value is greater than the second value, the engine will automatically reverse them.
The following two patterns are equivalent to each other:
$eventA : EventA(this before[-3m30s, -2m] $eventB) $eventA : EventA(this before[-2m, -3m30s] $eventB)
7.6.5. Coincides
The coincides
operator correlates two events and matches when both events happen at the same time.
For example:
$eventA : EventA(this coincides $eventB)
This pattern only matches if both the start timestamps of $eventA
and $eventB
are identical and the end timestamps of both $eventA
and $eventB
are also identical.
The coincides
operator accepts optional thresholds for the distance between the events' start times and the events' end times, so the events do not have to start at exactly the same time or end at exactly the same time, but they need to be within the provided thresholds.
The following rules apply when defining thresholds for the coincides
operator:
- If only one parameter is given, it is used to set the threshold for both the start and end times of both events.
- If two parameters are given, the first is used as a threshold for the start time and the second one is used as a threshold for the end time.
For example:
$eventA : EventA(this coincides[15s, 10s] $eventB)
This pattern will only match if the following conditions are met:
abs($eventA.startTimestamp - $eventB.startTimestamp) <= 15s && abs($eventA.endTimestamp - $eventB.endTimestamp) <= 10s
The coincides
operator does not accept negative intervals, and the rules engine will throw an exception if an attempt is made to use negative distance internals.
7.6.6. During
The during
operator correlates two events and matches when the current event happens during the event being correlated.
For example:
$eventA : EventA(this during $eventB)
This pattern only matches if $eventA
starts after $eventB
and ends before $eventB
ends.
This can also be represented as follows:
$eventB.startTimestamp < $eventA.startTimestamp <= $eventA.endTimestamp < $eventB.endTimestamp
The during
operator accepts one, two, or four optional parameters:
The following rules apply when providing parameters for the during
operator:
- If one value is defined, this value will represent the maximum distance between the start times of the two events and the maximum distance between the end times of the two events.
If two values are defined, these values represent a threshold that the current event’s start time and end time must occur between in relation to the correlated event’s start and end times.
If the values
5s
and10s
are provided, the current event must start between 5 and 10 seconds after the correlated event, and similarly the current event must end between 5 and 10 seconds before the correlated event.- If four values are defined, the first and second values will be used as the minimum and maximum distances between the starting times of the events, and the third and fourth values will be used as the minimum and maximum distances between the end times of the two events.
7.6.7. Finishes
The finishes
operator correlates two events and matches when the current event’s start timestamp post-dates the correlated event’s start timestamp and both events end simultaneously.
For example:
$eventA : EventA(this finishes $eventB)
This pattern only matches if $eventA
starts after $eventB
starts and ends at the same time as $eventB
ends.
This can be represented as follows:
$eventB.startTimestamp < $eventA.startTimestamp && $eventA.endTimestamp == $eventB.endTimestamp
The finishes
operator accepts one optional parameter. If defined, the optional parameter sets the maximum time allowed between the end times of the two events.
For example:
$eventA : EventA(this finishes[5s] $eventB)
This pattern matches if these conditions are met:
$eventB.startTimestamp < $eventA.startTimestamp && abs($eventA.endTimestamp - $eventB.endTimestamp) <= 5s
The finishes
operator does not accept negative intervals, and the rules engine will throw an exception if an attempt is made to use negative distance intervals.
7.6.8. Finishes By
The finishedby
operator correlates two events and matches when the current event’s start time predates the correlated event’s start time but both events end simultaneously. finishedby
is the symmetrical opposite of the finishes
operator.
For example:
$eventA : EventA(this finishedby $eventB)
This pattern only matches if $eventA
starts before $eventB
starts and ends at the same time as $eventB
ends.
This can be represented as follows:
$eventA.startTimestamp < $eventB.startTimestamp && $eventA.endTimestamp == $eventB.endTimestamp
The finishedby
operator accepts one optional parameter. If defined, the optional parameter sets the maximum time allowed between the end times of the two events.
$eventA : EventA(this finishedby[5s] $eventB)
This pattern matches if these conditions are met:
$eventA.startTimestamp < $eventB.startTimestamp && abs($eventA.endTimestamp - $eventB.endTimestamp) <= 5s
The finishedby
operator does not accept negative intervals, and the rules engine will throw an exception if an attempt is made to use negative distance intervals.
7.6.9. Includes
The includes
operator examines two events and matches when the event being correlated happens during the current event. It is the symmetrical opposite of the during
operator.
For example:
$eventA : EventA(this includes $eventB)
This pattern only matches if $eventB
starts after $eventA
and ends before $eventA
ends.
This can be represented as follows:
$eventA.startTimestamp < $eventB.startTimestamp <= $eventB.endTimestamp < $eventA.endTimestamp
The includes
operator accepts 1, 2 or 4 optional parameters:
- If one value is defined, this value will represent the maximum distance between the start times of the two events and the maximum distance between the end times of the two events.
If two values are defined, these values represent a threshold that the current event’s start time and end time must occur between in relation to the correlated event’s start and end times.
If the values
5s
and10s
are provided, the current event must start between 5 and 10 seconds after the correlated event, and similarly the current event must end between 5 and 10 seconds before the correlated event.- If four values are defined, the first and second values will be used as the minimum and maximum distances between the starting times of the events, and the third and fourth values will be used as the minimum and maximum distances between the end times of the two events.
7.6.10. Meets
The meets
operator correlates two events and matches when the current event ends at the same time as the correlated event starts.
For example:
$eventA : EventA(this meets $eventB)
This pattern matches if $eventA
ends at the same time as $eventB
starts.
This can be represented as follows:
abs($eventB.startTimestamp - $eventA.endTimestamp) == 0
The meets
operator accepts one optional parameter. If defined, it determines the maximum time allowed between the end time of the current event and the start time of the correlated event.
For example:
$eventA : EventA(this meets[5s] $eventB)
This pattern matches if these conditions are met:
abs($eventB.startTimestamp - $eventA.endTimestamp) <= 5s
The meets
operator does not accept negative intervals, and the rules engine will throw an exception if an attempt is made to use negative distance intervals.
7.6.11. Met By
The metby
operator correlates two events and matches when the current event starts at the same time as the correlated event ends.
For example:
$eventA : EventA(this metby $eventB)
This pattern matches if $eventA
starts at the same time as $eventB
ends.
This can be represented as follows:
abs($eventA.startTimestamp - $eventB.endTimestamp) == 0
The metby
operator accepts one optional parameter. If defined, it sets the maximum distance between the end time of the correlated event and the start time of the current event.
For example:
$eventA : EventA(this metby[5s] $eventB)
This pattern matches if these conditions are met:
abs($eventA.startTimestamp - $eventB.endTimestamp) <= 5s
The metby
operator does not accept negative intervals, and the rules engine will throw an exception if an attempt is made to use negative distance intervals.
7.6.12. Overlaps
The overlaps
operator correlates two events and matches when the current event starts before the correlated event starts and ends after the correlated event starts, but it ends before the correlated event ends.
For example:
$eventA : EventA(this overlaps $eventB)
This pattern matches if these conditions are met:
$eventA.startTimestamp < $eventB.startTimestamp < $eventA.endTimestamp < $eventB.endTimestamp
The overlaps
operator accepts one or two optional parameters:
- If one parameter is defined, it will define the maximum distance between the start time of the correlated event and the end time of the current event.
- If two values are defined, the first value will be the minimum distance, and the second value will be the maximum distance between the start time of the correlated event and the end time of the current event.
7.6.13. Overlapped By
The overlappedby
operator correlates two events and matches when the correlated event starts before the current event, and the correlated event ends after the current event starts but before the current event ends.
For example:
$eventA : EventA(this overlappedby $eventB)
This pattern matches if these conditions are met:
$eventB.startTimestamp < $eventA.startTimestamp < $eventB.endTimestamp < $eventA.endTimestamp
The overlappedby
operator accepts one or two optional parameters:
- If one parameter is defined, it sets the maximum distance between the start time of the correlated event and the end time of the current event.
- If two values are defined, the first value will be the minimum distance, and the second value will be the maximum distance between the start time of the correlated event and the end time of the current event.
7.6.14. Starts
The starts
operator correlates two events and matches when they start at the same time, but the current event ends before the correlated event ends.
For example:
$eventA : EventA(this starts $eventB)
This pattern matches if $eventA
and $eventB
start at the same time, and $eventA
ends before $eventB
ends.
This can be represented as follows:
$eventA.startTimestamp == $eventB.startTimestamp && $eventA.endTimestamp < $eventB.endTimestamp
The starts
operator accepts one optional parameter. If defined, it determines the maximum distance between the start times of events in order for the operator to still match:
$eventA : EventA(this starts[5s] $eventB)
This pattern matches if these conditions are met:
abs($eventA.startTimestamp - $eventB.startTimestamp) <= 5s && $eventA.endTimestamp < $eventB.endTimestamp
The starts
operator does not accept negative intervals, and the rules engine will throw an exception if an attempt is made to use negative distance intervals.
7.6.15. Started By
The startedby
operator correlates two events. It matches when both events start at the same time and the correlating event ends before the current event.
For example:
$eventA : EventA(this startedby $eventB)
This pattern matches if $eventA
and $eventB
start at the same time, and $eventB
ends before $eventA
ends.
This can be represented as follows:
$eventA.startTimestamp == $eventB.startTimestamp && $eventA.endTimestamp > $eventB.endTimestamp
The startedby
operator accepts one optional parameter. If defined, it sets the maximum distance between the start time of the two events in order for the operator to still match:
$eventA : EventA( this starts[5s] $eventB)
This pattern matches if these conditions are met:
abs( $eventA.startTimestamp - $eventB.startTimestamp ) <= 5s && $eventA.endTimestamp > $eventB.endTimestamp
The startedby
operator does not accept negative intervals, and the rules engine will throw an exception if an attempt is made to use negative distance intervals.
7.7. Sliding Windows
7.7.1. Sliding Time Windows
Stream mode allows events to be matched over a sliding time window. A sliding window is a time period that stretches back in time from the present. For instance, a sliding window of two minutes includes any events that have occurred in the past two minutes. As events fall out of the sliding time window (in this case because they occurred more than two minutes ago), they will no longer match against rules using this particular sliding window.
For example:
StockTick() over window:time(2m)
JBoss BRMS Complex Event Processing uses the over
keyword to associate windows with patterns.
Sliding time windows can also be used to calculate averages and over time. For instance, a rule could be written that states "If the average temperature reading for the last ten minutes goes above a certain point, sound the alarm."
Example 7.15. Average Value over Time
rule "Sound the Alarm in Case Temperature Rises Above Threshold" when TemperatureThreshold($max : max) Number(doubleValue > $max) from accumulate( SensorReading($temp : temperature) over window:time(10m), average($temp)) then // sound the alarm end
The engine will automatically discard any SensorReading
more than ten minutes old and keep re-calculating the average.
7.7.2. Sliding Length Windows
Similar to Time Windows, Sliding Length Windows work in the same manner; however, they consider events based on order of their insertion into the session instead of flow of time.
The pattern below demonstrates this order by only considering the last 10 RHT Stock Ticks independent of how old they are. Unlike the previous StockTick from the Sliding Time Windows pattern, this pattern uses window:length.
StockTick(company == "RHT") over window:length(10)
The example below portrays window length instead of window time; that is, it allows the user to sound an alarm in case the average temperature over the last 100 readings from a sensor is above the threshold value.
Example 7.16. Average Value over Length
rule "Sound the Alarm in Case Temperature Rises Above Threshold" when TemperatureThreshold($max : max) Number(doubleValue > $max) from accumulate( SensorReading($temp : temperature) over window:length(100), average($temp)) then // sound the alarm end
The engine disregards events that fall off a window when calculating that window, but it does not remove the event from the session based on that condition alone as there might be other rules that depend on that event.
Length based windows do not define temporal constraints for event expiration from the session, and the engine will not consider them. If events have no other rules defining temporal constraints and no explicit expiration policy, the engine will keep them in the session indefinitely.
7.8. Memory Management for Events
Automatic memory management for events is available when running the rules engine in Stream mode. Events that no longer match any rule due to their temporal constraints can be safely retracted from the session by the rules engine without any side effects, releasing any resources held by the retracted events.
The rules engine has two ways of determining if an event is still of interest:
- Explicitly
-
Event expiration can be explicitly set with the
@expires
. - Implicitly
- The rules engine can analyze the temporal constraints in rules to determine the window of interest for events.
7.8.1. Explicit Expiration
Explicit expiration is set with a declare
statement and the metadata @expires
tag.
For example:
Example 7.17. Declaring Explicit Expiration
declare StockTick @expires(30m) end
Declaring expiration against an event-type will, in the above example StockTick
events, remove any StockTick
events from the session automatically after the defined expiration time if no rules still need the events.
7.8.2. Inferred Expiration
The rules engine can calculate the expiration offset for a given event implicitly by analyzing the temporal constraints in the rules.
For example:
Example 7.18. Rule with Temporal Constraints
rule "correlate orders" when $bo : BuyOrder($id : id) $ae : AckOrder(id == $id, this after[0,10s] $bo) then // do something end
For the example rule, the rules engine automatically calculates that whenever a BuyOrder
event occurs it needs to store the event for up to ten seconds to wait for the matching AckOrder
event, making the implicit expiration offset for BuyOrder
events ten seconds. An AckOrder
event can only match an existing BuyOrder
event making its implicit expiration offset zero seconds.
The engine analyzes the entire rule-base to find the offset for every event-type. Whenever an implicit expiration clashes with an explicit expiration the engine uses the greater value of the two.