Event infrastructure

Event infrastructure

Overview

Architecture

The IPF event infrastructure defines how to manage and handle system and application events inside IPF applications. An implementation of this infrastructure is provided by the platform-camel-event component. Examples of domain-neutral events (from an IPF application viewpoint) are message arrived, message successfully processed or message delivery failed, to mention a few. Depending on the application domain there are additional domain-specific events like patient data updated or discharge letter created. Events usually require to trigger some subsequent actions e.g. storing an incoming message after a message arrived event or writing an entry in an error log file after a message delivery failed event. Triggering domain-specific actions is usually coded in the form of IPF route definitions. Domain-neutral events are usually processed by event consumers that communicate with event publishers via resources (event channels) created via the platform-camel-event component.

To communicate events, publisher components add event messages to event channels via channel adapters. For creation of events inside route definitions platform-camel-event provides a domain-specific language (DSL). Before event messages are send to event channels they must be transformed to fit the needs of the channel.

The platform-camel-event component provides event channels for asynchronous and synchronous communication. This is important because some actions triggered by events must be executed synchronously such as storing inbound messages in a database. Event handlers receive event messages via event channels. Some event handlers trigger actions in backend systems or write log files while others may analyse event streams for occurences of certain patterns and trigger complex events if such patterns exist (this is done by complex event processing (CEP) engines). It is up to the application which event handlers to configure. Support classes for implementing them are provided by the IPF.

Terminology

Event
An event can be anything that occurs in a system (state change, action ...). If the system wants to notify observers about that event it creates an event object, wraps it into an event message and sends it to an event channel (see below).

Event (notification) object
An instance of a class that represents the state change or action used to transfer information relating to an event. It is quite common to use the term "event" to refer to the (notification) object as well, e.g. "A sends an event to B". It would be more precise to say "A sends an event (notification) object to B". This document uses both terms freely.

Event Property
A single attribute of an event object.

Event Meta Data
A set of attributes that are relevant to the event infrastructure. The meta data is independent of the type of event that occurred.

Event notification
The process of informing interested parties about an event.

Event Source
A component that sends event objects.

Event Handler
A component that processes event objects.

Event Filter
A component that filters events before they are passed on to an event handler.

Event Channel
A resource that decouples handlers and sources. Event objects are send to a channel, handlers subscribe to the channel to receive the event objects. How the objects are distributed is controlled by the channel. This includes allocations of physical or logical resources (connections), quality of service aspects, etc.

Event Channel Adapter
A component that understands details of the actual channel implementation and adapts the channel to the general contract defined by the infrastructure.

Event Message
The data related to an event that is actually distributed via a messaging system.

Topic
Topics are used to categorize events by means other than their type. Publishers can specify the topic of the event and handlers can subscribe to only event of a specific topic.

Usage summary

Here is a brief example of how an application might implement event objects, event sources and event handlers and how these are used in subscriptions and publishing:

Event engine config (Spring)

    <bean id="eventEngine" class="org.openehealth.ipf.platform.camel.event.AutoDiscoveryEventEngine"/>

Event implementation (Java)

public class MyEventObject extends EventObject {
    /** Serial version UID */
    private static final long serialVersionUID = -6112721595017049322L;
    private final String prop;

    public MyEventImpl1(String prop) {
        super();
        this.prop = prop;
    }

    public String getProp() {
        return prop;
    }
}

Event source and publishing (Groovy)

    from('...')
        ...
        publish { new MyEventObject('hello world') }.toTopic('myTopic')
        ...

Event handler (Groovy)

class MyEventHandler implements EventHandler {
    void handle(EventObject eventObject) {
        // react on event
    }
}

Event filter (Groovy)

public class MyEventFilter implements EventFilter {
    public boolean accepts(EventObject eventObject) {
        // return true for events that must be passed to the handler
    }
}

Handler subscription (Spring)

    <bean id="myEventHandler" class="MyEventHandler" />
    
    <bean id="myEventFilter" class="MyEventFilter" />

    <bean id="mySubscription" class="org.openehealth.ipf.commons.event.Subscription">
        <property name="handler" ref="myEventHandler" />
        <property name="filter" ref="myEventFilter" />
        <property name="topic" value="myTopic" />
    </bean>

Details

Event Engine

The engine serves as a registry of event handlers and channels. If an event object is published via an engine, it will distribute it to the event handlers by using the configured channels. By default the engine has a single channel to provide synchronous delivery within the same Java VM. Although subscriptions of handlers can be performed directly via the API of the engine, it is easier to use the Spring application context. The AutoDiscoveryEventEngine automatically subscribes any Subcription beans that it find within the context. Other than that it behaves identical to its base class, the standard EventEngine.

Events

Events (or more precisely event objects) should contain:

  • Domain-neutral meta data: Data provided by the IPF. This data can be for internal use (i.e. it should only be visible to the IPF or specific channel adapter implementations) or it can be of general interest
  • Domain-specific payload data: Data provided by the sender, not interpreted by the infrastructure and usually not used to route or filter an event message

Domain-neutral meta data is stored and retrieved via the base class of all custom event objects (EventObject). The domain-specific data is set by the event publisher.

The meta data contained in EventObject is defined by the IPF event infrastructure and the channel adapters. Metadata can be set and retrieved via a key. The IPF defines various keys of which some might also be of interest to event handlers. All of these are contained in the enum EventObject.MetaDataKeys.

The following meta data is public:

  • the time stamp of the creation of the event
  • the event object ID
  • the topic

The engine creates a unique ID for each event object that is published. This ID is created using a random UUID.

Event sources

Event sources publish events by using the IPF event infrastructure. The different event source types require different support for publishing:

  • via DSL elements
  • directly via the IPF event API

Event publishing via the DSL

Route publishing via the DSL works via the publish processor that is provided by the IPF event infrastructure. In Groovy:

from('...')
    ...
    .publish { Exchange exchange ->
        // Create and configure the event
    }
    .toTopic('topicName')
    ...

The publish processor calls the provided closure when an exchange is processed. The closure creates and configures the event object with information about the event. The processor adds the meta data upon returning the new event object. Additional delivery aspects can be configured via the publish processor as well (e.g. synchronous vs. asynchronous delivery). The optional toTopic DSL element of the publish processor specifies the topic that the event object is distributed with. If not specified it default to "default".

It is important that the publish processor does not use an EventObject as its parameter. Such an EventObject would be a single instance created when the route is build. In general this would not be thread-safe. Therefore, the closure must return a new instance of an event object every time it is called.

Event publishing via API

The IPF also offers an API (in the form of the EventEngine) to create events and publish them outside of routes. E.g. to publish a custom event with synchronous delivery the following call is used:

eventEngine.publish(topic, new MyEvent("hello world"), true);

Event handlers and filters

Event handlers and filters are simple beans, that implement the interfaces EventHandler and EventFilter:

public class MyEventHandler implements EventHandler {
    public void handle(EventObject eventObject) {
        // handle events
    }
}

public class MyEventFilter implements EventFilter {
    public boolean accepts(EventObject eventObject) {
        // filter events
    }
}

The Spring beans are used to subscribe with the event engine. Filter and topic are optional. Without a filter, the handler receives all event objects send to the topic. The topic default to "default" if it is not specified.

    <bean id="myEventHandler" class="MyEventHandler" />
    
    <bean id="myEventFilter" class="MyEventFilter" />

    <bean id="mySubscription" class="org.openehealth.ipf.commons.event.Subscription">
        <property name="handler" ref="myEventHandler" />
        <property name="filter" ref="myEventFilter" />
        <property name="topic" value="myTopic" />
    </bean>

To distribute an event object the engine calls the filter first. Only if the accept method returns true, the handler will be called. Event handlers are always passive components, i.e. they do not perform any kind of polling. If a specific event channel requires polling, the event engine or the channel adapters provide this feature.

Event channels and adapters

Event channels are accessed by the IPF event infrastructure via channel adapters. Adapters address specific channel implementations, e.g. they transform a message into a format required by the concrete channel implementation or they accept messages via polling or callbacks. They also provide quality and delivery aspects, such as reliability via persistence and ordering, synchronous/asynchronous behavior and queuing. Of course these aspects might be provided by the channel implementation itself and the adapter simply reuses these mechanisms.

It is important to note that event channels are usually only addressed internally by the event engine. Publishing and handling of event objects does not require knowledge of event channels and their adapters.

Using Camel routes as channels

Camel offers a simple way to address various messaging systems. With the IPF event infrastructure it is possible to use two Camel routes as the basis of a channel adapter. These routes define how to send and receive events. E.g.:

        // Sender route
        from("direct:send")
            .to("jms:FOO.BAR");

        // Receiver route
        from("jms:FOO.BAR")
            .to("bean:adapter?method=distributeToHandlers");

The sender route receives an exchange that contains the event object in its input body. Similarly, it is expected that the receiver route sends the event object in the input body to the bean called "adapter". The above routes only work if the event object is serializable. However, it is possible to transform the event into a format compatible with the Camel JMS endpoint - in the IPF terminology this done via a transmogrifier.

The IPF event infrastructure provides a simple channel adapter stub that can be configured for usage with these two routes. The stub implements the adapter interface and sends and receives the exchanges.

Modularization

In general the event infrastructure is neither tied to a single Java VM instance, nor is it strictly Java-based. Therefore, event object implementations are required to be available for all components involved in the system. The following diagram shows the intended usage of the event engine as a decentralized component, i.e. it is provided as a simple library and not as a service. The library requires access to the event implementations used by the application.

Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.