Context and Dependency Injection part-II ( CDI-Events)

P.Madhanasekaran

Introduction:

As mentioned in the earlier articles, Contexts and Dependency Injection(CDI) is a new addition to the Java EE specification from Java EE 6. It provides several benefits that were earlier missing to Java EE developers, such as allowing any JavaBean to be used as a JSF managed bean, including stateless and stateful session beans.

In CDI elementary unit is still the Bean. Compared with EJBs(or a Spring bean), CDI features a different, more flexible kind of Bean; one of the most important differences between the two approaches is that CDI Beans are contextual; that is they live in a well defined scope. But as mentioned earlier CDI is not a replacement for EJB – rather a complement that simplifies its use. The CDI container provides built-in support for injection and contextual lifecycle management to any bean – whether Managed bean or EJB Session bean. Any concrete Java class can become a managed bean if it has a parameter- less constructor; CDI, an extension to Managed bean specification, relaxes even this and allows a constructor with parameters annotated with @Inject, in its place.

Have a look at the figure.

Same process; but no instantiation by it but gets a reference from EJB container and adds and maintains it in the Context.

The various steps in the above process are:

  1. Scans the archive(war) - for @Inject annotations  in a Java class or whether  any JSF -EL references a Java class annotated with @Named.
  2. Type resolution:  Resolves its Java type  - this step not there for objects in JSF EL where only its bean name is resolved
  3. Creates a contextual reference:  Instantiates if it is not already there in the concerned scope and manages/maintains that in that scope. For Ejb it gets a reference from EJB container and verifies it and creates a contextual reference.
  4. Injection:  Injects it and its dependencies-specified in the constructor or setter /initializer at the injection point- where the @Inject annotation was found.
  5. Visibility : It can see other beans in the same context and vice-versa.
  6. When context ends beans are destroyed.

In brief the CDI container provides 1) a lifecycle for stateful objects, 2) binding of objects to well-defined contexts.3)“type-safe” dependency injection 4)  an event notification facility, and 5) robust interceptors - all the services that  enable loose coupling.

Event Notification:

Dependency injection enables loose-coupling by allowing the implementation of the injected bean type to vary, either at deployment time or runtime. Events go one step further, allowing beans to interact with no compile time dependency at all. Events are raised by the event producer and subscribed to by event observers; they are delivered by the container to observersregistered for a particular event. But this registration is done just passing the event as a parameter and affixing the annotation “@Observes” before it  in the observer method.  Like most of CDI, event production and subscription is type-safe. The event object is nothing more than an instance of a concrete Java class. The only restriction is that an event type may not contain type variables. An event may be assigned qualifiers, which allows observers to distinguish it from other events of the same type. The qualifiers function like topic selectors, allowing an observer to narrow the set of events it observes.

 

Sample: As in the earlier samples, first we will create a database & table in MySQL. Then we will create a Java Web Application in NetBeans and generate in it an entity class –for storing our books- from the database table “Book”. Then we will create a Session Bean from the entity class. See the New Session Bean Wizard below:

As shown in the above wizard, the created files will include a Façade for each Entity apart from “AbstractFacade”, which contains common CRUD methods. The generated skeleton of BookFacade.java is given below:  

BookFacade.java  //  a Stateless EJB which will carry on transactional jobs (Inserts, Deletes)

import java.util.List;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Stateless
public class BookFacade extends AbstractFacade<Book> {
     

    @PersistenceContext(unitName = "EventDemoPU")
private EntityManager em;

    @Override
protected EntityManager getEntityManager() {
return em;
}

    public BookFacade() {
super(Book.class);
}
}

It has all the infrastructure code and we can just fill up the code up with “CRUD” methods for Book Entity. As this is an EJB, it will perform transactional activities such as storing new Book and deleting them as well from the DB; we don’t need to configure anything in “xml”   or attach any annotation. The code we have to add in the above file is given below:

import javax.enterprise.event.Event;  //------1)

@Inject
private Event<Book> bookEventSrc;    //-------2)
……………………
……………………

@Override
public void create(Book book) {
super.create(book);
bookEventSrc.fire(book);    //----------3)
}
@Override
public List<Book> findAll() {
return super.findAll();

public void remove( Book book) {
Object id = book.getId();
super.remove(super.find(id));
bookEventSrc.fire(book);  //---------4)
}

  1. This Package is necessary for programming with CDI events.
  2. ‘bookEventSrc” is the object of the  generic Event,  which  is typed for Book.
  3. The create() method apart from persisting the book Entity, invokes the  fire() in the Event object with  the book as parameter. The fire() method in the Event object broadcasts “book” object; bear in mind  bookSrcEvent is an Event object typed for Book.
  4. The remove()method also fires a book event.

Any method in any other bean can observe this “book” event, if it satisfies certain conditions. See a sample below:

public void onBookListChanged(@Observes Book book) {
        retrieveAllBooks();
    }

The @Observes annotation before the event parameter in the method makes the method the observer/handler for the particular event. No other configuration is necessary. The code of the full class containing the observer/handler method is given below:

Producer.java. // a  CDI Producer Bean used as a factory of Book  for the JSF view

package ram;

import java.util.List;
import javax.annotation.PostConstruct;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.event.Observes;
import javax.enterprise.event.Reception;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.inject.Named;

@RequestScoped
public class Producer {
@Inject BookFacade facade;  // -------1)

private List<Book> bookList;

public void onBookListChanged(@Observes Book book) {
retrieveAllBooks();
}        //----------2)

@Produces     //------3)
@Named
public List<Book> getBookList() {
return bookList;
}

public void setBookList(List<Book> bookList) {
this.bookList = bookList;
}

@PostConstruct       //------------4)
public void retrieveAllBooks() {
bookList = facade.findAll();
}    
} 

  1. Session- bean created earlier  is injected.
  2. Observers can be used to observe objects. Here the observer method is notified whenever a book object is created or removed. It allows refreshing the list of Books whenever they are needed.
  3. A producer method acts as a source of objects to be injected, where the objects to be injected are not instances of a Java class. Producer here is used  to Produce an ArrayList  @Named booklist  to expose it  to the JSF EL:
  4. Any @PostConstruct callback declared by a class is called after all initializer methods declared by the class or by its super-classes have been called.

CDI bean

In a Simpler app for example a “Hello World”, the Ejb itself will be annotated with @Named  and accessed in JSF EL. But a bigger app with a number of classes requires a CDI manager/controller to manage everything. The code for the same is given below:

Manager.java   // a CDI Bean which acts as a glue between the JSF view and the EJB

package ram;

import javax.annotation.PostConstruct;
import javax.enterprise.inject.Model;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.inject.Named;

@Model   //---------------------1)
public class Manager {

@Inject
BookFacade ejb;

@Produces     //-------------2)
@Named
Book book = New Book();   

public void save() {
ejb.create(book);  //-------3)
}

public void clear(Book book) {  //-----3)
ejb.remove(book);
}       
}

  1. @Model is a built-in stereotype that combines @RequestScoped and @Named.
  2. Book instance field is annotated with a @Produces @Named annotations. This means that this field will be used as a factory for an instance of the Book, which will be eventually exposed (via @Named annotation) to the JSF EL. In the earlier article the need for the use of @Produces with an Entity instance was explained and Book is entity class here.
  3. Methods create()  and remove in the injected instance of BookFacade EJB are just  invoked here. There’s no need to access JNDI or write a custom JNDI abstraction layer.

View Technology JBoss-Seam the predecessor of CDI was created to simplify JSF and is integration with EJB. Though any other Component framework like Wicket can be used, JSF is the first choice for CDI. We can have a look at “index.xhtml” , which will capture the Book fields in the upper panel and display them in a data table in the lower panel. The generated code for the above can be modified for the purpose as under:

index.xhtml

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>

<h:form id="jsfexample">  //The <h:form> tag is equivalent to the <form> tag in standard HTML
<h:panelGrid columns="2" styleClass="default">  //layout

<h:outputText value="Enter title:" />
<h:inputText value="#{book.title}" />   //input stored in ‘title’ attribute of book object

<h:outputText value="Enter author:" />
<h:inputText value="#{book.author}" />

/*EL accesses method in CDI bean. @Named annotation affixed to ‘Manager’ class does not bear any name. By convention the first letter of class name is lower-cased and name of object of the class “Manager” is taken as “manager”. */

            <h:commandButton actionListener="#{manager.save}"  /*clicking the button will trigger save() defined in Manager class */
styleClass="buttons" value="Save Book" />

<h:messages /> /*displays all messages that were stored in the faces context
during the course of the JSF life cycle */
</h:panelGrid>

<h:dataTable value="#{bookList}" var="book" /* h:dataTable tag iterates over data to create an HTML table. The value attribute represents the data over which h:dataTable iterates. The name of the item in the list is specified with var attribute*/
styleClass="table" headerClass="table-header"
rowClasses="table-odd-row, table-even-row">
<h:column>
               <f:facet name="header">Title</f:facet> /*<f:facet> is a tag from jsf core tag library. <h:dataTable> places the components specified for the header facet in the HTML table’s header. */
<h:outputText value="#{book.title}" /> //EL displays “title” attribute from book
</h:column>
<h:column>
<f:facet name="header">Author</f:facet>
<h:outputText value="#{book.author}" />
</h:column>
<h:column>
<f:facet name="header">Delete</f:facet>
<h:commandButton actionListener="#{manager.clear(item)}"                styleClass="buttons" value="Delete" />
</h:column>

</h:dataTable>
</h:form>

    </h:body>
</html>

Deploy the project

Deploy the Project, right clicking on it in the Project Window and choosing Deploy. When you get the message the project was successfully deployed in the Output window , you can go to Services -> Servers-> Glassfish Server and expand Applications and right click on your  project and choose Open in Browser. You will get Figure-2 displaying in the lower panel the records already in database and providing a form in the upper panel for entering data .You can enter data and Click the button “Save Book”

Figure-2

Conclusion

CDI services were created to enables “loose coupling” between components in an application. The Event Notification in CDI goes further and makes possible interaction between the components without any compile time dependencies. Observer resolution usually occurs at runtime. The process of matching an event to its observer methods is called observer resolution. An event is delivered to an observer method in a CDI bean , if the event object is assignable to the observed event type, taking type parameters and Qualifiers  into consideration. It is similar to JMS but no “Broker”/destination  is involved and it can be used for “messaging” between components within the same application/JVM. CDI events are publish-subscribe implementation with transaction support, and they are a full-featured JMS replacement for local events. Although CDI events work only inside a single process (in the default case, CDI is extensible), they are perfectly suitable for decoupling components. The only dependency between the publisher and subscriber is the event itself and, optionally, the qualifier annotation. You need JMS for inter-application messaging and we will have a look at it in a future article.








}