Spring & JSF integration: Navigation

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.

Reference: Integrating Spring & JavaServer Faces : Navigation from our JCG partner Phillip Webb at the Phil Webb’s Blog.

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

3 Responses to "Spring & JSF integration: Navigation"

  1. Amit says:

    Hi,

    Currently I am working on SpringMVC with JSF2 Integration project. I am able to render a jsf page from controller but getting error when I sumit jsf form to controller or calling controller method from jsf commandButton. May be I am missing some configuration, Could you please help me in this?

  2. sudhir says:

    i will help surely…..

  3. Anita says:

    Hi,
    I do the same, you write, but when I click on the button, it reloads the same page, and does not redirect.
    This is the view:

    And the controller:
    @RequestMapping(value=”/application”, method = RequestMethod.GET)
    public ModelAndView jumpToApplication() {
    return new ModelAndView(“member/application”);
    }

    What am I doing wrong?

Leave a Reply


× 7 = forty two



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy | Contact
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close