Phil Webb's Blog

Random thoughts from a software developer

Archive for June 18th, 2011

Integrating Spring & JavaServer Faces : Navigation

with 2 comments

This is the first in what I hope will be a series of blogs about my efforts to provide deep integration between Spring and JavaServer Faces.  Everything mentioned here is a “work in progress” so if you checkout the code please be aware that it is a moving target; expect some rough edges and don’t be surprised if it’s sometimes broken.

You can already use Spring with JSF pretty happily out of the box, with Spring managing your beans and JSF handling your screens.  There is also some really great support for JSF in Spring Web Flow, if you are doing any flow based application you really should be using Web Flow.  Web Flow also provides the org.springframework.faces.mvc.JsfView class that will let you render a JSF page from Spring MVC.  Unfortunately JsfView only renders transient (stateless) views, if you want to handle postbacks you are out of luck.

Allowing Spring MVC to render JSF views that can handle postback has been my primary driver for starting this project.  Thanks to the flexibility of both MVC and JSF it is entirely possible to integrate these technologies (although the exact details of how are probably best saved for another post).  I want to spend the rest of this post talking about how we can create really nice JSF navigation.

If you have used standard JSF navigation you are probably used to the following type of thing in your faces-config.xml:

<navigation-rule>
    <from-view-id>/pages/list.xhtml</from-view-id>
    <navigation-case>
        <from-outcome>select</from-outcome>
        <to-view-id>/pages/details.xhtml</to-view-id>
        <redirect/>
    </navigation-case>
</navigation-rule>

Whilst it is pretty easy to understand, there are some obvious drawbacks of the standard approach, for starters its pretty verbose.  Most of the time I want to redirect my users rather than leaving them confused as to why the URL shows something different to their current page.  Needing that <redirect/> on virtually every element is really annoying.  The amount of XML obviously upset the developers of JSF themselves, and luckily JSF 2.0 has introduced the concept of implicit navigation. This is something we will make use of later.  If you want to read a really good article about JSF navigation checkout Fluent Navigation in JSF 2 by Dan Allen.

Navigation is really all about destinations, there is not much point in redirecting someone to a 404 page not found error.  Creating nice readable URL destinations has always been a bit of a struggle for JSF.  Right now, without developing your own code, the best option for creating readable URLs is probably to use PrettyFaces.  Of course, with JSF and Spring integrated nicely you don’t need to use anything other than the @RequestMapping annotation to create readable URLs. The example below shows a how you can map a nice readable URL to show hotel details from an ID.

@Controller
public class HotelsController {
  @RequestMapping(value = "/hotels/{id}", method = RequestMethod.GET)
  public String show(@PathVariable Long id, Model model) {
    model.addAttribute(bookingService.findHotelById(id));
    return "hotels/show";
  }
}

With @RequestMapping annotations in place we can think again about the navigation.  Usually the <h:commandButton>, <h:button>, <h:commandLink> or <h:link> components will be used to trigger navigation, for example:

<h:commandButton value="Go" action="select">

Here when the user clicks on the "Go" button the "select" action kicks in and the navigation rules are used to find a destination.  As we want to move away from defining navigation XML we need an alternative approach to find MVC destinations.  Slightly subverting JSFs support for implicit navigation gives us quite a nice way to do this.  With a bit of integration code we can support a special "spring:" prefix that tells JSF to resolve destinations using Spring MVC.

<h:commandButton value="Go" action="spring:redirect:/spring/hotels/123"/>

The example above will resolve "redirect:/spring/hotel/123" using the ViewResolvers registered with Spring MVC.  In this case UrlBasedViewResolver will pick up "redirect:" and a RedirectView will be used.

That’s quite nice, but hard-coding the hotel ID "123" into the view name is not all that practical.  Luckily there is an answer:

<h:commandButton value="Go" action="spring:redirect:/spring/hotels/{id}">
    <f:param name="id" value="#{resultItem.id}/>
</h:commandButton>

All <f:param> child tags of the commandButton will be used to construct a model for the MVC view.  In this case we get a model containing “id=#{resultItem.id}“.  The EL value expression #{resultItem.id} will be resolved before the view is rendered.  The RedirectView class in Spring 3.1 will deal with URL template variables, so “/spring/hotels/{id}” will pick up “id” to render the complete URL.

One slight irritation with the above method is that you need to define your URLs inside your XHTML files as well as in your @RequestMapping annotations.  As an alternative to this you can use a special “@bean.method” notation to indicate that you want to navigate to the value of the @RequestMapping on the specified controller bean method:

<h:commandButton value="Go" action="spring:@hotelsController.show">
    <f:param name="id" value="#{resultItem.id}/>
</h:commandButton>

If you have more than one @RequestMapping method on your controller bean you can navigate between them using the even shorter syntax “@method” (here the bean is assumed to be the current handler).  Of course not every type of @RequestMapping can be reversed into a URL, for example if you use wildcards then this will not work.  The advice is to keep your mappings as simple as possible.

One final benefit of this method is that we can also reverse the DataBinder process.  For example:

public class SearchCriteria implements Serializable {
  private String searchString;
  private int page;
  // ... getters / setters
}
@RequestMapping(value = "/hotels")
public String list(SearchCriteria criteria, Model model) {
  // ...
}
<h:link outcome="spring:@list">
    <f:param name="sc" value="#{searchCriteria}"/>
</h:link>

Assuming the #{searchCriteria} EL expression resolves to a SearchCriteria object containing the string "California" and the integer 10 the URL built would be "/spring/hotels?searchString=California&page=10".

If you would like to have a look at code for this project it is currently available at http://github.com/philwebb/springfaces.  As mentioned at the top of the post this code is a work in progress so please expect some problems.  My next task on the roadmap is to support a @NavigationMapping annotation that will allow programmatic navigation.

Written by Phillip Webb

June 18, 2011 at 11:48 pm

Follow

Get every new post delivered to your Inbox.