Skip to content

Posts from the ‘JSF’ Category

17
Aug

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.

package net.comdynamics.myapp.web.spring.customscope;

import java.util.Map;

import javax.faces.context.FacesContext;

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;

/**
 * Implements the JSF View Scope for use by Spring. This class is registered as a Spring bean with the CustomScopeConfigurer.
*/
public class ViewScope implements Scope {

	public Object get(String name, ObjectFactory<?> objectFactory) {
		if (FacesContext.getCurrentInstance().getViewRoot() != null) {
			Map<String, Object> viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();
			if (viewMap.containsKey(name)) {
				return viewMap.get(name);
			} else {
				Object object = objectFactory.getObject();
				viewMap.put(name, object);
				return object;
			}
		} else {
			return null;
		}
	}

	public Object remove(String name) {
		if (FacesContext.getCurrentInstance().getViewRoot() != null) {
			return FacesContext.getCurrentInstance().getViewRoot().getViewMap().remove(name);
		} else {
			return null;
		}
	}

	public void registerDestructionCallback(String name, Runnable callback) {
		// Do nothing
	}

	public Object resolveContextualObject(String key) {
		return null;
	}

	public String getConversationId() {
		return null;
	}

}

Register the View Scope implementation

To enable the View Scope implementation in your web application, register the custom Scope implementation in your application context.

<?xml version="1.0" encoding="UTF-8"?>
<b:beans xmlns:b="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

	<b:bean
		class="org.springframework.beans.factory.config.CustomScopeConfigurer">
		<b:property name="scopes">
			<b:map>
				<b:entry key="view">
					<b:bean class="net.comdynamics.myapp.web.spring.customscope.ViewScope" />
				</b:entry>
			</b:map>
		</b:property>
	</b:bean>

</b: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:

javax.servlet.ServletException: No Scope registered for scope 'view'
	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.

package net.comdynamics.myapp.web;

import javax.inject.Named;
import org.springframework.context.annotation.Scope;

/**
 * ReportListPage JSF backing bean.
 */
@Named("reportListPage")
@Scope("view")
public class ReportListPage extends BasePage {

    @Inject
    @Named("reportService")
    private ReportService reportService;

    public ReportListPage() {
    }

    public ReportService getReportService() {
        return reportService;
    }

    public void setReportService(ReportService ReportService) {
        this.reportService = reportService;
    }

    // etc…

}

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.

4
Aug

JSF 2 with Spring 3 and JSR-330

Introduction

Recently I was involved in migrating an application from JSF 1.2 to JSF 2.0 with a stated goal of refactoring removing the plethora of JSF XML configuration files that permeated the codebase. Faces configuration files are notoriously difficult to maintain due to their verbose nature and reliance on the Expression Language (EL) syntax.

Other factors motivating the migration included:

  • Use of annotation-based configuration for managed beans across all layers within the application stack
  • Facelets templating for JSF views
  • Leveraging the Spring 3.x Inversion of Control (IoC) container support for JSR-330 – Dependency Injection for Java

Dependency Injection (DI) is a pattern for resolving component dependencies by having a container inject an instantiated component as opposed to explicitly instantiating a component from within an enclosing class.

Enable dependency injection within Spring

The Maven dependency for JSR-330 annotations can be referenced within a project POM file as follows:

<dependency>
	<groupId>javax.inject</groupId>
	<artifactId>javax.inject</artifactId>
	<version>1</version>
</dependency>

To enable Dependency Injection using JSR-330 annotations in Spring, the application context file must be updated with component scanning from the application base package as follows:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:p="http://www.springframework.org/schema/p"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:context="http://www.springframework.org/schema/context"
     xsi:schemaLocation="

http://www.springframework.org/schema/context


http://www.springframework.org/schema/context/spring-context-2.5.xsd">

     <context:component-scan base-package="net.comdynamics.myapp"/>

</beans>

This configuration element triggers annotation-based autowiring with the base-package reflecting that of your application.

Services and data tier updates

The application services and data tier for the system had originally been developed using the Spring 2.5 framework. In order to leverage JSR-330 in Spring 3.x, changes were made to remove the @Service annotation and replace with the @Named annotation.

The @Autowired annotation for Data Access Objects (DAO) was changed to @Inject, with combined updates illustrated below.

Before

package net.comdynamics.myapp;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service("reportService")
public class ReportServiceImpl implements ReportService {

	@Autowired
	private ReportFilterDao reportFilterDao;

        // bean getters/setters…
}

After

package net.comdynamics.myapp;

import javax.inject.Inject;
import javax.inject.Named;

@Named("reportService")
public class ReportServiceImpl implements ReportService {

    @Inject
    @Named("reportFilterDao")
    private ReportFilterDao reportFilterDao;

    // bean getters/setters…

}

The @Named annotation is used to associate a name for the bean. If no name is specified as an argument to the annotation, the bean name will be the name of the class with the first letter set to lowercase.

The @Inject annotation is used to identify a dependency injection point for which a dependency on a Java class or interface can be set by the container.

The upshot of this change is that bean references using the @Named annotation can be removed from the Spring application context. For example, after making changes above, the following bean declaration can be commented out deleted.

    <!--
    <bean id="reportService" class="net.comdynamics.myapp.ReportServiceImpl" />
    -->

JSF managed bean updates

JSF2 introduced the @ManagedBean annotation which was intended to minimise Faces configuration in XML files. Whilst this alleviates the need to declare JSF managed beans in XML it does not promote annotation consistency across all layers in the application.

To ensure consistency in a Spring project and alignment with JEE 6, use the JSR330 @Named annotation for JSF backing beans. For example:

package net.comdynamics.myapp.web;

import javax.inject.Named;
import org.springframework.context.annotation.Scope;

/**
 * ReportListPage JSF backing bean.
 */
@Named("reportListPage")
@Scope("request")
public class ReportListPage extends BasePage {

    @Inject
    @Named("reportService")
    private ReportService reportService;

    public ReportListPage() {
    }

    public ReportService getReportService() {
        return reportService;
    }

    public void setReportService(ReportService ReportService) {
        this.reportService = reportService;
    }

    // etc…

}

Failure to set the scope of the managed bean will result in Spring creating a Singleton instance for that bean. For JSF beans, specify the scope as Request or Session as either @Scope("request") or @Scope("session")

After making the above change, JSF managed bean references in the faces configuration files can be commented out deleted.

<?xml version='1.0' encoding='UTF-8'?>
<faces-config version='1.2' xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd">

        <!--
	<managed-bean>
		<description>Backing bean for Report List page</description>
		<managed-bean-name>reportListPage</managed-bean-name>
		<managed-bean-class>net.comdynamics.myapp.web.ReportListPage</managed-bean-class>
		<managed-bean-scope>request</managed-bean-scope>
		<managed-property>
	    	<property-name>reportService</property-name>
     		<value>#{reportService}</value>
	   	</managed-property>
	</managed-bean>
        -->

</faces-config>

Conclusion

Use of annotation-based configuration for managed beans across all layers within the application stack can simplify configuration and enforce consistency. Spring 3.x supports JSR-330 which can be leveraged by your JSF 2 application with minimal effort.

CDI (JSR-299) scopes and JSF 2 backing beans will be discussed in a future article.