Events

Exclusive offer: get 50% off this eBook here
JBoss Weld CDI for Java Platform

JBoss Weld CDI for Java Platform — Save 50%

Learn CDI concepts and develop modern web applications using JBoss Weld with this book and ebook

$13.99    $7.00
by Ken Finnigan | July 2013 | JBoss Java Open Source

In this article by Ken Finnigan author of JBoss Weld CDI for Java Platform,we are going to find out about events within CDI, such as how they are fired, how we listen for new events, what we can use as an event payload, and how we can narrow what events we can listen to and fire. All of these we will discover while staying within the boundaries of typesafe Java, making our runtime less error-prone.

Events may be produced and consumed by beans of our application, but there is absolutely no coupling between the bean producing an event and the one consuming it. This allows our beans to interact without any coupling or compile-time dependencies between them.

(For more resources related to this topic, see here.)

What is a payload?

The payload of an event, the event object, carries any necessary state from the producer to the consumer and is nothing but an instance of a Java class.

An event object may not contain a type variable, such as <T>.

 

We can assign qualifiers to an event and thus distinguish an event from other events of an event object type. These qualifiers act like selectors, narrowing the set of events that will be observed for an event object type.

There is no distinction between a qualifier of a bean type and that of an event, as they are both defined with @Qualifier. This commonality provides a distinct advantage when using qualifiers to distinguish between bean types, as those same qualifiers can be used to distinguish between events where those bean types are the event objects.

An event qualifier is shown here:

@Qualifier @Target( { FIELD, PARAMETER } ) @Retention( RUNTIME ) public @interface Removed {}

How do I listen for an event?

An event is consumed by an observer method , and we inform Weld that our method is used to observe an event by annotating a parameter of the method, the event parameter , with @Observes . The type of event parameter is the event type we want to observe, and we may specify qualifiers on the event parameter to narrow what events we want to observe.

We may have an observer method for all events produced about a Book event type, as follows:

public void onBookEvent(@Observes Book book) { ... }

Or we may choose to only observe when a Book is removed, as follows:

public void onBookRemoval(@Observes @Removed Book book) { ... }

Any additional parameters on an observer method are treated as injection points.

An observer method will receive an event to consume if:

  • The observer method is present on a bean that is enabled within our application
  • The event object is assignable to the event parameter type of the observer method
JBoss Weld CDI for Java Platform Learn CDI concepts and develop modern web applications using JBoss Weld with this book and ebook
Published: July 2013
eBook Price: $13.99
Book Price: $29.99
See more
Select your format and quantity:

How do I fire an event?

We fire events with an instance of the parameterized Event interface, which can be obtained through injection as follows:

@Inject Event<Book> bookEvent;

We then fire the event using the following:

bookEvent.fire(book);

This event can be consumed by any observer method that matches the event object type and does not specify additional qualifiers. In our case, that would be the first observer method from the previous section.

If an exception is thrown within an observer method, Weld stops further calls to matching observer methods and the exception is rethrown by fire().

Event qualifiers

When we want to specify qualifiers on an event we intend to fire, there are two ways it can be achieved:

  • Annotating the Event injection point with qualifiers
  • Passing qualifiers to the select() method of Event dynamically

As we've seen in previous examples, specifying qualifiers at an injection point is easy.

@Inject @Removed Event<Book> bookRemovedEvent;

Every call to bookRemovedEvent.fire() will have the event qualifier @Removed and would match the second observer method we defined earlier in the chapter.

The downside of specifying event qualifiers on the injection point, as just done, is that we are not able to specify event qualifiers dynamically. We can modify our previous Event injection point to the following:

@Inject @Any Event<Book> bookRemovedEvent;

And instead set the qualifier dynamically by using this:

bookEvent .select( new AnnotationLiteral<Removed>(){} ) .fire(book);

The AnnotationLiteral class is a helper class provided by CDI to make it easier for us to obtain a qualifier instance without having to create concrete classes.

Event qualifiers can comprise a combination of annotations at the Event injection point and qualifier instances passed to select().

Members of event qualifiers

As with other qualifiers, we're able to add members to our event qualifiers that can either be part of differentiating qualifier instances or just for providing additional information.

@Qualifier @Target( { PARAMETER, FIELD } ) @Retention( RUNTIME ) public @interface Book { BookType value(); }

Say we fire an event as follows:

public void addBook(Book book) { bookEvent .select(new BookLiteral(book.getBookType())) .fire(book); }

To complete the preceding code, we need to create a BookLiteral class using the CDI AnnotationLiteral helper class.

public abstract class BookLiteral extends AnnotationLiteral<Book> implements Book { private BookType type; public BookLiteral(BookType type) { this.type = type; } public BookType value() { return type; } }

We could have also created an empty BookLiteral implementation and overridden value() within our call to select(), but the preceding approach ensures we aren't repeating code by firing the same event and event qualifier somewhere else within our applications.

In such cases, we can then listen to these events with observer methods as follows:

public void fictionBookAdded(@Observes @Book(FICTION) Book book) { ... } public void nonFictionBookAdded(@Observes @Book(NONFICTION) Book book) { ... }

Instead of using BookLiteral to dynamically set the event qualifiers when firing the event, we could have specified them on the injection point as follows:

@Inject @Book(FICTION) Event<Book> bookEvent;

Event qualifier members can be marked with @Nonbinding to prevent them from being a part of the process of matching observer methods to fired events.

Combining event qualifiers

Just as with qualifiers, we can combine any number of event qualifiers either on an injection point, by dynamically setting them on the event, or a combination of both approaches.

@Inject @Book(FICTION) Event<Book> bookEvent; ... bookEvent.select(new AnnotationLiteral<Added>(){}).fire(book);

To have an observer method notified when the preceding event is fired, it needs to match all event qualifiers associated with the event when it was fired. If we're missing just one event qualifier, or a member of an event qualifier has a different value, our observer method will not be called as we expected.

Say we have observer methods such as the following:

public void afterFictionBookAdded(@Observes @Book(FICTION) @Added Book book) { ... } public void afterBookAdded(@Observes @Added Book book) { ... } public void onBookEvent(@Observes Book book) { ... }

In such cases, only afterFictionBookAdded() will be called as it matches the fired event with @Added and @Book(FICTION).

Now say we had an observer method such as the following:

public void afterAdding(@Observes @Any Book book) { ... }

The preceding observer method would also be notified, as @Any informs Weld that we want to be notified of all Book events, irrespective of what event qualifiers may be set.

JBoss Weld CDI for Java Platform Learn CDI concepts and develop modern web applications using JBoss Weld with this book and ebook
Published: July 2013
eBook Price: $13.99
Book Price: $29.99
See more
Select your format and quantity:

Observing events in different transaction phases

If an observer method wishes to receive events as part of the before or after completion phases of a transaction in which the event was fired, they are referred to as transactional observer methods .

We could incorporate the transaction phase into our preceding observer with the following:

public void refreshOnBookRemoval (@Observes( during = AFTER_SUCCESS ) @Removed Book bk) { ... }

The possible values for during are defined on javax.enterprise.event.TransactionPhase. In the context of a transactional observer method, the values for TransactionPhase have the following meanings:

  • IN_PROGRESS: An observer method is called immediately. As this is the default value for all observers, there is no need to set it if the standard behavior is required.
  • BEFORE_COMPLETION: An observer method is triggered during the before completion phase of the transaction.
  • AFTER_COMPLETION: An observer method is triggered during the after completion phase of the transaction.
  • AFTER_SUCCESS: In the same phase of the transaction as AFTER_COMPLETION, but only if the transaction completes successfully.
  • AFTER_FAILURE: In the same phase of the transaction as AFTER_COMPLETION, but only if the transaction fails to complete successfully.

Although AFTER_COMPLETION, AFTER_SUCCESS, and AFTER_FAILURE all result in an observer method being called at the same point in the transaction lifecycle, it can be advantageous to distinguish between whether the transaction completed, completed successfully, or failed.

If there is no transaction in progress when an event is fired, a transactional observer method receives the event at the same time as other observer methods. It acts as though the during member of @Observes had not been set.

Now that we've learned about transactional observer methods, it's time to see how useful they can be! When our application utilizes a stateful object model, and we have state that is maintained for longer than a single transaction, we want the ability to update that long-held state without refreshing an entire set of objects through additional database calls.

For a bookstore application, we may want to retain a list of books that are currently available for purchase, but we don't want to retrieve that data for every user request as it changes infrequently. We need an applicationscoped bean to retain the list of books.

@ApplicationScoped @Singleton public class BookCatalog { @PersistenceContext EntityManager em; List<Book> books; @Produces @Available public List<Book> getAvailableBooks() { if (null == books) { books = em.createQuery ("select b from Book b where b.archived = false") .getResultList(); } return books; } }

Say we have a bean that raises an event when it adds a new Book element:

bookEvent.select(new AnnotationLiteral<Added>(){}).fire(book);

And when it removes a Book element:

bookEvent.select(new AnnotationLiteral<Removed>(){}).fire(book);

In such cases, we can modify BookCatalog to update the list of available books when it receives the events for adding and removing books, as follows:

public void addBook(@Observes(during = AFTER_SUCCESS) @Added Book book) { books.add(book); } public void removeBook(@Observes(during = AFTER_SUCCESS) @Removed Book book) { books.remove(book); }

Event-observer bean creation

When Weld is in the process of delivering events to observers during our call to Event.fire() , it will automatically instantiate the bean that defines the observer method to be called if there isn't currently an instance of that bean present within the current context.

For most cases that is perfectly fine, but there may be situations where that default behavior is unwanted. Thankfully, CDI allows us to define a conditional observer , such that the bean will only observe events if it's already present within the current context.

We can specify a conditional observer by setting a value for the notifyObserver member of @Observes.

public void refreshOnBookRemoval (@Observes( notifyObserver = IF_EXISTS ) @Removed Book bk) { ... }

Any bean in the scope of @Dependent is not allowed to be a conditional observer, as there would be no way for the bean to be instantiated and observe the event.

The possible values for notifyObserver are defined in javax.enterprise.event.Reception. Apart from IF_EXISTS, the only other option is ALWAYS, which is the default.

Summary

We discussed some of the advantages of decoupling our application beans by using event producers and consumers. We then looked at what constitutes an event payload and how to define an event qualifier. Using event qualifiers and @Observes, we created observer methods to consume events that were produced with a call to Event.fire().

We expanded on event qualifiers to cover how to use qualifier members with events and the different approaches for combining multiple event qualifiers when firing and observing events. We also saw how events can be observed based on different phases of the transaction lifecycle, and whether a transaction was successfully completed or not.

Lastly, we looked at how a bean with an observer method is automatically instantiated, if it isn't already present in the current context, and how to prevent that instantiation if it is not desired.

Resources for Article :


Further resources on this subject:


About the Author :


Ken Finnigan

Ken Finnigan is a Senior Software Engineer at Red Hat, technical lead of the JBoss Portlet Bridge project, a member of the GateIn development team, and the founder of the Arquillian Portal Extension. As a consultant and engineer he has over 15 years development experience with enterprises throughout the world using technologies that include Java EE frameworks (JSF, CDI, EJB3, Hibernate, and Seam), Java testing frameworks (Arquillian, JUnit, and TestNG), Maven, Ant, and a variety of others. In his spare time, he is a committer for Apache DeltaSpike, ShrinkWrap, and Arquillian. He is also the author of GateIn Cookbook, Packt Publishing.

Books From Packt


Business Process Management with JBoss jBPM
Business Process Management with JBoss jBPM

 JBoss AS 5 Development
JBoss AS 5 Development

JSF 2.0 Cookbook
JSF 2.0 Cookbook

JBoss Drools Business Rules
JBoss Drools Business Rules

Drools JBoss Rules 5.0 Developer's Guide
Drools JBoss Rules 5.0 Developer's Guide

 JBoss AS 7 Configuration, Deployment and Administration
JBoss AS 7 Configuration, Deployment and Administration

JBoss ESB Beginner’s Guide
JBoss ESB Beginner’s Guide

JBoss RichFaces 3.3
JBoss RichFaces 3.3


Your rating: None Average: 1.8 (4 votes)

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
f
Z
a
x
N
G
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software