The event mechanism is used to inform objects and/or services, called event listeners, that something has happened in another object or service, called event source, so that appropriate actions can be taken in the event listeners. The information about 'something has happened' is contained in an object, called event data, or simply event. The event source's act of informing event listeners about an event is called generating an event (also called firing an event). The action taken in an event listener is defined through a so called event handler
A real life example would be a news publisher that sends a message to all subscribers, whenever something important has happened. In this example, the event source is the news publisher, the event listeners are the subscribers, and the event data is the message that is send from the publisher to all the subscribers
Each object and each service can generate 0, one or more different types of events. The list of all event types that are generated must be declared in the event source. At runtime, 0 1 or more event listeners can exist for each event type in an event source. Each event listener must define one event handler for each event it listens to. One event handler can be used to handle several types of events coming from different event sources, as long as the types of all those events are compatible to the type of event that is handled by the event handler. For example, one event handler could listen to left and right mouse clicks coming from buttons and text input fields.
The following figure shows possible interactions:
There are different instructions used to declare, generate, and handle events.
Each event that can be generated from an object or service must be declared. Events generated from an object are declared in the object's type and events generated from a service are declared in the service.
Each event is identified by a unique event identifier. An event identifier is a prefixed identifier starting with ev_ as prefix. For more information about prefixed identifiers see the section called “Prefixed identifier”.
The type of the event data must also be declared and it must be a child type of:
li_obix.li_events.ty_object_event if the event source is an objectli_obix.li_events.ty_service_event if the event source is a service.Both above types inherit from li_obix.li_events.ty_event, which is the root type of all events.
The syntax to declare an event is:
| Production | Syntax | Links |
|---|---|---|
event |
| the section called “Event” |
example:
event coffee_ready type:coffee_ready_event end
An event is generated with the generate event instruction which is defined as:
| Production | Syntax | Links |
|---|---|---|
generate_event_instruction | "generate" "event" event_id "from" expression | the section called “generate event instruction” |
expression defines the event object (event data) that will be generated
example:
generate event coffee_ready from fa_coffee_ready_event.co_create ( kind = "Espresso" )
For more information see: the section called “generate event instruction”
To start listening for an event (i.e. to 'register as a listener' or to 'subscribe to an event'), the following syntax is used:
| Production | Syntax | Links |
|---|---|---|
on_event_instruction | "on" "event" event_id_list ( "in" event_source ) ? "execute" event_handler ( "handler" ":" variable_id ) ? | the section called “on event instruction” |
example:
on event coffee_ready in coffee_machine execute drink_coffee
For more information see: the section called “on event instruction”
To stop listening for an event (i.e. to 'unregister as a listener' or to 'unsubscribe from an event'), the following syntax is used:
| Production | Syntax | Links |
|---|---|---|
stop_event_handler_instruction | "stop" "event" "handler" expression | the section called “stop event handler instruction” |
expression must evaluate to the event handler object that has been created with the on event instruction.
example:
stop event handler drink_coffee_event_handler
For more information see: the section called “stop event handler instruction”
Example 6.10. Events in a television delivery stack
![]() | Note |
|---|---|
| The following example uses features not covered yet (e.g. type inheritance and generic types). No need to worry if some details are not yet explained; they can be considered as a sneak preview of techniques explained in subsequent chapters. |
Suppose we have a first-in-first-out stack of televisions to be delivered. A new television to be delivered can be added to the stack with a push command, and the least recently added television can be removed from the stack for delivery with a pop command.
First we have to define type television:
type television default_factory:yes attribute model type:string end attribute customer type:string end // other attributes omitted for brevity end
Type television_stack can be written as:
type television_stack_1
attribute is_empty type:yes_no kind:readonly_variable end
command push
in television type:television end
end
command pop
in_check check: not i_object_.a_is_empty end
out television type:television end
end
end typeNow suppose that other objects or services want to be informed whenever a television is pushed or popped. For example, it might be necessary to log each action in a database, or to send an email whenever a television is removed from the stack for delivery. This can be done with events.
First we have to define two types for the two events, television_pushed and television_popped. We assume that both events hold the same simple data: the television that has been pushed or popped, and the time the event occurred. Hence, we first define a parent type that inherits from type object_event:
type television_pushed_or_popped_event inherit object_event end attribute television type:television end attribute time type:date_time end end
![]() | Note |
|---|---|
Type type object_event inherit event end end type And type type event attribute source type:any_type end // attribute time type:date_time voidable:yes end end type |
The two event types can now be written using type inheritance again:
type television_pushed_event default_factory:yes inherit television_pushed_or_popped_event end end
type television_popped_event default_factory:yes inherit television_pushed_or_popped_event end end
Both events are generated from objects of type television_stack, so we have to declare that by adding two event declaration instructions at the end of type television_stack:
type television_stack
attribute is_empty type:yes_no kind:readonly_variable end
command push
in television type:television end
end
command pop
in_check check: not i_object_.a_is_empty end
out television type:television end
end
event television_pushed type: television_pushed_event end
event television_popped type: television_popped_event end
end typeWhat remains to be done for our event source is to write an implementation for type television_stack. A possible solution would be:
factory television_stack type: television_stack
// this private attribute contains the list of all pending televisions in the stack
attribute television_list type:!mutable_indexed_list<television> default: !mutable_indexed_list_factory<television>.co_create private:yes end
attribute is_empty
get
script
// simply return attribute 'a_is_empty' of private attribute 'a_television_list'
o_is_empty = a_television_list.a_is_empty
end
end
end
command push
script
// append new television to the list of pending televisions
a_television_list.co_append ( i_television )
// generate a 'television_pushed' event
generate event television_pushed from fa_television_pushed_event.co_create ( &
source = this &
television = i_television &
time = se_date_time.a_current_date_time )
end
end
command pop
script
// take least recently television from the list of pending televisions
o_television = a_television_list.co_first_item
a_television_list.co_remove_first
// generate a 'television_popped' event
generate event television_popped from fa_television_popped_event.co_create ( &
source = this &
television = o_television &
time = se_date_time.a_current_date_time )
end
end
creator create kind:in_all end
end factory
That's it for the event source. Let's now create an event listener. The following service listens to events in a television_stack object. To keep the example simple we will just display a message on the system console, each time an event is generated.
service television_stack_logger
command start_logging
in television_stack type: television_stack end
script
// register two event handlers for input argument 'television_stack'
// each time a 'television_pushed' event is fired in 'i_television_stack', execute command 'new_pushed_event'
on event television_pushed in i_television_stack execute script new_pushed_event
// each time a 'television_popped' event is fired in 'i_television_stack', execute command 'new_popped_event'
on event television_popped in i_television_stack execute script new_popped_event
end
end
command_list private:yes // all following commands are private
command new_pushed_event
in event type: television_pushed_event end
script
co_log_event ( &
television = i_event.a_television &
action = "pushed" &
time = i_event.a_time )
end
end
command new_popped_event
in event type: television_popped_event end
script
co_log_event ( &
television = i_event.a_television &
action = "popped" &
time = i_event.a_time )
end
end
command log_event
in television type: television end
in action type: string end
in time type: date_time end
script
se_console.co_message ( "Television " & i_television.a_model & &
" of customer " & i_television.a_customer & " has been " & i_action & &
" on " & i_time.co_to_string )
end
end
end command_list
end service
We are now ready to use our tiny application. Let's create a television_stack, ask service television_stack_logger to listen for events in television_stack, push and pop some televisions and see what is displayed on the system console:
service television_stack_test
command test
script
// create a 'television_stack'
var television_stack television_stack = fa_television_stack.co_create
// ask 'se_television_stack_logger' to log events
se_television_stack_logger.co_start_logging ( v_television_stack )
// push a new television to be delivered
v_television_stack.co_push ( fa_television.co_create ( &
model = "Nice telli" &
customer = "Nicolas Paganini" ) )
// wait 5 seconds
se_time.co_wait_seconds ( 5 )
// push a second television
v_television_stack.co_push ( fa_television.co_create ( &
model = "ABC 123" &
customer = "Wolfgang" ) )
// wait 7 seconds
se_time.co_wait_seconds ( 7 )
// pop television
var television popped_television = v_television_stack.co_pop
end
end
end
Executing the above command will display the following on the system console:
Television Nice telli of customer Nicolas Paganini has been pushed on Aug 10, 2007 1:39:22 PM Television ABC 123 of customer Wolfgang has been pushed on Aug 10, 2007 1:39:27PM Television Nice telli of customer Nicolas Paganini has been popped on Aug 10, 2007 1:39:34 PM
![]() | Note |
|---|---|
It is of course legitimate to ask why the same result couldn't be achieved by simply displaying a message in commands For example, suppose that besides logging each |