Integrate JSF 2.2, CDI with Spring Framework

Integrate JSF 2.2, CDI with Spring Framework

The integration issue between JSF and Spring

Original: http://blog.flexdms.com/2013/11/integrate-jsf-22-cdi-with-spring.html by jason zhang.

JSF 2.2 comes with several cool features: html5 support, resource contracts, flow scope, etc. As a JSF enthusiast, you may want to use it in your new project or update your old project. But most likely you project already has spring MVC as it is the ‘glod’ standard.

It is always a trouble to using Spring together with JSF. JSF is a completely MVC framework while Spring WebMVC is designed for other view technology to plugin. When it comes to JSF, you will find many function duplication between MVC and JSF. This duplication will definitely cause trouble for JSF developer. For example, are you going to use Spring view mapping or JSF navigation? To handle a JSF commandButton action, are you going to use Spring WebFlow action or JSF action Listener? In a Spring WebFLow, can you still use the whole JSF process flow?

This is just the beginning of the problem. JSF 2.2 is tightly coupled with CDI for some of its new feature. For example, it uses CDI for flowScope and new ViewScope.However, Spring does not fully support CDI in JEE. I guess it will become more and more difficult in using JSF under Spring as new JSF comes out in future.

Here I did some experiment to figure out how to use JSF 2.2 besides Spring Framework. The principle is not using Spring MVC for JSF.

Using two CDI frameworks

JSF 2.2 definitely needs CDI framework if FlowScope or ViewScope is used. Please note that ViewScope has many troubles in JSF 2.1 and it is the most used scope in JSF in my opnion. So I assume that CDI is definitely needed. Spring itself is a CDI framework, but not compliant with JEE standard(JSR 330). Besides Spring, we need a JSR 330-compliant CDI libary.

Do not use Spring MVC

Do not use any Spring MVC feature for your Facelet. This means you JSF facelet page is driven by FacesServlet, not by Spring MVC DispatchServlet.

Look up Spring Bean

So how can you find Spring bean in JSF code? Here is a piece code to show this:

  ApplicationContext ctx = FacesContextUtils.getWebApplicationContext(FacesContext.getCurrentInstance());
  SBeanApp beanApp=ctx.getBean("sBeanApp", SBeanApp.class);
  System.out.println("found bean with desc "+beanApp.getDesc());

I post my pom.xml and web.xml to show a real project looks like:
pom.xml


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.jason</groupId>
    <artifactId>stdjsf</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>stdjsf</name>

    <properties>
        <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
         <!-- Servlet Spec 3.0 -->
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-servlet-api</artifactId>
            <version>7.0.42</version>
            <scope>provided</scope>
        </dependency>
          <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-jsp-api</artifactId>
            <version>7.0.42</version>
            <scope>provided</scope>
        </dependency>
<!-- JSF 2.2 -->
        <dependency>
            <groupId>com.sun.faces</groupId>
            <artifactId>jsf-impl</artifactId>
            <version>2.2.4</version>
        </dependency>
        <dependency>
            <groupId>com.sun.faces</groupId>
            <artifactId>jsf-api</artifactId>
            <version>2.2.4</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
            <scope>provided</scope>
        </dependency>

       <!--

         Enables common annotations,

             which is provided by default in an EE container, on Tomcat Begin-->      

        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>jsr250-api</artifactId>
            <version>1.0</version>
            <scope>provided</scope>
        </dependency>

       <!-- Spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>3.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.5</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.0.13</version>
        </dependency>

<!-- Not used for JSF, may be used for other purpose such as RESTful communication -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>3.2.5.RELEASE</version>
        </dependency>
      <!-- WELD -->
         <dependency>
            <groupId>javax.enterprise</groupId>
            <artifactId>cdi-api</artifactId>
            <version>1.1</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.weld.servlet</groupId>
            <artifactId>weld-servlet</artifactId>
            <version>2.0.4.Final</version>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                    <compilerArguments>
                        <endorseddirs>${endorsed.dir}</endorseddirs>
                    </compilerArguments>
                    <debug>true</debug>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

Web.xml


<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">
    <display-name>stdjsf</display-name>

    <!-- Spring -->
    <welcome-file-list>
        <welcome-file>index.xhtml</welcome-file>
    </welcome-file-list>


    <!-- The master configuration file for this Spring web application -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/config/web-application-config.xml
        </param-value>
    </context-param>

   

    <!-- Enforce UTF-8 Character Encoding -->
    <filter>
        <filter-name>charEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>charEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>



    <!-- Loads the Spring web application context -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
  <listener>
    <listener-class>
        org.springframework.web.context.request.RequestContextListener
    </listener-class>
  </listener>

    <!-- JSF -->
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.xhtml</url-pattern>
        <url-pattern>/javax.faces.resource/*</url-pattern>
    </servlet-mapping>
   
    <context-param>
        <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
        <param-value>.xhtml</param-value>
    </context-param>
    <context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <param-value>Development</param-value>
    </context-param>

    <context-param>
        <description>State saving method: 'client' or 'server' (=default). See JSF Specification 2.5.2</description>
        <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
        <param-value>client</param-value>
    </context-param>
    <context-param>
        <param-name>javax.servlet.jsp.jstl.fmt.localizationContext</param-name>
        <param-value>resources.application</param-value>
    </context-param>
   
    <!-- <context-param>
        <param-name>javax.faces.CONFIG_FILES</param-name>
        <param-value>/project/project-flow.xml</param-value>
    </context-param> -->
   
    <context-param>
        <param-name>javax.faces.CLIENT_WINDOW_MODE</param-name>
        <param-value>url</param-value>
    </context-param>
   
    <!-- will be invoked automatically by Mojarra -->
    <!-- <listener>
        <listener-class>com.sun.faces.config.ConfigureListener</listener-class>
    </listener> -->


   
<!-- WELD -->
    <listener>
        <listener-class>org.jboss.weld.environment.servlet.Listener</listener-class>
    </listener>

    <resource-env-ref>
        <description>Object factory for the CDI Bean Manager</description>
        <resource-env-ref-name>BeanManager</resource-env-ref-name>
        <resource-env-ref-type>javax.enterprise.inject.spi.BeanManager</resource-env-ref-type>
    </resource-env-ref>

</web-app>

Problem solved. You can use your favorite JSF library and keep Spring enthusiast happy at the same time.