www.espertech.comDocumentation
This chapter presents information related to the development lifecycle for developing an event processing application with EPL. It includes information on authoring, testing, debugging, packaging and deploying.
For information on authoring event classes or event definitions in general please see Chapter 2, Event Representations or Section 5.15, “Declaring an Event Type: Create Schema”.
Esper's API provides test framework classes to simplify automated testing of EPL statements. Please see Section 15.21, “Test and Assertion Support” for more information.
We recommend performing latency and throughput tests early in the development lifecycle. Please consider the performance tips in Chapter 21, Performance for optimal performance.
Consider engine and statement metrics reporting for identifying slow-performing statements, for example. See Section 15.15, “Engine and Statement Metrics Reporting”.
Enterprise Edition includes a debugger for EPL statement execution.
Another tool for logging engine-level detail is Section 16.4.14.1, “Execution Path Debug Logging”.
Please see Section 16.7, “Logging Configuration” for information on configuring logging in general.
@Name('All Order Events') @Audit select * from OrderEvent
@Name('All Order Events') @Audit('stream,property') select price from OrderEvent
epService.getEPAdministrator().createEPL("create schema OrderEvent(price double)"); String epl = "@Name('All-Order-Events') @Audit('stream,property') select price from OrderEvent"; epService.getEPAdministrator().createEPL(epl).addListener(listener); epService.getEPRuntime().sendEvent(Collections.singletonMap("price", 100d), "OrderEvent");
The output is similar to the following:
INFO [audit] Statement All-Order-Events stream OrderEvent inserted {price=100.0} INFO [audit] Statement All-Order-Events property price value 100.0
Note that the engine only evaluates select-clause expressions if either a listener or subscriber is attached to the statement or if used with insert-into.
For undeploying, deploying or re-deploying single or multiple statements or modules as an atomic management unit please see Section 15.3.7, “Atomic Statement Management”.
To support packaging and deploying event-driven applications, Esper offers infrastructure as outlined herein:
EPL modules to build a cohesive, easily-externalizable deployment unit out of related statements as described in Section 17.5, “EPL Modules”.
The deployment administrative interface is described in Section 17.6, “The Deployment Administrative Interface”.
Instructions and code for use when the deployment target is a J2EE web application server or servlet runtime, please see Section 17.7, “J2EE Packaging and Deployment”.
An EPL module file is a plain text file in which EPL statements appear separated by the semicolon (;) character. It bundles EPL statements with optional deployment instructions. A service provider instance keeps track of the known and/or deployed EPL modules and makes it easy to add, remove, deploy and undeploy EPL modules.
The synopsis of an EPL module file is:
[module module_name;] [uses module_name; | import import_name;] [uses module_name; | import import_name;] [...] [epl_statement;] [epl_statement;] [...]
Use the module
keyword followed a module_name identifier or a package (identifiers separated by dots) to declare the name of the module. The module name declaration must be at the beginning of the file, comments and whitespace excluded. The module name
serves to check uses-dependences of other modules.
If a module file requires certain constructs that may be shared by other module files, such as named windows, tables, variables, event types, variant streams or inserted-into streams required by statements,
a module file may specify zero to many dependent modules with the uses
keyword. At deployment time the engine checks the uses-dependencies
and ensures that a module of that name is already deployed or will be deployed as part of the deployments. The deployment API supports ordering modules according to their uses-relationship.
If the EPL statements in the module require Java classes such as for underlying events or user-defined functions, use the import
keyword followed by the fully-qualified class name or package name in the format package.*
.
The uses
and import
keywords are optional and must occur after the module
declaration.
Following the optional deployment instructions are any number of epl_statement EPL statements that are separated by semicolon (;
).
The following is a sample EPL module file explained in detail thereafter:
// Declare the name for the module module org.myorganization.switchmonitor; // Declare other module(s) that this module depends on uses org.myorganization.common; // Import any Java/.NET classes in an application package import org.myorganization.events.*; // Declare an event type based on a Java class in the package that was imported as above create schema MySwitchEvent as MySwitchEventPOJO; // Sample statement @Name('Off-On-Detector') insert into MyOffOnStream select * from pattern[every-distinct(id) a=MySwitchEvent(status='off') -> b=MySwitchEvent(id=a.id, status='on')]; // Sample statement @Name('Count-Switched-On') @Description('Count per switch id of the number of Off-to-On switches in the last 1 hour') select id, count(*) from MyOffOnStream.win:time(1 hour) group by id;
The example above declares a module name of org.myorganization.switchmonitor
. As defined by the uses
keyword, it ensures that the org.myorganization.common
module is already deployed.
The example demonstrates the import
keyword to make a package name known to the engine for resolving POJO class names, as the example assumes that MySwitchEventPOJO
is a POJO event class.
In addition the example module contains two statements separated by semicolon characters.
Your application code may, after deployment, look up a statement and attach listeners as shown here:
epService.getEPAdministrator().getStatement("Count-Switched-On").addListener(...);
The com.espertech.esper.client.deploy.EPDeploymentAdmin
service available from the EPAdministrator
interface by method getDeploymentAdmin
provides the functionality available to manage packaging and deployment.
Please consult the JavaDoc documentation for more information.
The deployment API allows to read resources and parse text strings to obtain an object representation of the EPL module, the Module
. A Module
object can also be simply constructed.
After your application obtains a Module
instance it may either use deploy
to deploy the module directly, starting all statements of the module. Alternatively your application
may add a module, making it known without starting statements for later deployment. In each case the module is assigned a deployment id, which acts as a unique primary key for all known modules. Your application may assign its own deployment id
or may have the engine generate a deployment id (two footprints for add
and deploy
methods).
A module may be in two states: undeployed or deployed. When calling add
to add a module, it starts life in the undeployed state. When calling deploy
to deploy a module, it starts life in the deployed state.
A module may be transitioned by providing the deployment id and by calling the deploy
or undeploy
methods.
Your code can remove a module in undeployed state using the remove
method or the undeployRemove
method. If the module is in deployed state, use undeployRemove
to undeploy and remove the module.
The DeploymentOptions
instance that can be passed to the deploy
method when validating or deploying modules controls validation, fail-fast, rollback and the isolated service provider, if any, for the deployment.
Also use DeploymentOptions
to set a user object per statement or to set a statement name per statement.
We also provide additional sample code to read and deploy modules as part of the J2EE considerations below.
This code snippet demonstrates reading and parsing a module given a file name:
EPServiceProvider epService = EPServiceProviderManager.getDefaultProvider(); EPDeploymentAdmin deployAdmin = epService.getEPAdministrator().getDeploymentAdmin(); Module module = deployAdmin.read(new File("switchmonitor.epl"));
The next code snippet deploys modules, starting each modulle's EPL statements:
for (Module mymodule : order.getOrdered()) { DeploymentResult deployResult = deployAdmin.deploy(mymodule, new DeploymentOptions()); }
Undeploying a module destroys all started statements associated to the module.
deployAdmin.undeployRemove(deployResult.getDeploymentId());
To obtain a list of all known modules or information for a specific module, the calls are:
DeploymentInformation[] info = deployAdmin.getDeploymentInformation(); // Given a deployment id, return the deployment information DeploymentInformation infoModule = deployAdmin.getDeploymen(deploymentId);
Esper can well be deployed as part of a J2EE web or enterprise application archive to a web application server. When designing for deployment into a J2EE web application server, please consider the items discussed here.
We provide a sample servlet context listener in this section that uses the deployment API to deploy and undeploy modules as part of the servlet lifecycle.
The distribution provides a message-driven bean (MDB) example that you may find useful.
Esper does not have a dependency on any J2EE or Servlet APIs to allow the engine to run in any environment or container.
A sample web.xml
configuration extract is:
<?xml version="1.0" encoding="UTF-8"?> <web-app> <listener> <listener-class>SampleServletListener</listener-class> </listener> <context-param> <param-name>eplmodules</param-name> <param-value>switchmonitor.epl</param-value> </context-param> </web-app>
public class SampleServletListener implements ServletContextListener { private List<String> deploymentIds = new ArrayList<String>(); public void contextInitialized(ServletContextEvent servletContextEvent) { try { EPServiceProvider epServiceProvider = EPServiceProviderManager.getDefaultProvider(); String modulesList = servletContextEvent.getServletContext().getInitParameter("eplmodules"); List<Module> modules = new ArrayList<Module>(); if (modulesList != null) { String[] split = modulesList.split(","); for (int i = 0; i < split.length; i++) { String resourceName = split[i].trim(); if (resourceName.length() == 0) { continue; } String realPath = servletContextEvent.getServletContext().getRealPath(resourceName); Module module = epServiceProvider.getEPAdministrator() .getDeploymentAdmin().read(new File(realPath)); modules.add(module); } } // Determine deployment order DeploymentOrder order = epServiceProvider.getEPAdministrator() .getDeploymentAdmin().getDeploymentOrder(modules, null); // Deploy for (Module module : order.getOrdered()) { DeploymentResult result = epServiceProvider.getEPAdministrator() .getDeploymentAdmin().deploy(module, new DeploymentOptions()); deploymentIds.add(result.getDeploymentId()); } } catch (Exception ex) { ex.printStackTrace(); } } public void contextDestroyed(ServletContextEvent servletContextEvent) { EPServiceProvider epServiceProvider = EPServiceProviderManager.getDefaultProvider(); for (String deploymentId : deploymentIds) { epServiceProvider.getEPAdministrator().getDeploymentAdmin().undeployRemove(deploymentId); } } }
The engine can report key processing metrics through the JMX platform mbean server by setting a single configuration flag described in Section 16.4.21, “Engine Settings related to JMX Metrics”.
Engine and statement-level metrics reporting is described in Section 15.15, “Engine and Statement Metrics Reporting”.