Spring & JSF integration: Navigation
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 ViewResolver
s 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.
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?
i will help surely…..
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?
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);
}
Everyone is having problem implementing your tutorial , hence I will not use it at all. Please refine it to make it run universally