Saturday, July 14, 2012


Spring 3 and the JSF 2 View Scope

Introduction

In addition to the existing JSF 1.x scopes – Request, Session, and Application, JSF 2 introduced two new scopes to the managed bean lifecycle.

View Scope

This scope persists the state of request parameters in the FacesContext View Map until the current view being rendered is discarded. As long as a user is interacting with the current page, request parameter state is maintained.

Flash Scope

First seen in the Ruby on Rails framework, this scope is for short lived conversations that only need to propagate parameters from one request to the next in the request processing lifecycle. Thereafter, the state of request parameters is discarded from the FacesContext View Map.

Implementing the JSF View Scope in Spring 3

Spring 3 supports the following bean scopes out-of-the-box:
  • singleton – Scopes a single bean definition to a single object instance per Spring IoC container.
  • prototype – Scopes a single bean definition to any number of object instances.
  • request – Scopes a single bean definition to the lifecycle of a single HTTP request; that is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext.
  • session – Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.
  • globalSession – Scopes a single bean definition to the lifecycle of a global HTTP Session. Typically only valid when used in a portlet context. Only valid in the context of a web-aware Spring ApplicationContext.
In addition to the above scopes it is possible to create custom scopes. To support the JSF2 managed bean View Scope in the Spring Spring IoC container it is necessary to create a custom scope which implements the org.springframework.beans.factory.config.Scope interface. Beans created using this interface should then be registered with the Spring Custom Scope Configurer.

Create the View Scope implementation

First, create the View Scope bean implementing the required logic for the get() and remove()methods.
1package net.comdynamics.myapp.web.spring.customscope;
2 
3import java.util.Map;
4 
5import javax.faces.context.FacesContext;
6 
7import org.springframework.beans.factory.ObjectFactory;
8import org.springframework.beans.factory.config.Scope;
9 
10/**
11 * Implements the JSF View Scope for use by Spring. This class is registered as a Spring bean with the CustomScopeConfigurer.
12*/
13public class ViewScope implements Scope {
14 
15    public Object get(String name, ObjectFactory objectFactory) {
16        if (FacesContext.getCurrentInstance().getViewRoot() != null) {
17            Map viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();
18            if (viewMap.containsKey(name)) {
19                return viewMap.get(name);
20            else {
21                Object object = objectFactory.getObject();
22                viewMap.put(name, object);
23                return object;
24            }
25        else {
26            return null;
27        }
28    }
29 
30    public Object remove(String name) {
31        if (FacesContext.getCurrentInstance().getViewRoot() != null) {
32            returnFacesContext.getCurrentInstance().getViewRoot().getViewMap().remove(name);
33        else {
34            return null;
35        }
36    }
37 
38    public void registerDestructionCallback(String name, Runnable callback) {
39        // Do nothing
40    }
41 
42    public Object resolveContextualObject(String key) {
43        return null;
44    }
45 
46    public String getConversationId() {
47        return null;
48    }
49 
50}

Register the View Scope implementation

To enable the View Scope implementation in your web application, register the custom Scope implementation in your application context.
1xml version="1.0" encoding="UTF-8"?>
3    xsi:schemaLocation="http://www.springframework.org/schema/beans
4 
6 
7    <b:bean
8        class="org.springframework.beans.factory.config.CustomScopeConfigurer">
9        <b:property name="scopes">
10            <b:map>
11                <b:entry key="view">
12                    <b:beanclass="net.comdynamics.myapp.web.spring.customscope.ViewScope" />
13                b:entry>
14            b:map>
15        b:property>
16    b:bean>
17 
18b:beans>

Important

If you specify @Scope("view") in your managed bean and the JSF page is loaded, failure to register the custom scope bean in your Spring application context will result in an error similar to the following:
1javax.servlet.ServletException: No Scope registered for scope 'view'
2    javax.faces.webapp.FacesServlet.service(FacesServlet.java:323)

Create your JSF managed bean

The following class from a previous article on the JSF2 JSR-330 annotations has been updated to use the new View Scope in the @Scope annotation.
1package net.comdynamics.myapp.web;
2 
3import javax.inject.Named;
4import org.springframework.context.annotation.Scope;
5 
6/**
7 * ReportListPage JSF backing bean.
8 */
9@Named("reportListPage")
10@Scope("view")
11public class ReportListPage extends BasePage {
12 
13    @Inject
14    @Named("reportService")
15    private ReportService reportService;
16 
17    public ReportListPage() {
18    }
19 
20    public ReportService getReportService() {
21        return reportService;
22    }
23 
24    public void setReportService(ReportService ReportService) {
25        this.reportService = reportService;
26    }
27 
28    // etc…
29 
30}
When the user loads the page bound to the above backing bean, request parameters entered in form fields will be persisted in the FacesContext View Map on form submissions until they navigate to another page in the application.

Conclusion

Support for the JSF 2 View Scope in Spring 3 can be realised by implementing a custom Scope and registering it as a bean in the Spring application context.
Key benefits provided by View Scope are persisting of request parameters across form submissions for the same page, thereby removing the need to manage request parameter state within the web application session.