Chapter 4. Learning Context and Dependency Injection

We saw that Chapter 3, Introducing Java EE 7 – EJBs, was challenging since we had to cover lots of ground, including Java Enterprise enhancements and a Maven-specific configuration. In this chapter, we'll discuss Contexts and Dependency Injection (CDI), which was added to the Java EE specification in Java EE 6 (starting from JSR 299). It provides several benefits to Java EE developers that were missing, such as allowing any JavaBean to be used as a JSF managed bean, including stateless and stateful session beans. You can find more information on CDI and the newest version of the specification itself (JSR 346) at http://www.cdi-spec.org/.

Some of the topics that will be covered in this chapter are as follows:

  • What Contexts and Dependency Injection is and how it relates to EJB
  • How to rewrite our ticket-booking example to use the CDI and JavaServer Faces technology
  • How to run the project using Maven

This chapter assumes familiarity with JavaServer Faces (JSF), which will be used to provide a graphical interface for our applications. If you are looking for a start up guide for JSF, there are several excellent resources available online, including the relevant sections in the official Java EE 7 tutorial at http://docs.oracle.com/javaee/7/tutorial/doc/jsf-develop.htm#BNATX.

Introducing Contexts and Dependency Injection

CDI for the Java EE platform introduces a standard set of component management services to the Java EE platform. As a component of Java EE 7, CDI is in many ways a standardization of concepts that have been brewing in Spring for a long time, such as dependency injection and interceptors. In fact, CDI and Spring 3 share many similar features. There are also other dependency injection frameworks available for developers that are more lightweight and easier to use in a Java SE environment. Google Guice (https://github.com/google/guice) is a notable example. Providing full-blown support for the CDI container in a standalone Java SE application and separation from the application server are one of the goals of the upcoming CDI 2.0 specification. This will allow developers to use a common programming model on both client and server sides.

CDI lets you decouple concerns by what it refers to as loose coupling and strong typing. In doing so, it provides an almost liberating escape from the banalities of everyday Java programming, allowing injections of its objects and controlling their lifetimes.

Tip

Why is CDI required for Java EE?

If you have been programming with Java EE 5, you might argue that it already features resources injection of resources. However, this kind of injection can be used only for resources known to the container (for example, @EJB, @PersistenceContext, @PersistenceUnit, and @Resource). CDI, on the other hand, provides a general-purpose dependency injection scheme, which can be used for any component.

The CDI elementary unit is still the bean. Compared to EJBs, CDI features a different, more flexible kind of bean, which would often be a good place to put your business logic in. 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.

Consider the following code snippet:

public class HelloServlet extends HttpServlet {

    @EJB
    private EJBSample ejb;

    public void doGet (HttpServletRequestreq,
                       HttpServletResponse res)
                throws ServletException, IOException {
        try(PrintWriter out = res.getWriter()) {
            out.println(ejb.greet());
        }
    }
}

Here, the injected EJB proxy (let's just assume that it is a POJO class annotated with a @Stateless annotation) just points to a pool of stateless instances (or a single bean instance for stateful beans). There is no automatic association between the HTTP request or HTTP session and a given EJB instance.

The opposite is true for CDI Beans, which live in well-defined scopes. For example, the following CDI Bean lives in RequestScoped; that is, it will be destroyed at the end of the request:

@RequestScoped
public class Customer {

    private String name;
    private String surname;

    public String getName(){
        return name;
    }

    public String getSurname(){
        return surname;
    }
}

The preceding CDI Bean can be safely injected into our former servlet; at the end of an HTTP session or HTTP request, all the instances associated with this scope are automatically destroyed, and thus, garbage collected:

public class HelloServlet extends HttpServlet {

    @Inject
    private Customer customer;

    public void doGet (HttpServletRequest req,
                       HttpServletResponse res)
                throws ServletException, IOException {
        // some code
    }
}

Named beans

In the earlier section, we came across the @Named annotation. Named beans allow us to easily inject our beans into other classes that depend on them and refer to them from JSF pages via the Unified Expression Language (UEL). Recall the earlier example:

@RequestScoped
@Named 
public class Customer {

    private String name;
    private String surname;

    public String getName(){
        return name;
    }

    public String getSurname(){
        return surname;
    }
}

This class, decorated with the @Named annotation, can then be referenced from a JSF page:

<?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 ">
   <h:body>
      <h:form>
         <h:panelGrid columns="2">
            <h:outputLabel for="name" value="Name" />
            <h:inputText id="name" value="#{customer.name}" />
            <h:outputLabel for="lastName" value="Surname" />
            <h:inputText id="surname" value="#{customer.surname}" />
            <h:panelGroup />
         </h:panelGrid>
      </h:form>
   </h:body>
</html>

Note

By default, the name of the bean will be the class name with its first letter switched to lowercase; thus, the Customer bean can be referred to as customer.

If you want to use a different naming policy for your bean, you could use the @Named annotation as follows:

@Named(value="customNamed")

This way, we will be able to reference our CDI Beans using the identified customNamed value.

Instead of two @RequestScoped and @Named annotations, we can just use the @Model annotation that aggregates them.

CDI scopes

CDI Beans come with a set of predefined scopes and annotations, and each CDI Bean has a distinct life cycle determined by the scope it belongs to. The following table describes the built-in CDI scopes and annotations required to set these scopes:

Scope

Description

@RequestScoped

The @RequestScoped beans are shared during the length of a single request. This could be an HTTP request, a remote EJB invocation, a web services invocation, or message delivered to a Message Driven Bean (MDB). These beans are destroyed at the end of the request.

@ConversationScoped

The @ConversationScoped beans are shared across multiple requests in the same HTTP session but only if there is an active conversation maintained. Conversations are supported for JSF requests through the javax.enterprise.context.Conversation bean.

@SessionScoped

The @SessionScoped beans are shared between all the requests that occur in the same HTTP session and destroyed when the session is destroyed.

@ApplicationScoped

An @ApplicationScoped bean will live for as long as the application is running and be destroyed when the application is shut down.

@Dependent

The @Dependent beans are never shared between injection points. Any injection of a dependent bean is a new instance whose life cycle is bound to the life cycle of the object it is being injected into.

Other parts of Java EE can extend the list of available scopes. In Java EE 7 (in the Java Transaction API specification), a new scope has been introduced: @TransactionScoped. It bounds the life cycle of a bean with the current transaction. It is of course possible to introduce your own custom scopes.

In this chapter example, we will use the RequestScoped and SessionScoped beans to drive our simple ticket-booking system. In the next chapter, we will further enhance our example using ConversationScoped beans, which are a peculiar scope of CDI Beans. Providing a detailed explanation of all the named beans scopes is beyond the scope of this book. However, you can quench your thirst for knowledge by having a look at CDI Reference Implementation (JBoss Weld) docs at http://docs.jboss.org/weld/reference/latest/en-US/html/scopescontexts.html.

WildFly CDI implementation

Weld is the CDI Reference Implementation that originated as part of the Seam 3 project (http://www.seamframework.org/). Weld provides a complete CDI implementation, which can be a part of a Java EE 7 container such as WildFly.

Therefore, in order to run CDI-based applications on WildFly, you don't need to download any extra libraries as Weld is part of the server modules, and it is included in all server configurations as stated by the following extension:

<extension module="org.jboss.as.weld"/>

Having your module installed, however, does not mean that you can blindly use it in your applications. The general rule is that on WildFly, every application module is isolated from other modules; this means, by default, it does not have visibility on the AS modules, nor do the AS modules have visibility on the application.

To be accurate, we could state that all WildFly modules fall into the following three categories:

  • Modules that are implicitly added to your applications: This category includes the most common APIs such as javax.activation, javax.annotation, javax.security, javax.transaction, javax.jms, and javax.xml. Using these modules does not require any extra effort as WildFly will add them for you if you are referencing them in your application.
  • Modules that are added on conditions: This category includes javax.ejb, org.jboss.resteasy and org.hibernate, org.jboss.as.web, and finally org.jboss.as.weld. All these modules will be added on the condition that you supply its core annotations (such as @Stateless for EJB) or its core configuration files, for example, web.xml for a web application.
  • Modules that need to be explicitly enabled by the application deployer: This includes all other modules, such as your custom modules, that you can add to the application server. The simplest way to allow you to have visibility to these modules is adding an explicit dependency to your META-INF/MANIFEST.MF file. For example, if you want to trigger the log4j dependency, you have to code your manifest file as follows:
    Dependencies: org.apache.log4j

There is also a custom descriptor file available, which is used by WildFly to resolve dependencies – jboss-deployment-structure.xml. It allows the developer to configure the required dependencies in a fine-grained matter. The file is placed in the top-level deployment file, in the META-INF directory (or WEB-INF for a web archive). A sample content of the XML file (along with the XSD schema) is available at https://docs.jboss.org/author/display/WFLY8/Class+Loading+in+WildFly.

So, if you have followed our checklist carefully, you will be aware that in order to let Weld libraries kick in and automatically discover your CDI beans, you should add its core configuration file, which is beans.xml. This file can be placed in your application at the following locations:

  • In your WEB-INF folder if you are developing a web application
  • In your META-INF folder if you are deploying a JAR archive

The beans.xml file is based on the following schema reference:

 <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
       http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
       version="1.1" bean-discovery-mode="all">
 </beans>

However, it is perfectly legal to place an empty beans.xml file in the correct location; if you do so, CDI will be enabled in your application. If you, however, do not place a beans.xml file, then only an annotated subset of classes will be considered as beans. In such a case, the container will create beans only for classes that are annotated with CDI-related annotations and ignore the rest. Most of the times, this is not the behavior we expect, and it differs from the default mode in Java EE 6 (when the beans.xml file was required).

You might have noticed that the bean-discovery-mode attribute is set to all in our beans.xml file. This allows us to configure the CDI discovery mode we discussed in the previous paragraph. It states that every legible class in our archive will be treated as a managed bean. You can place a @Vetoed annotation on a class to filter it out from the bean discovery process. It is also possible to set the discovery mode to annotated so that you can place a scope annotation for every class that you would like to use as a bean. This is the default value of the newest CDI version (also when there is no beans.xml), so be sure to set it on for all our samples.

Rethinking your ticketing system

Once you have learned the basics of CDI, we will start re-engineering the ticket-booking system using CDI Beans wherever necessary. We will turn it into a leaner application by dropping a few items such as remote interfaces or asynchronous methods, which are not needed in this example. By doing this, you will be able to focus just on the components that are actually used in the web application.

Let's create a new Maven project, just as we did in the previous chapter:

  1. From the File menu, go to New | Maven Project; follow the wizard as we did previously (remember to check the Create a simple project option).
  2. On the next screen, enter com.packtpub.wflydevelopment.chapter4 as Group Id, ticket-agency-cdi as Artifact Id, and set packaging to war:
    Rethinking your ticketing system
  3. Click on Finish. The Maven plugin for Eclipse will generate a project structure for you that you know from the previous chapter.
  4. The only difference is that besides the standard java (for Java classes) and resources (for configuration files) folders, a new directory named webapp that will host the web application views.

Adding the required dependencies

In order to compile and run the project, our Maven's pom.xml file will require the following set of dependencies known from the previous chapter:

    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.logging</groupId>
            <artifactId>jboss-logging</artifactId>
            <version>3.1.4.GA</version>
            <scope>provided</scope> 
        </dependency>
     </dependencies>

We will also require two plugins from the previous chapter (note that we changed the extension of the filename from jar to war):

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <!-- WildFly plugin to deploy the application -->
            <plugin>
                <groupId>org.wildfly.plugins</groupId>
                <artifactId>wildfly-maven-plugin</artifactId>
                <version>1.0.2.Final</version>
                <configuration>
                <filename>${project.build.finalName}.war</filename>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <!-- enforce Java 8 -->
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

In case you have any problems with the POM configuration file, be sure that you check the source code attached to this book and the material from the previous chapter.

Creating the beans

Once your project is properly configured, we can start modeling our beans. The first bean we will upgrade is TheatreBooker, which will drive the user session, accessing the ticket list from our TheatreBox bean:

package com.packtpub.wflydevelopment.chapter4.controller;

import com.packtpub.wflydevelopment.chapter4.boundary.TheatreBox;
import org.jboss.logging.Logger;

import javax.annotation.PostConstruct;
import javax.enterprise.context.SessionScoped;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import java.io.Serializable;

@Named [1]
@SessionScoped [2]
public class TheatreBooker implements Serializable {

    @Inject
    private Logger logger; [3]
     
    @Inject
    private TheatreBox theatreBox; [4]

    @Inject
    private FacesContext facesContext; [5]

    private int money;

    @PostConstruct
    public void createCustomer() {
        this.money = 100;
    }

    public void bookSeat(int seatId) {
        logger.info("Booking seat " + seatId);
        int seatPrice = theatreBox.getSeatPrice(seatId);

        if (seatPrice > money) {
            FacesMessage m = new FacesMessage(FacesMessage.SEVERITY_ERROR, "Not enough Money!", "Registration unsuccessful"); [6]
            facesContext.addMessage(null, m);
            return;
        }

        theatreBox.buyTicket(seatId);

        FacesMessage m = new FacesMessage(FacesMessage.SEVERITY_INFO, "Booked!", "Booking successful");
        facesContext.addMessage(null, m);
        logger.info("Seat booked.");

        money = money - seatPrice;
    }

    public int getMoney() {
        return money;
    }
}

As you can see, the bean has been tagged as Named [1], which means that it can be directly referenced in our JSF pages. The bean is SessionScoped [2] since it stores the amount of money available to the customer during its session.

We would also like to inject logger [3] and FacesContextFacexContexts [5] instead of manually defining it. To do this, we will need to register a bean that produces loggers, which are parameterized with the name of the class. We will cover this process of producing beans in a moment.

Finally, notice that we can safely inject EJBs into our CDI Beans using the Inject [4] annotation. Also, the reverse is perfectly legal, that is, injecting CDI Beans into EJBs.

Compared to our earlier project, here we don't raise Java exceptions when the customer is not able to afford a ticket. Since the application is web based, we simply display a warning message to the client using JSF Faces Messages [6].

The other bean that we still use in our application is TheatreInfo, which has been moved to the controller package as it will actually provide the application with the list of available seats:

package com.packtpub.wflydevelopment.chapter4.controller;

import com.google.common.collect.Lists;
import com.packtpub.wflydevelopment.chapter4.boundary.TheatreBox;
import com.packtpub.wflydevelopment.chapter4.entity.Seat;

import javax.annotation.PostConstruct;
import javax.enterprise.event.Observes;
import javax.enterprise.event.Reception;
import javax.enterprise.inject.Model;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.inject.Named;
import java.util.Collection;

@Model [1]
public class TheatreInfo {

    @Inject
    private TheatreBox box;

    private Collection<Seat> seats;

    @PostConstruct
    public void retrieveAllSeatsOrderedByName() {
        seats = box.getSeats();
    }

    @Produces [2]
    @Named
    public Collection<Seat> getSeats() {
        return Lists.newArrayList(seats);
    }
    public void onMemberListChanged(@Observes(notifyObserver = Reception.IF_EXISTS) final Seat member) {
        retrieveAllSeatsOrderedByName(); [3]
    }
}

At first, have a look at the @Model annotation [1], which is an alias (we call this kind of annotations stereotypes) for two commonly used annotations: @Named and @RequestScoped. Therefore, this bean will be named into our JSF page and will carry a request scope.

Next, pay attention to the getSeats method. This method returns a list of seats, exposing it as a producer method [2].

Note

The producer method allows you to have control over the production of the dependency objects. As a Java factory pattern, they can be used as a source of objects whose implementation may vary at runtime or if the object requires some custom initialization that is not to be performed in the constructor.

It can be used to provide any kind of concrete class implementation; however, it is especially useful to inject Java EE resources into your application.

One advantage of using a @Producer annotation for the getSeats method is that its objects can be exposed directly via JSF's Expression Language (EL), as we will see in a minute.

Finally, another feature of CDI that was unleashed in this example is the observer. An observer, as the name suggests, can be used to observe events. An observer method is notified whenever an object is created, removed, or updated. In our example, it allows the list of seats to be refreshed whenever they are needed.

Note

To be precise, in our example, we are using a conditional observer that is denoted by the expression notifyObserver = Reception.IF_EXISTS. This means that in practice, the observer method is only called if an instance of the component already exists. If not specified, the default option (ALWAYS) will be that the observer method is always called. (If an instance doesn't exist, it will be created.)

In the newest CDI version, it is possible to get additional information about the fired event in the observer by adding an EventMetadata parameter to the observer's method.

Whenever a change in our list of seats occurs, we will use the javax.enterprise.event.Event object to notify the observer about the changes. This will be done in our singleton bean, which gets injected with the seat's event [1], and notifies the observer by firing the event when a seat is booked [2]:

package com.packtpub.wflydevelopment.chapter4.boundary;
import javax.enterprise.event.Event;

@Singleton
@Startup
@AccessTimeout(value = 5, unit = TimeUnit.MINUTES)
public class TheatreBox {

    @Inject [1]
    private Event<Seat> seatEvent;

    @Lock(WRITE)
    public void buyTicket(int seatId) {
        final Seat seat = getSeat(seatId);
        final Seat bookedSeat = seat.getBookedSeat();
        addSeat(bookedSeat);

        seatEvent.fire(bookedSeat); [2]
    }  
    // Rest of the code stays the same, as in the previous chapter
}

Earlier, we mentioned that a preconfigured logger should be injected to a bean if it requests it. We will create a simple logger producer that will use the information about the injection point (the bean that requests a logger) to configure an instance:

package com.packtpub.wflydevelopment.chapter4.util;

import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
import org.jboss.logging.Logger;

public class LoggerProducer {
 
    @Produces
    public Logger produceLogger(InjectionPoint injectionPoint) {
        return Logger.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
    }
}

We also allowed the injection of FacesContext instead of using the standard FacesContext.getCurrentInstance() static method. This context is used, for example, to display the stated error messages:

package com.packtpub.wflydevelopment.chapter4.util;

import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Produces;
import javax.faces.context.FacesContext;

public class FacesContextProducer {

    @Produces
    @RequestScoped
    public FacesContext produceFacesContext() {
        return FacesContext.getCurrentInstance();
    }
}

The last class we will include in our project is the Seat bean, known from the previous chapter, which will be used as our model without any change (remember to include it in your project with a proper package).

Building the view

Once we have coded the server side of our example, creating the front end will be quite easy, as we have made all our resources available through CDI Beans.

One notable difference between some of the earlier editions of this book is that Facelets are now the preferred view technology for JSF. Earlier versions of JSF used JavaServer Pages (JSP) as their default view technology. As JSP technology predates JSF, using JSP with JSF sometimes felt unnatural or created problems. For example, the life cycle of JSPs is different from the life cycle of JSF.

Note

Compared to the simpler request-response paradigm on which the JSP life cycle is based, the JSF life cycle is much more complex since the core of JSF is the MVC pattern, which has several implications. User actions in JSF-generated views take place in a client that does not have a permanent connection to the server. The delivery of user actions or page events is delayed until a new connection is established. The JSF life cycle must handle this delay between event and event processing. Also, the JSF life cycle must ensure that the view is correct before rendering it, and also that the JSF system includes a phase to validate inputs and another to update the model only after all the inputs pass validation.

Most of the time Facelets are used to build JavaServer Faces views using HTML-style templates and component trees. Templating is a useful feature available with Facelets that allows you to create a page that will act as the template for the other pages in an application (something like Struts Tiles). The idea is to obtain portions of reusable code without repeating the same code on different pages.

So here's the main application structure that contains a template page named default.xhtml that is referenced by views in the template attribute of the page's composition element. The template contains two main HTML div elements that will be used to contain the main application panel (content) and a footer div (footer), which will barely output the application title.

In order to add the template at first, add a new JSF page to the WEB-INF/templates folder of your application and name it default.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:ui="http://xmlns.jcp.org/jsf/facelets">
<h:head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <h:outputStylesheet name="style.css"/>
</h:head>
<h:body>
    <div id="container">
        <div id="content">
            <ui:insert name="content">
                [Template content will be inserted here]
            </ui:insert>
        </div>
        <div id="footer">
            <p>
                <em>WildFly Development Ticket Booking example.</em><br/>
            </p>
        </div>
    </div>
</h:body>
</html>

Next, we will add the main page view, which will be embedded into your template. For this purpose, add a JSF page named index.xhtml to the webapp folder of your Maven project:

<?xml version="1.0" encoding="UTF-8"?>
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
                xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
                xmlns:f="http://xmlns.jcp.org/jsf/core"
                xmlns:h="http://xmlns.jcp.org/jsf/html"
                template="/WEB-INF/templates/default.xhtml"> [1]
    <ui:define name="content">
        <h1>TicketBooker Machine</h1>
        <h:form id="reg">
            <h3>Money: $ #{theatreBooker.money}</h3> [2]
            <h:messages errorClass="error" infoClass="info"
                        globalOnly="true"/>
            <h:panelGrid columns="1" border="1" styleClass="smoke">
                <h:dataTable var="_seat" value="#{seats}" [3]
                             rendered="#{not empty seats}" styleClass="simpletablestyle">

                    <h:column>
                        <f:facet name="header">Id</f:facet>
                        #{_seat.id}
                    </h:column>

                    <h:column>
                        <f:facet name="header">Name</f:facet>
                        #{_seat.name}
                    </h:column>
                    <h:column>
                        <f:facet name="header">Price</f:facet>
                        #{_seat.price}$
                    </h:column>
                    <h:column>
                       <f:facet name="header">Booked</f:facet>
                       #{_seat.booked}
                    </h:column>
                    <h:column>
                       <f:facet name="header">Action</f:facet>
                       <h:commandButton id="book"
                 action="#{theatreBooker.bookSeat(_seat.id)}" [4]
                       disabled="#{_seat.booked}"
                       value="#{_seat.booked ? 'Reserved' : 'Book'}" />
                    </h:column>

                </h:dataTable>
            </h:panelGrid>
        </h:form>
    </ui:define>
</ui:composition>

The ui:composition element is a templating tag that wraps content to be included in another Facelet. Specifically, it will be included in the default.xhtml[1] template.

The creation of the view is done in three steps. First, we will display the customer's money [2], which is bound to the session variable called money.

Note

Notice how we directly reference CDI Beans (for example, TheatreBooker) from JSF expressions, just as we used to do with JSF Managed Beans.

The next thing on the checklist is printing all JSF messages [3] that are meant to be produced by the application via the messages element.

The main task of this view is to produce a view of all tickets and let the users purchase them. This is achieved by means of a dataTable object [3]that can be used to produce a tabular list of objects, which are generally stored as java.util.List in your beans.

Pay attention to the value attribute of the dataTable object:

<h:dataTable var="_seat" value="#{seats}" 
rendered="#{not empty seats}" styleClass="simpletablestyle">

In this case, we don't directly reference a CDI Bean, but we reference an object that has been produced by a CDI Bean. To be precise, it has been produced by TheatreInfo that, as we have seen, has a @Produces and @Named annotation on our list of seats:

private List<Seat> seats;

@Produces
@Named
public List<Seat>getSeats() {
   return seats;
}

This dataTable object will be displayed only if it contains some data in it (as dictated by the not empty seats EL expression). In one of the dataTable columns, we have added commandButton [4] that will be used to book the seat displayed on that row. Notice one of the JSF 2 goodies here, as we call the bookSeat method of TheatreBooker passing an argument as one parameter, which is the seatId field.

JSF 2 facet suggestions

By enabling JSF 2 facets on your project configuration, you can enjoy some additional benefits while designing your views.

Enabling JSF 2 project facets takes half a minute. Right-click on your project and navigate to Properties | Project Facets. Then, select the JSF 2.2 Project facets checkbox and click on the OK button:

JSF 2 facet suggestions

Note

Once the JSF facet is enabled, Eclipse will notify you that the JSF library configuration is missing; just disable the JSF library configuration that is a part of Maven's duty.

Once JSF 2 facets are configured, if you press Ctrl + Space bar before referencing a field or method, a suggestion pop-up window will let you choose the method or attribute of the Bean you want to reference.

Getting ready to run the application

OK, now your application is almost ready. We just need to configure a JSF mapping in a web.xml file as follows:

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>/faces/*</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
        <welcome-file>faces/index.xhtml</welcome-file>
    </welcome-file-list>
</web-app>

This will then run the FacesServlet servlet for all the pages at /faces/* url.

Finally, as stated previously, in order to activate our war file as an explicit bean archive, we need to add an empty beans.xml file to the WEB-INF folder of your application.

So, if you follow the same naming convention used in this chapter, you will end up with the following project structure:

Getting ready to run the application

At this point, you must be familiar with building and deploying your Maven applications using Eclipse or a shell. Assuming that you are managing your application from a shell, start by building up the project using the following:

mvn package

Then, publish it using the WildFly Maven plugin, as we did in the previous chapter.

If the WildFly server is started, you can execute the following command:

mvn wildfly:deploy

If the WildFly server is not started, you can execute the following command and then the WildFly Maven plugin will automatically start an instance:

mvn wildfly:run

The application will be available at http://localhost:8080/ticket-agency-cdi.

Then, to do this with a unique command, you can execute the following:

mvn clean package wildfly:deploy

After so much work, you will be pleased to have your application running on your browser:

Getting ready to run the application

Right now, you will be able to book tickets up to the budget ($ 100) defined in your SessionScoped bean. So enjoy this first taste of JSF and CDI.

Of course, in this chapter, we only scratched the surface of JSF features. There is also a new higher-level approach introduced in JSF 2.2 that can be used for flow-based scenarios such as a shopping cart. The new feature is called FacesFlow and comes with a @FlowScoped annotation. However, we will now focus on adding some other features to our current application.

Combining the scheduler into our application

Up to now, we have not included the scheduler, which was in charge of simulating other customer-requesting tickets, into our application. This was not an oversight; as a matter of fact, introducing an external system in a web application poses some challenges. For example, what if the scheduler updates some data used by the application? How will the user know it?

There are several strategies to address this requirement; however, they all boil down to using some intelligence in your client application. For example, if you are familiar with web scripting languages, you can use the popular jQuery API to poll the server for some updates. The newest version of JSF 2.2 comes with great support for HTML5 and JavaScript frameworks, thanks to the custom data attributes and pass-through elements. These are simple mechanisms that allow the JSF's render kit to render parts of the page without any further changes so that custom tags may be interpreted by the browser (or a JavaScript framework).

Since not all Java EE developers might be skilled in JavaScript, we would rather show a simple and effective way to fulfill our requirement using RichFaces libraries (http://www.jboss.org/richfaces), which provide advanced Ajax support along with a rich set of ready-to-use components.

Installing RichFaces

Installing RichFaces requires a set of core libraries that are generally available at the RichFaces download page.

Additionally, you need to provide a set of third-party dependencies that are used by the RichFaces API. Never mind, that's what Maven is for! Start by adding the latest Bill of Materials (BOM) for the RichFaces API in the upper dependency-management section:

<dependencyManagement>
  ...
    <dependency>
        <groupId>org.richfaces</groupId>
        <artifactId>richfaces-bom</artifactId>
        <version>4.3.5.Final</version>
        <scope>import</scope>
        <type>pom</type>
    </dependencies>
</dependencyManagement>

Then, it's just a matter of adding the rich UI libraries and the core API:

<dependency>
    <groupId>org.richfaces.ui</groupId>
    <artifactId>richfaces-components-ui</artifactId>
</dependency>
<dependency>
    <groupId>org.richfaces.core</groupId>
    <artifactId>richfaces-core-impl</artifactId>
</dependency>

Making your application rich

Once we have installed RichFaces libraries, we will just need to reference them on each XHTML page in your project. Here's the new index.xhtml page using the RichFaces namespaces:

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
                xmlns:h="http://xmlns.jcp.org/jsf/html"
                xmlns:f="http://xmlns.jcp.org/jsf/core"
                xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
                xmlns:a4j="http://richfaces.org/a4j"
                xmlns:rich="http://richfaces.org/rich"
                template="/WEB-INF/templates/default.xhtml">
    <ui:define name="content">
        <f:view>

            <h:form>
                <a4j:poll id="poll" interval="2000"
                 enabled="#{pollerBean.pollingActive}"
                 render="poll,grid,bookedCounter"/>
                <rich:panel header="TicketBooker Machine" 
                            style="width:350px">

                    <h2>Book your Ticket</h2>

                    <h3>Money: $ #{theatreBooker.money}</h3>
                    <h:messages errorClass="error" infoClass="info" globalOnly="true"/>


                    <rich:dataTable id="grid" var="_seat" 
                                    value="#{seats}" 
                                    rendered="#{not empty seats}" 
                                    styleClass="simpletablestyle">

                        <h:column>
                            <f:facet name="header">Id</f:facet>
                            #{_seat.id}
                        </h:column>

                        <h:column>
                            <f:facet name="header">Name</f:facet>
                            #{_seat.name}
                        </h:column>
                        <h:column>
                            <f:facet name="header">Price</f:facet>
                            #{_seat.price}
                        </h:column>
                        <h:column>
                            <f:facet name="header">Booked</f:facet>
                            #{_seat.booked}
                        </h:column>
                        <h:column>
                            <f:facet name="header">Action</f:facet>
                            <h:commandButton id="book"
                        action="#{theatreBooker.bookSeat(_seat.id)}"
                        disabled="#{_seat.booked}"
                        value="#{_seat.booked ? 'Not Available' : 'Book'}"/>
                        </h:column>
                    </rich:dataTable>
         <h:outputText value="Booked seats on this page: #{bookingRecord.bookedCount}"  id="bookedCounter" />
                </rich:panel>
            </h:form>
        </f:view>
    </ui:define>
</ui:composition>

We have highlighted the core enhancements added to this page. At first, as we said, we need to reference the RichFaces libraries at the top of the XHTML page.

Next, we added a rich Ajax component, a4j:poll, which does a simple but an effective job of polling the server for updates, allowing the re-rendering of our components—grid (which contains the main datatable), poller (to check whether it should still be running), and bookedCounter.

Additionally, this component references a CDI bean named Poller, which acts just as an on/off flag for our poller. We expect to turn off polling as soon as all the seats are sold out:

package com.packtpub.wflydevelopment.chapter4.controller;

import java.util.Optional;

import javax.enterprise.inject.Model;
import javax.inject.Inject;

import com.packtpub.wflydevelopment.chapter4.boundary.TheatreBox;
import com.packtpub.wflydevelopment.chapter4.entity.Seat;

@Model
public class Poller {

    @Inject
    private TheatreBox theatreBox;

    public boolean isPollingActive() {
        return areFreeSeatsAvailable();
    }

    private boolean areFreeSeatsAvailable() {
        final Optional<Seat> firstSeat = theatreBox.getSeats().stream().filter(seat -> !seat.isBooked()).findFirst();
        return firstSeat.isPresent();
    }
}

Our seller service stays nearly the same as in the previous chapter (the only difference is the logger injection):

package com.packtpub.wflydevelopment.chapter4.control;

import com.packtpub.wflydevelopment.chapter4.boundary.TheatreBox;
import com.packtpub.wflydevelopment.chapter4.entity.Seat;
import org.jboss.logging.Logger;

import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.ejb.Schedule;
import javax.ejb.Stateless;
import javax.ejb.Timer;
import javax.ejb.TimerService;
import javax.inject.Inject;
import java.util.Collection;
import java.util.Optional;

@Stateless
public class AutomaticSellerService {

    @Inject
    private Logger logger;

    @Inject
    private TheatreBox theatreBox;

    @Resource
    private TimerService timerService;

    @Schedule(hour = "*", minute = "*", second = "*/30", persistent = false)
    public void automaticCustomer() {
        final Optional<Seat> seatOptional = findFreeSeat();

        if (!seatOptional.isPresent()) {
            cancelTimers();
            logger.info("Scheduler gone!");
            return; // No more seats
        }

        final Seat seat = seatOptional.get();

        theatreBox.buyTicket(seat.getId());

        logger.info("Somebody just booked seat number " + seat.getId());
    }

    private Optional<Seat> findFreeSeat() {
        final Collection<Seat> list = theatreBox.getSeats();
        return list.stream().filter(seat -> !seat.isBooked()).findFirst();
    }

    private void cancelTimers() {
        for (Timer timer : timerService.getTimers()) {
            timer.cancel();
        }
    }
}

Finally, we'll add a booking record, which will be bounded with the current view using the view scope. Its role will be to count the number of bookings done by the user in the current view (a single browser tab is considered a single view):

package com.packtpub.wflydevelopment.chapter4.controller;

import com.packtpub.wflydevelopment.chapter4.entity.Seat;

import java.io.Serializable;

import javax.enterprise.event.Observes;
import javax.faces.view.ViewScoped;
import javax.inject.Named;

@Named
@ViewScoped
public class BookingRecord implements Serializable {

    private int bookedCount = 0;

    public int getBookedCount() {
        return bookedCount;
    }

    public void bookEvent(@Observes Seat bookedSeat) {
        bookedCount++;
    }
}

You can experiment with the booked counter by trying to book tickets via two separate tabs in your browser.

You might have noticed that we placed two annotations on the bean: @Named and @ViewScoped. If you would like to define multiple beans with a specific set of CDI annotations, it would be a good idea to create your own custom annotation that already contains the desired ones. This kind of construction is called a stereotype. It is possible to incorporate the following elements:

  • A default scope
  • Optionally, interceptor bindings
  • Optionally, a @Named annotation
  • Optionally, an @Alternative annotation

To create a stereotype, you need to add the wanted annotations along with the @Stereotype annotation:

@ViewScoped
@Named
@Stereotype
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface NamedView {

}

Now you can define the BookinRecord bean as follows:

@NamedView
public class BookingRecord implements Serializable {
    //Some code here
}

The @Model stereotype is available in CDI by default. It defines a request scoped named bean, and you can use it on your beans right out of the box.

Running the application

With all the libraries in place, you can now test run your new rich application. As you can see, every 30 seconds a ticket is sold out and buttons are turned, in real time, into Not available:

Running the application

Creating interceptors

There is one more CDI feature worth mentioning here, the interceptors. Sometimes, applications contain logic and cross-cutting multiple layers; the most simple example is logging. Under the Java EE platform, it can be achieved using interceptors. First, we need to create a new annotation:

@Inherited
@InterceptorBinding [1]
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
public @interface Logged {
    // empty
}

This annotation defines an interceptor binding. It can be used to specify methods that you would like to intercept. The bindings can be used on types as well; in that case, every method call on that type is intercepted. The most important part of this definition is the @InterceptorBinding [1] annotation. Be sure to add it!

Then, we have to create the interceptor definition itself:

@Interceptor
@Logged [1]
public class LoggingInterceptor implements Serializable {

    @AroundInvoke [2]
    public Object log(InvocationContext context) throws Exception {
        final Logger logger = Logger.getLogger(context.getTarget().getClass());
        logger.infov("Executing method {0}", context.getMethod().toString());
        return context.proceed() [3];
    }
}

We start by stating that our class is @Interceptor and it will be using the interceptor binding that we've defined earlier (@Logged [1]). Next, we create a method log that will be executed around every method execution (@AroundInvoke [2]) on annotated classes. Inside of it, we will call the context.proceed() method that will basically forward the call to the original receiver. Note that the interceptor can decide (based on some security logic, for instance) whether the call should be dropped. It could even analyze or change the returned value.

Finally, we have to enable it in the beans.xml file by adding the following code:

<interceptors>
  <class>com.packtpub.wflydevelopment.chapter4.util.LoggingInterceptor</class>
</interceptors>

Now, let's move on to just the annotated classes or methods that you want to log using the @Logged annotation. For instance, refer to the following:

@Named
@SessionScoped
@Logged
public class TheatreBooker implements Serializable {
    // Some code
}

All calls to the TheatreBooker public methods will now be logged in to the console:

21:02:11 INFO  [com.packtpub.wflydevelopment.chapter4 .controller.TheatreBooker$Proxy$_$$_WeldSubclass] (default task-8) Executing method public int com.packtpub.wflydevelopment.chapter4.controller. TheatreBooker.getMoney()

In the case of multiple interceptors, the order in which they are executed is determined by the @Interceptor.Priority annotation. Interceptors with lowest priorities will be called first. Be sure to check the constants defined in the Priority annotation. Your own interceptor's priorities should be between the APPLICATION and LIBRARY_AFTER scope.

There are also other interesting CDI mechanisms that we will not cover in this book, but are definitely worth exploring: decorators and alternatives. Decorators are basically strongly typed interceptors that are focused on the business logic of your application. Alternatives can be used to provide alternative implementations for specific beans.

Are EJBs and JSF Managed Beans obsolete?

At the end of this chapter, we would like to give our honest opinion about a common question posed by developers, that is, how EJB, JSF Managed Beans, and CDI interact and where the boundary between them lies. Are there redundancies between them? It is indeed a bit confusing since there are now multiple component models available in Java EE.

JSF Managed Beans have been, for a long time, the actual glue between the application view and the business methods. Since Release 2.0 of JSF, you can declare JSF Managed Beans via an annotation, and the scopes are expanded with a view scope and the ability to create custom scopes. However, there is very little still going on for JSF Managed Beans. Most of its features can be replaced by CDI Beans that are much more flexible and allow you to have a better integration with other Java EE components. Even the view scope, in the newest version of JSF, has been implemented as a CDI custom scope (javax.faces.view.ViewScoped), which replaces the old javax.faces.bean.ViewScoped (notice the name of the package; it's a common mistake to mix them up).

On the other hand, EJBs, even though they use a less flexible injection mechanism, still maintain some unique features such as schedulable timers, asynchronous operations, and pooling that are essential for throttling and assuring that the application provides a good quality of service. Beginning from Java EE 7, EJBs no longer are the only components that have a transactional nature. The new @Transactional annotation allows you to use declarative transactions in CDI beans by simply placing it on selected methods.

Despite this, it's likely that EJBs are not disappearing from our code, rather it is likely (and desirable too) that they will continue to be used for some of their unique features. For the remaining part though, its functionality will be exposed via CDI instead of EJBs' own annotations such as @Stateless and @EJB.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.5.12