- Developing Middleware in Java EE 8
- Abdalla Mahmoud
- 876字
- 2021-07-23 19:24:35
Using events
Events, as you may know, are runtime incidents that you do care about. CDI provides a loosely coupled model, following the observer design pattern, which allows you to fire an event from a CDI bean, and handling the event from one or more other CDI beans. The key advantages of the CDI events model are:
- Event producers and observers are decoupled from each other
- Observers can define selectors to limit the set of events they consume
- Observers are aware of database transactions and can consume events according to transaction states
In our booking application, we are going to use events in order to notify end users by email with either the success or failure of their booking operations, without tightly coupling the mailing code with the business logic of the booking itself.
Let's start by defining our booking database model; in the next chapter, this will be persisted in a relational database:
public class Booking { private long id; private long cinemaId; private long slotId; private long filmId; private List<Long> seatIds; private BigDecimal amountPaid; public Booking() { } public Booking(long id, long cinemaId, long slotId, long filmId, List<Long> seatIds, BigDecimal amountPaid) { this.id = id; this.cinemaId = cinemaId; this.slotId = slotId; this.filmId = filmId; this.seatIds = seatIds; this.amountPaid = amountPaid; } // getter and setter methods }
Next, we will implement a booking notifier bean, which will represent the observer object for the booking event. The role of this object is to receive a signal when a booking operation has been made, then it prepares and sends an email to the end user confirming with them the booking details. The real code of sending email will be shown in Chapter 9, Sending Mails with JavaMail 1.6. For now, we will log a message to the application server console. Create a class with the name BookingNotifier and write the following code:
@Dependent public class BookingNotifier { public void onBooking(@Observes Booking booking) { System.out.println("New Booking with id " + booking.getId()); // a notification mail should be sent to the user } }
As you have just noticed, the observer is no more than a normal CDI bean. What is special to events here is that we used the @Observers annotation on a parameter of a plain Java method; this tells the container that this method is interested in receiving the events regarding booking objects.
Now, when will this event be fired? Another CDI bean, whose role will be to actually perform the booking operation, will ask to notify all interested objects, as in the following code:
@Dependent public class BookingHandler { private @Inject @Any Event<Booking> bookingEvent; public void book(Booking booking) { // do booking
bookingEvent.fire(booking); } }
As you see, when the booking operation is performed, it will notify our observer about the booking incident, where it will take the appropriate action.
Now, let's complete our example by writing a servlet that simulates a user booking operation:
@WebServlet(urlPatterns = "/cdi-example") public class ExampleServlet extends HttpServlet { @Inject private BookingHandler bookingHandler; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { Booking booking = new Booking(1234, 1, 122, 241, (List<Long>) Arrays.asList(1L, 2L, 3L), new BigDecimal("123")); bookingHandler.book(booking); } }
By running the previous example, the output will be as follows:
New Booking with id 1234
Qualifying Events
Events can also be qualified, so we can propagate our event to a bean that is interested in a specific aspect of the event. Let's extend our example to design two types of events related to the booking operation, one for success, and the other for failure. There will be a bean that will react to the success event, where another one will react to the failure. We will define two qualifiers as follows:
@Qualifier @Retention(RUNTIME) @Target({TYPE, METHOD, FIELD, PARAMETER}) public @interface Success {} @Qualifier @Retention(RUNTIME) @Target({TYPE, METHOD, FIELD, PARAMETER}) public @interface Failure { }
Then, we will replace @Any with the @Success annotation for the onBooking method, and write another one for @Failure as follows:
@Dependent public class BookingNotifier { public void onBooking(@Observes @Success Booking booking) { System.out.println("New Booking with id " + booking.getId()); // a notification mail should be sent to the user } public void onBookingFailed(@Observes @Failure Booking booking) { System.out.println("New Booking failed with id " + booking.getId()); // a notification mail should be sent to the user } }
Now, we will inject two different event propagators in BookingHandler, one for the success, and the other for the failure, as follows:
@Dependent public class BookingHandler { private @Inject @Success Event<Booking> bookingSuccessEvent; private @Inject @Failure Event<Booking> bookingFailedEvent; public void book(Booking booking) { try { // do booking bookingSuccessEvent.fire(booking); } catch (Exception e) { // booking failed bookingFailedEvent.fire(booking); } } }
In the previous code, if the booking has been performed successfully, the success event will be fired, and the onBooking observer will be invoked. If some runtime errors occurred during the booking operation, the failure event will be fired, and the onBookingFailed observer will be invoked. This technique is very useful when we need to track different aspects of the same events in our enterprise application.
- Learning NServiceBus(Second Edition)
- Spring 5.0 By Example
- 微信公眾平臺與小程序開發:從零搭建整套系統
- Angular UI Development with PrimeNG
- Java Web基礎與實例教程(第2版·微課版)
- Implementing Cisco Networking Solutions
- Jupyter數據科學實戰
- 區塊鏈國產化實踐指南:基于Fabric 2.0
- JavaScript程序設計:基礎·PHP·XML
- Appcelerator Titanium:Patterns and Best Practices
- Java從入門到精通(視頻實戰版)
- Java RESTful Web Service實戰
- Java程序設計入門(第2版)
- Web前端測試與集成:Jasmine/Selenium/Protractor/Jenkins的最佳實踐
- R語言:邁向大數據之路