JSF Central - Introduction to Spring Faces Part 2
JSF Central

 
 Home 
 
 Products 
 
 Articles & Books 
 
 Resources 
Introduction to Spring Faces
 
Introduction to Spring Faces Part 2
by Jeremy Grelle
28 Jan 2010 02:00 EDT

In this second article, Jeremy Grelle continues his exploration of Spring Faces with a sample application that demonstrates the Spring-centric integration approach.


The first article in this series introduced Spring Faces at a high, conceptual level. It examined how Spring Faces enables a Spring-centric approach to integration between JSF and Spring, allowing you to take advantage of the strengths of the JSF component model while retaining access to the full breadth of the de facto standard Spring programming model. It showed some of the advantages of assuming Spring Web Flow as the primary controller model for a JSF application, and began to examine the structure of the Spring Travel sample application that demonstrates the Spring-centric integration approach. In part 2, I'm going to pick up where we left off and dive into the code of the sample application to show how Spring Faces simplifies JSF development.

This application is part of the Spring Faces version of the Spring Travel sample application (included in the Spring Web Flow 2 distribution) to illustrate the Spring Faces approach to building a JSF web application. The full source for the Spring Faces sample can be found under /projects/spring-webflow-samples/booking-faces/ from the root of the distribution. If you want to familiarize yourself with the functionality of the application, a running version of the sample is available here.

In examining the main search flow for the sample (/src/main/webapp/WEB-INF/flows/main/main.xml) the first thing you will see on line 6 is a flow variable declaration, as shown in figure 1:

  Figure 1 – Flow variable declaration
Figure 1 – Flow variable declaration

SearchCriteria is essentially a data holder for the various hotel search parameters. Declaring a flow variable in this way will cause an instance of the class to be created and placed in flow scope when the flow is started. This variable instance can then be referenced via EL expressions both in the view template and in the flow definition itself with the expression #{searchCriteria}. Spring Faces provides ELResolvers that search through SWF’s additional scopes, just as JSF’s standard ELResolvers search through the request, session, and application scopes. When using Spring Faces, an extended version of the Unified EL (that allows for method execution in ValueExpressions) is the default expression implementation for the flow definition, as it provides a more consistent experience for JSF developers.

Flow variables can use the Spring 2.5 @Autowired annotation for dependency injection if needed. Since flow variables need to be Serializable, you can mark dependencies (such as a singleton Spring service) as transient, and the container will re-inject the dependencies when they are restored from storage upon subsequent requests.

Beginning on line 8 of the main flow, you will see the first <view-state> definition, shown in figure 2:

  Figure 2 – The enterSearchCriteria <view-state>
Figure 2 – The enterSearchCriteria <view-state>

This <view-state> corresponds to the enterSearchCriteria.xhtml Facelets template. On line 9, you see the <on-render> element, which is an execution hook that allows you to prepare your model for the rendering of the view. Similar execution hooks exist for entry into and exit from a <view-state>.

Now take a look at the <evaluate> element on line 10 that executes for <on-render>. Here a call is made to bookingService (a Spring bean) to load the bookings for the current user. The list of bookings is wrapped in a special Spring Faces DataModel implementation and placed into view scope for use with a standard <h:dataTable> component in the view. The provided DataModel is Serializable and provides extra value beyond the standard JSF DataModel implementation, such as the ability to reference the currently selected row instance through EL (as seen on line 16). The dataModel result-type is a special alias; you can also convert evaluation results to any type that has a configured converter.

JSF action events are handled directly with the <transition> element. Instead of having to coordinate navigation rules in faces-config.xml with ActionListener and Action methods in a JSF managed-bean, all control logic for an event can be encapsulated in this one place. The <transition> on line 12 demonstrates handling of the search event. The searchCriteria’s resetPage() method is called and navigation to the reviewHotels view is signaled. Try changing either the action attribute of the search button on line 40 of the enterSearchCriteria.xhtml view template, or the on attribute of the <transition> element, and then hit the search button and see that SWF gives you a detailed NoMatchingTransitionException that tells you exactly what went wrong. Keep in mind that the flow definition files are dynamic and hot-refreshable, so you can experiment with such changes at development time and just hit refresh in your browser to see them in effect. There is no need to restart the container as you would traditionally have to do when making the equivalent changes using a combination of faces-config.xml and JSF-managed beans.

  Figure 3 – The reviewHotels <view-state>
Figure 3 – The reviewHotels <view-state>

In examining the reviewHotels <view-state> of the main flow (line 21) in figure 3, notice several <transition> elements that have an on attribute (indicating the event being handled) but no to attribute. These are called in-page transitions. They cause the same view to be re-rendered without using a redirect, and are especially useful for complex JSF views with numerous events that cause the current view to mutate in some way. This pattern is common when using Ajax-capable component libraries such as the Spring Faces Ajax components, RichFaces, Trinidad, or ICEfaces.

The SWF 2 flow DSL has special support for Ajax built directly into the language. Notice the <render> element that is used in handling the above in-page events. This is a special instruction that specifies subsections of the component tree to be rendered. This Ajax support is tailored specifically to the Spring Faces Ajax components, but support is provided for other Ajax component libraries where possible.

In addition to the partial rendering support, there is support for rendering a <view-state> in an Ajax popup. For example, take a look at the changeSearchCriteria <view-state> of the main flow (line 54), shown in figure 4:

  Figure 4 – The changeSearchCriteria <view-state>
Figure 4 – The changeSearchCriteria <view-state>

The <view-state> is marked as popup="true", which will cause the view to be rendered into a modal dialog on the client-side if the event was signaled using the Spring Faces Ajax components. This <view-state> is able to re-use the same view template as the enterSearchCriteria <view-state>, but in this case it is providing special instructions to only render the hotelSearchFragment component, and its children in the case of an Ajax request.

A central concept that sets the SWF and Spring Faces Ajax support apart is the focus on progressive enhancement and graceful degradation. If you disable JavaScript in your browser, you will notice that the application continues to completely function using full page refreshes. This happens automatically without having to change anything in the client-side or server-side code.

Another central area that Spring Faces aims to improve when working with JSF is the validation story. It provides enhanced support both for client-side field-level validation and server-side model-level validation. The enterBookingDetails view of the booking flow provides examples of both concepts.

Client-side validation is provided by means of custom JSF components that are designed to decorate existing standard input components with enhanced behavior. In general, client-side validation is a nice alternative that can avoid unnecessary roundtrips to the server. Take the following examples from the enterBookingDetails.xhtml template in figure 5:

  Figure 5 – Client-side date validation
Figure 5 – Client-side date validation

The checkinDate input is enhanced with required date validation, as shown in figure 6.

  Figure 6 – Client-side regular expression validation
Figure 6 – Client-side regular expression validation

The creditCard input is enhanced with required regular expression validation.

The client-side validation behavior can be enforced before allowing submission of the form by decorating individual command components with the Spring Faces validateAllOnClick component, shown in figure 7:

  Figure 7 – Client-side validation enforcement
Figure 7 – Client-side validation enforcement

This ability to choose when to fire the validation is useful when building complex views where you want to be able to trigger other events (i.e., events that just mutate the view) without requiring validation.

To go beyond the simple field-level validation of JSF on the server-side, you can take advantage of SWF's support for model-level validation. The model-level validation works by convention (figure 8):

  Figure 8 – A <view-state> with model validation
Figure 8 – A <view-state> with model validation

Here it is indicated that the booking variable is the model for this <view-state>. By convention, before transitioning to the next view-state, SWF will look for a validateEnterBookingDetails method either on the model itself, or in a separate Spring-managed Validator class, and invoke the validation before the transition is allowed to continue. Also notice the bind="false" attribute on the cancel event, which will bypass validation for that event. In this case, there is a validation method on the Booking object, shown in figure 9:

  Figure 9 – Model-level validation method
Figure 9 – Model-level validation method

Business rules are applied against the model, and in the case of invalid data, messages are added via the provided MessageContext. Any messages added to the MessageContext will be available as FacesMessages for rendering in an <h:messages> or <h:message> component. Also by convention, in the case of a Spring Faces Ajax request, if there is a UIMessages component in the tree with an id of messages or a UIForm component with an id that matches the current model (booking in this case), these components will automatically be re-rendered in the Ajax response.

Conclusion

I hope that this introduction to Spring Faces has given you an idea of the possible productivity gains that can be realized through using the Spring-centric approach to developing applications with JSF and Spring. The features that have been discussed here highlight just a portion of the functionality available in SWF 2. I encourage you to dive deeper into the sample application and the SWF 2 reference guide to see use of further features, such as the support for flow-managed conversational persistence and tight integration with Spring Security 2.

With the release of JSF 2.0, many significant improvements have been made to the overall JSF platform. In support of JSF 2.0, a new revision of Spring Faces is planned that will take advantage of these improvements and continue its mission of making JSF a first-class citizen in a Spring-centric development environment. This revision will see Spring Faces graduate from a Web Flow module into a top-level project in the Spring portfolio so that it can continue to evolve and release independently of Spring Web Flow. Though Web Flow will still play a central role in providing a simplified controller model for stateful interactions, there is further room for Spring Faces to innovate on top of the RESTful Spring MVC 3.0 infrastructure and provide the necessary integration to support stateless use cases.

The community response to these releases has been strong, and we invite you to get involved through participation in the Spring Community Forums and help us continue to evolve Spring Faces as the preferred solution for developing rich JSF applications.



RSS feed(all feeds)

The Editor's Desk
Podcasts
Inside Facelets
In the Trenches

Site version 1.83  Report web site problems

Copyright (C) 2003-2015 Virtua, Inc. All Rights Reserved. Java, JavaServer Faces, and all Java-based marks are trademarks or registered trademarks of Oracle Corporation. in the United States and other countries. Virtua, Inc. is independent of Oracle Corporation. All other trademarks are the sole property of their respective owners.