Monday, 29 June 2015

CDI event when transaction is finished

Introduction

CDI events are a very handy way of loosely coupling event driven code. At some point in your code you raise the event, and in some other area, you execute some functionality when this event occurs.
And there is no relation or code dependency between the 2 areas except the CDI event payload class.

For example

public class DataUpdatedEvent {
}

@Inject
private Event<DataUpdatedEvent> dataUpdatedEvent;
public void doSomething() {
    dataUpdatedEvent.fire(new DataUpdatedEvent());
}

public void onDataUpdate(@Observes DataUpdatedEvent dataUpdatedEvent) {
    // React on event
}

Synchronous

By default, the code is executed synchronous. But there are some other use case scenarios already possible without the need for the new, under development, CDI 2.0 spec which will allow also asynchronous execution.

A scenario that I used recently was to have some Cache mechanism available for data stored in a database.  I know there are a lot of full blown solutions like Infinispan, EHCache or TerraCotta to name a few. But I wanted to have a simple Map instance which also contains the data from the database table which changes very rarely.

When you place the event fire within a EJB stateless method, you don't have the required behaviour.


@Stateless
public class DataBoundary {

    @PersistenceContext
    private EntityManager em;
    @Inject
    private Event<DataUpdatedEvent> dataUpdatedEvent;
    public void saveData(Data data) {
        // Obviously, some more clever things to do
        em.persist(data);
        dataUpdatedEvent.fire(new DataUpdatedEvent());
    }
}


Since the execution of the CDI event mechanism is synchronous, the reading of the database table is done before the new data is committed and thus we are reading the old situation.

@Observer(during)

The Observer annotation has an element called during, which you can use to define when the observer method (the one with the @Observer annotation parameter) is executed.
By default, as already mentioned, it is immediately after the rase of the event. But you can also specify here the value TransactionPhase.AFTER_SUCCESS

public void onDataUpdate(@Observes(during = TransactionPhase.AFTER_SUCCESS) DataUpdatedEvent dataUpdatedEvent) {
    // Load the cache again
}
When the event is fired within a transaction, the observer method is executed after the transaction is committed successful to the database.

That is what we need in our simplistic caching example. The code will run after all the data is in the database table and thus the cache will be using the latest data.


Have fun

No comments:

Post a Comment