Defining event expressions

Consider this code snippet from the previous example:

public void onLoad() {
   EventExpression eventExpr =
      new EventExpression("Tick(*, >10.0)");
   eventExpr.addMatchListener(this);
}

The highlighted code is creating an event expression, and embeds the following event expression definition string:

Tick(*, >10.0)

This is the simplest form of an event expression; specifically it contains a single event template.

In this case, the event expression is specifying “the first Tick event whose price parameter contains a value greater than 10.0”.

If you are already familiar with EPL, the syntax for writing JMon event expressions is the same as for EPL event expressions.

About event templates

The first part of an event template defines the event type of suitable events (in this case Tick), while the section in brackets describes filtering criteria that must be applied to the contents of events of the desired type for them to match.

In the example at the beginning of the chapter, the first parameter within the event template has been set to a wildcard (*), specifying that all Tick events, regardless of the value of their name parameter, are suitable. That is, as long as their second parameter, price, is greater than 10.The filtering criteria supplied are applied to the event’s contents in the same order as within the event definition for that event type. This is known as positional syntax.

Specifying parameter constraints in event templates lists all the filtering operators (like “>”) that can be applied to the value of a parameter within an event template.

Specifying positional syntax

In positional syntax, the event template must define a value (or a wildcard) to match against for every parameter of that event’s type, in the same order as the parameter’s definition in the event type definition. Therefore, for the event type,

public class MobileUser extends Event {
   public long userID;
   public Location position;
   public String hairColour;
   public String starsign;
   public long gender;
   public long incomeBracket;
   public String preferredHairColour;
   public String preferredStarsign;
   public long preferredGender;
   //... Constructors
}

a suitable event template definition might look like

MobileUser(*,*, "red", "Capricorn", *, *, *, *, 1)

This can get unwieldy when you are working with event types with a large number of parameters and very few of them are actually being used to filter on. An alternative syntax can be used that addresses this. The above can instead be expressed as:

MobileUser(hairColour="red", starsign="Capricorn",
  preferredGender=1)

This is known as named parameter syntax and in this style all other non-specified fields are set to wildcard.

Given the following event types:

public class A extends Event {
   public long a;
   public String b;

   //... Constructors
}

public class B extends Event {
   public long a;

   //... Constructors
}

public class C extends Event {
   public long a;
   public long b;
   public long c;

   //... Constructors
}

Here are some equivalent event expressions that demonstrate how to use the two syntaxes:

Positional Syntax

Name/Value Syntax

Using constants and literals

on A(3,"string")
on A(=3,="string")

on A(a=3,b="string")
on A(b="string",a=3)

Relational comparisons

on B(>3)

on B(a>3)

Ranges

on B([2:3])

on B(a in [2:3])

Wildcards

on C(*,4,*)
on C(*,*,*)

on C(b=4)
on C(a=*,b=4,c=*)
on C()

More details about the operators and expressions possible within event templates are given in the next section.

Note that it is possible to mix the two styles as long as you specify positional parameters before named ones. There cannot be any positional parameters after named ones. Therefore the following syntax is legal:

D(3,>4,i in [2:4])

while the following is not:

E(k=9,"error")

Specifying completed event templates

In some situations, you want to ensure that the correlator completes all work related to a particular event before your application performs some other work. In your event template, specify the completed keyword to accomplish this. For example:

on all completed A(f < 10.0) {}

When an event that matches the template comes into the correlator, the correlator

  1. Runs all of the event’s normal and unmatched listeners.

  2. Processes all routed events that result from those listeners.

  3. Calls the completed listeners.

Specifying parameter constraints in event templates

The first part of an event template defines the event type of the event the 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 a match.

Event template parameter operators specify constraints that define what values, or range of values, are acceptable for a successful event match.

Operator

Meaning

[value1 : value2]

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

This event template will match a stockPrice event where the price is between 0 and 10 inclusive. This range operator can only be applied to double and long types.

[value1 : value2)

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

This example will match a stockPrice event where the price is between 0 and 9 inclusive (assuming the parameter was of long type).

This range operator can only be applied to double and long types.

(value1 : value2]

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

This example will match a stockPrice event where the price is between 1 and 10 inclusive (assuming the parameter was of long type).

This operator can only be applied to double and long types.

(value1 : value2)

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

This example will match if a stockPrice event where the price is between 1 and 9 inclusive (assuming the parameter was of long type).

This operator can only be applied to double and long types.

> value

All values greater than the value supplied will satisfy the condition and match.

This operator can only be applied to double and long types.

< value

All values less than the value supplied will satisfy the condition and match.This operator can only be applied to double and long types.

>= value

All values greater than or equal to the value supplied will satisfy the condition and match.

This operator can only be applied to double and long types.

<= value

All values less than or equal to the value supplied will satisfy the condition and match.

This operator can only be applied to double and long types.

value

Only a value equivalent to the value supplied will satisfy the condition and match.

A String value must be enclosed in double quotes (" "), and therefore these need to be preceded with an escape character inside event expression definitions in an EventExpression constructor ( \")

A Location value must consist of a structure with four doubles representing the coordinates of the corners of the rectangular space being represented.

*

Any value for this parameter will satisfy the condition and match.

Obtaining matching events

An event template provides a definition against which several event instances could match. Once a listener triggers, sometimes it is necessary to get hold of the actual event that matched the template.

This can be achieved through event tagging.

If you are familiar with EPL, event tagging in JMon is similar in principle to variable coassignment in EPL. For this reason the term coassigned is sometimes used to refer to event tagging.

Consider this revised Simple monitor:

import com.apama.jmon.*;

public class Simple implements Monitor, MatchListener {

   /**
    * No argument constructor used by the jmon framework on
    * application loading
    */
   public Simple() {}

   /**
    * Implementation of the Monitor interface onLoad method. Sets up
    * a single event expression looking for all stock trade events
    * with a trade price of greater than 10.0. This class instantiation
    * is added as a match listener to the event expression.
    */
   public void onLoad() {
      EventExpression eventExpr = new EventExpression("Tick(*, >10.0):t");
      eventExpr.addMatchListener(this);
   }

   /**
    * Implementation of the MatchListener interface match method.
    * Extracts the tick event that caused the event expression to
    * trigger and emits the event onto the default channel
    */
   public void match(MatchEvent event) {
      Tick tick = (Tick)event.getMatchingEvents().get("t");
      System.out.println("Event details: "  + tick.name
         + " "   + tick.price);
      tick.emit();
   }
}

Note the revised event expression

Tick(*, >10.0):t

This specifies that when a suitable Tick event is detected, it must be recorded with the t tag. This allows a developer to get hold of the actual event that matched the event expression within the registered match listener’s match method.

Once the eventExpr listener detects a suitable event it will trigger and call match, passing to it a MatchEvent object. This object embeds within it all the individual event instances that together caused the event expression to be satisfied and were tagged.

In this example our event expression still consists of a single event template, and since this is tagged, then the MatchEvent object will contain the single Tick event that triggered the eventExpr listener. This will be tagged as t.

A MatchEvent object has two methods:

  • HashMap getMatchingEvents() - Get the set of tagged Events that caused the match. This method returns a Map of the tagged Event objects that hold the values that matched the source EventExpression.
  • Event getMatchingEvent(String key) - Get one of the tagged Events that caused the match. This method returns the tagged Event object that matched in the source EventExpression.

For complete class and method signatures, refer to the API reference for Java (Javadoc).

The lines:

Tick tick = (Tick)event.getMatchingEvents().get("t");

or

Tick tick = event.getMatchingEvent("t");

show how the tagged event can be extracted by using the tag as a key.

Emitting, routing, and enqueuing events

Once the event has been extracted it can also be emitted, routed, or enqueued.

This functionality is provided by the following methods of the Event class:

  • route() — Route this event internally within the correlator.
  • emit() — Emit this event from the correlator onto the default channel.
  • emit(String channel) — Emit this event from the correlator onto the named channel.
  • enqueue() — Route this event internally within the correlator to a special queue just for enqueued events.
  • enqueueTo() — Route this event internally within the correlator to the input queue of the specified context or contexts.

The route method generates a new event that is dispatched back into the correlator. Any active listeners seeking that event then receive this. There is no difference between an externally sourced event (passed in through a live message feed) and an event that was issued internally through a route method, other than that internally routed events are placed at the front of the input queue, although in the same order as they are routed within an action.

The emit method dispatches events to external registered event receivers, that is, sends them out from the correlator. Active listeners will not receive events that are emitted.

Events are emitted onto named channels. For an application to receive events from the correlator it must register itself as an event receiver and subscribe to one or more channels. Then if events are emitted to those channels they will be forwarded to it.

Channels effectively allow both point-to-point message delivery as well as through publish-subscribe. Channels can be set up to represent topics. External applications can then subscribe to event messages of the relevant topics. Otherwise a channel can be set up purely to indicate a destination and have only one application connected to it.

The enqueue() method generates an event and places the event on a special queue just for events generated by the enqueue() method. A separate thread moves each enqueued event to the input queue of each public context. This arrangement ensures that if a public context’s input queue is full, the event generated by enqueue() still arrives on its special queue, and is moved to that context’s input queue when there is a room. Active listeners will eventually receive events that are enqueued, once those events make their way to the head of the context’s input queue alongside normal events.

Use the enqueue() method when you want to ensure that the correlator processes the generated event after it processes all routed events. This means that you want the correlator to finish processing the current external event. Completion of processing the current external event means that all routed events that resulted from that external event have been processed.

In a parallel application, you can enqueue an event to a particular context by calling the following method on an instance of com.apama.jmon.Event:

public void enqueueTo(Context ctx)

This method provides the same functionality provided by the EPL enqueue... to statement. See Sending an event to a particular context.

However, it is important to mention that when you enqueue an event to a particular context the event goes on that context’s input queue and not on the special queue for enqueued events. Consequently, when you call this method from an application thread that was created from the main JMon application and the destination context’s input queue is full, this method blocks until the queue is able to accept the event.

Call the following method to enqueue an event to a array of contexts:

public void enqueueTo(Context[] ctxArray)

Call the following method to enqueue an event to a list of contexts:

public void enqueueTo(List < Context> ctxList)

Specifying temporal sequencing

If you want to search for a temporal sequence of two events, for example, “locate the sequence of a NewsItem event followed by a Tick event”, there are two ways you can proceed in JMon.

Chaining listeners

You can chain listeners, as follows:

// Code within the monitor class

public void onLoad() {
   EventExpression eventExpr = new EventExpression("NewsItem(*, *)");
   eventExpr.addMatchListener(matchListener1);
}

// Code within the first Match Listener class – matchListener1
public void match(MatchEvent event) {

   // Arbitrary additional code …
   EventExpression eventExpr = new EventExpression("Tick(*, *)");
   eventExpr.addMatchListener(matchListener2);
}

// Code within the second Match Listener class – matchListener2

public void match(MatchEvent event) {
   System.out.println("Detected a NewsItem followed"
      + " by a Tick event, both regarding any company.");
}

The Java code above shows how to set up a listener to seek the first event, and then once that is located, start searching for the second. This programming style is particularly appropriate when further actions need to be taken at each stage of the event detection, in this case between detecting the NewsItem and seeking the Tick.

It is also the only way in which the event templates can be “linked” together. If the desired effect was to locate “any” first NewsItem and then seek a Tick specifically for the same company mentioned in the NewsItem, you could amend the example as follows,

// Code within the monitor class
public void onLoad() {
   EventExpression eventExpr
      = new EventExpression("NewsItem(*, *):n");
   eventExpr.addMatchListener(matchListener1);
}

// Code within the first Match Listener class – matchListener1
public void match(MatchEvent event) {

   NewsItem newsItem = (NewsItem)event.getMatchingEvents().get("n");
   EventExpression eventExpr
      = new EventExpression("Tick(\"" + newsItem.name + "\", *)");
   eventExpr.addMatchListener(matchListener2);
}

// Code within the second Match Listener class – matchListener2
public void match(MatchEvent event) {
   System.out.println("Detected a NewsItem, followed"
      + " by an Tick event regarding the same company.");
}

Note how the above code seeks out a NewsItem on any company, but then extracts the actual NewsItem event detected, and uses its name parameter to create the event template for seeking the Tick event.

Using temporal operators

Let us return to how to express searching for a temporal sequence. If there is no requirement to execute any arbitrary code in between events and there is no requirement to link searches as illustrated above, then you can embed a temporal event expression within a single listener.

The first code excerpt could be re-written as follows,

// Code within the monitor class
   public void onLoad() {
      EventExpression eventExpr
         = new EventExpression("NewsItem(*,*) -> Tick(*,*)");
      eventExpr.addMatchListener(matchListener1);
  }

// Code within the first (and only) Match Listener class – matchListener1
   public void match(MatchEvent event) {
      System.out.println("Detected a NewsItem followed"
         + " by a Tick event, both regarding any company.");
  }

The event expression definition for eventExpr no longer consists of a single event template. It now has multiple clauses and contains a temporal operator.

In this case, the operator used is ->, or the followed-by operator. This is the primary temporal operator for use in event expressions. It allows a developer to express a sequence of events to match against within a single listener, with the listener triggering once the whole sequence is encountered.

In Java, an event sequence does not imply that the events have to occur right after each other, or that no other events are allowed to occur in the meantime.

For the sake of brevity, let A, B, C and D represent event templates, and A', B', C' and D' be individual events that match those templates, respectively. If a listener is created to seek the event expression (A -> B), the event feed {A',C',B',D'} would result in a match once the B' is received by the correlator.

Followed-by operators can be chained to express longer sequences. Therefore you could write,

A -> B -> C -> D

within an event expression definition.

The next section focuses on the use of temporal operators in event expressions.

Defining advanced event expressions

An event template is the simplest form of an event expression. All event expression operators, including ->, can themselves take entire event expressions as operands.

It is useful to think of event expressions as being Boolean expressions. Each clause in an event expression can be true or false, and the whole event expression must evaluate to true before the listener triggers and calls the match listener’s match method.

As before, for the sake of brevity, let us use the letters A, B, C and D to represent event templates, and A', B', C' and D' to represent individual events that match those templates, respectively.

Once more, consider this representation of an event expression,

A -> B -> C -> D

When the listener is first activated it is helpful to consider the expression as starting off by being false. When an event that satisfies the A clause occurs, the A clause becomes true. Once B is satisfied, A -> B becomes true in turn, and evaluation progresses in a similar manner until eventually all A -> B -> C -> D evaluates to true. Only then does the listener trigger and call the associated match listener’s match method. Of course, this event expression might never become true in its entirety (as the events required might never occur) since no time constraint (see Specifying the timer operators) has been applied to any part of the event expression.

Specifying other temporal operators

For a listener to trigger on an event sequence, the event expression defining what to match against must evaluate to true.

The or operator allows you to specify event expressions where a variety of event sequences could lead to a successful match. It effectively evaluates two event templates (or entire nested event expressions) simultaneously and returns true when either of them become true.

For example,

A or B

means that either A or B need to be detected to match. That is, the occurrence of one of the operand expressions (an A or a B) is enough to satisfy the listener.

The and operator specifies an event sequence that might occur in any temporal order. It evaluates two event templates (or nested event expressions) simultaneously but only returns true when they are both true.

A and B

will seek “an A followed by a B” or “a B followed by an A”. Both are valid matching sequences, and the listener will seek both concurrently. However, the first to occur will terminate all monitoring and trigger the listener.

The following example code snippets indicate a few patterns that can be expressed using the three operators presented so far.

Example

Meaning

A -> (B or C)

Match on an A followed by either a B or a C.

(A -> B) or C

Match on either the sequence A followed by a B, or just a C on its own.

A -> ((B -> C) or (C -> D))

Find an A first, and then seek for either the sequence B followed by a C or C followed by a D. The latter sequences will be looked for concurrently, but the monitor will match upon the first complete sequence that occurs. This is because the or operator treats its operands atomically; that is, in this case it is looking for the sequences themselves rather than their constituent events.

(A -> B) and (C -> D)

Find the sequence A followed by a B (A -> B) followed by the sequence C -> D, or else the sequence C -> D followed by the sequence A -> B. The and operator treats its operands atomically; that is, in this case it is looking for the sequences themselves and the order of their occurrence, rather than their constituent events. It does not matter when a sequence starts but it occurs when the last event in it is matched. Therefore {A',C',B',D'} would match the specification, because it contains an A -> B followed by a C -> D. In fact the specification would match against either of the following sequences of event instances; {A',C',B',D'}, {C',A',B',D'}, {A',B',C',D'}, {C',A',D',B'}, {A',C',D',B'}, and {C',D',A',B'}.

The `not` operator is unary and acts to invert the truth value of the event expression it is applied to.
A -> B and not C

therefore means that the correlator will match only if it encounters an A followed by a B without a C occurring at any time before the B is encountered.

Info
The not operator can cause an event expression to reach a state where it can never evaluate to true any more, that is, it will become permanently false.

Consider this listener event sequence:

on (A -> B) and not C

The listener will start seeking both A -> B and not C concurrently. If an event matching C is received at any time before one matching B, the C clause will evaluate to true, and hence not Cwill become false. This will mean that (A -> B) and not C will never be able to evaluate to true, and hence this listener will never trigger. In practice the correlator cleans out these zombie listeners periodically.

Info
It is possible to write an event expression that always evaluates to true immediately, without any events occurring.

Consider this listener:

on (A -> B) or not C

Assuming that A, B, and C represent event templates, their value will start off as being false. However, that means that not C will become true immediately, and hence the whole expression will become true right away. This listener will therefore trigger immediately as soon as it is instantiated. If any of A, B or C were nested event expressions the same logic would apply for their own evaluation.

Specifying a perpetual listener for repeated matching

So far all the examples given have created listeners that will trigger on the first occurrence of an event (or sequence of events) that satisfies the supplied event expression.

For example,

public void onLoad() {
   EventExpression eventExpr = new EventExpression("Tick(*, >10.0)");
   eventExpr.addMatchListener(this);
}

locates the first occurrence of a Tick event that satisfies the Tick(*, >10.0) event template. This first suitable event triggers the listener and calls the match method of the registered match listener object.

However, you might want to detect all Tick events that satisfy the above event template (or event expression). To do this you must create a perpetual listener, that is, one that does not terminate on the first suitable occurrence, but instead stays alive and triggers repeatedly on every subsequent occurrence.

This effect can be achieved through use of the all event expression operator.

If the above is rewritten as follows,

public void onLoad() {
   EventExpression eventExpr =
      new EventExpression("all Tick(*, >10.0)");
   eventExpr.addMatchListener(this);
}

the listener created will now seek the first Tick event whose price is greater than 10. Upon detecting such an event it will trigger and call the match method. It will then return to monitoring the incoming event streams to look for the next suitable occurrence. This behavior will be repeated indefinitely until the listener is explicitly deactivated. This means that potentially the match method could be invoked multiple times.

Deactivating a listener

A listener whose event expression embeds an all operator will stay active indefinitely and trigger repeatedly. It will continue doing this until it is explicitly deactivated. This can be done using the removeMatchListener method on the EventExpression object.

For complete class and method signatures, refer to the API reference for Java (Javadoc).

Temporal contexts

Imagine that we have seven event templates defined, which for the sake of brevity are represented by the letters A, B, C, D, E, F and G in the following text. Now, consider a stream of incoming events, where Xn indicates an event instance that matches the event template X. Likewise, Xn+1 indicates another event instance that matches against X, but which need not necessarily be identical to Xn.

Consider the following sequence of incoming events:

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

Given the above event sequence, what should the event expression

A -> B

match upon?

In theory the combinations of events that correspond to “an A followed by a B” are:

{A1, B1}, {A1, B2}, {A1, B3}, {A2, B1}, {A2, B2}, {A2, B3}, {A3, B3}

In practice it is unlikely that a developer wanted their monitor to match seven times on the above example sequence, and it is uncommon for all the combinations to be useful.

In fact, consistent with the truth-value based matching behavior already described, the event expression A -> B will only match on the first event sequence that matches the expression. Given the above event sequence the listener will trigger only on {A1, B1}, call the associated match method, and then terminate.

If a developer wishes to alter this behavior, and have the monitor match on more of the combinations, they can use the all operator within the event expression.

If the listener’s specification was rewritten to read:

all A -> B

the listener would match on “every A” and the first B that follows it.

The way this works is that upon encountering an A, a second child listener (or sub-listener) is created to seek for the next A. Both listeners would continue looking for a B to successfully match the sequence specified. If more As are encountered the procedure is repeated; this behavior continues until the parent listener is explicitly deactivated.

Therefore all A -> B would match on {A1, B1}, {A2, B1} and {A3, B3}.

Note that all is a unary operator and has higher precedence than ->, or and and. Therefore all A -> B is the same as (all A) -> B or ( ( all A ) -> B ).

The following table illustrates how the execution of on all A -> B proceeds over time as the above sequence of input events is processed by the correlator. The timeline is from left to right, and each stage is labeled with a time tn, where tn+1 occurs after tn. To the left are listed the listeners, and next to each one (after the ?) is shown what event template that listener is looking for at that point in time. In the example, assuming L was the initial listener, L', L'' and L''' are other sub-listeners that are created as a result of the all operator.

Guide to the symbols used:

This symbol indicates the following
Down arrow A specific point in time when a particular event is received.
Red X No match was found at that time.
Green checkmark The listener has successfully located an event that matches its current active template.
Equal sign A listener has successfully triggered.
Plus sign A new listener is going to be created.

Illustration of timeline of incoming events

The parent listener denoted by all A -> B will never terminate as there will always be a sub-listener active looking for an A.

If, on the other hand, the specification is written as,

A -> all B

the listener would now match on all the sequences consisting of the first A and each possible following B.

The way this works is by creating a second listener upon matching a B that then goes on to search for an additional B, and so on repeatedly until the listener is explicitly killed.

Therefore A -> all B would match {A1, B1}, {A1, B2} and {A1, B3}.

Graphically this would now look as follows:

Illustration of timeline of incoming events

The table shows the early states of L' and L'' in light color because those listeners actually never really went through those states themselves. However, since they were created as a clone of another listener, it is as though they were.

The parent listener denoted by A -> all B will never terminate, as there will always be a sub-listener looking for a B.

The final permutation is to write the monitor as,

all A -> all B

Now the listener would match on an A and create another listener to look for further As. Each of these listeners will go on to search for a B after it encounters an A. However, in this instance all listeners are duplicated once more after matching against a B.

The effect of this would be that all A -> all B would match {A1, B1}, {A1, B2}, {A1, B3}, {A2, B1}, {A2, B2}, {A2, B3} and {A3, B3}, i.e. all the possible permutations. This could cause a very large number of sub-listeners to be created.

Info
The all operator must be used with caution as it can create a very large number of sub-listeners, all looking for concurrent patterns. This is particularly applicable if multiple all operators are nested within each other. This can have an adverse impact on performance.

As with all other event expression operators, the all operator can be used within nested event expressions, and be nested within the operating context of another all operator. This can have a dramatic effect on the number of sub-listeners created.

Consider the example,

all (A -> all B)

This will match the first A followed by all subsequent Bs. However, as on every match of an A followed by B, (A -> all B) becomes true, then a new search for the next A followed by all subsequent Bs will start. This will repeat itself recursively, and eventually there could be several concurrent sub-listeners that might match on the same sequences, thus causing duplicate triggering.

On the same event sequence as previously, graphically, this would be evaluated as follows:

Illustration of timeline of incoming events

Thus matching against {A1, B1}, {A1, B2}, {A1, B3}, and twice against {A3, B3}. Notice how the number of active listeners is progressively increasing, until after t12 there would actually be six active listeners, three looking for a B and three looking for an A.

Specifying the timer operators

So far we have shown how to use event expressions to define interesting sequences of events to look for, where the events of interest depend not only on their type and content, but also on their temporal relationship to (whether they occur before or after) other events.

Being able to define temporal relationships can be useful, but typically it also needs to be constrained over some temporal interval.

Looking for event sequences within a set time

Consider this earlier example:

// Code within the monitor class
public void onLoad() {
   EventExpression eventExpr = new EventExpression(
      "NewsItem(\"ACME\",*) -> Tick(\"ACME\",*)");
   eventExpr.addMatchListener(matchListener1);
}

// Code within the first (and only) Match Listener
// class – matchListener1

   public void match(MatchEvent event) {
      System.out.println("Detected a NewsItem followed"
         + " by an Tick event, both regarding the ACME company.");
   }

This will look for the event sequence of a news item about a company followed by a stock price tick about that company. Once improved this could be used to detect the beginning of a rise (or fall) in the value of shares of a company following the release of a relevant news headline.

However, unless a temporal constraint is put in place, the monitor is not going to be that pertinent, as it might trigger on an event sequence where the price change occurs weeks after the news item. That would clearly not be so useful to a trader, as the two events were most likely unrelated and hence not indicative of a possible trend.

If the event expression above is rewritten as follows,

EventExpression eventExpr = new EventExpression(
   "NewsItem(\"ACME\",*) -> Tick(\"ACME\",*) within(30.0)");

the Tick event would now need to occur within 30 seconds of NewsItem for the listener to trigger.

The within(float) operator is a postfix unary operator that can be applied to an event expression (the Tick event template in the above example). Think of it like a stopwatch. The clock starts ticking as soon as the event expression it is attached to becomes active, i.e. when the listener actually starts looking for it. If the stopwatch reaches the specified figure before the event expression evaluates to true the event expression becomes permanently false.

In the above code, the timer is only activated once a suitable NewsItem is encountered. Unless an adequate StockTick then occurs within 30 seconds and makes the expression evaluate to true, the timer will fire and fail the whole listener.

As already specified, the within operator can be applied to any event expression, hence A within(x), where A represents just an event template and x is a float value specifying a number in seconds, is perfectly valid.

Waiting within a listener

The second timer operator available for use within event expressions is wait(float).

wait allows you to insert a “temporal pause” within an event expression. Once activated, a wait expression becomes true automatically once a set amount of time passes. For example,

A -> wait(x seconds) -> C

will proceed as follows: activate the listener and look for the A event expression or template, then once A becomes true, pause (that is, wait) for x seconds, then finally start looking for the C event expression or template.

In addition to being part of an event expression, wait can also be used on its own,

wait(20.0)

is a valid event expression in its own right. When its listener activates it just waits for the number of seconds specified (here being 20), then it evaluates to true and calls any registered match methods.

Therefore a wait clause starts off being false, and then turns to true once its time period expires. This behavior can be inverted through use of not. The expression

not wait(20.0)

would start off being true, and stay true for 20 seconds before becoming false.

The following,

B and not wait(20.0)

is an interesting example. It effectively means that this listener will trigger only if a B occurs within 20 seconds of its activation. After that, the not wait(20) clause would become false and prevent the listener from ever triggering.

By using all with wait, you can easily implement a periodic repeating timer:

all wait(5.0)

This listener will trigger every 5 seconds and calls any registered match methods.

Working with absolute time

The final temporal operator is the at operator. This operator allows you to express temporal activity with regards to absolute time.

The at operator allows triggering of a timer:

  • At a specific time; for example, at 12:30pm on April, 5th.
  • Repeatedly with regards to the calendar when used in conjunction with the all operator, across seconds, minutes, hours, days of the week, days of the month, and months; for example, on every hour, or on the first day of the month, or every 10 minutes past and 40 minutes past.
Important
Triggering using the at operator always uses the time zone in which the correlator is running.

The syntax is as follows:

at(minutes, hours, days_of_the_month, month, days_of_the_week [, seconds])

where the last operand, seconds, is optional.

Valid values for each operand are as follows:

Timer operand Values
minutes 0 to 59, indicating minutes past the hour.
hours 0 to 23, indicating the hours of the day.
days_of_the_month 1 to 31, indicating days of the month. For some months only 1 to 28, 1 to 29 or 1 to 30 are valid ranges.
month 1 to 12, indicating months of the year, with 1 corresponding to January
days_of_the_week 0 to 6, indicating days of the week, where 0 corresponds to Sunday.
seconds 0 to 59, indicating seconds past the minute.

The operator can be embedded within an event expression in a manner similar to the wait operator. If used outside the scope of an all operator it will trigger only once, at the next valid time as expressed within its elements. In conjunction with an all operator, it will trigger at every valid time.

The wildcard symbol (*) can be specified to indicate that all values are valid, that is

at(5, *, *, *, *)

would trigger at the next “five minutes past the hour”, while

all at(5, *, *, *, *)

would trigger at five minutes past each hour (that is, every day, every month).

Whereas,

all at(5, 9, *, *, *)

would trigger at 9:05am every day.

However,

all at(5, 9, *, *, 1)

would trigger at 9:05am only on Mondays, and never on any other weekday. This is because the effect of the wildcard operator is different when applied to the days_of_the_week and the days_of_the_month elements. This is due to the fact that both specify the same entity. The rule is therefore as follows:

  • As long as both elements are set to wildcard, then each day is valid.
  • If either of the days_of_the_week or the days_of_the_month elements is not a wildcard, then only the days that match that element will be valid. The wildcard in the other element is effectively ignored.
  • If both the days_of_the_week and the days_of_the_month elements are not a wildcard, then the days valid will be the days which match either. That is, the two criteria are or, not and.

A range operator (:) can be used with each element to define a range of valid values. For example

all at(5:15, *, *, *, *)

would trigger every minute from 5 minutes past the hour till 15 minutes past the hour.

A divisor operator (/x) can be used to specify that every xth value is valid. Therefore

all at(*/10, *, *, *, *)

would trigger every ten minutes, that is, at 0, 10, 20, 30, 40 and 50 minutes past every hour.

If you wish to specify a combination of the above operators you must enclose the element in square brackets ([]), and separate the value definitions with a comma (,). For example,

all at([*/10,30:35,22], *, *, *, *)

indicates as following values for minutes to trigger on: 0,10, 20, 22, 30, 31, 32, 33, 34, 35, 40 and 50.

A further example,

all at(*/30,9:17,[*/2,1],*,*)

would trigger every 30 minutes from 9am to 5pm on even numbered days of the month as well as specifically the first day of the month.

Optimizing event expressions

When a developer creates an event expression, a substantial percentage of the computational overhead goes into parsing the event expression itself.

If you need to create several instances of an event expression where only literal values in event templates vary, this repeated parsing cost can be removed through the use of a prepared event expression.

Instead of writing,

EventExpression eventExpr1 = new EventExpression(
   "NewsItem(\"ACME\",*) -> Tick(\"ACME\",*)");
EventExpression eventExpr2 = new EventExpression(
   "NewsItem(\"EMCA\",*) -> Tick(\"EMCA\",*)");
eventExpr1.addMatchListener(matchListener1);
eventExpr2.addMatchListener(matchListener2);

you could write,

PreparedEventExpressionTemplate et
   = new PreparedEventExpressionTemplate(
      "NewsItem(?,*) -> Tick(?,*)");

PreparedEventExpression pex1=et.getInstance();
pex1.setString(0, "ACME");
pex1.setString(1, "ACME");

PreparedEventExpression pex2=et.getInstance();
pex2.setString(0, "EMCA");
pex2.setString(1, "EMCA");

pex1.addMatchListener(matchListener1);
pex2.addMatchListener(matchListener2);

The above example shows how instead of creating two very similar event expressions you can create a single prepared event expression template, and then customize multiple instances of it. The main advantage of the latter approach is the fact that the event expression was parsed in Java only once. With an example as simple as the ones above this would in fact hardly make any difference, but in Java code with hundreds of such event expressions the difference in performance can be significant.

As shown in the code snippet above, the procedure for creating listeners with prepared event expressions is slightly different from that of normal event expressions.

You must create a PreparedEventExpressionTemplate and define within that the event expression. The syntax for event expression definitions is the same as previously with the exception of the ? operator. This can be used instead of any literal value. The next step is to get an instance of a PreparedEventExpression, and then to set values for any literals replaced by ? in the prepared event expression template. Finally, you can create listeners on the PreparedEventExpression instances just as with normal event expressions.

Validation of event expressions

When an EventExpression or PreparedEventExpressionTemplate is created or when addMatchListener() is called on an event expression within a JMon monitor the event expression is not validated immediately. It is queued for processing later when the JMon monitor yields control back to the correlator. This means that a badly formed event expression does not cause an exception to be thrown from the constructor. Instead, the correlator logs an error message later when it tries to validate the event expression.