Monitors

A .mon file is a file that contains the source text for an optional package specification and one or more event declarations and/or monitor definitions. A file can consist entirely of event declarations without any monitors.

A monitor is a group of related variable declarations and actions. An action is a group of related variable declarations and statements. An action can either be part of a monitor or part of an event declaration.

The executable statements (except for global variable initializers) are always inside an action. An action can be either a subprogram or a function. The difference is that a function has a return value and a subprogram does not.

Each file is injected whole or not at all; if some parts compile validly but others do not, nothing is injected and an error is returned. Injecting can also return warnings about the code injected. For example, use of keywords that may be reserved in the future.

Monitor lifecycle

Monitors are compiled and run (executed) by the Apama correlator. The correlator starts executing in the monitor’s onload() action. To execute a monitor, you load (inject) it into the correlator. The correlator then does the following:

  1. Compiles the monitor’s source text
  2. If no errors are detected, creates the main monitor instance along with its global variables
  3. Invokes the monitor instance’s onload() action

When the onload() action has executed to completion (that is, the control path reaches the closing curly brace of the onload() action), if the monitor instance has event listeners or streaming networks, then it remains active but in a suspended state.

The correlator calls the monitor instance’s event listeners whenever it detects events that match the event listeners’ event expressions.

A monitor instance terminates when one of the following events occurs:

  • The monitor instance executes a die statement in one of its actions.
  • A runtime error condition is raised.
  • The monitor is terminated externally (for example, with the engine_delete utility.
  • The monitor instance has executed all its code and there are no remaining listeners or streaming networks. This will occur rapidly if the onload() action does not create any.

When a monitor instance terminates, the correlator does the following:

  1. Invokes the monitor instance’s ondie() action, if it is defined.
  2. If the monitor instance that is terminating is the last active instance of that monitor, the correlator also does the following:
    • Invokes the monitor’s onunload() action if it is defined.
    • Removes the monitor’s code from the correlator.
    • Frees all the monitor’s resources.

To summarize, consider that when a monitor spawns monitor instances, there is a set of monitors that includes the original monitor instance and any spawned monitor instances. As the monitor instances in this set terminate, the correlator calls the ondie() action, if it is defined, for each monitor instance that terminates. When the last monitor instance in the set terminates, the correlator also calls the onunload() action. Thus, the correlator calls ondie() once for each monitor instance in the set, and calls onunload() only once for the entire set.

See About executing ondie() actions for information about how ondie() can optionally receive exception information if an instance dies due to an uncaught exception.

Monitor files

An EPL monitor file contains an optional package declaration, optional using declaration, event declarations and/or monitor declarations and/or custom aggregate definitions.

Syntax diagram for monitor files

Packages

A package declaration provides a scope for events and/or monitors.

Example:

package com.myCorporation.myproject;

See Names for further information.

The using declaration

The using declaration lets you use a type in a package other than the package the type was defined in without having to specify the fully qualified name of the type.

Insert a using declaration (after the optional package declaration and before any other declarations) that specifies the fully qualified name of the type. For example:

using com.myCorporation.custom.myCustomAggregate;

You can specify multiple using declarations in a file.

In a file, you cannot specify two using declarations that bring in types that have the same base name. See also Name Precedence.

You cannot specify a using declaration for named objects such as monitors and namespaces.

A using declaration can be in a monitor.

Monitor declarations

Specify persistent when you want a persistence-enabled correlator to save the state of the monitor in a recovery datastore on disk. In a monitor, import declarations, event declarations, variable declarations, and action definitions can be freely mixed in any order. For detailed information, see Defining monitors.

A monitor can be optionally prefixed (before the persistent keyword) with annotations. See also Annotations.

The import declaration

The import declaration loads a plug-in library and makes it available to an EPL program. Plug-in libraries are shared libraries on Linux and UNIX systems and Dynamic Link Libraries on Windows systems.

On Linux and UNIX systems, the library is loaded from a *libPlugInName*.so file located in one of the directories listed in the environment variable LD_LIBRARY_PATH. On Windows, the library is loaded from a *PlugInName*.dll file located in the bin folder.

You can name a plug-in. The plug-in name is a library filename, not a full filepath, and is not allowed to contain any of the characters used as directory or device separators (forward slash, colon, or backslash).

You can also give the plug-in an identifier (an alias name) for use in the EPL program when you call the library’s actions.

For example, to call a plug-in action foo() in the plug-in library wffftl.so or wffftl.dll, you would write the following:

monitor m {
   import "wffftl" as fft;
   action onload()
   {
      sequence <float> data := [];
      fft.foo (data);
   }
}

For detailed information, see Using EPL plug-ins.

Monitor actions

Monitors can have two forms of actions: simple actions and actions with parameters and/or return values. These types of actions are discussed in the topics below.

Monitor actions can optionally be prefixed with annotations. See Annotations .

Simple actions

A simple action has a name and a body consisting of a block. The body contains the executable code of the action. There are no parameters.

The action names given in the table below have special meaning in a monitor. These actions are invoked automatically when certain events in a monitor’s life cycle occur.

A block must follow the action name. Note that there are no formal parameters in this form of action definition and the action cannot return a value.

Action Description
onload() This action is invoked immediately after a monitor has been loaded. This action must be present in every monitor.
ondie() If present, this action is invoked by the correlator when a monitor instance terminates.
onunload() If present, this action is invoked by the correlator after all instances of a monitor have terminated, just before the last monitor instance is unloaded.
onBeginRecovery() If present, this action is invoked by the correlator during recovery of a persistence-enabled correlator. The correlator executes onBeginRecovery() on monitors and any live events after it reinjects source code and restores state in persistent monitors.
onConcludeRecovery() If present, this action is invoked by the correlator during recovery of a persistence-enabled correlator. The correlator executes onConcludeRecovery() on monitors and any live events before it begins to send clock ticks.

Actions with parameters

An action can take an optional list of parameters.

Formal parameters

The formal parameters are a comma-separated list of type name and identifier pairs.

The identifier is the name of a parameter variable that will be bound to a copy of the value of an expression specified by the caller (that is, the value passed by the caller) when the action is invoked. The number and type of actual parameters passed by a caller must match those listed in the action’s formal parameters.

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

Action return value

If you specify a returns clause, then the action must return a value whose type matches that specified in the returns clause. You specify the return value by using a return statement and result expression within the action. Every control path (see Transfer of control statements) within the action body must lead to a return statement with a result expression of the correct type.

Action body

After the returns clause (or after the formal parameters if there is no returns clause), a statement forms the action body. The action body can be a single statement or a block.

Within the action body, you use the parameter variable names to obtain the values that are passed to the action by its caller.

Contexts

Contexts allow EPL applications to organize work into threads that the correlator can concurrently execute. For detailed information, see Implementing parallel processing. This also provides information on the properties of a context (see About context properties).

Note: In monitors, you must implement the use of contexts.

You can create any number of contexts. Creating a context just allocates an ID and creates a small object. See also Creating contexts.

For information on how to obtain a reference to a context, see Obtaining context references.

Plug-ins

EPL can be extended through the use of plug-ins, which are modules written either in C++, C, or Java and loaded dynamically into the EPL runtime with the import statement. Plug-in modules are invoked in exactly the same way as actions in an EPL event.

See Using EPL plug-ins.

Garbage collection

EPL, like languages such as Java or C#, relies on garbage collection. Intermittently, the correlator analyses the events that have been allocated, including dictionaries, sequences, closures and streaming networks, and allows memory used by events that can no longer be referenced to be re-used. Thus, the actual memory usage of the correlator might be temporarily above the size of all live objects. While running EPL, the correlator might wait until a listener or onload() action completes before performing garbage collection. Therefore, any garbage generated within a single action or listener invocation might not be disposed of before the action/ listener has completed. It is thus advisable to limit individual actions/listeners to performing small pieces of work. This also aids in reducing system latency.

The cost of garbage collection increases as the number of events a monitor instance creates and references increases. If latency is a concern, it is recommended to keep this number low, dividing the working set by spawning new monitor instances if possible and appropriate. Reducing the number of event creations, including string operations that result in a new string being created, also helps to reduce the cost of garbage collection. The exact cost of garbage collection could change in future releases as product improvements are made.