Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

An Overview of CDI Events

DZone's Guide to

An Overview of CDI Events

Let's look into CDI events and how you can interact with them. We'll cover what they are, how to fire them, listen to them, and add qualifiers.

· Java Zone
Free Resource

Learn how to troubleshoot and diagnose some of the most common performance issues in Java today. Brought to you in partnership with AppDynamics.

Java Enterprise Edition has many features that really stand out. One of the best is the event mechanism, which is part of the Contexts & Dependency Injection for Java specification. Events have been present in Java EE for a long time. The design of the events mechanism is extremely clean, and learning how to use events is therefore very simple. This overview is aimed at developers who are not familiar with the event mechanism and want to get to know the basics. Advanced features of CDI 2.0, like Asynchronous events, are not covered. You will learn to:

  • Fire specific events.
  • Use event qualifier.
  • Observe events fired during transactions.
  • Configure even observer bean instantiation.

At the end of this article, you will find instructions for a quick-to-run sample application available on GitHub, demonstrating capabilities of CDI events.

Simple Events

The goal is to fire an event in one component of the application and listen to it anywhere else. This allows the application components to be more loosely coupled. Whenever an event is fired, all the subscribers/listeners receive it. Java EE (CDI) internally manages a list of all listeners and decides which one to call when. Automatically, without any configuration.

Firing an Event

Firing an event is as simple as injecting a javax.enterprise.event.Event<T> into a bean and invoking its fire(T t) method.

@Named
public class EventSource {

    @Inject
    private Event<String> simpleMessageEvent;

    public void fireEvent(){
    simpleMessageEvent.fire("Hello");
    }

}


The class Event<T> from the javax.enterprise.event package is instantiated and managed automatically by CDI. As you have probably already noticed, there is one generic type T inside the Event<T> class. This is the type of message held inside each and every event fired. Only methods observing the same type will receive the event. The generic type can, of course, be any Java type.

Listening to an Event

In order to observe events application-wide, two steps are required:

  • Create a method accepting the desired type of event as an argument.
  • Annotate the method’s argument with the javax.enterprise.event.Observes annotation.
@Named
public class EventObserver {

    public void observeEvent(@Observes String message){
    System.out.println(message);
    }

}


The number of methods listening to a certain type of event, where the type is the generic parameter T defined inside the injected Event<T>, is not limited. Of course, the method must be present inside any kind of Java EE bean (CDI, EJB).

Event Qualifiers

A common use case is to categorize or in general differentiate events from each other. Event observers can then decide which events they want to listen to. This is done by means of a CDI Qualifier. A qualifier is, in fact, a fancy annotation.

@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface Important {
}


The resulting @Important annotation is recognized as a qualifier by placing the @Qualifier annotation on it. The name of the annotation is arbitrary. You can use just about anything that is allowed for Java annotations.

There is one strong reason annotations are used as qualifiers: CDI is well thought out and leverages type safety. Using built-in Java types instead of e.g. prehistoric Strings has many benefits for the programmer. One of the strongest reasons are compile-time checks — developers are no longer forced to waste time running the application with each change to check if everything is wired correctly. The compiler does the check naturally.

Firing an Event With a Qualifier

The mechanism of firing an event remains. Only the created qualifier @Important is used to annotate the injected Event<String>. Nothing more is required. The fire() method will automatically let the observers know the message is @Important. There is no work for the developer.

@Named
public class SelectedEventSource {

    @Inject
    @Important
    private Event<String> importantMessageEvent;

    public void fireEvent(){
    importantMessageEvent.fire("Hello");
    }

}


There is also an alternative way of firing an event with a qualifier demonstrated in the [sample application], using the select() method of an Event<T>.

Observing Only Selected Events

Using the @Important qualifier, the event is internally marked as @Important. But this does not stop general observers from receiving the event. Event qualifiers are instead used to limit the mass of the set of event types an observer receives. This limitation is done by simply using the qualifier @Important together with the @Observes annotation.

@Named
public class ImportantEventObserver {

    public void observeImportantMessage(@Observes @Important String message){
    System.out.println(message);
    }

}


This method will receive only events marked as @Important with type String and no other events.

Transactions and Events

Java EE has also had the concept of tranasctions for a long time. Transactions may fail and be rolled backed or may be completed successfully. During transactions, events may be fired. But what if the event observers wants to receive the event only if the transaction ended up in a certain state — for example, successful completion ?

This is very easy to achieve with CDI. The @Observes annotation has a field named during. This field accepts values from the javax.enterprise.event.TransactionPhase enumeration.

@Named
public class TransactionEventObserver {

    public void observeImportantMessage(@Observes(during = TransactionPhase.AFTER_SUCCESS) String message){
    System.out.println(message);
    }

}


This way, the observer will be notified only after the transaction ends successfully. There are five possible states defined by TransactionPhase. The default value is TransactionPhase.IN_PROGRESS — the event is delivered immediately after it was fired.

public enum TransactionPhase {
    IN_PROGRESS,
    BEFORE_COMPLETION,
    AFTER_COMPLETION,
    AFTER_FAILURE,
    AFTER_SUCCESS
}


Event Reception

In a situation when an event is fired, but the observer resides in a bean with no living instance, there are two scenarios possible.

  1. CDI creates the instance and delivers the event to the observer (Reception.ALWAYS)
  2. The event is not delivered (Reception.IF_EXISTS)

This is configured easily per each observer by means of a notifyObserver attribute of the @Observes annotation. The attribute accepts values from the javax.enterprise.event.Reception enumeration. There are only two possible values, Reception.ALWAYS and Reception.IF_EXISTS. By default, when the bean instance is not created, CDI will create it and deliver the event. This corresponds to the first option. The Reception.ALWAYS is used as a default value and can be omitted. In the following example, only the @Observes annotation could be used.

@Named
public class EventObserver {
// Explicit declaration of the default value - can be omitted
    public void observeEvent(@Observes(notifyObserver = Reception.ALWAYS) String message){
    System.out.println(message);
    }

}


If the value is changed to Reception.IF_EXISTS, CDI will deliver the event only to the currently instantiated beans.

@Named
public class EventObserver {
// If there is no bean instantiated, create one
    public void observeEvent(@Observes(notifyObserver = Reception.IF_EXISTS) String message){
    System.out.println(message);
    }

}

Sample Project

There is a sample application on GitHub created to demonstrate the capabilities of CDI events. Starting the application is easy, and no configuration or manual downloads are required, only Maven’s presence is needed.

The simplest and fastest way to do it is to execute maven goal wildfly-swarm:run. On the first run, the WildFly Swarm microcontainer will be downloaded. The application will be scanned, and only the parts required to run the application will be downloaded automatically — a few megabates. In the end, Java EE is about choice, so feel free to choose whichever way of running the Java EE sample application fits you the best.

The application also has clickable GUI to play with.

Understand the needs and benefits around implementing the right monitoring solution for a growing containerized market. Brought to you in partnership with AppDynamics.

Topics:
cdi ,java ,java ee ,events ,tutorial

Published at DZone with permission of Pavel Pscheidl. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}