Using Events to Speak to the Outside World
Introduction
Using events in Erlang is a great way to pass on information about what is going on in a running system. This might be sending alerts to some external system, sending an email in the middle of the night, or just logging some interesting information for later.
The event module in xqerl allows XQuery authors to send events asynchronously to the Erlang environment for processing there.
All that’s needed to process events from xqerl is an Erlang event handler that has been added to the locally registered event manager, xqerl_event_man
.
The event module is rather simple as it only has one function (but that’s all we need).
Below is an example of using events in the simplest form, but should also give you an idea of what is possible in more complex cases.
Steps
- Write an Erlang module to handle events.
- Write an XQuery to produce events.
- Run it!
The Event Handler
Writing and using an event handler in Erlang is quite simple and is explained in more depth here.
Below is a simple handler that will handle events by printing them out to the console:
-module(xq_event_h).
-behaviour(gen_event).
-export([init/1, handle_event/2, terminate/2]).
init(_Args) ->
{ok, []}.
handle_event({xqerl_event, <<"square">>, A, B}, State) ->
io:format("***Got Square*** ~p^2 = ~p~n", [A, B]),
{ok, State};
handle_event({xqerl_event, <<"even">>, A}, State) ->
io:format("***Got Even*** ~p~n", [A]),
{ok, State};
handle_event(_Event, State) ->
{ok, State}.
terminate(_Args, _State) ->
ok.
Producing events in XQuery
Sending out events is done with the statically bound functions event:notify#1
, event:notify#2
, event:notify#3
or event:notify#4
.
These functions will send a tuple in the form {xqerl_event, Arg1, ...}
.
So, let’s make some events! Below is a simple XQuery to return numbers that are square (along with other busy work). It will send an event for each square, and another event for each of those that is an even number:
declare function local:is-square($a, $b)
{
if (($a * $a) eq $b) then
(
event:notify('square', $a, $b),
true()
)
else
false()
};
declare function local:is-even($a)
{
if (($a mod 2) eq 0) then
(
event:notify('even', $a),
$a
)
else
$a
};
for $i in 1 to 100
, $j in 1 to 10000
where
local:is-square($i, $j)
return
$j => local:is-even()
Taking it for a spin
Assuming:
- The Erlang module here is saved as /tmp/data/xqerl/xq_event_h.erl
- the XQuery module is saved as /tmp/data/xqerl/squares.xq
- xqerl is running
- you have an Erlang shell open on the node where xqerl is running
- Compile and load the Erlang module, add the handler
c("/tmp/data/xqerl/xq_event_h.erl").
gen_event:add_handler(xqerl_event_man, xq_event_h, []).
- Compile the main XQuery
Squares = xqerl:compile("/tmp/data/xqerl/squares.xq").
- Run the XQuery
xqerl:run(Squares).
You should see a bunch of lines reported to the console (from the event handler) and the return value of the query:
***Got Square*** 1^2 = 1
***Got Square*** 2^2 = 4
***Got Even*** 4
***Got Square*** 3^2 = 9
***Got Square*** 4^2 = 16
***Got Even*** 16
...
***Got Even*** 9216
***Got Square*** 97^2 = 9409
***Got Square*** 98^2 = 9604
***Got Even*** 9604
***Got Square*** 99^2 = 9801
***Got Square*** 100^2 = 10000
***Got Even*** 10000
[1,4,9,16,25,36,49,64,81,100,121,144,169,196,225,256,289,
324,361,400,441,484,529,576,625,676,729,784,841|...]
Bam!! Producing events in XQuery and handling them in Erlang. Enjoy!