CDI events
When a post appears in the blog, several things need doing: notify subscribers, update the search index, send emails. If the publishing service calls all of them, it has to know about every one of them and keeps growing. CDI events solve this differently: the service only announces "a post was created", and everyone interested subscribes on their own.
The event
An event is an ordinary object with data. A record describes it nicely:
// src/main/java/com/example/blog/PostCreated.java
package com.example.blog;
public record PostCreated(long id, String title) {
}Firing
The service gets an Event<PostCreated> object and calls fire. Update src/main/java/com/example/blog/PostService.java:
// src/main/java/com/example/blog/PostService.java
package com.example.blog;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Event;
import jakarta.inject.Inject;
@ApplicationScoped
public class PostService {
@Inject
PostStore store;
@Inject
Event<PostCreated> postCreated;
public Post publish(NewPost data) {
Post post = store.add(data);
postCreated.fire(new PostCreated(post.id(), post.title()));
return post;
}
}Here the dependencies are injected into fields — it is shorter, and no no-argument constructor is needed.
Observers
An observer is a bean method whose parameter is marked @Observes:
// src/main/java/com/example/blog/NotificationService.java
package com.example.blog;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
import java.util.logging.Logger;
@ApplicationScoped
public class NotificationService {
private static final Logger LOG = Logger.getLogger(NotificationService.class.getName());
void onPostCreated(@Observes PostCreated event) {
LOG.info("Notifying subscribers about the post: " + event.title());
}
}A second observer of the same event knows nothing about the first:
// src/main/java/com/example/blog/SearchIndex.java
package com.example.blog;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
import java.util.logging.Logger;
@ApplicationScoped
public class SearchIndex {
private static final Logger LOG = Logger.getLogger(SearchIndex.class.getName());
void onPostCreated(@Observes PostCreated event) {
LOG.info("Adding post " + event.id() + " to the search index");
}
}Let's create a post:
curl -H "Content-Type: application/json" -d '{"title":"CDI events","body":"Fire and observe."}' http://localhost:8080/blog/api/poststarget/liberty/wlp/usr/servers/defaultServer/logs/messages.log gets these lines (date shortened):
com.example.blog.NotificationService I Notifying subscribers about the post: CDI events
com.example.blog.SearchIndex I Adding post 3 to the search index
fire calls the observers right away, in the same thread: the response goes to the client only after all of them finish.
Asynchronous events
Long work, such as sending emails, is better kept out of the request. The fireAsync method sends the event to another thread, and methods with @ObservesAsync receive it:
// src/main/java/com/example/blog/EmailSender.java
package com.example.blog;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.ObservesAsync;
import java.util.logging.Logger;
@ApplicationScoped
public class EmailSender {
private static final Logger LOG = Logger.getLogger(EmailSender.class.getName());
void onPostCreated(@ObservesAsync PostCreated event) {
LOG.info("Sending emails about post " + event.id() + " in thread "
+ Thread.currentThread().getName());
}
}In PostService, after fire add postCreated.fireAsync(event), keeping the event in an event variable. The log gets a line from another thread:
com.example.blog.EmailSender I Sending emails about post 3 in thread Default Executor-thread-101
| Firing | Observer | Thread |
|---|---|---|
fire(event) | @Observes | The same, the response waits |
fireAsync(event) | @ObservesAsync | Another, the response does not wait |
Common mistake
Firing an event with fireAsync but marking the observer @Observes, or the other way round. There is no error — the method is simply never called. Synchronous and asynchronous observers do not mix: if an event must reach both, call both fire and fireAsync, as in the example above.