Web Flow

Spring Web Flow Tutorial

In this example, we will demonstrate what is Spring Web-Flow, what are its benefits, and how to configure it in a web application. In the previous article, we have demonstrated how Spring MVC can be configured.

1. Introduction

Spring MVC is a powerful framework that allows the user to configure and manage the flow of web-application in any possible way. However, sometimes the scenario may require be to have a more tight control over the flow of the application or to manage the possible ways to navigate through the application.

Spring Web-Flow helps in this kind of scenario by clearly defining the views and the transition between them. Web-Flow is itself based on top of Spring MVC and hence provides all the goodies of Spring MVC plus the added control over the transitions. Let’s look at how we can configure the Web-Flow for our applications:

2. Project Set-Up

Let’s start by first setting up the project in Eclipse or any other you have in mind. We shall use Maven to setup our project. Open Eclipse and create a simple Maven project and check the skip archetype selection checkbox on the dialogue box that appears. Replace the content of the existing pom.xml with the one provided below:

pom.xml

<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>com.jcg.examples.springWebFlowExample</groupId>
  <artifactId>SpringWebFlowExample</artifactId>
  <packaging>war</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <name>SpringWebFlowExample</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
     <dependency>
        <groupId>org.springframework.webflow</groupId>
        <artifactId>spring-webflow</artifactId>
        <version>2.4.2.RELEASE</version>
    </dependency>
  </dependencies>
  <build>
    <finalName>SpringWebFlowExample</finalName>
  </build>
</project>

This will import the required JAR dependencies in the project. We can now start with the actual implementation of the Spring Web-Flow in our project.

Our application will be a simple login based application. Upon hitting the URL for the first time, the user will directed to a login page.
The user enters his credentials and clicks on the login button.
If the password is correct the view transitions to success view or else the user is directed back to the login screen.
While this is a very basic scenario for the beginner users to understand, Spring Web-Flow is capable of handling many more complex scenarios.

3. Implementation

The implementation starts with the basic PoJo for login purposes which will hold the username and password.

LoginBean.java

package com.jcg.examples.bean;

import java.io.Serializable;

public class LoginBean implements Serializable
{
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		private String userName;

		private String password;

		public String getUserName()
		{
				return userName;
		}

		public void setUserName(String userName)
		{
				this.userName = userName;
		}

		public String getPassword()
		{
				return password;
		}

		public void setPassword(String password)
		{
				this.password = password;
		}

		@Override
		public String toString()
		{
				return "LoginBean [userName=" + userName + ", password=" + password + "]";
		}

}

Next is the Service file which will authenticate the user. Based on the output of its validateUser method, web-flow will decide the view to be rendered. The Service class is marked with annotation to be picked up at run time by the Spring Bean Factory. For the sake of brevity, I have hard-coded the credentials in the source file itself.

LoginService.java

package com.jcg.examples.service;

import org.springframework.stereotype.Service;

import com.jcg.examples.bean.LoginBean;

@Service
public class LoginService
{
		public String validateUser(LoginBean loginBean)
		{
				String userName = loginBean.getUserName();
				String password = loginBean.getPassword();
				if(userName.equals("Chandan") && password.equals("TestPassword"))
				{
						return "true";
				}
				else
				{
						return "false";
				}
		}
		
}

Now, we need to implement the define the flow. Flow is basically a cycle of events that will lead to completion of a single task in the context of the application. This cycle or flow will include multiple events and the user maybe made to navigate to and fro between various views, depending upon the choice he makes. Let’s have a look at the xml that we have to use for our application for flow navigation:

book-search-flow.xml

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

	<var name="loginBean" class="com.jcg.examples.bean.LoginBean" />
	
	<view-state id="displayLoginView" view="jsp/login.jsp" model="loginBean">
		<transition on="performLogin" to="performLoginAction" />
	</view-state>

	<action-state id="performLoginAction">
		<evaluate expression="loginService.validateUser(loginBean)" />

		<transition on="true" to="displaySuccess" />
		<transition on="false" to="displayError" />

	</action-state>
	
	<view-state id="displaySuccess" view="jsp/success.jsp" model="loginBean"/>

	<view-state id="displayError" view="jsp/failure.jsp" />
</flow>

The first view in the flow becomes the default view and hence, is shown when the URL for that particular Flow is hit for the first time. Once the user submits the flow moves to action tag to determine dynamically which view should be rendered. The action directive in turn uses the backing Service Bean we created earlier.
Also the view may have a backing Bean as we have in Spring MVC, which is defined by the model attribute. The view contain two important variables which tell the container, the event that has occurred and the current state of the application. These variables are _eventId and _flowExecutionKey. When coding for the view, the developer should not forget to include these variables in the view code.

Now that the flow is ready we need to hook it up somewhere in the system, so that it can be picked up by the Spring Container.

flow-definition.xml file defines a Flow-Executor and a Flow-Registry. As the name indicates, the Flow-Executor, actually orchestrates the flow while it refers to Flow-Registry to determine the next action to be taken for the flow.

FlowHandlerMapping is responsible for creating the appropriate URLs for all the flows defined in the application.
FlowHandlerAdapter encapsulates the actual flow and delegates the specific flows to be handled by the Spring Flow Controllers. We will include this file in the main spring configuration sheet so that our web-flow gets hooked into the main Spring Container and the requests are directed to the Flow Controllers.

flow-definition.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:flow="http://www.springframework.org/schema/webflow-config"
	xsi:schemaLocation="http://www.springframework.org/schema/webflow-config
http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.4.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

	<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping">
		<property name="flowRegistry" ref="bookSearchFlowRegistry" />
	</bean>

	<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerAdapter">
		<property name="flowExecutor" ref="bookSearchFlowExecutor" />
	</bean>

	<flow:flow-executor id="bookSearchFlowExecutor" flow-registry="bookSearchFlowRegistry" />

	<flow:flow-registry id="bookSearchFlowRegistry">
		<flow:flow-location id="bookSearchFlow" path="/flows/book-search-flow.xml" />
	</flow:flow-registry>

</beans>

spring-config.xml contains the basic information for the spring container for tasks like rendering the views, bean declarations, annotation scanning etc. It also includes the flow-definition.xml file for the container to load its contents.

spring-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:flow="http://www.springframework.org/schema/webflow-config"
	xsi:schemaLocation="
	http://www.springframework.org/schema/webflow-config
	http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.4.xsd
   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">

	<context:component-scan base-package="com.jcg.examples" />

	<bean
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/jsp/" />
		<property name="suffix" value=".jsp" />
	</bean>
	
	<import resource="flow-definition.xml"/>
	
</beans>

The web.xml is similar to any spring mvc application. It starts the Spring container with the above xml and directs all the reuqests to the DispatcherServlet.

web.xml

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Spring-Flow Web-Application Example</display-name>
  
  <servlet>
		<servlet-name>springFlowApplication</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath://spring-config.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
	
	 <servlet-mapping>
      <servlet-name>springFlowApplication</servlet-name>
      <url-pattern>/</url-pattern>
   </servlet-mapping>
</web-app>

Here’s the default view of our Flow which is backed by a Spring bean. The name of the input boxes are same as the property names in the backing PoJo. In case the developer wants to name them separately, he may use the Spring tag library and the path attribute.

login.jsp

<%@ page isELIgnored="false"%>
<html>
<body>
	<h2>Please Login</h2>

	<form method="post" action="${flowExecutionUrl}">

		<input type="hidden" name="_eventId" value="performLogin"> 
		<input type="hidden" name="_flowExecutionKey" value="${flowExecutionKey}" />

		<input type="text" name="userName" maxlength="40"><br> 
		<input type="password" name="password" maxlength="40">
		<input type="submit" value="Login" />

	</form>

</body>
</html>

This is the view rendered upon successful authentication.

success.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
    <%@ page isELIgnored ="false" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Login Successful</title>
</head>
<body>
Welcome ${loginBean.userName}!!
</body>
</html>

When entering the wrong credentials, the user is notified via this view:

failure.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
    <%@ page isELIgnored ="false" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Login Successful</title>
</head>
<body>
Invalid username or password. Please try again!
</body>
</html>

Now, let’s deploy and run the code. I have used Apache Tomcat 7 for this example. Here’s the output for the first page:

spring web flow - Login page
Fig 1 : Login page

Upon successful authentication :

spring web flow - Successful login flow
Fig 2 : Successful login flow

Upon entering Invalid Credentials:

spring web flow - Failed Login Flow
Fig 3 : Failed Login Flow

4. Download The Source Code

In this example, we demonstrated how our view flows could be defined clearly and hence, managed easily and minutely using Spring Web-Flow.

Download
You can download the source code of this example here: Spring Web Flow Tutorial

Last updated on Jun. 29th, 2021

Chandan Singh

Chandan holds a degree in Computer Engineering and is a passionate software programmer. He has good experience in Java/J2EE Web-Application development for Banking and E-Commerce Domains.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

6 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
sahil
sahil
6 years ago

Hi. Not able to start application properly. On deploying application on tomcat and hitting locolhost:8080/ , it says ‘origin server did not found a current representation’

David
David
5 years ago

Hi. Great article. Looking for info to adapt to Springboot and thymeleaf. Any update??. Thanks

Lochan
Lochan
5 years ago

Hi Chandan, I am beginner for web flow, kindly tell me what the “${flowExecutionUrl}” contain and how I get to know?

In my project I also see action=”${flowExecuter.defination.id}” kindly make me understand what is the value and how it fetch the url value?

Karunan
Karunan
5 years ago
Reply to  Lochan

flowExecutionUrl to access the context-relative URI for the current flow execution

Lochan
Lochan
5 years ago
Reply to  Karunan

Thanks to reply karunan

Pushpindersingh
Pushpindersingh
1 year ago

Hi,
Able to import the project. But not able to run the project.
Run on Server is asking “Selection cannot run on server”
Using jdk1.8 with Apache 9.0

Back to top button