Asynchronous event handling in CUBA
Event handling in an asynchronous fashion oftentimes leads to an architecture that is more flexible and decoupled. In this article we will have a look at how to implement a CUBA application mostly communicating through messages.
The life before messages
To start with this whole topic, we can have a look at a sample application. We will use (once again) an ordermanagement application. A customer can have orders that might have order lines etc.
Let’s look at the following requirement:
“When a customer is created, a user account should get generated for this customer as well, so that the persons can have a look at their own orders”.
Another requirement might be
“When a customer is created, detailed information should be logged into a particular file that gets displayed on a monitor on the wall of the office”
The list might be continued, but we will leave it for now. The traditional approach in most applications (and in a CUBA app as well) is to have an implementation of the following kind:
The CustomerService gets called from the UI and will do all necessary things that have to be done when a customer is created.
So the problem with this is the coupling between the different parts of the system. When thinking about, you might notice that for the CustomerService it does not really need to know about the effects that its action - “to create a customer” - has.
If you don’t care about it, the CustomerServiceBean is going to be the God object of your system in the long run. It will violate the single responsibility priciple, because instead of changing the class for the one reason “the storate of a customer”, you will end up in a situation where changing the class for one of any reasons that are related to the customer storage is the actual situation.
Event based architectures to the rescue
Because of this, we want to have a look at one option for us to improve the design with an event based architecture. A lot has been written about this stuff and it is already been around for a very long time.
There are a lot of examples of event based architectures in totally different granularities and time frames. Starting with something like UI events in visual basic, over to event driven architectures in a SOA context or with something more modern: reactive programming.
One major difference between the example above and the event based approach is the removal of the dependencies between the
CustomerService as the caller to the
OfficeWallService as the callee goes away. Instead, both services depend on the “contract”:
With this approach we are closer to fulfilling the single responsiblity principle. Additionally, testing way gets easier, because the need to mock the different collaborators will go down dramatically. We will close the theoretical (nevertheless important) part and have a look at how we can do this sort of things in CUBA.
Application events in CUBA
CUBA already has a lot of events that are created from the framework. On the persistence layer, there is the feature of Entity Listeners, where you can create classes that react to certain actions during the persistence process. Then, there are a lot of UI-related events that can be catched and act on your UI needs. But these are all framework –> application events.
What we want take a look at instead is the possibility to create custom application events that can be send and received within your CUBA application. This is more like application –> application.
Spring application events
Luckily, CUBA as a meta-framework is nested on top of very major and feature-rich frameworks that themselfs have a lot of stuff to offer. One of these frameworks is Spring. The Spring framework has an event-mechanism baked in from the very beginning (which is a fairly long time actually). The main purposes for an application developer is to get notified when certain technical things in the running application occur (like
Nevertheless, custom application events are supported as well. In fact, in recent versions Spring improved the creation and handling of these events even futher (see the docs for more information).
So, basically it boils down to the three major parts of the actual event object, the event sender and the event receiver. Let’s take a closer look at those.
1. Event object
You have to create a PO(J|G)O class that represents an event like this:
2. Event publisher
Having that, we need a publisher of an event. Here’s an example of a service that does that:
ApplicationEventProducerService is responsible for pushing an event into the atmosphere, so that other parts of the system can see it flying and catch it if they want. The service (which acts as an example for your business logic that creates events) uses
org.springframework.context.ApplicationEventPublisher to fulfil its purpose. This bean is the point in the Spring framework which lets us publish application events. As you might have noticed, the method
publishEvent can take any object to publish, it does not require a certain class type to do that (anymore…).
3. Event receiver
The last part is the receiving part - which is called EventListener. An event listener qualifies itself by having a particular method annotation that should handle a certain event. Here’s an example:
CustomerUserCreator creates a user account in our application when a Customer has been created. The
handleCustomerCreated(CustomerCreatedEvent event) method is the actual handler of the event due to the
The parameter in the method definition qualifies which event is handled in this method. The qualification is normally done on a class level (through the type of the parameter).
If you want to define even further for what events of a particular times this method should get called, you can either do a big
if-statement within the method, or you can set the
condition attribute like this:
@EventListener(condition = "someCondition") (while
someCondition has to be a valid SpEL expression).
One important point is, that in order to have multiple actions in your application that have to be executed when a customer is created, you just have to create another event listener bean. So, going back to our example from above, we would have another bean called
OfficeWallService that would look like this:
With this, everything is in place to create an event based application. There are many more options to consider, but as I will not mirror the Spring docs, I will leave it at this point. You can read more about this in the Spring docs. Another very good article is the following: Better application events in Spring Framework 4.2.
Sync or Async - depending on your needs
As you might have seen, I used the
@Async annotation in the handlers. The reason for that is, that I want to execute the stuff that happens when is user is created independently of the thread where it is created. There are some situations where you want a synchronous execution, but in this example I want the execution in another thread.
To make that happen, one thing is to add the above mentioned annotation. Another thing is, that there needs to be a configuration in the spring.xml that defines futher information about the asynchronous execution.
The “scheduler” reference is an existing CUBA bean (CubaThreadPoolTaskScheduler) which is an implementation of Springs ThreadPoolTaskScheduler.
task:annotation-driven tag defines that Spring will scan the classpath for beans for the corresponding annotations.
CUBA application event example
Let’s have a look at how we can create a running example of this approach in CUBA. The complete example can be found on github: mariodavid/cuba-example-application-events.
The example does the following things:
First, the user creates a customer through the standard editor for the customer entity. After that, the registered CustomerCreatedEntityListener which is a normal CUBA entity listener, creates a CustomerCreatedEvent through the above mentioned ApplicationEventProducerService. The events get fired aynchronously, so the DB transaction of storing the customer is finished independently of everything else that might happen with the CustomerCreatedEvent.
The event is catched by multiple event handlers. The CustomerUserCreator which creates a corresponding user for the customer so that the customer is capable of login to the system and see its own orders. The next event handler is the CustomerCreatedUserNotificator, which creates a notification for every user that is subscribed on these events. This leads to the UI that updates itself for the corresponding user and shows a new notification message. As the last part has some more business logic, let’s have a closer look at it.
Users can subscribe to application events
One example of what can be done as a useful event handler is to notify users through the UI that certain things happend in the application.
This example of notifiing users does not have a direct correlation between itself and the fact that we use application events. Although they play nicely together, these things are orthogonal. We could implement a notification system without using an event based architecture, as well as using an event based architecture without showing these notifications on the UI.
In order to allow the user to subscribe to certain events, we have to create an entity that stores these subscriptions. The user can define the class of the nofitication and a condition that has to be true in order to get a notification.
The CustomerCreatedUserNotificator will pick up all subscriptions of every user where the event fulfils the entity class criteria as well as the condition. For each of those subscriptions it will create a new Notification entity for the corresponding user. It will additionally link the entity where the notification comes from in the entity, so that the user can hit a link to go directly to this entity instance.
After the notifications have been persisted in the database, we can have a look at how to get the information in the users UI.
Update the UI through polling
The MainWindow of the app uses the recently added SideMenu component. I decided to do a polling of the database through the trigger option we have in CUBA for UI screens. The timer calls updateCounters which will ask the database for notifications for the current user, that are unread and update the badge text of the menu item accordingly.
The resulting user interface looks like this:
This might be a reasonable solution for the problem. We could have done it differently, e.g. when there are a lot of users and the polling will result in a large amount of load for the database. In this case, it might be worth finding the corresponding sessions and set a session attribute with the counter. But I think that this shows the basic functionality. Everything else is up to the specific application.
I hope you got a basic idea of what application events can bring on the table and if they are suitable for your needs. If you have any questions or comments - please let me know.