Enterprise Java

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.

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

5 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Amit
Amit
11 years ago

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?

sudhir
11 years ago

i will help surely…..

Anita
Anita
11 years ago

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?

sandeep
sandeep
9 years ago

Hello,

Am trying the same thing but not able to submit to controller from xhtml, my doubt is u have used hotelController where the name of controller is HotelController so where exactly you have configured the name and you are directly using the

spring@:hotelController.show

what you have configured to use directly the above line.

Index.xhtml

Controller———>

@Controller
@RequestMapping(value=”testController”)
public class PaymentController {

@RequestMapping(value = “/verifypayment”,method = RequestMethod.GET)
public ModelAndView verifyPayment(PaymentDetails paymentDetails,BindingResult result,ModelMap model, HttpServletRequest request, HttpSession session,@RequestParam String Continue,HttpServletResponse response) throws Exception
{

return new ModelAndView(hello);
}

Chang
8 years ago

Everyone is having problem implementing your tutorial , hence I will not use it at all. Please refine it to make it run universally

Back to top button