Sequences of EPL statements define the steps that are performed by a program. They are executed in the order they are written: sequentially from top to bottom and left to right within a statement block. (For expressions, the evaluation order is affected by parentheses, associativity, and operator precedence.)
The order in which statements are executed is called the flow of control or the control path. Some statements can contain other statements enclosed within their structure and can be used to execute statements conditionally, thus altering the normal control path. You can use the break, continue, and return statements to change the normal control path.
A block is zero or more statements enclosed in curly braces. A block can be used wherever a single statement can be used. Variables declared in a block are able to be referenced only in the block in which they are declared, and only in statements that come after the variable’s declaration.
Simple statements
Simple statements are statements that do not enclose other statements or statement blocks and that do not cause a transfer of control. They are executed in the order they are written.
Assignments
An assignment binds a value to a variable, member, sequence element or dictionary value. The value is determined by evaluating the expression on the right side of the assignment operator :=. The result type of the expression must match the type of the variable.
For variables of the reference types, the same value can be bound to more than one variable. See the API reference for EPL (ApamaDoc) for detailed descriptions of all types.
The emit statement
The emit statement publishes an event to a named channel of the correlator’s output queue. If a channel name is not specified, then the event goes to the default channel whose name is the empty string ( ""). External receivers get events on the default channel only if they are subscribed to all channels.
Note:
The emit statement will be deprecated in a future release. Use the send statement instead. See The send… to statement.
The first expression is an expression whose result type is either an event type or string. If the type is string, then the value of the string is assumed to be in the same format as that produced by the event’s toString() method.
The expression following the keyword to must be of type string and is the name of the channel to which the event will be sent.
The emit method dispatches events to external registered event receivers. That is, the emit statement causes events to go out of the correlator. Active event listeners will not receive events that are emitted.
Events are emitted onto named channels. For an application to receive events from the correlator it must register itself as an event receiver and subscribe to one or more channels. Then if events are emitted to those channels they will be forwarded to it.
Channels effectively allow both point-to-point message delivery as well as through publish-subscribe. Channels can be set up to represent topics. External applications can then subscribe to event messages of the relevant topics. Otherwise a channel can be set up purely to indicate a destination and have only one application connected to it.
You cannot emit an event whose type is defined inside a monitor.
The emit statement can operate on any values as well as events, provided that the any value is of a routable event type.
You cannot emit an event that has a field of type action, chunk, listener, or stream. There is a runtime check if the event (or one of its members) can contain an any field; an exception is thrown if the any field contains an object of type action, chunk, listener, or stream.
When you emit an event type that has a dictionary field, the items in the dictionary are sorted in ascending order of their key values.
The enqueue… to statement
The enqueue...to statement sends an event to a context you identify.
Note:
The enqueue...to statement is superseded by the send...to statement. The enqueue...to statement will be deprecated in a future release. Use the send...to statement instead. See The send… to statement.
You must enqueue an expression of type event, and the destination must be one of the following:
context — The enqueue...to statement sends an event to the back of the input queue of the specified context. The expression is evaluated and the resulting event is sent to the input queue of only the specified context.
sequence<context> — The enqueue...to statement sends a copy of the event to the back of the input queue of each context in the specified sequence. The expression is evaluated and the resulting event is sent to the input queue of all the contexts in the sequence.
You cannot enqueue an event to a com.apama.Channel object that contains a context. You cannot enqueue an event to a dictionary of contexts. However, it is a common pattern to enqueue to a sequence generated by dictionary.values(). For example:
enqueue x to d.values;
If the target context’s input queue is full the sending context blocks and waits for space on the queue unless doing so would cause a deadlock. See Deadlock avoidance when parallel processing.
Note that enqueued events are processed in the order they are enqueued. Enqueued events are put on the back of the input queue, behind any externally sourced events already queued.
You must create the context before you enqueue an event to the context. You cannot enqueue an event to a context that you have declared but not created. For example, the following code causes the correlator to terminate the monitor instance:
monitor m {
context c;
action onload()
{
enqueue A() to c;
}
}
If you enqueue an event to a sequence of contexts and one of the contexts has not been created first then the correlator terminates the monitor instance. For details, see Sending an event to a particular context.
The enqueue...to statement can operate on any values as well as events, provided that the any value is of a routable event type.
You cannot enqueue an event that has a field of type action, chunk, listener, or stream. There is a runtime check if the event (or one of its members) can contain an any field; an exception is thrown if the any field contains an object of type action, chunk, listener, or stream.
The log statement
The log statement writes messages and accompanying date and time information to the correlator’s log file, if one was specified when the correlator was started.
If there is no log file, then the message is written to the correlator’s standard output stream stdout.
The expression that you log must be of type string. The value is written only if the current logging level in effect is a priority equal to or higher than the log level specified in the log statement, with the exception of OFF. If you do not specify a level, INFO is used. At a log level equal to OFF, only logs explicitly set to this level are written. For details, see Logging and printing.
For example:
log "Your message here" at INFO;
This EPL statement produces a log message that looks like this:
2020-01-11 09:08:49.200 INFO [3716] - MyMonitor[1] Your message here
The print statement
The print statement writes textual messages followed by a newline to the correlator’s standard output stream — stdout. The expression you print must be of type string.
For example:
print "Your message here.";
This EPL statement produces output that looks like this:
Your message here.
The print statement is less useful for reporting diagnostic information than the log statement, as it does not contain any information about the time or origin of the message, and cannot be turned off by changing the log level.
The route statement evaluates the expression and then sends the resulting event to the front of the current context’s input queue.
The expression you route must be an event. The event is processed only within the same context that executes the route statement.
Routed events are put on the input queue, ahead of any externally sourced events, and ahead of any previously routed events that have not yet been processed. For more details, see Event processing order for monitors.
The isExternal() property on events is not changed by routing an event.
The route statement can operate on any values as well as events, provided that the any value is of a routable event type.
You cannot route an event that has a field of type action, chunk, listener, or stream. There is a runtime check if the event (or one of its members) can contain an any field; an exception is thrown if the any field contains an object of type action, chunk, listener, or stream.
The send… to statement
The send...to statement sends an event to the channel, context, sequence of contexts, or com.apama.Channel object that you specify.
You must send an expression of type event, and the destination must be one of the following:
string — The send...to statement sends the event to the specified channel. All contexts and external receivers subscribed to that channel receive the event. If there are no subscribers to the specified channel or if no receivers are listening on the specified channel then the event is discarded.
context — The send...to statement sends the event to the back of the input queue of the specified context. The event expression is evaluated and the resulting event is sent to the input queue of only the specified context.
sequence<context> — The send...to statement sends a copy of the event to the back of the input queue of each context in the specified sequence. The event expression is evaluated and the resulting event is sent to the input queue of each context in the sequence.
com.apama.Channel — The send...to statement sends the event to the specified Channel object. If the Channel object contains a string, the event is sent to the channel with that name. If the Channel object contains a context, the event is sent to that context. You cannot send an event to an empty context object.
You cannot send an event to a dictionary of contexts. However, it is a common pattern to send to a sequence generated by dictionary.values(). For example:
send x to d.values;
If the target context’s input queue is full the sending context blocks and waits for space on the queue unless doing so would cause a deadlock. See Deadlock avoidance when parallel processing.
Sent events are processed in the order they are sent. Sent events are put on the back of the input queue, behind any events already queued.
You must create the context before you send an event to the context. You cannot send an event to a context that you have declared but not created. For example, the following code causes the correlator to terminate the monitor instance:
monitor m {
context c;
action onload()
{
send A() to c;
}
}
If you send an event to a sequence of contexts and one of the contexts has not been created first then the correlator terminates the monitor instance. For details, see Sending an event to a particular context.
The send...to statement can operate on any values as well as events, provided that the any value is of a routable event type.
You cannot send an event that has a field of type action, chunk, listener, or stream. There is a runtime check if the event (or one of its members) can contain an any field; an exception is thrown if the any field contains an object of type action, chunk, listener, or stream.
The spawn statement
The spawn statement creates a copy of the currently executing monitor instance in the current context.
The spawn *action*() to *context* statement creates a copy of the currently executing monitor instance in the specified context. A monitor instance must have a reference for the specified context in order to spawn to that context.
The expression that you spawn must be of type context. The spawn *action*()to context statement spawns a new monitor instance in the specified context.
The throw statement causes an exception to be thrown. If it is not caught by a try... catch in that action or any calling actions, then the monitor instance is terminated along with any listeners it has. The syntax is:
Compound statements enclose other statements or blocks and affect how the enclosed statements are executed.
The for statement
The for statement is used to iterate over the members of a sequence and execute the enclosing statement or block once for each member.
The iteration variable is assigned a value successively obtained from each element of the sequence, starting with the first, and if the last sequence entry has not been reached, the statement that forms the loop body is executed.
The iteration variable’s type must match the type of the sequence elements.
The loop body is either a single statement or a block.
Within the loop body, the break statement can be used to cause early termination of the loop by transferring control to the next statement after the loop body. The continue statement can be used to transfer control to the end of the body, after which the sequence size is tested to determine if the last entry has been reached. If it has not, then the loop body is executed. The return statement can be used to terminate both the loop and the action that contains it.
The from statement is used to create a stream listener. A stream listener watches for items from a stream and passes output items to procedural code.
A from statement is similar to an on statement, which listens for events processed by the correlator and then executes an event listener action for each matching event or pattern. See The on statement.
You can assign the result of a from statement to a listener variable. This lets you call quit() on the stream listener.
A stream listener passes output items from a stream to procedural code. The stream, specified in the expression, can be a reference to an existing stream or a stream source template. Alternatively, it can be the stream created by an in-line stream query.
A colon and an identifier follow the expression or in-line stream query. This signifies a coassignment: when new items are available from the stream, the stream listener coassigns each output item to the specified variable.
The statement following the identifier can be a single EPL statement or a block of EPL statements. The from statement passes the output item to this statement or block and executes the statement or block once for each output item. If the output of the query is a lot that contains more than one item, and you want to execute the statement or block just once for the lot, coassign the output to a sequence. See Working with streams and stream queries, and Working with lots that contain multiple items.
The if statement
The if statement is used to conditionally execute a block of code. It checks whether a condition is true or false, that is, the result type must be boolean. The conditional statement may optionally be followed by the keyword then followed by a block of code.
If the condition result is true, the block is executed. After the body of the if has been executed, control is transferred to the next statement following the if statement.
If the condition result is false and an else clause is present, the statement or block following the else is executed. After the body of the else clause has been executed, control is transferred to the next statement following the if statement.
If the condition result is false and the else clause is not present, control is transferred to the next statement following the if statement.
The ifpresent statement is used to check if one or more values are empty. It unpacks the values into new local variables and conditionally executes a block of code.
If all of the expressions are non-empty, then their values are unpacked into new local variables (in the scope of the first block of the ifpresent statement) and the first block of code is executed. If the expression is a simple identifier (that is, it is referring to a variable or parameter) or casting an any type, then the as *identifier* part can be omitted; the new local retains the same name. An optional else block is executed if any of the expressions have an empty value.
ifpresent operates on expressions of the following types:
optional (in which case the new local variable is of the unpacked type)
chunk (ifpresent treats chunk as empty only if its value is the default value)
stream (ifpresent treats stream as empty if its value is the default value or if it has been quit)
listener (ifpresent treats listener as empty if its value is the default value or if it has been quit)
context ( ifpresent treats context as empty only if its value is the default value)
action (ifpresent treats action as empty only if its value is the default value)
any (ifpresent treats any as empty only if it has an empty value)
The on statement is used to create an event listener that looks for input events that match the pattern specified by an event condition. When a matching event is detected, the event listener fires (also referred to as triggers) and the specified event listener action is executed.
A listener assignment clause is used to obtain a reference to the event listener that is created by the on statement. One can either define a new variable of type listener or specify a reference to an existing listener variable.
Example:
listener l := on...
sequence <listener> aSequence;
aSequence[0] := on...
The event condition specifies what events are of interest. See Event expressions.
A listener action defines the processing that will be performed when a matching event is detected and the event listener fires. The listener action can be one of the following:
A statement
A block
The listener action is invoked automatically by the correlator when the event condition is satisfied. This may be:
When a matching event is detected.
If unmatched is specified in the condition, the event matches the condition, and there are no matching event listeners that do not specify the unmatched keyword.
If completed is specified in the condition, and any matching events have been completely processed by other event listeners.
The switch statement is used to conditionally execute a block of code based on the type of an any expression. Unlike the if and if... else statements, the switch statement can have a number of possible execution paths.
The switch statement names the expression as an identifier with the as keyword followed by an identifier to name the value. In each case clause block, the identifier has the same type as the case clause.
If the expression is a simple identifier (that is, it is referring to a variable or parameter), then the as Identifier part can be omitted. The new local retains the same name.
The try... catch statement is used to handle runtime exceptions.
The catch clause must specify a variable whose type is com.apama.exceptions.Exception.
You can nest try... catch statements in an action, and you can specify multiple actions in a try block and specify a try... catch statement in any number of actions.
The while statement is used to repeatedly evaluate a boolean condition and execute a block as many times as the condition result is found to be true.
The condition, whose result type must be boolean, is evaluated and if the result is true, the block is executed. Control then transfers to the top of the loop and the condition is evaluated again. When the condition result is false, control is transferred to the next statement following the while statement.
The body of the loop must be a block; it must be inside curly braces.
Within the loop body, the break statement can be used to cause early termination of the loop by transferring control to the next statement after the loop body. The continue statement can be used to transfer control to the end of the body, after which the condition will be evaluated again and the loop body executed if the condition result is true. The return statement can be used to terminate both the loop and the action that contains it.
Transfer of control statements alter the normal control path by stopping the sequential execution of statements within a block. All of them end execution of the block that contains them. After a continue statement is executed, the containing block might be executed again in a new loop iteration. The die and return statements also end the action in which they are executed.
The break statement
The break statement transfers control to the next statement following the loop (for or while statement) that encloses the break statement. A break statement can only be used within a for or while statement. Any statements between the break statement and the end of the block are not executed. For more information, see Defining loops.
The continue statement
The continue statement can be used in a block enclosed by a for or while statement to end execution of the current iteration and transfer control to the beginning of the loop. When a continue statement is executed, control is immediately transferred to the beginning of the inner most enclosing for or while statement. Any statements between the continue statement and the end of the block are not executed. For more information, see Defining loops.
The die statement
The die statement terminates the execution of a monitor. When the correlator executes a die statement, it terminates only the monitor instance that contains the die statement being executed. If the monitor instance that spawned the monitor instance being terminated is still active, that monitor instance is not affected. If that original monitor instance spawned any other monitor instances, those monitor instances are not affected. If the monitor instance being terminated defines an ondie() action, the correlator executes the ondie() action for just the monitor instance being terminated, and then terminates the monitor instance.
The return statement ends the execution of an action and control is transferred to the action’s caller, at the point following the action call (which might be in the middle of an expression). Any statements between the return statement and the end of action are not executed.
If the action does not have a returns clause, then an expression is not permitted in the return statement.
If the action has a returns clause, then an expression whose value is the action’s return value is required in the return statement. The expression type must match the type specified in the returns clause.