CDI Events in Action: DQL Explains in the JNoSQL Driver

Fri Aug 29 14:40:12 EDT 2025

Whenever I want to explain what CDI is to somebody who is unfamiliar with it, I start with "managed beans but better". It definitely is that - just using annotations to define scopes and @Inject to reference beans in code makes them much better than the ancient mechanism in XPages.

However, CDI definitely goes far beyond that, and I had use of one of its capabilities today: the event publish/subscribe system.

For a good while now, I had a task to add DQL "explain" logging to the NoSQL driver in the XPages JEE project. This would be useful in troubleshooting performance of the queries generated by NoSQL's mapping of method names like "findByFirstName" to the actual calls. The explain results could be used to determine where adding index views could speed things up.

The trouble with this was that it's never been clear how it should be logged. I could just use a normal java.util.Logger instance, but that would make the developer have to check the XML-based logs on the filesystem, which would be annoying for several reasons. I also didn't want to restrict it to just being something to go to a log, so I decided to instead use CDI events to publish the data when configured to do so. The mechanism to do this is in the project README, but I figure it's also a good example of using CDI's extended capabilities.

Publishing Events

Events themselves are arbitrary Java objects of any type and are published using the confusingly-named jakarta.enterprise.event.Event interface. For this purpose, we'll use a Java record as our event type:

1
2
public record MyEventClass(String payload) {
}

In a normal bean, you can get a handle on these emitters by declaring @Inject Event<MyEventClass> emitter in one of your beans. If your code is outside an existing bean (as is the case in my driver), you can call CDI.current().select(new TypeLiteral<Event<MyEventClass>>() {}).get() to get the same thing.

Once you have this, you can emit events by passing event objects to it:

1
emitter.fire(new MyEventClass("this is my payload"));

Receiving Events

Once you have code that emits events, the other side is to listen for them. This can be done by having any bean that has a method where a parameter is annotated with jakarta.enterprise.event.Observes. For example:

1
2
3
4
5
6
@ApplicationScoped
public class ListenerBean {
	public void eventHandler(@Observes MyEventClass event) {
		System.out.println("I received an event: " + event);
	}
}

And that's it - when an event of this type is fired anywhere, your bean will hear about it without needing any knowledge of the sender. In the case of the NoSQL driver, this means that developers can listen for ExplainEvents and log or otherwise process them as desired. That also means that my code doesn't have to make any assumptions about the results are to be used.

As is often the case, a publish/subscribe system like this can be a very potent tool, and you can go a lot further with this, writing your applications to be a lot more event-based. You could go TOO far with it, of course, but judicious use can make your code a lot cleaner, more explicit about what's going on, and more extensible for future needs.

New Comment