Spring MVC 3, JSF 2 with Maven 2 and Tomcat 7

So, recently I started on a little hobby-project and I wanted to see if I could harness the strengths of Spring MVC together with JSF 2 and run it all on Tomcat 7. The reasons for these are several:

  • I wanted a combined template and component-oriented approach to view construction. Granted, there are numerous different frameworks out there that does this, but I wanted to try out JSF 2 as it seems to accomplish these things in a fairly easy and concise way.
  • I don’t care much for the request handling pattern and URL scheme that JSF brings with it. Call me conservative, but I like a clean MVC pattern with a clear division of roles. Most component-based frameworks, in my opinion, blur the lines unduly between controller and view. I’m typically not very fond of automagic form generation and post-back and the whole “code behind” concept that JSF (and Tapestry and ASP.NET and a bunch of other component frameworks) use. So, Spring MVC is more my style. There are other action-oriented frameworks you can use, but Spring MVC is as good as any plus…
  • …I’m not to eager to go the full JEE CDI approach. Spring can still offers most of what JEE’s got and it’s definitely got everything I need. It also has some things that JEE is still lacking, for example AOP support. With Spring’s support for JPA, the circle is complete and CDI is out.
  • Since I don’t need the full JEE package, I can deploy it all in my favorite light-weight container, Tomcat. Yay!
  • Since I prefer to write most of my client-side code myself (using either Prototype/Scriptaculous or jQuery), I typically fore go using pre-built component frameworks (PrimeFaces et al). In my experience, they tend to deliver almost what you want, but never exactly. And when you run into situations and cases that aren’t covered by the framework, you’ll start to work against it instead of with it, which usually ends up burning more time than just doing it yourself.

You’d think that all this would be a walk in the park. I mean, it’s two of the largest and most established frameworks – there should be no problems bridging the two, right? Well, it turns out it’s still a bit tricky…

The major obstacles that you have to get around are

  1. Exposing Spring beans to the JSF pages.
  2. Using JSF pages as views in a Spring MVC request.
  3. Maintaining the JSF resource handling intact
  4. Maintaining support for JSF form handling (h:form, h:commandButton etc)

Number 4, i found, is very tricky to accomplish. It would require hooking in a lot of custom handling into the JSF framework. However, like I stated previously, I don’t even want the whole automagical form madness. I wan’t clean forms I have full control over myself (sometimes I want regular posts, sometimes I want AJAX posts with HTML fragment responses, sometimes I want AJAX posts with JSON responses), and I want them to post to my own Spring MVC controllers. With all that in mind, I completely dropped trying to get JSF h:form to work. If you can’t live without that, this might not be for you.

So, without further ado I’ll just go ahead and show you my various configurations.

Directory layout

Standard maven 2 layout, with JSF conventions applied

- src/
  - main/
    - java/ (Java sources
    - resources/
      - applicationContext.xml (spring config)
      - log4j.properties (log4j config)
      - MessageResources.properties (JSF message resources)
      - META-INF/
        - persistence.xml (JPA config)
    - webapp/
      - resources/ (all static resources go in this directory, as per JSF convention)
      - WEB-INF/
        - faces-config.xml (JSF config)
        - web.xml (web config)
        - templates/ (all JSF templates go in this directory)
        - views/ (all JSF views go in this directory)

pom.xml

The maven config is fairly standard. As you might notice, I’m using the reference implementation of JSF 2 here (Mojarra). If you plan to deploy this to a JEE container (JBoss etc), you should mark them as “provided” to prevent conflicts. You might also notice I’m including spring-webflow. I’m not planning to use anything related to webflow as such, but that module includes the class JsfView that is required when configuring Spring MVC to use JSF views. You could strip that class out and clean it up from webflow dependencies, but I couldn’t be bothered yet.

<?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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>helloworld</groupId>
    <artifactId>helloworld</artifactId>
    <packaging>war</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>helloworld</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.sun.faces</groupId>
            <artifactId>jsf-api</artifactId>
            <version>2.0.4-b09</version>
        </dependency>
        <dependency>
            <groupId>com.sun.faces</groupId>
            <artifactId>jsf-impl</artifactId>
            <version>2.0.4-b09</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>3.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>3.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>3.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.webflow</groupId>
            <artifactId>spring-faces</artifactId>
            <version>2.3.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jul-to-slf4j</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2.2</version>
        </dependency>
    </dependencies>
</project>

web.xml

No mysteries here, just bootstrap spring and JSF, make Spring handle everything except .jsf requests (this is so the JSF resource handling will still work, more on that further down).

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        classpath:applicationContext.xml
    </param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
    <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet>
    <servlet-name>DispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.jsf</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>DispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

faces-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<faces-config version="2.0" 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_2_0.xsd">

    <application>
        <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
        <resource-handler>helloworld.faces.application.CustomResourceHandler</resource-handler>
        <locale-config>
        </locale-config>
        <resource-bundle>
            <base-name>MessageResources</base-name>
            <var>messages</var>
        </resource-bundle>
    </application>
</faces-config>

The SpringBeanFacesELResolver is the glue that allows JSF pages to use Spring beans is if they were @ManagedBeans. In our scenario here, I’m completely ignoring to use @ManagedBean. Instead, I’m using spring beans and spring scopes.

The CustomResourceHandler is something I wrote to allow the JSF resource handler to function properly. This is needed if you want JSF tags like h:outputScript, h:outputStylesheet and h:graphicImage to work (served from the webapp/resources directory).

CustomResourceHandler

package helloworld.faces.application;

import javax.faces.application.Resource;
import javax.faces.application.ResourceHandler;
import javax.faces.application.ResourceHandlerWrapper;
import javax.faces.application.ResourceWrapper;
import javax.faces.context.FacesContext;

import com.sun.faces.util.Util;

/**
 * Custom JSF ResourceHandler.
 * 
 * This handler bridges between Spring MVC and JSF managed resources. The handler takes
 * care of the case when a JSF facelet is used as a view by a Spring MVC Controller and the
 * view uses components like h:outputScript and h:outputStylesheet by correctly pointing the 
 * resource URLs generated to the JSF resource handler.
 * 
 * The reason this custom handler wrapper is needed is because the JSF internal logic assumes
 * that the request URL for the current page/view is a JSF url. If it is a Spring MVC request, JSF
 * will create URLs that incorrectly includes the Spring controller context.
 * 
 * This handler will strip out the Spring context for the URL and add the ".jsf" suffix, so the
 * resource request will be routed to the FacesServlet with a correct resource context (assuming the
 * faces servlet is mapped to the *.jsf pattern). 
 * 
 *
 */
public class CustomResourceHandler extends ResourceHandlerWrapper {

    private ResourceHandler wrapped;
    
    public CustomResourceHandler(ResourceHandler wrapped) {
        this.wrapped = wrapped;
    }
    @Override
    public ResourceHandler getWrapped() {
        return this.wrapped;    
    }
    
    @Override
    public Resource createResource(String resourceName, String libraryName) {
        return new CustomResource(super.createResource(resourceName, libraryName));
    }
    @Override
    public Resource createResource(String resourceName, String libraryName,
            String contentType) {
        return new CustomResource(super.createResource(resourceName, libraryName, contentType));
    }
    
    private static class CustomResource extends ResourceWrapper {

        private Resource wrapped;
        
        private CustomResource(Resource wrapped) {
            this.wrapped = wrapped;
        }
        @Override
        public Resource getWrapped() {
            return this.wrapped;
        }
        @Override
        public String getRequestPath() {
            String path = super.getRequestPath();
            FacesContext context = FacesContext.getCurrentInstance();
            String facesServletMapping = Util.getFacesMapping(context);
            // if prefix-mapped, this is a resource that is requested from a faces page
            // rendered as a view to a Spring MVC controller.
            // facesServletMapping will, in fact, be the Spring mapping
            if (Util.isPrefixMapped(facesServletMapping)) {
                // remove the Spring mapping
                path = path.replaceFirst("(" + facesServletMapping + ")/", "/");
                // append .jsf to route this URL to the FacesServlet
                path = path.replace(wrapped.getResourceName(), wrapped.getResourceName() + ".jsf");
            }
            return path;
        }
        
    }
}

applicationContext.xml

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

    <context:component-scan base-package="helloworld" />
    
    <!-- map all requests to /resources/** to the container default servlet (ie, don't let Spring handle them) -->
    
    <bean id="defaultServletHttpRequestHandler" class="org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler" />
    
    <bean id="simpleUrlHandlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="urlMap">
        <map>
            <entry key="/resources/**" value-ref="defaultServletHttpRequestHandler" />
        </map>
        </property>
    </bean>
    
    <bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter" />
    
    <mvc:annotation-driven/>
    <bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
        <property name="cache" value="false" />
        <property name="viewClass" value="org.springframework.faces.mvc.JsfView" />
        <property name="prefix" value="/WEB-INF/views/" />
        <property name="suffix" value=".xhtml" />
    </bean>
</beans>

Important things to note here is the view resolver that uses the JsfView viewClass. Like I mentioned, this class lives in the spring-faces module in spring webflow. The three beans defined above are to allow static resources to be served outside of Spring. Since I mapped the DispatcherServlet in web.xml to the “/” url-pattern, Spring will effectivly take over all request handling. The DefaultServletHttpRequestHandler is a handler that will defer handling to the container default handler (ie Tomcats default servlet). My simpleUrlHandlerMapping maps all requests to /resources/** to the default handler and finally you need to create and adapter (HttpRquestHandlerAdapter) that can work with the handler. All this will allow me to access static resources under the /resources folder using normal linking. I mentioned above that my custom resource handler allowed me to access them using JSF tags like h:graphicImage. This is for when I want to access them using a regular img, script or link tag.

Hello World example

This short example consists of 2 java classes (controller and model) and one JSF xhtml page (view).

HelloWorld (request-scoped model bean>

package helloworld;

import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;

@Component
@Scope(value="request", proxyMode=ScopedProxyMode.TARGET_CLASS)
public class HelloWorld {
    private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

HelloWorldController (controller)

package helloworld;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class HelloWorldController {

	@Autowired
	private HelloWorld helloWorld;
	
	@RequestMapping(value="/helloWorld", method=RequestMethod.GET)
	public void helloWorld() {
		helloWorld.setMessage("Hello World from Spring MVC to JSF");
	}
	@RequestMapping(value="/helloWorld", method=RequestMethod.POST)
	public void helloWorldPost(@RequestParam String msg) {
		helloWorld.setMessage(msg);
	}
}

helloWorld.xhtml (JSF view, goes under WEB-INF/views

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets">
    <h:head>
        <title>Hello World</title>
    </h:head>
    <h:body>
        <p>${helloWorld.message}</p>
        <p>Say something: </p>
        <form method="post">
            <input name="msg" type="text"></input>
            <input type="submit" ></input>
        </form>
        <p>This image is linked as a JSF resource</p>
        <h:graphicImage name="images/jsf.jpg" />
        <p>This image is directly linked</p>
        <img src="resources/images/spring.png"></img>
    </h:body>
</html>

As you can see, this page demonstrates how to access a Spring bean using JSF EL (${helloWorld.message}) and how to render an image both using JSF resources handling and a regular img tag. You will have to manually put some images in the webapp/resources/images folder to try this.
Posting the form should reload the page and display the message you entered in the textbox.

28 Responses to Spring MVC 3, JSF 2 with Maven 2 and Tomcat 7

  1. Javier says:

    Hi:
    I don’t try it yet but i believe that’s it’s work, great!!, so the moral it’ s don’t try this with some jsf implementation others than mojarra!
    Thanks

    • pellep01 says:

      It should work with other implementations as well (as long as they follow the JSF 2 spec), except the CustomResourceHandler might need some minor tweaking. It makes use of a util-class (com.sun.faces.util.Util) for convenience that may or may not be included in other implementations. IBM WebSphere:s JSF implementation has it, not sure about others (jBoss etc). You should be able to refactor it out though as all it’s doing is checking if the first character of the mapping is a ‘/’.

      public static boolean isPrefixMapped(String mapping) {
      return (mapping.charAt(0) == ‘/’);
      }

      Just remember to mark the com.sun.faces dependencies with “provided” scope in the pom.xml if you are deploying to a JEE container (like JBoss) so they don’t get packaged with the web app and cause conflict. Or better yet, substitute the dependencies for the jsf-api library included with the container.

  2. Jay says:

    How to submit a form to controller ? Could you please give some examples ….? Thanks in advance…

    • pellep01 says:

      No magic needed. Just a regular form should do the trick


      <form action="post">
      <input type="text" name="someProperty"><input type="submit">
      </form>


      class MyModel {
      private String someProperty;
      public String getSomeProperty { return someProperty; }
      public void setSomeProperty(String val) { someProperty = val; }
      }


      @Controller
      class MyController {
      @RequestMapping(value="/bla", method=RequestMethod.POST)
      public void handlePost(@ModelAttribute MyModel myModel) {
      // do something with myModel.
      }
      }

  3. Jay says:

    Thanks for your quick response. I’am asking how to submit a JSF form to spring controller.In spring MVC we are using form tag library. COuld you please give a sample of code just like above to submit a JSF form to Spring controller and redirecting user to the new view. Also Can we use ModelAndView ?

  4. jsfSpringTester says:

    this code is working!) can you please provide a sample project with some features for download? )

  5. You have to download the project?

    • pellep01 says:

      Which project are you referring to? Easiest is to use the POM i posted and create a new project from it (depending on which IDE you use. IntelliJ supports maven nativly, Eclipse needs plugins). Then modify the directory structure as I indicated and add files to it.

  6. victor says:

    help, say :

    PageNotFound W org.springframework.web.servlet.DispatcherServlet noHandlerFound No mapping found for HTTP request with URI [/Prueba8SecurityJSFWeb/WEB-INF/views/helloWorld.xhtml] in DispatcherServlet with name ‘DispatcherServlet’

    why?

  7. Do Minh Tuan says:

    Thank you usefull article. But i have problem with while using FacesContext.getCurrentInstance() in a managed bean. Anyone have idea for this.

    Thanks and regards,
    TuanDo

  8. Dawid says:

    I hope you can help me.

    I`ve done eveyrthing in the same way BUT if I won`t return a String name of template or a ModelAndView object I get exception that he can`t find file “.xhtml” cause he doesn not get any file name. Should he take the name from method I mean shouldn`t the template name be same like method name but automaticly ?

    Second thing is that ${helloWorld.message} does not work for me. It`s just a blank space, no error no nothing. Please help, I’m so close thanks to you 🙂

    • Dawid says:

      Hmm I had to miss some detail. I`ve cleaned my pom, checked the configuration and it worked. Thank you very much for this article

    • Dawid says:

      Hmm but I still have to provide template name. Why is that ?

      • Baskar says:

        Though i return template name or ModelAndView, still its not working. 😦 Throws a java.lang.NullPointerException at javavax.faces.application.ResourceWrapper.getRequestPath.
        Actually CustomResourceHandler.createResource takes resourceName = images/jsf.jpg
        and libraryName = null and returns null rather than CustomResource object.
        Do we miss anything in the config part ?

  9. Aziz says:

    Thank you it’s a very usefull article

  10. Bobi says:

    I have one remark. The part with the custom resource handler writing may be avoided. We can use instead.

  11. Bobi says:

    mvc/resources annotation

  12. SANSSAN says:

    Do u have this example to download? I am new to JSF, would like to download and try.

  13. Claudio says:

    Hi,

    I’m trying to run this project, but I get an “bug” on Internet Explorer 9 with Document Mode: IE9 Standards. It simply don’t load the css.

    In Chrome, Firefox, IE 7 and 8, IE 9 with IE8 Standards / IE7 Standards it runs ok.

    What I could to do to fix this “bug”???

    In My helloWorld file, I added:

    xmlns:p=”http://primefaces.org/ui”

    and

    abc

  14. Claudio says:

    In My helloWorld file, I added:

    xmlns:p=”http://primefaces.org/ui”

    and

    |p:panel header=”qualquercoisa”| abc |p:panel|

  15. leader says:

    Hello .
    I am new jsf 2 and can somebody help me
    how do ajax to show another form in same xhtml page. for example in jsp html we use div from javascript xmlhttpsend and xmlhttpopen

  16. Hi,
    This solution was extremly helpfull for me, especially the CustomResourceHandler class.

    I want to use it in my own projects. What are the conditions of using the source of this class.

    Thansk for the answer!

  17. Yassine says:

    Thank you for your effort this helped me. but I’m wandering how to handle navigations between JSF pages throw Controllers. I’m new to Spring MVC and JSF too and I want to take advantage of JSF components (primefaces) and handle views throw controllers of MVC Spring.
    Could you give me an example in how to forward JSF page from an other JSF page using a controller.
    Thanks !

  18. Zhandos says:

    Hi. Your example is awesome.
    Could you give examlpe JSF ajax + spring’s controller.

  19. Olga says:

    Thanks!

  20. prox says:

    Its not working….. :(((((

  21. prox says:

    ok its working ;D lol… why its always that way… that first of all u have to admire to whole world that u sux and after that its working ;D

Leave a reply to leader Cancel reply