Events and event listeners

Event definitions

An event definition specifies the event type, and any event fields and/or event action fields.

Example:

event MyEvent {
    string str;
    MyOtherEvent evt;
    location loc;
    wildcard integer int;
}

To create an instance of MyEvent, you can use the following syntax:

MyEvent myEvent := MyEvent(str1, evt1, loc1, int1);

where str1 is a string, evt1 is an instance of MyOtherEvent, loc1 is an instance of location, and int1 is an integer (this field being a wildcard makes no difference to how you instantiate an event; for more information on wildcards, see Improving performance by ignoring some fields in matching events).

For detailed information, see Defining event types

Event fields

An event field definition specifies the type and name of the field.

Event fields and variables are similar, but unlike variables, event fields cannot be initialized with a value.

Event fields that do not have the wildcard attribute are indexed by the correlator when you listen for them. There can be at most 32 indexes on an event type. Event fields of the type location use two indexes for each field.

An event that contains an action, chunk, listener, and/or stream field is valid only within the monitor that creates it. You cannot send, enqueue or route an event that contains, directly or indirectly, a field of such types.

Event actions

An event action is a subprogram or function that is associated with the event definition. It can be invoked or called from any monitor or from another action in the same event. Like monitor actions, the caller must supply actual parameters of the same type and number as the event action’s formal parameters and if the action returns a value, then the return value must be consumed by the caller.

Like monitor actions, event actions can optionally be prefixed with annotations. See Annotations.

Unlike monitor actions (see Monitor actions), events do not have the special actions onload(), onunload(), and ondie().

Event action example:

action myEventAction(string s, location l) returns float {
	...
	return 10.0;
}

See Defining actions for further information.

It is also possible to define static actions which apply only to the event type (and not to specific instances of an event). For example:

event E {
   static action someStaticAction(){
      print "I am a static action on event type E";
   }
}

See Defining static actions for further information.

Event field and action scope

The scope of an event’s fields and actions is the same as the scope of the event itself except that the event fields are always referenceable within the event’s actions.

Event action formal parameters

The formal parameters are a comma-separated list of parameter definitions, enclosed in parentheses. A parameter definition consists of a type name and an identifier. The identifier is the name of a parameter variable which will be bound to a copy of the value of an expression specified by the caller (that is, the value passed by the caller) when the action is invoked. The number and type of actual parameter values passed by a caller must match those listed in the action’s formal parameters.

The scope of a parameter variable is the statement or block that forms the action body. Parameter variables are very similar to an action’s local variables.

Event action return value

An event action return value specifies the return value type.

If the event action definition includes a returns clause, then the action returns a value of the specified type. All control paths within the action body must lead to a return statement before the end of the action body.

Event action body

The block construct forms the event action body. All variable references within an event action body must be one of the following:

  • A field of the event
  • A formal parameter of the action
  • A local variable defined in the action body

Event templates

An event template is a construct that allows you to specify qualifying or matching criteria based on values of one or more of an event’s fields. In event templates, you can qualify only on those event fields whose type is a primitive type. Event templates are used with on statements. See The on statement.

An event template begins with the name of an event type that is to be matched. It can also start with any, in which case it will match against all events regardless of their type; see Listening for events of all types.

Event templates can be either positional (see By-position qualifiers) or named (see By-name qualifiers) or a combination of both. Further, the criteria can be omitted entirely, in which case any event of the same event type will match. When both positional and named qualifiers are present in an event template qualifier expression list, the positional matches must come first.

Optionally, a colon and an identifier can follow the event expressions. This is called an event coassignment and specifies a variable whose value will become (that is, will be assigned) a reference to the matched event structure when the correlator detects a matching event and listener, and invokes the actions defined in the listener.

See also Stream source templates.

By-position qualifiers

The correlator evaluates a positional event template against the event field that is at the same position in the event definition as the qualifier’s position in the qualifier list.

For example, suppose an event has the fields shown below:

event sample1
{
   string  itemName;
   float   price;
   integer quantity;
}

An example of a by-position qualifier list for this event is as follows:

sample1 ("eggs", 0.50, 3)

This template matches sample1 events that have an itemName value of "eggs", a price value of 0.50, and a quantity value of 3.

In a by-position qualifier, an asterisk (*) matches any value of an event field in the corresponding position.

A range expression matches the event field values in the corresponding position to a low and high boundary value of the range. A match occurs when the field value is within the range. See Range expressions.

The comparison operators < (less than), <= (less than or equal to), > (greater than), >= (greater than or equal to), and = (equal to) specify a comparison of the event field value with the expression value that follows. A match occurs when the relation result is true. The expression to the right of the comparison operator cannot contain any references to the event’s fields and must have a result type that is the same as the event field’s type and must be one of decimal, float, integer or string.

By-name qualifiers

A by-name qualifier names an event field whose value is to be matched, instead of matching by position.

The identifier must be the name of one of the event’s fields. The field’s type must be integer, decimal, float, or string. Each event field is allowed to appear only once on the left side of a by-name qualifier, and the same field is not allowed in both a by-position qualifier and a by-name qualifier in the same event template.

An example of a by-name qualifier list is as follows (see the example in By-position qualifiers for the event fields that are also used for this example):

sample1 (itemName="eggs", price=0.50, quantity=3)

If the qualifier uses = *, then the qualifier matches all possible values of the specified event field.

If the qualifier uses one of the relational operators < (less than), <= (less than or equal to), > (greater than), >= (greater than or equal to), and = (equal to), then the event field value is compared with the event template’s value, and a match occurs when the result of the comparison is true.

If the qualifier uses in followed by a range expression, then the field is compared against the boundary values of the range.

The expression or range expression on the right side is not allowed to refer to any of the event’s fields.

The expression or range expression is evaluated once, when the on statement containing the template is executed and its event expressions evaluated, not each time an event of the same type is processed by the correlator.

Range expressions

A range expression is a part of a qualifier expression that describes a range of consecutive decimal, float, integer, or string values between a low boundary and a high boundary. The correlator tests an event’s field value against this range to determine whether or not it falls within the specified range.

The values for the low boundary and the high boundary are the expression values. Both expression values must be of the same type and one of decimal, float, integer, or string. Both expression types must be of the same type as the event field being tested. Neither expression can contain any references to the event’s fields.

If the low boundary value is greater than the high boundary value, the EPL runtime automatically reverses them.

Field operators

Field operators can appear within event templates to define a field value.

The on keyword creates an event listener that watches the series of events processed by the correlator for individual events or patterns of particular events. You define the sequence of interest in an event expression made up of one or more event templates. The first part of an event template defines the event type of the event the event listener is to match against, while the section in brackets describes further filtering criteria that must be satisfied by the contents of events of that type for there to be a match. Event template field operators define what values, or range of values, are acceptable for a successful event match.

The value that a field operator applies to can be the result of an expression. Therefore, it is possible to have >, <, >=, <=, and/or = present in both their roles, as expression operators and as field operators, within an event template. This is not a problem, since the latter are unary while the former are binary and the semantics are quite different.

The following table describes the field operators:

Operator

Description

[*value1*:*value2*]

Specifies a range of values that can match. The values themselves are included in the range to match against. For example: ``` on stockPrice(*, [0 : 10]) doSomething();


This example will invoke the `doSomething()` action if a `stockPrice` event is received where the price is between 0 and 10 inclusive. You can apply this range operator to `decimal`, `float`, `integer` and `string` types.

</td></tr><tr><td>

`[*value1*:*value2*)`

</td><td>

Specifies a range of values that can match. The first value itself is included in the range to match against while the second value is excluded from the range to match against. For example: ```
on stockPrice(*, [0 : 10)) doSomething();

This example will invoke the doSomething() action if a stockPrice event is received where the price is between 0 and 9 inclusive (assuming the field was of integer type). You can apply this range operator to decimal, float, integer and string types.

(*value1*:*value2*]

Specifies a range of values that can match. The first value is excluded from the range to match against while the second value is included. For example: ``` on stockPrice(*, (0 : 10]) doSomething();


This example invokes the `doSomething()` action if a `stockPrice` event is received where the price is between 1 and 10 inclusive \(assuming the field was an `integer`\). This operator can apply to `decimal`, `float`, `integer` and `string` types.

</td></tr><tr><td>

`(*value1*:*value2*)`

</td><td>

Specifies a range of values that can match. The values themselves are excluded from the range to match against. For example: ```
on stockPrice(*, (0 : 10)) doSomething();

This example will invoke the doSomething() action if a stockPrice event is received where the price is between 1 and 9 inclusive (assuming the field was of integer type).You can apply this range operator to decimal, float, integer and string types.

> *value*

All values greater than the value supplied will satisfy the condition for a match. You can apply this operator to decimal, float, integer, and string types. When used with a string, the operator assumes lexical ordering.

< *value*

All values less than the value supplied will satisfy the condition for a match. You can apply this operator to decimal, float, integer, and string types. When used with a string, the operator assumes lexical ordering.

>= *value*

All values greater than or equal to the value supplied will satisfy the condition for a match. You can apply this operator to decimal, float, integer, and string types. When used with a string, the operator assumes lexical ordering.

<= *value*

All values less than or equal to the value supplied will satisfy the condition for a match. You can apply this operator to decimal, float, integer, and string types. When used with a string, the operator assumes lexical ordering.

= *value*

All values equal to the value supplied will satisfy the condition for a match. You can apply this operator to decimal, float, integer, and string types. When used with a string, the operator assumes lexical ordering.

*value*

With one exception, only a value equivalent to the value supplied will satisfy the condition for a match. The exception is a location type field. A location value consists of a structure with four floats representing the coordinates of the corners of the rectangular space being represented. A listener that is watching for a particular value for a location field matches when it finds a location field that intersects with the location value specified in the listener’s event expression. In the following example, the listener matches each A event whose loc field specifies a location that intersects with the square defined by (0.0, 0.0, 1.0, 1.0). ``` location l := location(0.0, 0.0, 1.0, 1.0); on all A(loc = l)… ...

*

Any value for this field satisfies the condition for a match.

Event listener definitions

You define an event listener in an on statement. See The on statement.

Event lifecycle

An event enters the correlator in one of the following ways:

  • An event is received from another component, such as the engine_send utility, an adapter, another correlator, or a process that is using the Apama client API. The correlator places the event on the input queue of each context that is subscribed to the channel on which the event is sent. If an event is not sent on a named channel then the correlator places the event on the input queue of each public context and each context that is processing a query.

    Events sent on the com.apama.queries channel are put on the input queue of each context that is processing a query. These contexts automatically receive events sent on the com.apama.queries channel.

    Note: Apama queries are deprecated and will be removed in a future release.

  • A correlator pulls an event from a JMS message queue that is set up to distribute events to a cluster of correlators that is processing queries. The correlator adds the event to the input queue of each context that is processing queries.

  • An EPL program creates an event instance and executes a send..to statement. If the target is a channel then the correlator places the event on the input queue of each context that is subscribed to that channel. If the target is a context (or a sequence of contexts) then the correlator places the event on the input queue of that context (or on the input queue of each context in the sequence).

  • An EPL program creates an event instance and executes an enqueue...to statement. The correlator places the event on the input queue of the specified context or on the input queue of each context in the specified sequence of contexts.

  • An EPL program creates an event instance and executes a route statement. The correlator places the event on the input queue of only the context that contains the monitor instance that routed the event.

Monitors

When the event gets to the front of the context’s input queue, the correlator evaluates the event to determine if it is a match for any active event listeners in that context. That is, the correlator checks whether there are any event listeners in that context that are watching for that particular event. If there is a match, the match triggers the event listener. This means that the correlator executes the actions defined in the matching event listener.

It is possible for the actions defined in the event listener to route one or more events back to the context’s input queue. A routed event goes right to the front of the context input queue. When the correlator is finished processing the event that triggered the event listener action, the correlator evaluates any routed events before it moves on to the event that was on the input queue after the matching event.

Queries

When the event gets to the front of the context’s input queue, the correlator extracts the key of the event according to the definitions of running queries that use that event. The window of events for that key value is retrieved from the distributed cache. The correlator adds the event to the retrieved window, which it writes back to the cache. The event pattern of interest is evaluated against the stored window to determine whether the addition of the event causes a match set.

The event remains in its window until the correlator ejects it to make room for a new event or until the query instance or parameterization terminates.

Event listener lifecycle

When you inject a monitor into the correlator, the correlator instantiates the monitor in the main context and executes the monitor’s onload() action. The onload() action typically specifies at least one on statement. An on statement includes an event expression that identifies the event or sequence of events that you are interested in. This is what you want to listen for. An onload() statement is not required to specify an on statement. If there is no on statement, the correlator immediately unloads the monitor.

When the correlator executes an on statement, it sets up an event listener for the specified event or sequence of events. After the correlator sets up the event listener, the event listener watches for an event that matches its event expression. When the event listener detects a matching event, the event listener triggers and the correlator executes the action specified in the on statement.

For an event listener that is looking for a single instance of an event, this is straightforward. However, the event expression that defines what you are looking for can specify all instances of an event, all instances of a sequence of events, and it can have temporal and logical constraints. This makes the lifecycle of an event listener less straightforward.

For example, consider the following event listener:

on all A() success();

When the correlator sets up this event listener, it sets up an event template to look for an A event. When an A event arrives, the correlator does the following:

  • Executes the success() event listener action.
  • Sets up a new event template to look for the next A event.

Now consider this event listener:

on all A() -> all B() success();

Again, suppose that the correlator sets up this event listener and an A event arrives. This time the correlator does the following:

  1. Sets up an event template to listen for the next B event.
  2. Sets up an event template to listen for the next A event.

This event listener will be active until it is explicitly killed because there will always be an event listener that is looking for the next A event.

Additional information about event listener lifecycles is in How the correlator executes event listeners.

Event processing order for monitors

As mentioned earlier, contexts allow EPL applications to organize work into threads that the correlator can execute concurrently. When you start a correlator it has a main context. You can create additional contexts to enable the correlator to concurrently process events. Each context, including the main context, has its own input queue. The correlator can process, concurrently, events in each context.

Concurrently, in each context, the correlator

  • Processes events in the order in which they arrive on the context’s input queue
  • Completely processes one event before it moves on to process the next event

When the correlator processes an event within a given context, it is possible for that processing to:

  • Send or enqueue an event to a particular channel. The correlator places the event on the input queue of each context that is subscribed to the specified channel.

  • Send or enqueue an event to a particular context or to a sequence of contexts. The correlator places the event on the input queue of the specified context or on the input queue of each context in the specified sequence.

  • Route an event. The correlator places the routed event at the front of that context’s input queue. The correlator processes the routed event before it processes the other events in that input queue.

    If the processing of a routed event routes one or more additional events, those additional routed events go to the front of that context’s input queue. The correlator processes them before it processes any events that are already on that context’s input queue.

For example, suppose the correlator is processing the E1 event and events E2, E3, and E4 are on the input queue in that order.

Illustration of processing the E1 event

While processing E1, suppose that events En1 and En2 are created in that order and enqueued or sent to the context. Assuming that there is room on the input queue of that context, those events go to the end of the input queue of that context:

Illustration of processing the E1 event

While still processing E1, suppose that events R1 and R2 are created in that order and routed. These events go to the front of the queue:

Illustration of processing the E1 event

When the correlator finishes processing E1, it processes R1. While processing R1, suppose that two event listeners trigger and each event listener action routes an event. This puts event R3 and event R4 at the front of that context’s input queue. The input queue now looks like this:

Illustration of processing the R1 event

It is important to note that R3 and R4 are on the input queue in front of R2. The correlator processes all routed events, and any events routed from those events, and so on, before it processes the next routed or non-routed event already on the queue.

Now suppose that the correlator is done processing R1 and it begins processing R3. This processing causes R5 to be routed to the front of that context’s input queue. The context’s queue now looks like the following:

Illustration of processing the R3 event

See also Understanding time in the correlator.

Event processing order for queries

Note: Apama queries are deprecated and will be removed in a future release.

Unlike EPL monitors, the order in which queries process events is not necessarily the order in which they were sent into the correlator. In particular, if two events that will be processed by the same query with the same key value are sent very close together in time (both events received less than about.1 seconds of each other) then they may be processed as if they had been sent in a different order. For example, consider a query that is looking for an A event followed by an A event. If two A events with the same key arrive 1 millisecond apart then the events might not be processed in the order in which they were sent.

Queries use multiple threads to process events and to scale across multiple correlators on multiple machines. To do this efficiently, there is no enforcement that the events are processed in order. However, when events that have the same key arrive roughly about.5 seconds apart or more then out-of-order processing is typically avoided provided the system can keep up with the load. Therefore, you want to specify a query so that it operates on partitions in which the arrival of consecutive events is spaced far enough apart. For example, consider a query that operates on credit card transaction events, which could mean thousands of events per second. You want to partition this query on the credit card number so that there is one event or less per partition per second. By following this recommendation, it becomes possible to process events that are generated at rates of up to 10,000 events per second.

When creating an evt file for testing purposes, the recommendation is to begin the file with a &FLUSHING(1) line to cause more predictable and reliable event-processing behavior. See Event timing.

Event expressions

An event expression is a special type of expression that is used with the on statement to define the rules for detecting events of interest and invoking an action when a matching event is detected. In an event expression, you can specify filtering rules based on an event’s field values, sequencing rules for events followed by other events, times and time ranges during which an event is of interest, and other rules. See also The on statement.

Event expressions should not be confused with ordinary EPL expressions of type event. Ordinary EPL expressions of all types are described in Expressions.

Event primaries

The event primary is the simplest form of an event expression clause and can be combined with other event primaries and event operators to form more complex event expressions.

An event primary can be an event template (see Event templates) optionally prefixed with completed or unmatched, or it can be a timer (see Timers).

Event templates are constructs that allow you to specify filtering or matching criteria based on values of one or more of an event’s fields.

Event primaries

The event primary is the simplest form of an event expression clause and can be combined with other event primaries and event operators to form more complex event expressions.

An event primary can be an event template (see Event templates) optionally prefixed with completed or unmatched, or it can be a timer (see Timers).

Event templates are constructs that allow you to specify filtering or matching criteria based on values of one or more of an event’s fields.

Timers

Specify a timer with the wait, at, or within keyword. For more detailed information, see Defining event listeners with temporal constraints.

The not operator

The not operator specifies logical negation.

Example:

on A() and not B() executeAction();

The all operator

When the all operator appears before an event template, when that event template finds a match, it continues to watch for subsequent events that also match the template.

Consider the following event expression:

all A -> B

This event listener would match on every A and the first B that follows it. The way this works is that upon encountering an A, the correlator creates a second event listener to seek the next A. Both event listeners would be active concurrently; one looking for a B to successfully match the sequence specified, the other initially looking for an A. If more As are encountered the procedure is repeated; this behavior continues until either the monitor or the event listener are explicitly killed.

Consider the following sequence of incoming events:

C1 A1 F1 A2 C2 B1 D1 E1 B2 A3 G1 B3

With these input events, on all A() -> B() would return the following:

{A1, B1}, {A2, B1} and {A3, B3}.

Note that all is a unary operator and has higher precedence than ->, or and and.

The and, xor, and or logical event operators

The logical operators and, xor, and or are binary operators, operating on event expressions that are either side of them. They are similar to the corresponding operators in primary and bitwise expressions, but do not have quite the same precedence. See also Event expression operator precedence.

Operator Description
and Logical intersection
xor Logical exclusive or
or Logical or

The followed-by event operator

The followed-by operator -> takes left and right operands, both event expressions. The followed-by operator waits for the left operand to become true and then waits for the right operand to become true. When both are true, then the result value is true. If either becomes false, then the result value is false.

Event expression operator precedence

The following table lists the event expression operators in order by their precedence, from lowest to highest. See Expression operator precedence for a corresponding table of primary and bitwise expression operator precedence.

Operation Operator Precedence
Followed-by -> 1
Logical union or 2
Logical exclusive or xor 3
Logical intersection and 4
All all 5
Logical negation not 6

For clarity, it is strongly recommended to use brackets in event expressions. This makes it very easy to understand what an expression means. Do not use un-bracketed expressions, except where trivial.

For example, the following expression:

on all A()or B() and not C() -> D()

is equivalent to this expression, which is much easier to understand:

on (
   (all A() )
   or
   (B() and (not C() ))
   ) -> D()

The completed operator

A completed event template matches only after all other work is completed. When an event that matches a completed template comes into the correlator, the correlator:

  1. Runs all of the event’s normal or unmatched event listeners. Normal event templates do not specify the completed or unmatched keyword.
  2. Processes all routed events that result from those event listeners.
  3. Triggers the completed event listeners.

For example:

on all completed A(f < 10.0) {}

The unmatched operator

An unmatched event template matches against events for which both of the following are true:

  • Except for completed and unmatched event templates, the event is not a match with any other event template currently loaded in the context.
  • The event matches the unmatched event template.

The correlator processes events as follows:

  1. The correlator tests the event against all normal event templates in the context. Normal event templates do not specify the completed or unmatched keyword. If there are any matches, those event listeners trigger and the correlator executes those event listener actions. If execution of the event listener actions routes any events, the correlator then processes those events.
  2. If the correlator does not find a match, the correlator tests the event against all event templates in the context that specify the unmatched keyword. If the correlator finds one or more matches, it triggers an event listener for each match found. In other words, if multiple unmatched event templates match a given event, they all trigger. The correlator executes the event listener actions defined by the event listeners that trigger. If any events are routed during execution of those actions, the correlator processes the routed events.
  3. The correlators tests the event against all event templates in the context that specify the completed keyword. If the correlator finds one or more matches, it triggers an event listener for each match found.

Example

For example, suppose you have the following code:

on all A("foo",  < 10) as a {
   print "Match: " + a.toString();
   a.count := a.count+1; // count is second field of A
   route a;
}
on all unmatched A(*,*) as a {
   print "Unmatched: " + a.toString();
}
on all completed A("foo", *) as a {
   print "Completed: " + a.toString();
}

The incoming events are as follows:

A("foo", 8);
A("bar", 7);

The output is as follows.

Match: A("foo", 8)
Match: A("foo", 9)
Unmatched: A("foo", 10)
Completed: A("foo", 10)
Completed: A("foo", 9)
Completed: A("foo", 8)
Unmatched: A("bar", 7)

Specify the unmatched keyword with care. Be sure to communicate with any others who write event templates. If you are relying on an unmatched event template, and someone else injects a monitor that happens to match some events that you expected to match your unmatched event template, you will not get the results you expect. The firing of an event-specific unmatched listener is suppressed if an on any() listener matches the event (not just a type-specific listener).

Parenthesized event expressions

Just as with primary and bitwise expressions, event expressions can be enclosed in parentheses to control expression evaluation order or to improve readability.

The wait event operator

The wait operator can be used to limit the amount of time that an event listener can match an event. The wait operator’s expression specifies the time in seconds. The result of evaluating the wait expression must be of type float.

See also Waiting within an event listener.

The at event operator

The at operator allows triggering of an event listener at a specific time or repeatedly at multiple times, depending on how the series of expressions that follow the at operator are constructed.

The time specification of the at operator consists of either five, six or seven expressions, corresponding to the number of minutes of the hour (0 to 59), hour of the day (0 to 23), day of the month (1 to 31), month of the year (1 to 12), day of the week (0 to 6, 0=Sunday), seconds (optional), and time zone (optional) respectively.

If the optional number of seconds is omitted, 0 is used.

If the optional time zone is omitted, the time zone configured for the correlator as a whole is used.

The * operator means that all times (minute, hour, etc.) for the corresponding part of the time specification will match.

You can specify one or more time values separated by commas.

See also Triggering event listeners at specific times.

The within operator

The within operator takes one operand, which is an expression of type float, whose value is the number of elapsed seconds from an event primary’s activation time that the event primary can be matched. The within operator’s result type is boolean. If the event is matched before the specified time has elapsed, the within operator’s result is true. When the time has elapsed and the event has not been matched, the within operator’s result is false.

See also Listening for event patterns within a set time.

Event channels

Adapter and client configurations can specify the channel to deliver events to. A channel is a string name that contexts and receivers can subscribe to in order to receive particular events. In EPL, you can send an event to a specified channel. Sending an event to a channel delivers it to any contexts that are subscribed to that channel, and to any clients or adapters that are listening on that channel.

You can use the com.apama.Channel type to send an event to a channel or context. The Channel type holds a string or a context. When it holds a string an event is sent to the channel that has that name. When it holds a context an event is sent to that context.

The default channel is the empty string. Events sent to the default channel and events sent without a channel specification are added to the input queue of each public context.