5.5. Presentation Layer

The three web applications which compose the Sportsclub package illustrate different ways of integrating Spring and Java EE technologies in the presentation layer:
  • JSF/EJB with an underlying Spring layer (Subscriptions)
  • JSF/Spring (Reservations)
  • Spring MVC/EJB (Invoicing)

5.5.1. Subscriptions: JSF and EJB

The Subscriptions application uses RichFaces and JSF for the presentation layer and EJB for the business layer, so this part of the application is not Spring-related.

5.5.2. Reservations: JSF/Spring integration

The Reservations application is an example of using Spring in an application that uses RichFaces and JSF. Here, Spring beans are used as business services for the application, as well as backing beans for the JSF pages. In the latter case, Spring beans replace the managed beans and other web artifacts used by JSF.
The Spring application context is bootstrapped by the ContextLoaderListener defined in /WEB-INF/web.xml. The Spring configuration file in use is /WEB-INF/spring-beans.xml, which:
  • imports the context definition fragments included in the other JARs of the application (that is, the JARs that contain the business logic)
  • defines a number of Spring beans that are used directly in the web tier by the JSF pages or by the RichFaces components;
The Spring configuration file imports the Spring business beans and infrastructure definitions as follows:
<import resource="classpath*:reservations-service.xml"/>
<import resource="classpath*:infrastructure.xml"/>
The following bean is used for backing JSF pages. Please note that Spring beans defined in the web layer may use scopes, and a significant number of the Spring beans used in Reservations application are session-scoped (like the one in the following example). Spring provides a request scope as well, but it is not used in this example.
<bean id="reservationCreate" class="org.jboss.snowdrop.samples.sportsclub.jsf.beans.ReservationCreate" scope="session" init-method="init">
        <property name="reservationService" ref="reservationService"/>
        <property name="accountService" ref="accountService"/>
        <property name="accountFilter" ref="accountFilterCreate"/>
        <property name="equipmentFilter" ref="equipmentFilterCreate"/>
        <property name="reservationSearch" ref="reservationSearch"/>
</bean>
In order to make the Spring beans visible to JSF pages, a special VariableResolver has to be defined in /WEB-INF/faces-config.xml.
<application>
    <!-- other definitions -->
        <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
</application>
The Spring bean defined above can be used directly in a JSF page, as in the following excerpt from createReservation.xhtml:
<rich:panel>
    <f:facet name="header">Select Account</f:facet>
    <h:form id="AccountSelectForm">
        <rich:extendedDataTable id="accountsTable"
            value="#{accountFilterCreate}" var="account"
            selectionMode="single"
            selection="#{accountFilterCreate.selection}"
            enableContextMenu="true" height="250px" rows="5">
            <a4j:ajax event="selectionchange"
                listener="#{reservationCreate.updateSelectedAccount}"
                render="reservationDetails" />

            <rich:column label="Id" width="7%">
                <f:facet name="header">
                    <h:outputText value="Id" />
                </f:facet>
                <h:outputText value="#{account.id}" />
            </rich:column>

            <rich:column label="First Name">
                <f:facet name="header">
                    <h:outputText value="First Name" />
                </f:facet>
                <h:outputText value="#{account.subscriber.name.firstName}" />
            </rich:column>

            <rich:column label="Last Name">
                <f:facet name="header">
                    <h:outputText value="Last Name" />
                </f:facet>
                <h:outputText value="#{account.subscriber.name.lastName}" />
            </rich:column>

            <rich:column label="City">
                <f:facet name="header">
                    <h:outputText value="City" />
                </f:facet>
                <h:outputText value="#{account.subscriber.address.city}" />
            </rich:column>

            <rich:column label="Country">
                <f:facet name="header">
                    <h:outputText value="Country" />
                </f:facet>
                <h:outputText value="#{account.subscriber.address.country}" />
            </rich:column>

            <f:facet name="footer">
                <rich:dataScroller id="scrollerAccount" for="accountsTable"
                    maxPages="5" page="#{accountFilterCreate.currentPage}" />
            </f:facet>
        </rich:extendedDataTable>
    </h:form>
</rich:panel>
All the EL variables that are used in the previous example, including the ones referenced in the RichFaces elements are Spring beans. They can be used either as backing beans for retrieving and setting values, as well as for invoking methods corresponding to JSF events.

5.5.3. Reservations-Webflow: Conversation-based reservations

The reservations application included in the Sportsclub packages uses Spring Webflow. As with the previous example, the main view technology is JSF2, using Richfaces as a component library, and Spring, which is used for managing the backing beans for JSF pages and business services.
However, unlike the previous example where the interaction is form-based, this application uses a conversation-based model for driving the user interaction. A reservation is created in a sequence of steps that represents a conversation - a distinct scope which spans multiple requests, but less broad than a session. The input data (selected account, selected equipment and date) are preserved for the duration of the conversation, and discarded automatically after the conversation has ended. The Sportsclub application uses Spring Webflow for implementing conversation-based multi-step dialogues.
The Spring business application context is bootstrapped by the ContextLoaderListener defined in /WEB-INF/web.xml. The Spring configuration file in use is /WEB-INF/spring-beans.xml, which:
  • imports the context definition fragments included in the other JARs of the application (that is, the JARs that contain the business logic)
  • defines a number of Spring beans that are used directly in the web tier by the JSF pages or by the Richfaces components;
For the UI and Spring Webflow integration, the application defines a DispatcherServlet in web.xml, in a similar way to Spring MVC applications.
<webflow:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices">
    <webflow:flow-location path="/WEB-INF/flows/reservation/reservation-flow.xml" id="reservation"/>
</webflow:flow-registry>

<webflow:flow-executor flow-registry="flowRegistry" id="flowExecutor">
    <webflow:flow-execution-listeners>
    <webflow:listener ref="facesWebFlowListener"/>
    </webflow:flow-execution-listeners>
</webflow:flow-executor>

<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerAdapter">
    <property name="flowExecutor" ref="flowExecutor"/>
    <property name="ajaxHandler">
        <bean class="org.springframework.faces.webflow.JsfAjaxHandler"/>
    </property>
</bean>

<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping">
    <property name="flowRegistry" ref="flowRegistry"/>
    <property name="order" value="1"/>
</bean>

<faces:resources/>

<faces:flow-builder-services id="flowBuilderServices" development="true"/>

<bean id="facesWebFlowListener" 
        class="org.springframework.faces.webflow.FlowFacesContextLifecycleListener"/>



The definitions above include:
  • a flow registry containing the flow definitions;
  • a flow executor which is the Spring Webflow engine;
  • a pair of FlowHandlerAdapter and FlowHandlerMapping beans which ensure that any requests that are handled by the DispatcherServlet declared in web.xml are delegated to the Spring Webflow executor;
  • the flowBuilderServices and facesWebFlowListener beans are used for integrating Spring Webflow with JSF. In other words, for using JSF views for rendering the view states.
The application is using a flow definition for creating a reservation, described in /WEB-INF/flows/reservation/reservation-flow.xml. The JSF pages used for rendering the flow views are located in the same /WEB-INF/flows/reservation/ directory.

5.5.4. Invoicing: Spring MVC and EJB

The Invoicing application provides another example of interoperability between Spring and the Java EE services provided by JBoss. If in the previous example, the business tier was implemented using Spring and the web tier was using JSF, in this example the roles are reversed; the business tier is using EJB and the web tier is using Spring.
The Spring MVC beans are defined in the /WEB-INF/springmvc-servlet-context.xml file, referenced by the Spring MVC DispatcherServlet definition that is described WEB-INF/web.xml. For this implementation, the annotation-based configuration has been used, and the AccountController class that implements the controller part of the configuration is injected with previously defined EJBs:
@Controller
public class AccountController
{

    @EJB(mappedName = "java:app/sportsclub-invoicing-ejb/BillingServiceImpl")
    BillingService billingService;

    @EJB(mappedName = "java:app/sportsclub-subscriptions-ejb/SubscriptionServiceImpl")
    SubscriptionService subscriptionService;

    /* some code ommitted */

    @RequestMapping(value = "/accountDetail.do", method = RequestMethod.GET)
    ModelMap getAccountDetail(@RequestParam("id") String id)
    {
        Account account = subscriptionService.findAccountById(Long.parseLong(id));

        List<Invoice> invoices = billingService.getInvoices(account);

        boolean hasCurrentInvoice = false;
        Date currentDate = new Date();

        for (Invoice invoice: invoices)
        {
            if (invoice.getBillingPeriod().contains(currentDate))
        {
            hasCurrentInvoice = true;
            break;
        }
    }

    List<Payment> payments = billingService.getPayments(account);

        ModelMap model = new ModelMap();
        model.addAttribute(account);
        model.addAttribute("invoices", invoices);
        model.addAttribute("payments", payments);
        model.addAttribute("hasCurrentInvoice",hasCurrentInvoice);
        return model;
    }


    @RequestMapping(value = "/generateInvoice.do", method = RequestMethod.POST)
    ModelMap generateInvoice(@RequestParam("id") String id)
    {
        Account account = subscriptionService.findAccountById(Long.parseLong(id));
        Invoice invoice = billingService.generateInvoice(account);

        ModelMap model = new ModelMap();
        model.addAttribute("id",id);
        model.addAttribute(invoice);
        return model;
    }

}
The @Controller annotation is detected by Spring, as it scans the classpath which is prompted by including the following line into /WEB-INF/springmvc-servlet-context.xml.
<context:component-scan base-package="org.jboss.snowdrop.samples.sportsclub.springmvc"/>
As a Spring-managed object, the bean is injected with the EJBs BillingService and SubscriptionService, as required by annotating the respective fields with the @EJB annotation.
The @RequestMapping-annotated methods are executed when the user is accessing the specified URL and HTTP method. The request parameters will be bound to method arguments. In the example above, invoking the URL http://localhost:8080/sportsclub/invoicing/accountDetail.do?id=1 will cause the invocation accountController.getAccountDetail(1). The method will invoke the appropriate business services (in this case, exposed as EJBs) and will return a map of business object collections, indexed by their names. Spring MVC will take care of setting them on the request, so that they can be used for rendering the response.
By default, Spring MVC will try to find a view that has the name 'accountDetail', and will use the JSP file at WEB-INF/jsp/accountDetail.jsp, based on the view resolver definition from /WEB-INF/springmvc-servlet-context.xml. This JSP uses the Spring tag libraries for form processing, so that the collections previously returned will be accessible using JSTL expressions, and the following declaration will be found:
<form:form action="generateInvoice.do">
    This account does not have an invoice for the current billing period.<p/>
    You can create one now:
    <input type="hidden" name="id" value="<c:out value="${account.id}"/>">
    <input type="submit" value="Create invoice"/><br/>
</form:form>
Clicking the Create Invoice button will result in a POST submission to http://localhost:8080/sportsclub/invoicing/generateInvoice.do?id=1 and the subsequent invocation of the generateInvoice(1) method.
In order to be able to demonstrate a few Spring/JBoss integration features, the Invoicing application also contains a number of business services that are using Spring. They do not play any role in the Spring MVC/EJB integration.

5.5.5. A problem of reusing content

Having three different web applications to be included in the same package raises the problem of reusing some content. Most of the static content used by the Sportsclub application is defined in a separate web module, Sportsclub-staticwebcontent, which is then included by Maven at build time as follows:
<dependency>
  <groupId>org.jboss.snowdrop.samples.sportsclub</groupId>
  <artifactId>sportsclub-staticwebcontent</artifactId>
  <version>${project.version}</version>
  <type>war</type>
  <scope>runtime</scope>
</dependency>

Note

The groupID is dependent on which Spring variant is used. The corresponding groupIDs are:
  • org.jboss.snowdrop.samples.sportsclub.spring-3_2
  • org.jboss.snowdrop.samples.sportsclub.spring-4_0
  • org.jboss.snowdrop.samples.sportsclub.spring-4_1

Note

When working in an IDE that does not know how to apply Maven overlays correctly, the static content may not be available when building and deploying the application. This does not affect the general functionality of the application, but the look and feels may be affected.