Creating A Spring Web Flow JSF Project From Scratch

(Updated – 9 August 2010 – This was written in my pre-Maven days and after a few requests for working source, I’ve built the same project using Maven which can be downloaded. Just unzip the maven project, go to the directory in the command line and type mvn jetty:run to start the server and deploy the project. Navigate to http://localhost:8080/swfproject/home.jsf or http://localhost:8080/swfproject/spring/testFlow to see the pages demonstrated in the tutorial.

I recently had to start another project using Spring Web Flow and found myself banging my head against a brick wall to get the web flow stuff set up and to request the page properly. As a result, I decided to write up my results as a quick how-to for other developers should they find themselves in the same situation and also as a reference for myself the next time I need to start a Spring Web Flow project using Spring Faces from scratch.This article is meant more of a “here’s-how” as opposed to a “how-to” or an “explain-why” so we’ll move at a quick pace with little explanation.

For the IDE, I used Eclipse 3.4.1 with Spring IDE plugins version 2.2.1, with Spring 2.5.6 (with dependencies) and Spring Web Flow 2.0.5. You should be able to use SWF 2.0.7 without any problems. For dependencies, I mostly used the ones provided with Spring except for a few, the details of which are included below. Hibernate was used as the JPA implementation and it was all deployed on Tomcat 6.0.18.

Getting Started

U started by installing Eclipse and then the Spring plugins. I created a new workspace, and added Tomcat as a server, and created a new dynamic web project. I right clicked on the project to add the Spring nature to the project.
The project will be arranged with multiple Spring configuration files for the database, spring web flow and the main applicationContext.xml to include them in. Flows will be in a directory under /WebContent/WEB-INF/flows.
I started off setting web.xml up with the faces pieces and the Spring MVC dispatcher servlet.

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

	<!-- Use JSF view templates saved as *.xhtml, for use with Facelets -->
	<context-param>
		<param-name>javax.faces.DEFAULT_SUFFIX</param-name>
		<param-value>.xhtml</param-value>
	</context-param>

	<!-- Enables special Facelets debug output during development -->
	<context-param>
		<param-name>facelets.DEVELOPMENT</param-name>
		<param-value>true</param-value>
	</context-param>

	<!-- Causes Facelets to refresh templates during development -->
	<context-param>
		<param-name>facelets.REFRESH_PERIOD</param-name>
		<param-value>1</param-value>
	</context-param>

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

	<!-- Serves static resource content from .jar files such as spring-faces.jar -->
	<servlet>
		<servlet-name>Resources Servlet</servlet-name>
		<servlet-class>
			org.springframework.js.resource.ResourceServlet
		</servlet-class>
		<load-on-startup>0</load-on-startup>
	</servlet>

	<!-- Map all /resources requests to the Resource Servlet for handling -->
	<servlet-mapping>
		<servlet-name>Resources Servlet</servlet-name>
		<url-pattern>/resources/*</url-pattern>
	</servlet-mapping>

	<!-- The front controller of this Spring Web application, responsible for handling all application requests -->
	<servlet>
		<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
		<servlet-class>
			org.springframework.web.servlet.DispatcherServlet
		</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value></param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<!-- Map all /spring requests to the Dispatcher Servlet for handling -->
	<servlet-mapping>
		<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
		<url-pattern>/spring/*</url-pattern>
	</servlet-mapping>

	<!-- Just here so the JSF implementation can initialize, *not* used at runtime -->
	<servlet>
		<servlet-name>Faces Servlet</servlet-name>
		<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<!-- Just here so the JSF implementation can initialize -->
	<servlet-mapping>
		<servlet-name>Faces Servlet</servlet-name>
		<url-pattern>*.jsf</url-pattern>
	</servlet-mapping>

	<welcome-file-list>
		<welcome-file>index.html</welcome-file>
	</welcome-file-list>
	

At the top of this web.xml file, we indicate that our primary Spring bean config file is called \WEB-INF\applicationContext.xml so we navigate to that folder (it’s in the WebContent folder) and right click and add a new Spring Bean Definition. We also add two other Spring Bean definitions in the same place called dbConfig.xml and flowConfig.xml. These files are defined below :

/WebContent/WEB-INF/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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema
/beans/spring-beans.xsd">

	<import resource="dbConfig.xml" />
	<import resource="flowConfig.xml" />
</beans>

/WebContent/WEB-INF/dbConfig.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:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

	<bean id="entityManagerFactory"
	class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="jpaVendorAdapter">
			<bean
	class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
				<property name="showSql" value="true" />
				<property name="generateDdl" value="true" />
				<property name="databasePlatform"
	value="org.hibernate.dialect.MySQLDialect" />
			</bean>
		</property>
		<property name="dataSource" ref="dataSource" />
		
	</bean>

	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
	destroy-method="close">
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="jdbc:mysql://localhost:3306/dbName" />
		<property name="username" value="someUser" />
		<property name="password" value="somePassword" />
	</bean>

	<bean id="transactionManager"
	class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="dataSource" ref="dataSource" />
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	</bean>

	<tx:annotation-driven />

	<bean
	class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

</beans>

/WebContent/WEB-INF/flowConfig.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:webflow="http://www.springframework.org/schema/webflow-config"
	xmlns:faces="http://www.springframework.org/schema/faces"
	xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/webflow-config
           http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd
           http://www.springframework.org/schema/faces
           http://www.springframework.org/schema/faces/spring-faces-2.0.xsd">

	<!--Executes flows: the central entry point into the Spring Web Flow system-->
	<webflow:flow-executor id="flowExecutor">
		<webflow:flow-execution-listeners>
			<webflow:listener ref="jpaFlowExecutionListener" />

		</webflow:flow-execution-listeners>
	</webflow:flow-executor>

	<!-- The registry of executable flow definitions -->
	<webflow:flow-registry id="flowRegistry"
	flow-builder-services="facesFlowBuilderServices">
		<webflow:flow-location path="/WEB-INF/flows/testFlow/testFlow.xml"></webflow:flow-location>
	</webflow:flow-registry>

	<!-- Configures the Spring Web Flow JSF integration -->
	<faces:flow-builder-services id="facesFlowBuilderServices"
	development="true" />

	<!--
		Installs a listener that manages JPA persistence contexts for flows
		that require them
	-->
	<bean id="jpaFlowExecutionListener"
	class="org.springframework.webflow.persistence.JpaFlowExecutionListener">
		<constructor-arg ref="entityManagerFactory" />
		<constructor-arg ref="transactionManager" />
	</bean>

	<!-- Maps request URIs to controllers -->
	<bean
	class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">

		<property name="mappings">

			<value>
				/testFlow=flowController
			</value>
		</property>
		<property name="defaultHandler">
			<!--
				Selects view names to render based on the request URI: e.g. /main
				selects "main"
			-->
			<bean
	class="org.springframework.web.servlet.mvc.UrlFilenameViewController" />
		</property>
	</bean>

	<!-- Handles requests mapped to the Spring Web Flow system -->
	<bean id="flowController"
	class="org.springframework.webflow.mvc.servlet.FlowController">
		<property name="flowExecutor" ref="flowExecutor" />
		
	</bean>

</beans>

Since we are using JPA, we need to include a persistence.xml file to the classpath in src/META-INF/persistence.xml.

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
      http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
	version="1.0">

    <persistence-unit name="default" transaction-type="RESOURCE_LOCAL" />
    
</persistence>

Now we need to add a faces-config.xml in the same location.

/WebContent/WEB-INF/faces-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<faces-config version="1.2" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xi="http://www.w3.org/2001/XInclude"
	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">

	<application>
		<el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
		<view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
	</application>
</faces-config>

We’ll also add a html page that redirects to a jsf page immediately. Since we are using facelets, we’ll also throw in a template to work from. In the WebContent folder, we’ll add the templates directory to contain our page layout.

WebContent\templates\template.xhtml

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:f="http://java.sun.com/jsf/core">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

  <title><ui:insert name="title">untitled</ui:insert></title>

</head>

<body>
  <h:messages globalOnly="true" />

  <ui:insert name="title">Title Here</ui:insert>

  <ui:insert name="body" />
</body>
</html>

Now to add our two pages that will initially launch is into a JSF page.

/WebContent/index.html

<html>
<head>
  <meta http-equiv="Refresh" content="0; URL=home.jsf">
</head>
</html>
</textarea> <code>/WebContent/home.xhtml</code> <pre name="code" class="xml">
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:h="http://java.sun.com/jsf/html"
	template="templates/template.xhtml">

	<ui:define name="body">
		<h:form>

			<h:outputText value="Hello From JSF!" />

		</h:form>
	</ui:define>
</ui:composition>

One more configuration file we need is for Log4j to get rid of the warnings that it has not been set up correctly. The properties file goes in the src directory.

/src/log4j.properties

### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
log4j.rootLogger=info, stdout
log4j.category.org.springframework=WARN
log4j.category.org.hibernate=WARN

Now let’s look at libraries. There are a bunch of libraries that are needed since we haven’t added any yet.

Libraries located in /WebContent/WEB-INF/lib/
antlr-2.7.6.jar Spring Dependencies
cglib-nodep-2.1_3.jar Spring Dependencies
commons-beanutils.jar Spring Dependencies
commons-collections.jar Spring Dependencies
commons-dbcp.jar Spring Dependencies
commons-digester.jar Spring Dependencies
commons-logging.jar Spring Dependencies
commons-pool.jar Spring Dependencies
dom4j-1.6.1.jar Spring Dependencies
hibernate3.jar Spring Dependencies
hibernate-annotations.jar Spring Dependencies
hibernate-commons-annotations.jar Spring Dependencies
hibernate-entitymanager.jar Spring Dependencies
javaee.jar Obtained From Glassfish
javassist-3.4.GA.jar Spring Dependencies
jboss-el.jar Obtained From JBoss Seam
jsf-api.jar Downloaded Mojarra 1.2_11
jsf-facelets.jar Obtained From JBoss Seam
jsf-impl.jar – Downloaded Mojarra 1.2_11
log4j-1.2.15.jar Spring Dependencies
mysql-connector-java-5.0.4-bin.jar Downloaded from MySQL
org.springframework.binding-2.0.5.RELEASE.jar Spring Web Flow Download
org.springframework.faces-2.0.5.RELEASE.jar Spring Web Flow Download
org.springframework.js-2.0.5.RELEASE.jar Spring Web Flow Download
org.springframework.webflow-2.0.5.RELEASE.jar Spring Web Flow Download
persistence.jar Spring Dependencies
slf4j-api-1.5.0.jar Spring Dependencies
slf4j-log4j12-1.5.0.jar Spring Dependencies
spring.jar Spring Dependencies
spring-webmvc.jar Spring Dependencies

One reason I downloaded the latest JSF version was because I was having problems with the JSF version I was using. The SpringBeanELResolver was being ignored at run-time, it didn’t even blink if I set it to an undefined class name, however the Spring Delegating variable resolver was working, but the IDE was saying it was deprecated. Once I upgraded to JSF 1.2_11, the EL resolver worked fine. I’m wondering if an old 1.1 JSF version had crept in there.

Depending on where you end up deploying your application (i.e.Glassfish), you may end up having to remove some of these libraries if they are already installed on the server. In this case, I was using Apache 6.0.18 with a clean install, and therefore with no libraries added to the server.

Creating a Flow

Now we should have a working application all ready to go. To test this, we’ll add a little code and a test page just to verify that everything is working ok. Create a new class called MessageHolder in a package called swfproject. This is a simple class that contains a string that can be set and retrieved from our pages.

/src/swfproject/MessageHolder.java

package swfproject;

public class MessageHolder implements Serializable {

	private String text = "Hello From the Message Holder";
	
	public String getText() {
		return text;
	}
	
	public void setText(String text) {
		this.text = text;
	}
}

Now we define the bean in the /WEB-INF/applicationContext.xml so we can call the bean from a regular jsf page, or a flow page.

	<bean name="springMessage" class="swfproject.MessageHolder">
		<property name="text" value="This was defined in Spring" />	
	</bean>

In the home.xhtml page, we add the following line to the page, somewhere between the ui:define tag on the page to display the message

Message is : #{springMessage.text}

If you start Tomcat, assuming you have already attached the project to the server if you are using an IDE, and go to http://localhost:8080/projectName/ you should redirect to home.jsf and it should the message that was defined in Spring.

Now let’s add a simple flow called testFlow. If you look in the flowConfig.xml file, there is already a mappings property where we already defined /testFlow=flowController. This means that when this url is requested, the flowController bean instance deals with it.

We need to create a new directory under /WebContent/WEB-INF/flows/, and in there, we need to create a directory called testFlow. In that, we add a page called testFlow.xhtml and testFlow.xml. This is the view and the flow defined respectively.
/WebContent/WEB-INF/flows/testFlow/testFlow.xml Flow

<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:faces="http://www.springframework.org/schema/faces"
	xsi:schemaLocation="http://www.springframework.org/schema/webflow
        http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"
	start-state="testFlow">

	<var name="flowMessage" class="swfproject.MessageHolder" />
	<view-state id="testFlow">
		<transition on="post" to="testFlow" />
	</view-state>
</flow>

/WebContent/WEB-INF/flows/testFlow/testFlow.xhtml View

<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:h="http://java.sun.com/jsf/html"
	template="/templates/template.xhtml">

	<ui:define name="body">
		<h:form>
			<h:outputText value="This is a test flow" />
			<br />
			Message From Spring = #{springMessage.text}<br />
			Message From Flow = #{flowMessage.text}<br />
			<h:inputText value="#{flowMessage.text}" style="width:200px" />
			<h:commandButton action="post" value="Update" />
 		</h:form>
	</ui:define>
</ui:composition>

Now if you go to http://localhost:8080/app_name/spring/testFlow (Remember to replace app_name with your project name) you should see the page we have made as part of our flow. It displays the message from the springMessage instance of the MessageHolder that contains the message from spring. I now also displays the instance from the flow which contains the default message displayed since the flow variable hasn’t had the text property changed. Also, the URL should change to http://localhost:8080/app_name/spring/testFlow?execution=e3s1 or something similar with the execution on the end. This page demonstrates that we will have access to the spring beans, as well as access to variables defined in a flow which is where flowMessage is defined. You can edit the message and click post to change the flowMessage text value. You can open the link up in two browser windows or tabs and see how the two values can be edited independently, and the flowMessage variable is scoped to the flow in the browser.
From this point, you can go ahead and move on with the application all you like. You can change the flow mappings, put in wildcarded flow locations, even get started with trying out Spring MVC (as I plan to). Hope you find this useful as a quick start guide to getting a JSF Spring Web Flow project up and running, as well as defining flows and calling them.

5 thoughts on “Creating A Spring Web Flow JSF Project From Scratch

  1. Hi Andy,

    I did the same thing step by step but in the flowconfig.xml ,it is showing errors

    “cvc-complex-type.2.4.c: The matching wildcard is strict, but no declaration can be found for element ‘webflow:flow-executor”

    I am using Eclipse 3.5 Galileo, Kindly help on this

  2. i would like to test the webflow project in jbossenterprise application server. i am getting incomplete deploymentexception.i removed the dependencies from maven.
    then copied the libraries in lib in webapp. Still unable to deploy on Jboss EAP 5.1