The Java EE 5 Tutorial

Coffee Break Server

The Coffee Break server uses JavaServer Faces technology to build its user interface. The JSP pages use JavaServer Faces UI component tags to represent widgets, such as text fields and tables. All the JSP pages use preludes and codas to achieve a common look and feel among the HTML pages, and many of the JSTL custom tags discussed in Chapter 7, JavaServer Pages Standard Tag Library.

The Coffee Break server implementation is organized along the Model-View-Controller design pattern. A FacesServlet instance (included with the JavaServer Faces API) acts as the controller. It examines the request URL, creates and initializes model JavaBeans components, and dispatches requests to view JSP pages. The JavaBeans components contain the business logic for the application; they call the web services and perform computations on the data returned from the services. The JSP pages format the data stored in the JavaBeans components. The mapping between JavaBeans components and pages is summarized in Table 36–1.

Table 36–1 Model and View Components

Function 

JSP Page 

JavaBeans Component 

Update order data 

orderForm

ShoppingCart

Update delivery and billing data 

checkoutForm

CheckoutFormBean

Display order confirmation 

checkoutAck

OrderConfirmations

JSP Pages

The JSP pages are as follows:

The orderForm Page

orderForm displays the current contents of the shopping cart. The first time the page is requested, the quantities of all the coffees are 0 (zero). Each time the customer changes the coffee amounts and clicks the Update button, the request is posted back to orderForm.

The CoffeeBreakBean bean component updates the values in the shopping cart, which are then redisplayed by orderForm. When the order is complete, the customer proceeds to the checkoutForm page by clicking the Checkout button.

The table of coffees displayed on the orderForm is rendered using one of the JavaServer Faces component tags, dataTable. Here is part of the dataTable tag from orderForm:

<h:dataTable id="table"
    columnClasses="list-column-center,list-column-right,
        list-column-center, list-column-right"
    headerClass="list-header" rowClasses="list-row"
    footerClass="list-column-right"
    styleClass="list-background-grid"
    value="#{CoffeeBreakBean.cart.items}" var="sci">
    <f:facet name="header">
        <h:outputText  value="#{CBMessages.OrderForm}"/>
    </f:facet>
    <h:column>
        <f:facet name="header">
            <h:outputText  value="Coffee"/>
        </f:facet>
        <h:outputText id="coffeeName"
            value="#{sci.item.coffeeName}"/>
    </h:column>
    ...
</h:dataTable>

When this tag is processed, a UIData component and a Table renderer are created on the server side. The UIData component supports a data binding to a collection of data objects. The Table renderer takes care of generating the HTML markup. The UIData component iterates through the list of coffees, and the Table renderer renders each row in the table.

This example is a classic use case for a UIData component because the number of coffees might not be known to the application developer or the page author at the time the application is developed. Also, the UIData component can dynamically adjust the number of rows in the table to accommodate the underlying data.

For more information on UIData, please see Using Data-Bound Table Components.

The checkoutForm Page

checkoutForm is used to collect delivery and billing information from the customer. When the Submit button is clicked, an ActionEvent is generated. This event is first handled by the submit method of the checkoutFormBean. This method acts as a listener for the event because the tag corresponding to the submit button references the submit method with its action attribute:

<h:commandButton value="#{CBMessages.Submit}"
    action="#{checkoutFormBean.submit}"/>

The submit method submits the suborders to each supplier and stores the result in the request-scoped OrderConfirmations bean.

The checkoutForm page has standard validators on several components and a custom validator on the email component. Here is the tag corresponding to the firstName component, which holds the customer’s first name:

<h:inputText id="firstName" value="#{checkoutFormBean.firstName}"
    size="15" maxlength="20" required="true"/>

With the required attribute set to true, the JavaServer Faces implementation will check whether the user entered something in the First Name field.

The email component has a custom validator registered on it. Here is the tag corresponding to the email component:

<h:inputText id="email" value="#{checkoutFormBean.email}"
    size="25" maxlength="125" validator="#{checkoutFormBean.validateEmail}"/>

The validator attribute refers to the validateEmail method on the CheckoutFormBean class. This method ensures that the value the user enters in the email field contains an @ character.

If the validation does not succeed, the checkoutForm is re-rendered, with error notifications in each invalid field. If the validation succeeds, checkoutFormBean submits suborders to each supplier and stores the result in the request-scoped OrderConfirmations JavaBeans component and control is passed to the checkoutAck page.

The checkoutAck Page

checkoutAck simply displays the contents of the OrderConfirmations JavaBeans component, which is a list of the suborders constituting an order and the ship dates of each suborder. This page also uses a UIData component. Again, the number of coffees the customer ordered is not known before runtime. The UIData component dynamically adds rows to accommodate the order.

The checkoutAck.jsp page also makes use of a custom converter that converts the shipping date into an XMLGregorianCalendar type:

<h:outputText id="coffeeName"
    value="#{oc.confirmationBean.shippingDate}">
    <f:converter converterId="XMLGregorianCalendarConverter" /
</h:outputText>

The custom converter is implemented by XMLGregorianCalendarConverter.java.

JavaBeans Components

The JavaBeans components are as follows:

The RetailPriceList JavaBeans Component

RetailPriceList is a list of retail price items. A retail price item contains a coffee name, a wholesale price per pound, a retail price per pound, and a supplier. This data is used for two purposes: it contains the price list presented to the end user and is used by CheckoutFormBean when it constructs the suborders dispatched to coffee suppliers.

RetailPriceList first calls the URLHelper.getEndpointURL method to determine the JAX-WS service endpoint. It then queries the JAX-WS service for a coffee price list. Finally it queries the SAAJ service for a price list. The two price lists are combined and a retail price per pound is determined by adding a markup of 35% to the wholesale prices.

The ShoppingCart JavaBeans Component

ShoppingCart is a list of shopping cart items. A ShoppingCartItem contains a retail price item, the number of pounds of that item, and the total price for that item.

The OrderConfirmations JavaBeans Component

OrderConfirmations is a list of order confirmation objects. An OrderConfirmation contains order and confirmation objects, as discussed in Service Implementation.

The CheckoutFormBean JavaBeans Component

CheckoutFormBean checks the completeness of information entered into checkoutForm. If the information is incomplete, the bean populates error messages, and redisplays checkoutForm with the error messages. If the information is complete, order requests are constructed from the shopping cart and the information supplied to checkoutForm, and these orders are sent to each supplier. As each confirmation is received, an order confirmation is created and added to OrderConfirmations.

Several of the tags on the checkoutForm page have their required attributes set to true. This will cause the implementation to check whether the user enters values in these fields. The tag corresponding to the email component registers a custom validator on the email component, as explained in The checkoutForm Page. The code that performs the validation is the validateEmail method:

public void validateEmail(FacesContext context,
        UIComponent toValidate, Object value) {
    String message = "";
    String email = (String) value;
    if (email.indexOf(’@’) == -1) {
        ((UIInput)toValidate).setValid(false);
        message = CoffeeBreakBean.loadErrorMessage(context,
            CoffeeBreakBean.CB_RESOURCE_BUNDLE_NAME, "EMailError");
        context.addMessage(toValidate.getClientId(context),
            new FacesMessage(message));
    }
}

The CoffeeBreakBean JavaBeans Component

CoffeeBreakBean acts as the backing bean to the JSP pages. See Backing Beans for more information on backing beans. CoffeeBreakBean creates the ShoppingCart object, which defines the model data for the components on the orderForm page that hold the data about each coffee. CoffeeBreakBean also loads the RetailPriceList object. In addition, it provides the methods that are invoked when the buttons on the orderForm and checkoutAck are clicked. For example, the checkout method is invoked when the Checkout button is clicked because the tag corresponding to the Checkout button refers to the checkout method by means of its action attribute:

<h:commandButton id="checkoutLink" value="#{CBMessages.Checkout}"
    action="#{CoffeeBreakBean.checkout}" />

The checkout method returns a String, which the JavaServer Faces page navigation system matches against a set of navigation rules to determine what page to access next. The navigation rules are defined in a separate XML file, described in Resource Configuration.

The RetailPriceListServlet Servlet

RetailPriceListServlet responds to requests to reload the price list via the URL /loadPriceList. It simply creates a new RetailPriceList and a new ShoppingCart.

Because this servlet would be used by administrators of the Coffee Break server, it is a protected web resource. To load the price list, a user must authenticate (using basic authentication), and the authenticated user must be in the admin role.

Resource Configuration

A JavaServer Faces application usually includes an XML file that configures resources for the application. These resources include JavaBeans components, navigation rules, and others.

Two of the resources configured for the JavaServer Faces version of the Coffee Break server are the CheckoutForm bean and navigation rules for the orderForm page:

<managed-bean>
    <managed-bean-name>checkoutFormBean</managed-bean-name>
    <managed-bean-class>
        com.sun.cb.CheckoutFormBean
    </managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
        <managed-property>
            <property-name>firstName</property-name>
            <value>Coffee</value>
        </managed-property>
        <managed-property>
            <property-name>lastName</property-name>
            <value>Lover</value>
        </managed-property>
        <managed-property>
            <property-name>email</property-name>
            <value>jane@home</value>
        </managed-property>
        ...
    </managed-bean>
<navigation-rule>
    <from-view-id>/orderForm.jsp</from-view-id>
    <navigation-case>
         <from-outcome>checkout</from-outcome>
        <to-view-id>/checkoutForm.jsp</to-view-id>
    </navigation-case>
</navigation-rule>

As shown in the managed-bean element, the checkoutForm bean properties are initialized with the values for the user, Coffee Lover. In this way, the hyperlink tag from orderForm is not required to submit these values in the request parameters.

As shown in the navigation-rule element, when the String, checkout, is returned from a method referred to by a component’s action attribute, the checkoutForm page displays.