Events are a great way to manage change in a complex software made up of many components. When you have decoupled software entities that need to be notified about changes, it's easier the represent the change itself explicitly, as an event entity, so that producers (originators) and consumers (receivers) of the event don't have to know about each other. This leads to fewer connections in the graph of dependencies between the software components comprising the system. This blog post documents the event framework in the Sharegov CiRM platform. This is a first draft and the framework is expected to be evolve of course.
Within the context of software, events essentially model data changes at various locations. So an event framework needs to define how events are represented, what kinds of data changes are supported, what kind of information would an event entity contain as well as the gluing infrastructure that allows components to publish events and others to consume them.
Events Ontology Model
The various types of events are modelled in the ontology under Event->SoftwareEventType. A seemingly natural way to model events in OWL is for each event occurrence to be an individual and the various event types to be described via punned classes. Since we don't record event occurrences anywhere, we don't really need to represent events as OWL individuals. So we model the types of events that can occur as OWL individuals with properties that govern their behavior to an extent and we categorize those event types into a few broad categories. One one hand we have events processed entirely at the client and on the other we have entity related events that can be processed on the server or result in server<->client communication. The client only events help in connecting otherwise decoupled client-side components and they are described lastly. The entity (i.w. OWL individuals) events are more thoroughly formalized and they are described next.
Server-Side Event Management
Event handling on the server-side is implemented by the classes in the org.sharegov.cirm.event package. The most common types of events are those that reflect a change in an entity. Such events are modelled with the SoftwareEventType->EntityChangeEvent class. Each individual belonging to that class models how a change of some kind of entity is dealt with. The "kind" of entity is specified through a DL query expression. The following properties comprise that model:
- hasChangeType : any suitable individual that represents the type of change, normally an instance of Activty.
- hasImplementation: the fully qualified class name of an org.sharegov.cirm.event.EventTrigger implementation that is invoked to process this event occurrence. There can multiple such properties and each will be invoked in an unspecified order.
- hasQueryExpression: A Description Logics (DL) query expression that specifies for what types of individuals this event will be triggered. The query expression is evaluated to obtain the set of all sub-classes. Then whenever an individual change is submitted for query processing, it is checked whether it belongs to one of the sub-classes as defined by that expression. Multiple hasQueryExpression properties are allowed.
Events are processed on the server by an org.sharegov.cirm.EventDispatcher singleton. All events defined in the ontology are loaded upon startup and the DL query expressions evaluated to create a map of OWLClass->EventTrigger. That singleton is accessed by the various services to explicitly publish events via one of the overloaded EventDispatcher.dispatch methods.
Server to Client Events
As a lot of application logic resides in the browser, it is wise to load the relevant data beforehand in order to minimize network traffic and improve response time. This of course poses the problem of updates on the server which invalidate the data at the client. Synchronization of such updates happens through server->client events, the so called "server push". The most efficient way to implement a server push is for the client to do what is refered to as long polling (see http://en.wikipedia.org/wiki/Push_technology) - open a connection with the server and let it timeout if the server has nothing to say, then open a new connection right away again. However, the Restlet framework we are currently using doesn't support this mode, so we had to revert to the traditional style of polling where the server returns right away if there are no events to deliver and the client polls again after a certain interval. In order to to decide which event a server should send to a particular client, the client send the timestamp of the last time it polled. The server then responds with all events timestamped with a later timestamp. Because the comparison is only relative to the client, there aren't any clock synchronization issues to worry about.
The queue of events sent to clients is implemented by the org.sharegov.cirm.event.ClientPushQueue class. Events are added to that queue by a org.sharegov.cirm.event.PushToClientEventTrigger associated to the event via the hasImplementation property in the event descriptor.
At the client, polling and event dispatching is managed by cirm.events object (see EventManager function inside that cirm.js library). To register for an event coming from the server call:
Call cirm.events.unbind to unregister a listener. The cirm.events also exposes startPolling, stopPolling methods and the ability to explicitly trigger an event via cirm.trigger.
Such events happen entirely on the client (browser). They are triggered by a change of some value on the client and processed by some other component on the same client. Such events are categorized under the ClientSideEventType class. One case of client-side events is connecting model changes of otherwise disconnected and independent components. When two components are completely decoupled, yet a part of their models represent the same underlying real-world entity, we want a change in one model to be reflect into the other model. When we have such a model that can receive its value from another model through events, we express declaratively in the following way:
- Declare the event individual under ClientSideEventType class.
- Declare a data source individual under the EventBasedDataSource with two properties:
- providedBy pointing to the event created in step (1)
- hasPropertyName specifying the name of the property in the runtime event data object that contains the model value.
- Add a hasDataSource property to the model individual that must be automatically updated when that event is triggered.