Application Events

Application Events

OpenMRS supports application level events via Spring Events. As of OpenMRS Core 2.9.x we introduced an improved events framework with support for the outbox pattern.

Publishing events

Events can be published via EventPublisher.publishEvent. EventPublisher is a Spring bean and can be easily injected into other beans. You can also inject Spring’s ApplicationEventPublisher with EventPublisher being injected under the hood.

Events can be of any object type. We provide some generic event types and interfaces to make it easier for modules to communicate via events sharing the same event classes:

  1. EntityEvent<T>, where <T> is entity type, e.g. EntityEvent<Person> - allows to publish an event with a related entity

  2. AggregatedEntityEvent - allows to group events happening in the same transaction and be published as a single event, see TransactionalEventAggregator on how to aggregate events

  3. CDCEvent - special kind of event published by a CDC engine, which allows to listen to any committed changes in DB

  4. OutboxableEvent - an interface indicating an event can be put in the outbox table and is JSON serialisable. Implemented by e.g. EntityEvent and AggregatedEntityEvent.

  5. BrokerIncomingEvent<T> / BrokerOutgoingEvent<T> - classes for events that should be published or coming from a configured broker.

In OpenMRS core we emit events for OpenmrsService save…, retire…, unretire…, void…, unvoid…, purge… methods (before executing methods) and DB level events upon flushing session to DB. See ...ServiceEvent<T> and ...DBEvent<T>. These events take part in a DB transaction and can influence its outcome… modify data or even cause a rollback when throwing an exception.

Data synchronisation

If your use case is to have a proper data synchronisation mechanism, you should be using CDCEvent<T>, which does not participate in a transaction (is emitted asynchronously for committed data), but you are guaranteed not to miss any DB change including raw SQL or schema changes.

In order to have CDCEvents in your system you need to provide a publisher. Such events may come from a module or a broker. An example of CDCEvent publisher is https://github.com/openmrs/openmrs-module-debezium.

External events

If you need to publish or subscribe events to external systems you shall be using BrokerIncomingEvent<T> / BrokerOutgoingEvent<T> events. OpenMRS core itself does not publish or consumes such events. You need to install a module that connects to an external broker or runs an embedded one. An example of a broker module is https://github.com/openmrs/openmrs-module-artemis. You can configure it to have an embedded Artemis (low-end hardware instances without replication) or connect to an external broker compatible with JMS (recommended in cluster environments).

Listening to events

Events can be listened to with the following annotated methods on Spring beans:

  1. @EventListener - synchronous events handled within the same thread and transaction as the publisher. Note that if a transaction is rolled-back, there’s no way to cancel events emitted during a transaction. The listener needs to be implemented in a way that accounts for that.

  2. @TransactionalEventListener - synchronous events handled within the same thread and transaction as the publisher in the before commit phase or after transaction in the after commit phase. If a transaction is rolled-back, the event is not emitted in the after commit phase. However, if any listener fails in the after commit phase, there’s no way to roll back a transaction.

  3. @OutboxEventListener - events persisted in the DB within the same thread and transaction as the publisher, but handled asynchronously in a different thread. If a publisher’s transaction is rolled-back the events are not persisted in the outbox and never get emitted. If any listener fails, the event will be re-tried until successful. This mechanism provides transactional guarantee for events delivery and is suitable for publishing events to e.g. external brokers. Please note that only events implementing OutboxableEvent can be listened to with @OutboxEventListener. They must be json serialisable.

  4. @BrokerEventListener - events coming from a broker.

Please see TestOutboxEventListener for examples on how to listen to events and TestEventPublisherService on how to publish events from your Spring beans.

In order to listen to events you need to implement a module, which creates listeners and make sure you have dedicated event publishers, if you intend to listen to CDCEvent or BrokerIncomingEvent.

Camel EIP

In order to transform data and integrate with external systems we endorse Camel. It is provided as an OpenMRS module giving access to OpenMRS API or it can be deployed as a standalone middleware.

Please see https://github.com/openmrs/openmrs-module-camel for state of the art module with embedded Camel and Hawtio for monitoring. You can develop your routes and add components in custom modules.

If you are looking for a standalone middleware, please see https://github.com/openmrs/openmrs-eip