Esper - Java Event Stream Processor

Esper Reference Documentation

1.3.0


Table of Contents

Preface
1. Technology Overview
1.1. Introduction to CEP and event stream analysis
1.2. CEP and relational databases
1.3. The Esper engine for CEP
2. Configuration
2.1. Programmatic Configuration
2.2. Configuration via XML File
2.3. XML Configuration File
2.4. Configuration Items
2.4.1. Events represented by Java Classes
2.4.1.1. Event type alias to Java class mapping
2.4.1.2. Non-JavaBean and Legacy Java Event Classes
2.4.1.3. Specifying Event Properties for Java Classes
2.4.1.4. Turning off Code Generation
2.4.2. Events represented by java.util.Map
2.4.3. Events represented by org.w3c.dom.Node
2.4.3.1. Schema Resource
2.4.3.2. XPath Property
2.4.4. Class and package imports
2.4.5. Relational Database Access
2.4.5.1. Connections obtained via DataSource
2.4.5.2. Connections obtained via DriverManager
2.4.5.3. Connections-level settings
2.4.5.4. Connections lifecycle settings
2.4.5.5. Cache settings
3. API Reference
3.1. API Overview
3.2. Engine Instances
3.3. The Administrative Interface
3.4. The Runtime Interface
3.5. Time-Keeping Events
3.6. Events Received from the Engine
4. Event Representations
4.1. Event Underlying Java Objects
4.2. Event Properties
4.3. Plain Java Object Events
4.3.1. Java Object Event Properties
4.4. java.util.Map Events
4.5. org.w3c.dom.Node XML Events
5. Event Pattern Reference
5.1. Event Pattern Overview
5.2. How to use Patterns
5.2.1. Pattern Syntax
5.2.2. Subscribing to Pattern Events
5.2.3. Pulling Data from Patterns
5.3. Filter Expressions
5.4. Pattern Operators
5.4.1. Every
5.4.2. And
5.4.3. Or
5.4.4. Not
5.4.5. Followed-by
5.5. Pattern Guards
5.5.1. timer:within
5.6. Pattern Observers
5.6.1. timer:interval
5.6.2. timer:at
6. EQL Reference
6.1. EQL Introduction
6.2. EQL Syntax
6.2.1. Specifying Time Periods
6.3. Choosing Event Properties And Events: the Select Clause
6.3.1. Choosing all event properties: select *
6.3.2. Choosing specific event properties
6.3.3. Expressions
6.3.4. Renaming event properties
6.3.5. Selecting istream and rstream events
6.4. Specifying Event Streams : the From Clause
6.4.1. Filter-based event streams
6.4.1.1. Specifying an event type
6.4.1.2. Specifying event filter criteria
6.4.2. Pattern-based event streams
6.4.3. Specifying views
6.5. Specifying Search Conditions: the Where Clause
6.6. Aggregates and grouping: the Group-by Clause and the Having Clause
6.6.1. Using aggregate functions
6.6.2. Organizing statement results into groups: the Group-by clause
6.6.3. Selecting groups of events: the Having clause
6.6.4. How the stream filter, Where, Group By and Having clauses interact
6.7. Stabilizing and Limiting Output: the Output Clause
6.7.1. Output Clause Options
6.7.2. Group By, Having and Output clause interaction
6.8. Sorting Output: the Order By Clause
6.9. Merging Streams and Continuous Insertion: the Insert Into Clause
6.10. Joining Event Streams
6.11. Outer Joins
6.12. Joining Relational Data via SQL
6.12.1. Joining SQL Query Results
6.12.2. Outer Joins With SQL Queries
6.12.3. Using Patterns to Request (Poll) Data
6.12.4. JDBC Implementation Overview
6.13. Single-row Function Reference
6.13.1. The Min and Max Functions
6.13.2. The Coalesce Function
6.13.3. The Case Control Flow Function
6.14. Operator Reference
6.14.1. Arithmatic Operators
6.14.2. Logical And Comparsion Operators
6.14.3. Concatenation Operators
6.14.4. Binary Operators
6.14.5. The 'in' Keyword
6.14.6. The 'between' Keyword
6.14.7. The 'like' Keyword
6.14.8. The 'regexp' Keyword
6.15. Build-in views
6.15.1. Window views
6.15.1.1. Length window
6.15.1.2. Time window
6.15.1.3. Externally-timed window
6.15.1.4. Time window buffer
6.15.2. Standard view set
6.15.2.1. Unique
6.15.2.2. Group By
6.15.2.3. Size
6.15.2.4. Last
6.15.3. Statistics views
6.15.3.1. Univariate statistics
6.15.3.2. Regression
6.15.3.3. Correlation
6.15.3.4. Weighted average
6.15.3.5. Multi-dimensional statistics
6.15.4. Extension View Set
6.15.4.1. Sorted Window View
6.16. User-Defined Functions
7. Adapters
7.1. Adapter
8. Indicators
8.1. Intro
8.2. JMX Indicator
9. Architecture
9.1. Overview
9.2. Building and Testing
10. Examples, Tutorials, Case Studies
10.1. Examples Overview
10.2. Market Data Feed Monitor
10.2.1. Input Events
10.2.2. Computing Rates Per Feed
10.2.3. Detecting a Fall-off
10.2.4. Event generator
10.3. Transaction 3-Event Challenge
10.3.1. The Events
10.3.2. Combined event
10.3.3. Real time summary data
10.3.4. Find problems
10.3.5. Event generator
10.4. J2EE Self-Service Terminal Management
10.4.1. Events
10.4.2. Detecting Customer Check-in Issues
10.4.3. Absence of Status Events
10.4.4. Activity Summary Data
10.4.5. Sample Application for J2EE Application Server
10.4.5.1. Running the Example
10.4.5.2. Building the Example
10.4.5.3. Running the Event Simulator and Receiver
10.5. AutoID RFID Reader
10.6. StockTicker
10.7. MatchMaker
10.8. QualityOfService
10.9. LinearRoad
10.10. StockTick RSI
11. References
11.1. Reference List

Preface

Analyzing and reacting to information in real-time oftentimes requires the development of custom applications. Typically these applications must obtain the data to analyze, filter data, derive information and then indicate this information through some form of presentation or communication. Data may arrive with high frequency requiring high throughput processing. And applications may need to be flexible and react to changes in requirements while the data is processed. Esper is an event stream processor that aims to enable a short development cycle from inception to production for these types of applications.

If you are new to Esper, please follow these steps:

  1. Read the tutorials, case studies and solution patterns available on the Esper public web site at http://esper.codehaus.org

  2. Read Section 1.1, “Introduction to CEP and event stream analysis” if you are new to CEP and ESP (complex event processing, event stream processing)

  3. Read Section 5.1, “Event Pattern Overview” for an overview over event patterns

  4. Read Section 6.1, “EQL Introduction” for an introduction to event stream processing via EQL

  5. Then glance over the examples Section 10.1, “Examples Overview”

Chapter 1. Technology Overview

1.1. Introduction to CEP and event stream analysis

The Esper engine has been developed to address the requirements of applications that analyze and react to events. Some typical examples of applications are:

  • Business process management and automation (process monitoring, BAM, reporting exceptions)

  • Finance (algorithmic trading, fraud detection, risk management)

  • Network and application monitoring (intrusion detection, SLA monitoring)

  • Sensor network applications (RFID reading, scheduling and control of fabrication lines, air traffic)

What these applications have in common is the requirement to process events (or messages) in real-time or near real-time. This is sometimes referred to as complex event processing (CEP) and event stream analysis. Key considerations for these types of applications are throughput, latency and the complexity of the logic required.

  • High throughput - applications that process large volumes of messages (between 1,000 to 100k messages per second)

  • Low latency - applications that react in real-time to conditions that occur (from a few milliseconds to a few seconds)

  • Complex computations - applications that detect patterns among events (event correlation), filter events, aggregate time or length windows of events, join event streams, trigger based on absence of events etc.

The Esper engine was designed to make it easier to build and extend CEP applications.

1.2. CEP and relational databases

Relational databases and the standard query language (SQL) are designed for applications in which most data is fairly static and complex queries are less frequent. Also, most databases store all data on disks (except for in-memory databases) and are therefore optimized for disk access.

To retrieve data from a database an application must issue a query. If an application need the data 10 times per second it must fire the query 10 times per second. This does not scale well to hundreds or thousands of queries per second.

Database triggers can be used to fire in response to database update events. However database triggers tend to be slow and often cannot easily perform complex condition checking and implement logic to react.

In-memory databases may be better suited to CEP applications then traditional relational database as they generally have good query performance. Yet they are not optimized to provide immediate, real-time query results required for CEP and event stream analysis.

1.3. The Esper engine for CEP

The Esper engine works a bit like a database turned upside-down. Instead of storing the data and running queries against stored data, the Esper engine allows applications to store queries and run the data through. Response from the Esper engine is real-time when conditions occur that match queries. The execution model is thus continuous rather then only when a query is submitted.

Esper provides two principal methods or mechanisms to process events: event patterns and event stream queries.

Esper offers an event pattern language to specify expression-based event pattern matching. Underlying the pattern matching engine is a state machine implementation. This method of event processing matches expected sequences of presence or absence of events or combinations of events. It includes time-based correlation of events.

Esper also offers event stream queries that address the event stream analysis requirements of CEP applications. Event stream queries provide the windows, aggregation, joining and analysis functions for use with streams of events. These queries are following the EQL syntax. EQL has been designed for similarity with the SQL query language but differs from SQL in its use of views rather then tables. Views represent the different operations needed to structure data in an event stream and to derive data from an event stream.

Esper provides these two methods as alternatives through the same API.

Chapter 2. Configuration

Esper engine configuration is entirely optional. Esper has a very small number of configuration parameters that can be used to simplify event pattern and EQL statements, and to tune the engine behavior to specific requirements. The Esper engine works out-of-the-box without configuration.

2.1. Programmatic Configuration

An instance of net.esper.client.Configuration represents all configuration parameters. The Configuration is used to build an (immutable) EPServiceProvider, which provides the administrative and runtime interfaces for an Esper engine instance.

You may obtain a Configuration instance by instantiating it directly and adding or setting values on it. The Configuration instance is then passed to EPServiceProviderManager to obtain a configured Esper engine.

Configuration configuration = new Configuration();
configuration.addEventTypeAlias("PriceLimit", PriceLimit.class.getName());
configuration.addEventTypeAlias("StockTick", StockTick.class.getName());
configuration.addImport("org.mycompany.mypackage.MyUtility");
configuration.addImport("org.mycompany.util.*");

EPServiceProvider epService = EPServiceProviderManager.getProvider("sample", configuration);

Note that Configuration is meant only as an initialization-time object. The Esper engine represented by an EPServiceProvider is immutable and does not retain any association back to the Configuration.

2.2. Configuration via XML File

An alternative approach to configuration is to specify a configuration in an XML file.

The default name for the XML configuration file is esper.cfg.xml. Esper reads this file from the root of the CLASSPATH as an application resource via the configure method.

Configuration configuration = new Configuration();		
configuration.configure();

The Configuration class can read the XML configuration file from other sources as well. The configure method accepts URL, File and String filename parameters.

Configuration configuration = new Configuration();		
configuration.configure("myengine.esper.cfg.xml");

2.3. XML Configuration File

Here is an example configuration file. The schema for the configuration file can be found in the etc folder and is named esper-configuration-1-0.

<?xml version="1.0" encoding="UTF-8"?>
<esper-configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
           xsi:noNamespaceSchemaLocation="esper-configuration-1-0.xsd">
  <event-type alias="StockTick" class="net.esper.example.stockticker.event.StockTick"/>
  <event-type alias="PriceLimit" class="net.esper.example.stockticker.event.PriceLimit"/>
  <auto-import import-name="org.mycompany.mypackage.MyUtility"/>
  <auto-import import-name="org.mycompany.util.*"/>
</esper-configuration>		

The example above is only a subset of the configuration items available. The next chapters outline the available configuration in greater detail.

2.4. Configuration Items

2.4.1. Events represented by Java Classes

2.4.1.1. Event type alias to Java class mapping

This configuration item can be used to allow event pattern statements and EQL statements to use an event type alias rather then the fully qualified Java class name. Note that Java Interface classes and abstract classes are also supported as event types via the fully qualified Java class name, and an event type alias can also be defined for such classes.

The example pattern statement below first shows a pattern that uses the alias StockTick. The second pattern statement is equivalent but specifies the fully-qualified Java class name.

every StockTick(symbol='IBM')"
every net.esper.example.stockticker.event.StockTick(symbol='IBM')

The event type alias can be listed in the XML configuration file as shown below. The Configuration API can also be used to programatically specify an event type alias, as shown in an earlier code snippet.

<event-type alias="StockTick" class="net.esper.example.stockticker.event.StockTick"/>

2.4.1.2. Non-JavaBean and Legacy Java Event Classes

Esper can process Java classes that provide event properties through other means then through JavaBean-style getter methods. It is not necessary that the method and member variable names in your Java class adhere to the JavaBean convention - any public methods and public member variables can be exposed as event properties via the below configuration.

A Java class can optionally be configured with an accessor style attribute. This attribute instructs the engine how it should expose methods and fields for use as event properties in statements.

Table 2.1. Accessor Styles

Style NameDescription
javabeanAs the default setting, the engine exposes an event property for each public method following the JavaBean getter-method conventions
publicThe engine exposes an event property for each public method and public member variable of the given class
explicitThe engine exposes an event property only for the explicitly configured public methods and public member variables

Using the public setting for the accessor-style attribute instructs the engine to expose an event property for each public method and public member variable of a Java class. The engine assigns event property names of the same name as the name of the method or member variable in the Java class.

For example, assuming the class MyLegacyEvent exposes a method named readValue and a member variable named myField, we can then use properties as shown.

select readValue, myField from MyLegacyEvent

Using the explicit setting for the accessor-style attribute requires that event properties are declared via configuration. This is outlined in the next chapter.

When configuring an engine instance from an XML configuration file, the XML snippet below demonstrates the use of the legacy-type element and the accessor-style attribute.

<event-type alias="MyLegacyEvent" class="com.mycompany.mypackage.MyLegacyEventClass">
  <legacy-type accessor-style="public"/>
</event-type>

When configuring an engine instance via Configuration API, the sample code below shows how to set the accessor style.

Configuration configuration = new Configuration();
ConfigurationEventTypeLegacy legacyDef = new ConfigurationEventTypeLegacy();
legacyDef.setAccessorStyle(ConfigurationEventTypeLegacy.AccessorStyle.PUBLIC);
config.addEventTypeAlias("MyLegacyEvent", MyLegacyEventClass.class.getName(), legacyDef);

EPServiceProvider epService = EPServiceProviderManager.getProvider("sample", configuration);

2.4.1.3. Specifying Event Properties for Java Classes

Sometimes it may be convenient to use event property names in pattern and EQL statements that are backed up by a given public method or member variable (field) in a Java class. And it can be useful to declare multiple event properties that each map to the same method or member variable.

We can configure properties of events via method-property and field-property elements, as the next example shows.

<event-type alias="StockTick" class="net.esper.example.stockticker.event.StockTickEvent">
	<legacy-type accessor-style="javabean" code-generation="enabled">
		<method-property name="price" accessor-method="getCurrentPrice" />
		<field-property name="volume" accessor-field="volumeField" />
	</legacy-type>
</event-type>

The XML configuration snippet above declared an event property named price backed by a getter-method named getCurrentPrice, and a second event property named volume that is backed by a public member variable named volumeField. Thus the price and volume properties can be used in a statement:

select avg(price * volume) from StockTick

As with all configuration options, the API can also be used:

Configuration configuration = new Configuration();
ConfigurationEventTypeLegacy legacyDef = new ConfigurationEventTypeLegacy();
legacyDef.addMethodProperty("price", "getCurrentPrice");
legacyDef.addFieldProperty("volume", "volumeField");
config.addEventTypeAlias("StockTick", StockTickEvent.class.getName(), legacyDef);

2.4.1.4. Turning off Code Generation

Esper employes the CGLIB library for very fast read access to event property values. For certain legacy Java classes it may be desirable to disable the use of this library and instead use Java reflection to obtain event property values from event objects.

In the XML configuration, the optional code-generation attribute in the legacy-type section can be set to disabled as shown next.

<event-type alias="MyLegacyEvent" class="com.mycompany.package.MyLegacyEventClass">
	<legacy-type accessor-style="javabean" code-generation="disabled" />
</event-type>

The sample below shows how to configure this option via the API.

Configuration configuration = new Configuration();
ConfigurationEventTypeLegacy legacyDef = new ConfigurationEventTypeLegacy();
legacyDef.setCodeGeneration(ConfigurationEventTypeLegacy.CodeGeneration.DISABLED);
config.addEventTypeAlias("MyLegacyEvent", MyLegacyEventClass.class.getName(), legacyDef);

2.4.2. Events represented by java.util.Map

The engine can process java.util.Map events via the sendEvent(Map map, String eventTypeAlias) method on the EPRuntime interface. Entries in the Map represent event properties. Keys must be of type java.util.String for the engine to be able to look up event property names in pattern or EQL statements. Values can be of any type. JavaBean-style objects as values in a Map can also be processed by the engine. Please see the Chapter 4, Event Representations section for details on how to use Map events with the engine.

Via configuration we provide an event type alias name for Map events for use in statements, and the event property names and types enabling the engine to validate properties in statements.

The below snippet of XML configuration configures an event named MyMapEvent.

<event-type alias="MyMapEvent">
  <java-util-map>
    <map-property name="carId" class="int"/>
    <map-property name="carType" class="string"/>
    <map-property name="assembly" class="com.mycompany.Assembly"/>    
  </java-util-map>
</event-type>

This configuration defines the carId property of MyMapEvent events to be of type int, and the carType property to be of type java.util.String. The assembly property of the Map event will contain instances of com.mycompany.Assembly for the engine to query.

The valid list of values for the type definition via the class attribute is:

  • string or java.lang.String

  • char or java.lang.Character

  • byte or java.lang.Byte

  • short or java.lang.Short

  • int or java.lang.Integer

  • long or java.lang.Long

  • float or java.lang.Float

  • double or java.lang.Double

  • boolean or java.lang.Boolean

  • Any fully-qualified Java class name that can be resolved by the engine via Class.forName

You can also use the configuration API to configure Map event types, as the short code snippet below demonstrates.

Properties properties = new Properties();
properties.put("carId", "int");
properties.put("carType", "string");
properties.put("assembly", Assembly.class.getName());

Configuration configuration = new Configuration();
configuration.addEventTypeAlias("MyMapEvent", properties);

Finally, here is a sample EQL statement that uses the configured MyMapEvent map event. This statement uses the chassisTag and numParts properties of Assembly objects in each map.

select carType, assembly.chassisTag, count(assembly.numParts) from MyMapEvent.win:time(60 sec)

2.4.3. Events represented by org.w3c.dom.Node

Via this configuration item the Esper engine can natively process org.w3c.dom.Node instances, i.e. XML document object model (DOM) nodes. Please see the Chapter 4, Event Representations section for details on how to use Node events with the engine.

Esper allows configuring XPath expressions as event properties. You can specify arbitrary XPath functions or expressions and provide a property name by which their result values will be available for use in expressions.

For XML documents that follow an XML schema, Esper can load and interrogate your schema and validate event property names and types against the schema information.

Nested, mapped and indexed event properties are also supported in expressions against org.w3c.dom.Node events. Thus XML trees can conveniently be interrogated using the existing event property syntax for querying JavaBean objects, JavaBean object graphs or java.util.Map events.

In the simplest form, the Esper engine only requires a configuration entry containing the root element name and the event type alias in order to process org.w3c.dom.Node events:

<event-type alias="MyXMLNodeEvent">
  <xml-dom root-element-name="myevent" />
</event-type>

You can also use the configuration API to configure XML event types, as the short example below demonstrates. In fact, all configuration options available through XML configuration can also be provided via setter methods on the ConfigurationEventTypeXMLDOM class.

Configuration configuration = new Configuration();
ConfigurationEventTypeXMLDOM desc = new ConfigurationEventTypeXMLDOM();
desc.setRootElementName("myevent");
configuration.addEventTypeAlias("MyXMLNodeEvent", desc);

The next example presents all relevant configuration options in a sample configuration entry.

<event-type alias="AutoIdRFIDEvent">
  <xml-dom root-element-name="Sensor" schema-resource="data/AutoIdPmlCore.xsd" 
       default-namespace="urn:autoid:specification:interchange:PMLCore:xml:schema:1">
    <namespace-prefix prefix="pmlcore" 
       namespace="urn:autoid:specification:interchange:PMLCore:xml:schema:1"/>
    <xpath-property property-name="countTags" 
       xpath="count(/pmlcore:Sensor/pmlcore:Observation/pmlcore:Tag)" type="number"/>
  </xml-dom>
</event-type>

This example configures an event property named countTags whose value is computed by an XPath expression. The namespace prefixes and default namespace are for use with XPath expressions and must also be made known to the engine in order for the engine to compile XPath expressions. Via the schema-resource attribute we instruct the engine to load a schema file.

Here is an example EQL statement using the configured event type named AutoIdRFIDEvent.

select ID, countTags from AutoIdRFIDEvent.win:time(30 sec)

2.4.3.1. Schema Resource

The schema-resource attribute takes a schema resource URL or classpath-relative filename. The engine attempts to resolve the schema resource as an URL. If the schema resource name is not a valid URL, the engine attempts to resolve the resource from classpath via the ClassLoader.getResource method using the thread context class loader. If the name could not be resolved, the engine uses the Configuration class classloader.

By configuring a schema file for the engine to load, the engine performs these additional services:

  • Validates the event properties in a statement, ensuring the event property name matches an attribute or element in the XML

  • Determines the type of the event property allowing event properties to be used in type-sensitive expressions such as expressions involving arithmatic (Note: XPath properties are also typed)

  • Matches event property names to either element names or attributes

If no schema resource is specified, none of the event properties specified in statements are validated at statement creation time and their type defaults to java.lang.String. Also, attributes are not supported if no schema resource is specified and must thus be declared via XPath expression.

2.4.3.2. XPath Property

The xpath-property element adds event properties to the event type that are computed via an XPath expression. In order for the XPath expression to compile, be sure to specify the default-namespace attribute and use the namespace-prefix to declare namespace prefixes.

XPath expression properties are strongly typed. The type attribute allows the following values. These values correspond to those declared by javax.xml.xpath.XPathConstants.

  • number (Note: resolves to a double)

  • string

  • boolean

2.4.4. Class and package imports

Esper allows invocations of static Java library functions as outlined in Section 6.13, “Single-row Function Reference”. This configuration item can be set to allow a partial rather than a fully qualified class name in such invocations. The imports work in the same way as in Java files, so both packages and classes can be imported.

select Math.max(priceOne, PriceTwo)
// via configuration equivalent to
select java.lang.Math.max(priceOne, priceTwo)

Esper auto-imports the following Java library packages if no other configuration is supplied. This list is replaced with any configuration specified in a configuration file or through the API.

  • java.lang.*

  • java.math.*

  • java.text.*

  • java.util.*

In an XML configuration file the auto-import configuration may look as below. Note that all configuration options are available through the Configuration API as well.

<auto-import import-name="com.mycompany.mypackage.*"/>
  <auto-import import-name="com.mycompany.myapp.MyUtilityClass"/>

2.4.5. Relational Database Access

Esper has the capability to join event streams against historical data sources, such as a relational database. This section describes the configuration entries that the engine requires to access data stored in your database. Please see Section 6.12, “Joining Relational Data via SQL” for information on the use of EQL queries that include historical data sources.

EQL queries that poll data from a relational database specify the name of the database as part of the EQL statement. The engine uses the configuration information described here to resolve the database name in the statement to database settings. The required and optional database settings are summarized below.

  • Database connections can be obtained via JDBC javax.xml.DataSource or alternatively via java.sql.DriverManager. Either one of these methods to obtain new database connections is a required configuration.

  • Optionally, JDBC connection-level settings such as auto-commit, transaction isolation level, read-only and the catalog name can be defined.

  • Optionally, a connection lifecycle can be set to indicate to the engine whether the engine must retain connections or must obtain a new connection for each lookup.

  • Optionally, define a cache policy to allow the engine to retrieve data from a query cache, reducing the number of query executions.

Some of the settings can have important performance implications that need to be carefully considered in relationship to your database software, JDBC driver and runtime environment. This section attempts to outline such implications where appropriate.

The sample XML configuration file in the "etc" folder can be used as a template for configuring database settings. All settings are also available by means of the configuration API through the classes Configuration and ConfigurationDBRef.

2.4.5.1. Connections obtained via DataSource

The snippet of XML below configures a database named mydb1 to obtain connections via a javax.sql.DataSource. The datasource-connection element instructs the engine to obtain new connections to the database mydb1 by performing a lookup via javax.naming.InitialContext for the given object lookup name. Optional environment properties for the InitialContext are also shown in the example.

<database-reference name="mydb1">
  <datasource-connection context-lookup-name="java:comp/env/jdbc/mydb">
    <env-property name="java.naming.factory.initial" value ="com.myclass.CtxFactory"/>
    <env-property name="java.naming.provider.url" value ="iiop://localhost:1050"/>
  </datasource-connection>
</database-reference>

To help you better understand how the engine uses this information to obtain connections, we have included the logic below.

if (envProperties.size() > 0) {
  initialContext = new InitialContext(envProperties);
}
else {
  initialContext = new InitialContext();
}
DataSource dataSource = (DataSource) initialContext.lookup(lookupName);
Connection connection = dataSource.getConnection();

2.4.5.2. Connections obtained via DriverManager

The next snippet of XML configures a database named mydb2 to obtain connections via java.sql.DriverManager. The drivermanager-connection element instructs the engine to obtain new connections to the database mydb2 by means of Class.forName and DriverManager.getConnection using the class name, URL and optional username, password and connection arguments.

<database-reference name="mydb2">
  <drivermanager-connection class-name="my.sql.Driver" 
        url="jdbc:mysql://localhost/test?user=root&amp;password=mypassword" 
        user="myuser" password="mypassword">
    <connection-arg name="user" value ="myuser"/>
    <connection-arg name="password" value ="mypassword"/>
    <connection-arg name="somearg" value ="someargvalue"/>
  </drivermanager-connection>
</database-reference>

The username and password are shown in multiple places in the XML only as an example. Please check with your database software on the required information in URL and connection arguments.

2.4.5.3. Connections-level settings

Additional connection-level settings can optionally be provided to the engine which the engine will apply to new connections. When the engine obtains a new connection, it applies only those settings to the connection that are explicitly configured. The engine leaves all other connection settings at default values.

The below XML is a sample of all available configuration settings. Please refer to the Java API JavaDocs for java.sql.Connection for more information to each option or check the documentation of your JDBC driver and database software.

<database-reference name="mydb2">
... configure data source or driver manager settings...
  <connection-settings auto-commit="true" catalog="mycatalog" 
      read-only="true" transaction-isolation="1" />
</database-reference>

The read-only setting can be used to indicate to your database engine that SQL statements are read-only. The transaction-isolation and auto-commit help you database software perform the right level of locking and lock release. Consider setting these values to reduce transactional overhead in your database queries.

2.4.5.4. Connections lifecycle settings

By default the engine retains a separate database connection for each started EQL statement. However, it is possible to override this behavior and require the engine to obtain a new database connection for each lookup, and to close that database connection after the lookup is completed. This often makes sense when you have a large number of EQL statements and require pooling of connections via a connection pool. If your runtime environment includes an application server, the connection pool may be exposed as a DataSource.

The XML for this option is below. The connection lifecycle allows the following values: pooled and retain.

<database-reference name="mydb2">
... configure data source or driver manager settings...
    <connection-lifecycle value="pooled"/>
</database-reference>

2.4.5.5. Cache settings

Cache settings can dramatically reduce the number of database queries that the engine executes for EQL statements. If no cache setting is specified, the engine does not cache query results and executes a separate database query for every event.

Caches store the results of database queries and make these results available to subsequent queries using the exact same query parameters as the query for which the result was stored. If your query returns one or more rows, the cache keep the result rows of the query keyed to the parameters of the query. If your query returns no rows, the cache also keeps the empty result. Query results are held by a cache until the cache entry is evicted. The strategies available for evicting cached query results are listed next.

2.4.5.5.1. LRU Cache

The least-recently-used (LRU) cache is configured by a maximum size. The cache discards the least recently used query results first once the cache reaches the maximum size.

The XML configuration entry for a LRU cache is as below. This entry configures an LRU cache holding up to 1000 query results.

<database-reference name="mydb">
... configure data source or driver manager settings...
    <lru-cache size="1000"/>
</database-reference>
2.4.5.5.2. Expiry-time Cache

The expiry time cache is configured by a maximum age in seconds and a purge interval. The cache discards (on the get operation) any query results that are older then the maximum age so that stale data is not used. If the cache is not empty, then every purge interval number of seconds the engine purges any expired entries from the cache.

The XML configuration entry for an expiry-time cache is as follows. The example configures an expiry time cache in which prior query results are valid for 60 seconds and which the engine inspects every 2 minutes to remove query results older then 60 seconds.

<database-reference name="mydb">
... configure data source or driver manager settings...
    <expiry-time-cache max-age-seconds="60" purge-interval-seconds="120"/>
</database-reference>

Chapter 3. API Reference

3.1. API Overview

Esper has 2 primary interfaces that this section outlines: The administrative interface and the runtime interface.

Use Esper's administrative interface to create event patterns and EQL statements as discussed in Section 5.1, “Event Pattern Overview” and Section 6.1, “EQL Introduction”.

Use Esper's runtime interface to send events into the engine, emit events and get statistics for an engine instance.

The JavaDoc documentation is also a great source for API information.

3.2. Engine Instances

Each instance of an Esper engine is completely independent of other engine instances and has its own administrative and runtime interface.

An instance of the Esper engine is obtained via static methods on the EPServiceProviderManager class. The getDefaultProvider method and the getProvider(String URI) methods return an instance of the Esper engine. The latter can be used to obtain multiple instances of the engine for different URI values. The EPServiceProviderManager determines if the URI matches all prior URI values and returns the same engine instance for the same URI value. If the URI has not been seen before, it creates a new engine instance.

The code snipped below gets the default instance Esper engine. Subsequent calls to get the default engine instance return the same instance.

EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();

This code snippet gets an Esper engine for URI RFIDProcessor1. Subsequent calls to get an engine with the same URI return the same instance.

EPServiceProvider epService = EPServiceProviderManager.getProvider("RFIDProcessor1");

An existing Esper engine instance can be reset via the initialize method on the EPServiceProvider instance. This stops and removes all statements in the Engine.

3.3. The Administrative Interface

Create event pattern expression and EQL statements via the administrative interface EPAdministrator.

This code snippet gets an Esper engine then creates an event pattern and an EQL statement.

EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();
EPAdministrator admin = epService.getEPAdministrator();

EPStatement 10secRecurTrigger = admin.createPattern(
  "every timer:at(*, *, *, *, *, */10)");

EPStatement countStmt = admin.createEQL(
  "select count(*) from MarketDataBean.win:time(60 sec)");

Note that event pattern expressions can also occur within EQL statements. This is outlined in more detail in Section 6.4.2, “Pattern-based event streams”.

The createPattern and createEQL methods return EPStatement instances. Statements are automatically started and active when created. A statement can also be stopped and started again via the stop and start methods shown in the code snippet below.

countStmt.stop();
countStmt.start();

We can subscribe to updates posted by a statement via the addListener and removeListener methods the EPStatement statement. We need to provide an implementation of the UpdateListener interface to the statement.

UpdateListener myListener = new MyUpdateListener();
countStmt.addListener(myListener);

EQL statements and event patterns publish old data and new data to registered UpdateListener listeners. New data published by statements is the events representing the new values of derived data held by the statement. Old data published by statements constists of the events representing the prior values of derived data held by the statement.

Subscribing to events posted by a statement is following a push model. The engine pushes data to listeners when events are received that cause data to change or patterns to match. Alternatively, statements can also serve up data in a pull model via the iterator method. This can come in handy if we are not interested in all new updates, but only want to perform a frequent poll for the latest data. For example, an event pattern that fires every 5 seconds could be used to pull data from an EQL statement. The code snippet below demonstrates some pull code.

Iterator<EventBean> eventIter = countStmt.iterator();
for (EventBean event : eventIter) {
   // .. do something ..
}

This is a second example:

double averagePrice = (Double) eqlStatement.iterator().next().get("average");

Esper places the following restrictions on the pull API and usage of the iterator method:

  1. EQL statements joining multiple event streams do not support the pull API

3.4. The Runtime Interface

The EPRuntime interface is used to send events for processing into an Esper engine, and to emit Events from an engine instance to the outside world.

The below code snippet shows how to send a Java object event to the engine. Note that the sendEvent method is overloaded. As events can take on different representation classes in Java, the sendEvent takes parameters to reflect the different types of events that can be send into the engine. The Chapter 4, Event Representations section explains the types of events accepted.

EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();
EPRuntime runtime = epService.getEPRuntime();

// Send an example event containing stock market data
runtime.sendEvent(new MarketDataBean('IBM', 75.0));		

Another important method in the runtime interface is the route method. This method is designed for use by UpdateListener implementations that need to send events into an engine instance.

The emit and addEmittedListener methods can be used to emit events from a runtime to a registered set of one or more emitted event listeners. This mechanism is available as a service to enable channel-based publish-subscribe of events emitted from an engine instance via the emit method. Emitting events is not integrated with EQL and is available only via the EPRuntime interface. Events are emitted on an event channel identified by a name. Listeners are implementations of the EmittedListener interface. Via the addEmittedListener method a listener can be added to the specified event channel. The lister receives only those events posted to that channel. The channel parameter to addEmittedListener also allows null values. If a null channel value is specified, the listeners receives emitted events posted on any channel.

3.5. Time-Keeping Events

Special events are provided that can be used to control the time-keeping of an engine instance. There are two models for an engine to keep track of time. Internal clocking is when the engine instance relies on the java.util.Timer class for time tick events. External clocking can be used to supply time ticks to the engine. The latter is useful for testing time-based event sequences or for synchronizing the engine with an external time source.

By default, the Esper engine uses internal time ticks. This behavior can be changed by sending a timer control event to the engine as shown below.

EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider();
EPRuntime runtime = epService.getEPRuntime();
// switch to external clocking
runtime.sendEvent(new TimerControlEvent(TimerControlEvent.ClockType.CLOCK_EXTERNAL));

// send a time tick
long timeInMillis = System.currentTimeMillis();	// Or get the time somewhere else
runtime.sendEvent(new CurrentTimeEvent(timeInMillis));

3.6. Events Received from the Engine

The Esper engine posts events to registered UpdateListener instances ('push' method for receiving events). For many statements events can also be pulled from statements via the iterator method. Both pull and push supply EventBean instances representing the events generated by the engine or events supplied to the engine. Each EventBean instance represents an event, with each event being either an artificial event, composite event or an event supplied to the engine via its runtime interface.

The getEventType method supplies an event's event type information represented by an EventType instance. The EventType supplies event property names and types as well as information about the underlying object to the event.

The engine may generate artificial events that contain information derived from event streams. A typical example for artificial events is the events posted for a statement to calculate univariate statistics on an event property. The below example shows such a statement and queries the generated events for an average value.

// Derive univariate statistics on price for the last 100 market data events
String stmt = "select * from MarketDataBean(symbol='IBM').win:length(100).stat:uni('price')";
EPStatement priceStatsView = epService.getEPAdministrator().createEQL(stmt);
priceStatsView.addListener(testListener);
// Example listener code
public class MyUpdateListener implements UpdateListener
{
    public void update(EventBean[] newData, EventBean[] oldData)
    {
        // Interrogate events
        System.out.println("new average price=" + newData[0].get("average");
	}
}

Composite events are events that aggregate one or more other events. Composite events are typically created by the engine for statements that join two event streams, and for event patterns in which the causal events are retained and reported in a composite event. The example below shows such an event pattern.

// Look for a pattern where AEvent follows BEvent
String pattern = "a=AEvent -> b=BEvent";
EPStatement stmt = epService.getEPAdministrator().createPattern(pattern);
stmt.addListener(testListener);
// Example listener code
public class MyUpdateListener implements UpdateListener
{
    public void update(EventBean[] newData, EventBean[] oldData)
    {
        System.out.println("a event=" + newData[0].get("a").getUnderlying());
        System.out.println("b event=" + newData[0].get("b").getUnderlying());
	}
}

Chapter 4. Event Representations

4.1. Event Underlying Java Objects

An event is an immutable record of a past occurrence of an action or state change. An event can have a set of event properties that supply information about the event. An event also has an underlying Java object type.

In Esper, an event can be represented by any of the following underlying Java objects:

Table 4.1. Event Underlying Java Objects

Java ClassDescription
java.lang.ObjectAny Java POJO (plain-old java object) with getter methods following JavaBean conventions
java.util.MapMap events are key-values pairs
org.w3c.dom.NodeXML document object model (DOM)

4.2. Event Properties

Esper expressions can include simple as well as indexed, mapped and nested event properties. The table below outlines the different types of properties and their syntax in an event expression. This syntax allows statements to query deep JavaBean objects graphs, XML structures and Map events.

Table 4.2. Types of Event Properties

TypeDescriptionSyntaxExample
SimpleA property that has a single value that may be retrieved.
name
sensorId
IndexedAn indexed property stores an ordered collection of objects (all of the same type) that can be individually accessed by an integer-valued, non-negative index (or subscript).
name[index]
sensor[0]
MappedA mapped property stores a keyed collection of objects (all of the same type).
name('key')
sensor('light')
NestedA nested property is a property that lives within another property of an event.
name.nestedname
sensor.value

Combinations are also possible. For example, a valid combination could be person.address('home').street[0].

4.3. Plain Java Object Events

Plain Java object events are object instances that expose event properties through JavaBean-style getter methods. Events classes or interfaces do not have to be fully compliant to the JavaBean specification; however for the Esper engine to obtain event properties, the required JavaBean getter methods must be present.

Esper supports JavaBean-style event classes that extend a superclass or implement one or more interfaces. Also, Esper event pattern and EQL statements can refer to Java interface classes and abstract classes.

Classes that represent events should be made immutable. As events are recordings of a state change or action that occurred in the past, the relevant event properties should not be changeable. However this is not a hard requirement and the Esper engine accepts events that are mutable as well.

Please see Chapter 2, Configuration on options for naming event types represented by Java object event classes.

4.3.1. Java Object Event Properties

As outlined earlier, the different property types are supported by the standard JavaBeans specification, and some of which are uniquely supported by Esper:

  • Simple properties have a single value that may be retrieved. The underlying property type might be a Java language primitive (such as int, a simple object (such as a java.lang.String), or a more complex object whose class is defined either by the Java language, by the application, or by a class library included with the application.

  • Indexed - An indexed property stores an ordered collection of objects (all of the same type) that can be individually accessed by an integer-valued, non-negative index (or subscript). Alternatively, the entire set of values may be retrieved using an array.

  • Mapped - As an extension to standard JavaBeans APIs, Esper considers any property that accepts a String-valued key a mapped property.

  • Nested - A nested property is a property that lives within another Java object which itself is a property of an event.

Assume there is an EmployeeEvent event class as shown below. The mapped and indexed properties in this example return Java objects but could also return Java language primitive types (such as int or String). The Address object and Employee objects can themselves have properties that are nested within them, such as a streetName in the Address object or a name of the employee in the Employee object.

public class EmployeeEvent {
	public String getFirstName();
	public Address getAddress(String type);
	public Employee getSubordinate(int index);
	public Employee[] getAllSubordinates();
}

Simple event properties require a getter-method that returns the property value. In this example, the getFirstName getter method returns the firstName event property of type String.

Indexed event properties require either one of the following getter-methods. A method that takes an integer-type key value and returns the property value, such as the getSubordinate method. Or a method that returns an array-type such as the getSubordinates getter method, which returns an array of Employee. In an EQL or event pattern statement, indexed properties are accessed via the property[index] syntax.

Mapped event properties require a getter-method that takes a String-typed key value and returns the property value, such as the getAddress method. In an EQL or event pattern statement, mapped properties are accessed via the property('key') syntax.

Nested event properties require a getter-method that returns the nesting object. The getAddress and getSubordinate methods are mapped and indexed properties that return a nesting object. In an EQL or event pattern statement, nested properties are accessed via the property.nestedProperty syntax.

All event pattern and EQL statements allow the use of indexed, mapped and nested properties (or a combination of these) anywhere where one or more event property names are expected. The below example shows different combinations of indexed, mapped and nested properties in filters of event pattern expressions.

every EmployeeEvent(firstName='myName')
every EmployeeEvent(address('home').streetName='Park Avenue')
every EmployeeEvent(subordinate[0].name='anotherName')
every EmployeeEvent(allSubordinates[1].name='thatName')
every EmployeeEvent(subordinate[0].address('home').streetName='Water Street')

Similarly, the syntax can be used in EQL statements in all places where an event property name is expected, such as in select lists, where-clauses or join criteria.

select firstName, address('work'), subordinate[0].name, subordinate[1].name
from EmployeeEvent
where address('work').streetName = 'Park Ave'

4.4. java.util.Map Events

Events can also be represented by objects that implement the java.util.Map interface. Event properties of Map events are the values in the map accessible through the get method exposed by the java.util.Map interface.

The engine can process java.util.Map events via the sendEvent(Map map, String eventTypeAlias) method on the EPRuntime interface. Entries in the Map represent event properties. Keys must be of type java.util.String for the engine to be able to look up event property names specified by pattern or EQL statements. Values can be of any type. JavaBean-style objects as values in a Map can also be processed by the engine.

In order to use Map events, the event type name and property names and types must be made known to the engine via Configuration. Please see the examples in Section 2.4.2, “Events represented by java.util.Map”.

The code snippet below creates and processes a Map event. The example assumes the CarLocationUpdateEvent event type alias has been configured.

Map event = new HashMap();
event.put("carId", carId);
event.put("direction", direction);
epRuntime.sendEvent(event, "CarLocUpdateEvent");

The CarLocUpdateEvent can now be used in a statement:

select carId from CarLocUpdateEvent.win:time(1 min) where direction = 1

The engine can also query Java objects as values in a Map event via the nested property syntax. Thus Map events can be used to aggregate multiple datastructures into a single event and query the composite information in a convenient way. The example below demonstrates a Map event with a transaction and an account object.

Map event = new HashMap();
event.put("txn", txn);
event.put("account", account);
epRuntime.sendEvent(event, "TxnEvent");

An example statement could look as follows.

select account.id, account.rate * txn.amount from TxnEvent.win:time(60 sec) group by account.id

4.5. org.w3c.dom.Node XML Events

Events can also be represented as org.w3c.dom.Node instances and send into the engine via the sendEvent method on EPRuntime. Please note that configuration is required for allowing the engine to map the event type alias to Node element names. See Chapter 2, Configuration.

Esper allows configuring XPath expressions as event properties. You can specify arbitrary XPath functions or expressions and provide a property name by which their result values will be available for use in expressions. For XML documents that follow an XML schema, Esper can load and interrogate your schema and validate event property names and types against the schema information.

Nested, mapped and indexed event properties are also supported in expressions against org.w3c.dom.Node events. Thus XML trees can conveniently be interrogated using the existing event property syntax for querying JavaBean objects, JavaBean object graphs or java.util.Map events.

Let's look at how a sample XML document could be queried, given the sample XML below.

<?xml version="1.0" encoding="UTF-8"?>
<Sensor>
	<ID>urn:epc:1:4.16.36<ID>
	<Observation Command="READ_PALLET_TAGS_ONLY">
		<ID>00000001<ID>
		<Tag>
			<ID>urn:epc:1:2.24.400<ID>
		</Tag>
		<Tag>
			<ID>urn:epc:1:2.24.401<ID>
		</Tag>
	</Observation>
</Sensor>

To configure the engine for processing Sensor documents, simply configure a SensorEvent event type alias for the Sensor element name via Configuration. Now the document can be queried as below.

select ID, Observation.ID, Observation.Command, Observation.Tag[0], countTags
from SensorEvent.win:time(30 sec)

The equivalent XPath expressions to each of the properties are listed below.

  • The equivalent XPath expression to Observeration.ID is /Sensor/Observation/ID

  • The equivalent XPath expression to Observeration.Command is /Sensor/Observation/@Command

  • The equivalent XPath expression to Observeration.Tag[0] is /Sensor/Observation/Tag[position() = 1]

  • The equivalent XPath expression to countTags is count(/Sensor/Observation/Tag) for returning a count of tag elements. This assumes the countTags property has been configured as an XPath property.

By specifying an event property such below:

nestedElement.mappedElement('key').indexedElement[1]

The equivalent XPath expression is as follows:

/simpleEvent/nestedElement/mappedElement[@id='key']/indexedElement[position() = 2]

Chapter 5. Event Pattern Reference

5.1. Event Pattern Overview

Event patterns match when an event or multiple events occur that match the pattern's definition. Patterns can also be time-based.

Pattern expressions can consist of filter expressions combined with pattern operators. Expressions can contain further nested pattern expressions by including the nested expression(s) in () round brackets.

There are 5 types of operators:

  1. Operators that control pattern finder creation and termination: every

  2. Logical operators: and, or, not

  3. Temporal operators that operate on event order: -> (followed-by)

  4. Guards are where-conditions that filter out events and cause termination of the pattern finder. Examples are timer:within.

  5. Observers observe time events as well as other events. Examples are timer:interval and timer:at.

5.2. How to use Patterns

5.2.1. Pattern Syntax

This is an example pattern expression that matches on every ServiceMeasurement events in which the value of the latency event property is over 20 seconds, and on every ServiceMeasurement event in which the success property is false. Either one or the other condition must be true for this pattern to match.

every (spike=ServiceMeasurement(latency>20000) or error=ServiceMeasurement(success=false))

In the example above, the pattern expression starts with an every operator to indicate that the pattern should fire for every matching events and not just the first matching event. Within the every operator in round brackets is a nested pattern expression using the or operator. The left hand of the or operator is a filter expression that filters for events with a high latency value. The right hand of the operator contains a filter expression that filters for events with error status. Filter expressions are explained in Section 5.3, “Filter Expressions”.

The example above assigned the tags spike and error to the events in the pattern. The tags are important since the engine only places tagged events into the output event(s) that a pattern generates, and that the engine supplies to listeners of the pattern statement. The tags can further be selected in the select-clause of an EQL statement as discussed in Section 6.4.2, “Pattern-based event streams”.

Pattern statements are created via the EPAdministrator interface. The EPAdministrator interface allows to create pattern statements in two ways: Pattern statements that want to make use of the EQL select clause or any other EQL constructs use the createEQL method to create a statement that specifies one or more pattern expressions. EQL statements that use patterns are described in more detail in Section 6.4.2, “Pattern-based event streams”. Use the syntax as shown in below example.

EPAdministrator admin = EPServiceProviderManager.getDefaultProvider().getEPAdministrator();

String eventName = ServiceMeasurement.class.getName();

EPStatement myTrigger = admin.createEQL("select * from pattern [" +
  "every (spike=" + eventName + "(latency>20000) or error=" + eventName + "(success=false))]");

Pattern statements that do not need to make use of the EQL select clause or any other EQL constructs can use the createPattern method, as in below example.

EPStatement myTrigger = admin.createPattern(
  "every (spike=" + eventName + "(latency>20000) or error=" + eventName + "(success=false))");

5.2.2. Subscribing to Pattern Events

When a pattern fires it publishes one or more events to any listeners to the pattern statement. The listener interface is the net.esper.client.UpdateListener interface.

The example below shows an anonymous implementation of the net.esper.client.UpdateListener interface. We add the anonymous listener implementation to the myPattern statement created earlier. The listener code simply extracts the underlying event class.

myPattern.addListener(new UpdateListener()
{
  public void update(EventBean[] newEvents, EventBean[] oldEvents)
  {
    ServiceMeasurement spike = (ServiceMeasurement) newEvents[0].get("spike");
    ServiceMeasurement error = (ServiceMeasurement) newEvents[0].get("error");
    ... // either spike or error can be null, depending on which occurred
    ... // add more logic here
  }
});

Listeners receive an array of EventBean instances in the newEvents parameter. There is one EventBean instance passed to the listener for each combination of events that matches the pattern expression. At least one EventBean instance is always passed to the listener.

The properties of each EventBean instance contain the underlying events that caused the pattern to fire, if events have been named in the filter expression via the name=eventType syntax. The property name is thus the name supplied in the pattern expression, while the property type is the type of the underlying class, in this example ServiceMeasurement.

5.2.3. Pulling Data from Patterns

Data can also be pulled from pattern statements via the iterator() method. If the pattern had fired at least once, then the iterator returns the last event for which it fired. The hasNext() method can be used to determine if the pattern had fired.

if (myPattern.iterator().hasNext())
{
	ServiceMeasurement event = (ServiceMeasurement) view.iterator().next().get("alert");
    ... // some more code here to process the event
}
else
{
    ... // no matching events at this time
}

5.3. Filter Expressions

This chapter outines how to filter events based on their properties.

The simplest form of filter is a filter for events of a given type without any conditions on the event property values. This filter matches any event of that type regardless of the event's properties. The example below is such a filter. Note that this event pattern would stop firing as soon as the first RfidEvent is encountered.

com.mypackage.myevents.RfidEvent

To make the event pattern fire for every RfidEvent and not just the first event, use the every keyword.

every com.mypackage.myevents.RfidEvent

The example above specifies the fully-qualified Java class name as the event type. Via configuration, the event pattern above can be simplified by using the alias that has been defined for the event type. Interfaces and abstract classes are also supported as event types.

every RfidEvent

Interfaces and superclasses are also supported as event types. In the below example IRfidReadable is an interface class.

every org.myorg.rfid.IRfidReadable

The filtering criteria to filter for events with certain event property values are placed within parenthesis after the event type name.

mypackage.RfidEvent(category="Perishable")

The supported filter operators are

  • equals =

  • not equals !=

  • comparison operators < , > , >=, <=

  • ranges use the keyword in and round (...) or square brackets []

Ranges come in the following 4 varieties. The use of round () or square [] bracket dictates whether an endpoint is included or excluded.

  • Open ranges that contain neither endpoint (low:high)

  • Closed ranges that contain both endpoints [low:high]

  • Half-open ranges that contain the low endpoint but not the high endpoint [low:high)

  • Half-closed ranges that contain the high endpoint but not the low endpoint (low:high]

Filter criteria are listed in a comma-separated format. In the example below we look for RfidEvent events with a grade property between 1 and 2 (endpoints included), a price less then 1, and a category of "Perishable".

mypackage.RfidEvent(category="Perishable", price<1.00, grade in [1:2])

Filter criteria can also refer to events matching prior named events in the same expression. Below pattern is an example in which the pattern matches once for every RfidEvent that is preceded by an RfidEvent with the same item id.

every A=mypackage.RfidEvent -> B=mypackage.RfidEvent(itemId=A.itemId)

The syntax shown above allows filter criteria to reference prior results by specifying the event name and event property. This syntax can be used with all filter operators.

Some limitations of filters are:

  • Range and comparison operators require the event property to be of a numeric type.

  • Null values in filter criteria are currently not allowed.

  • Filter criteria can list the same event property only once.

  • Events that have null values for event properties listed in the filter criteria do not match the criteria.

5.4. Pattern Operators

5.4.1. Every

The every operator indicates that the pattern expression should restart when the pattern matches. Without the every operator the pattern expressions matcher stops when the pattern matches once.

Thus the every operator works like a factory for the pattern expression contained within. When the pattern expression within it fires and thus quits checking for events, the every causes the start of a new pattern matcher listening for more occurances of the same event or set of events.

Every time a pattern expression within an every operator turns true a new active pattern matcher is started looking for more event(s) or timing conditions that match the pattern expression. If the every operator is not specified for an expression, the expression stops after the first match was found.

This pattern fires when encountering event A and then stops looking.

A

This pattern keeps firing when encountering event A, and doesn't stop looking.

every A

Let's consider an example event sequence as follows.

A1 B1 C1 B2 A2 D1 A3 B3 E1 A4 F1 B4

Table 5.1. 'Every' operator examples

ExampleDescription
every ( A -> B )

Detect event A followed by event B. At the time when B occurs the pattern matches, then the pattern matcher restarts and looks for event A again.

  1. Matches on B1 for combination {A1, B1}

  2. Matches on B3 for combination {A2, B3}

  3. Matches on B4 for combination {A4, B4}

every A -> B

The pattern fires for every event A followed by an event B.

  1. Matches on B1 for combination {A1, B1}

  2. Matches on B3 for combination {A2, B3} and {A3, B3}

  3. Matches on B4 for combination {A4, B4}

A -> every B

The pattern fires for an event A followed by every event B.

  1. Matches on B1 for combination {A1, B1}.

  2. Matches on B2 for combination {A1, B2}.

  3. Matches on B3 for combination {A1, B3}

  4. Matches on B4 for combination {A1, B4}

every A -> every B

The pattern fires for every event A followed by every event B.

  1. Matches on B1 for combination {A1, B1}.

  2. Matches on B2 for combination {A1, B2}.

  3. Matches on B3 for combination {A1, B3} and {A2, B3} and {A3, B3}

  4. Matches on B4 for combination {A1, B4} and {A2, B4} and {A3, B4} and {A4, B4}

The examples show that it is possible that a pattern fires for multiple combinations of events that match a pattern expression. Each combination is posted as an EventBean instance to the update method in the UpdateListener implementation.

5.4.2. And

Similar to the Java && operator the and operator requires both nested pattern expressions to turn true before the whole expression turns true (a join pattern).

Pattern matches when both event A and event B are found.

A and B

Pattern matches on any sequence A followed by B and C followed by D, or C followed by D and A followed by B

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

5.4.3. Or

Similar to the Java “||” operator the or operator requires either one of the expressions to turn true before the whole expression turns true.

Look for either event A or event B. As always, A and B can itself be nested expressions as well.

A or B

Detect all stock ticks that are either above or below a threshold.

every (StockTick(symbol='IBM', price < 100) or StockTick(symbol='IBM', price > 105)

5.4.4. Not

The not operator negates the truth value of an expression. Pattern expressions prefixed with not are automatically defaulted to true.

This pattern matches only when an event A is encountered followed by event B but only if no event C was encountered before event B.

( A -> B ) and not C

5.4.5. Followed-by

The followed by -> operator specifies that first the left hand expression must turn true and only then is the right hand expression evaluated for matching events.

Look for event A and if encountered, look for event B. As always, A and B can itself be nested event pattern expressions.

A -> B

This is a pattern that fires when 2 status events indicating an error occur one after the other.

StatusEvent(status='ERROR') -> StatusEvent(status='ERROR')

5.5. Pattern Guards

5.5.1. timer:within

The timer:within guard acts like a stopwatch. If the associated pattern expression does not turn true within the specified time period it is stopped and permanently false. The timer:within guard takes a time period (see Section 6.2.1, “Specifying Time Periods”) or a number of seconds as a parameter.

This pattern fires if an A event arrives within 5 seconds after statement creation.

A where timer:within (5 seconds)

This pattern fires for all A events that arrive within 5 seconds. After 5 seconds, this pattern stops matching even if more A events arrive.

(every A) where timer:within (5 seconds)

This pattern is similar to the first pattern but here every time A arrives within 5 seconds, the pattern begins looking for A for another 5 seconds. As long as A events arrive within 5 seconds after the last A, the pattern does not stop matching.

every (A where timer:within (5 sec))

This pattern matches for any one A or B event in the next 5 seconds.

( A or B ) where timer:within (5 sec)

This pattern matches for any 2 errors that happen 10 seconds within each other.

every (StatusEvent(status='ERROR') -> StatusEvent(status='ERROR') where timer:within (10 sec))

The following guards are equivalent:

timer:within(2 minutes 5 seconds)
timer:within(125 sec)
timer:within(125)

5.6. Pattern Observers

5.6.1. timer:interval

The timer:interval observer waits for the defined time before the truth value of the observer turns true. The observer takes a time period (see Section 6.2.1, “Specifying Time Periods”) or a number of seconds as a parameter.

After event A arrived wait 10 seconds then indicate that the pattern matches.

A -> timer:interval(10 seconds) 

The pattern below fires every 20 seconds.

every timer:interval(20 sec)

The next example pattern fires for every event A that is not followed by an event B within 60 seconds after event A arrived. B must have the same "id" property value as A.

every a=A -> (timer:interval(60 sec) and not B(id=a.id)) 

5.6.2. timer:at

The timer:at observer is similar in function to the Unix “crontab” command. At a specified time the expression turns true. The at operator can also be made to pattern match at regular intervals by using an every operator in front of the timer:at operator.

The syntax is: timer:at (minutes, hours, days of month, months, days of week [, seconds]).

The value for seconds is optional. Each element allows wildcard * values. Ranges can be specified by means of lower bounds then a colon ‘:’ then the upper bound. The division operator */x can be used to specify that every xth value is valid. Combinations of these operators can be used by placing these into square brackets([]).

This expression pattern matches every 5 minutes past the hour.

every timer:at(5, *, *, *, *)

The below at operator pattern matches every 15 minutes from 8am to 5pm on even numbered days of the month as well as on the first day of the month.

timer:at (*/15, 8:17, [*/2, 1], *, *)

Chapter 6. EQL Reference

6.1. EQL Introduction

EQL statements are used to derive and aggregate information from one or more streams of events, and to join or merge event streams. This section outlines EQL syntax. It also outlines the built-in views, which are the building blocks for deriving and aggregating information from event streams.

EQL is similar to SQL in its use of the select clause and the where clause. Where EQL differs most from SQL is in the use of tables. EQL replaces tables with the concept of event streams.

EQL statements contain definitions of one or more views. Similar to tables in an SQL statement, views define the data available for querying and filtering. Some views represent windows over a stream of events. Other views derive statistics from event properties, group events or handle unique event property values. Views can be staggered onto each other to build a chain of views. The Esper engine makes sure that views are reused among EQL statements for efficiency.

The built-in set of views is:

  1. Views that represent moving event windows: win:length, win:time, win:time_batch, win:ext_time, ext:sort_window

  2. Views for aggregation: std:unique, std:groupby, std:lastevent (note: the group-by clause and the std:groupby view are very similar in function, see view description for differences)

  3. Views that derive statistics: std:size, stat:uni, stat:linest, stat:correl, stat:weighted_avg, stat:multidim_stat

Esper can be extended by plugging-in custom developed views.

6.2. EQL Syntax

EQL queries are created and stored in the engine, and publish results as events are received by the engine or timer events occur that match the criteria specified in the query. Events can also be pulled from running EQL queries.

The select clause in an EQL query specifies the event properties or events to retrieve. The from clause in an EQL query specifies the event stream definitions and stream names to use. The where clause in an EQL query specifies search conditions that specify which event or event combination to search for. For example, the following statement returns the average price for IBM stock ticks in the last 30 seconds.

select avg(price) from StockTick.win:time(30 sec) where symbol='IBM'

EQL queries follow the below syntax. EQL queries can be simple queries or more complex queries. A simple select contains only a select clause and a single stream definition. Complex EQL queries can be build that feature a more elaborate select list utilizing expressions, may join multiple streams, may contain a where clause with search conditions and so on.

[insert into insert_into_def]
select select_list
from stream_def [as name] [, stream_def [as name]] [,...]
[where search_conditions]
[group by grouping_expression_list]
[having grouping_search_conditions]
[output output_specification]
[order by order_by_expression_list]

6.2.1. Specifying Time Periods

Time-based windows as well as pattern observers and guards take a time period as a parameter. Time periods follow the syntax below.

time-period : [day-part] [hour-part] [minute-part] [seconds-part] [milliseconds-part]

day-part : number ("days" | "day")
hour-part : number ("hours" | "hour")
minute-part : number ("minutes" | "minute" | "min")
seconds-part : number ("seconds" | "second" | "sec")
milliseconds-part : number ("milliseconds" | "millisecond" | "msec")

Some examples of time periods are:

10 seconds
10 minutes 30 seconds
20 sec 100 msec
1 day 2 hours 20 minutes 15 seconds 110 milliseconds
0.5 minutes

6.3. Choosing Event Properties And Events: the Select Clause

The select clause is required in all EQL statements. The select clause can be used to select all properties via the wildcard *, or to specify a list of event properties and expressions. The select clause defines the event type (event property names and types) of the resulting events published by the statement, or pulled from the statement.

The select clause also offers optional istream and rstream keywords to control how events are posted to UpdateListener instances listening to the statement.

The syntax for the select clause is summarized below.

select [rstream | istream] * | expression_list ... 

6.3.1. Choosing all event properties: select *

The syntax for selecting all event properties in a stream is:

select * from stream_def

The following statement selects all univariate statistics properties for the last 30 seconds of IBM stock ticks for price.

select * from StockTick(symbol='IBM').win:time(30 sec).stat:uni('price')

In a join statement, using the select * syntax selects event properties that contain the events representing the joined streams themselves.

6.3.2. Choosing specific event properties

To chose the particular event properties to return:

select event_property [, event_property] [, ...] from stream_def

The following statement selects the count and standard deviation properties for the last 100 events of IBM stock ticks for volume.

select count, stdev from StockTick(symbol='IBM').win:length(100).stat:uni('volume')

6.3.3. Expressions

The select clause can contain one or more expressions.

select expression [, expression] [, ...] from stream_def

The following statement selects the volume multiplied by price for a time batch of the last 30 seconds of stock tick events.

select volume * price from StockTick.win:time_batch(30 sec)

6.3.4. Renaming event properties

Event properties and expressions can be renamed using below syntax.

select [event property | expression] as identifier [, ...]

The following statement selects volume multiplied by price and specifies the name volPrice for the event property.

select volume * price as volPrice from StockTick.win:length(100)

6.3.5. Selecting istream and rstream events

The optional istream and rstream keywords in the select clause define the event stream posted to listeners to the statement.

If neither keyword is specified, the engine posts insert stream events via the newEvents parameter to the update method of UpdateListener instances listening to the statement. The engine posts remove stream events to the oldEvents parameter of the update method. The insert stream consists of the events entering the respective window(s) or stream(s), while the remove stream consists of the events leaving the respective window(s).

By specifying the istream keyword you can instruct the engine to only post insert stream events via the newEvents parameter to the update method on listeners. The engine will then not post any remove stream events, and the oldEvents parameter is always a null value.

By specifying the rstream keyword you can instruct the engine to only post remove stream events via the newEvents parameter to the update method on listeners. The engine will then not post any insert stream events, and the oldEvents parameter is also always a null value.

The following statement selects only the events that are leaving the 30 second time window.

select rstream * from StockTick.win:time(30 sec)

The istream and rstream keywords in the select clause are matched by same-name keywords available in the insert into clause. While the keywords in the select clause control the event stream posted to listeners to the statement, the same keywords in the insert into clause specify the event stream that the engine makes available to other statements.

6.4. Specifying Event Streams : the From Clause

The from clause is required in all EQL statements. It specifies one or more event streams. Each event stream can optionally be given a name by means of the as syntax.

from stream_def [as name] [, stream_def [as name]] [, ...]

The event stream definition stream_def as shown in the syntax above can consists of either a filter-based event stream definition or a pattern-based event stream definition.

For joins and outer joins, specify two or more event streams. Joins between pattern-based and filter-based event streams are also supported.

Esper supports joins against relational databases for access to historical or reference data as explained in Section 6.12, “Joining Relational Data via SQL”.

6.4.1. Filter-based event streams

</