Spring Web Flow 2.3.2

compared with
Version 6 by Mark Collette
on May 14, 2013 00:30.


Key
These lines were removed. This word was removed.
These lines were added. This word was added.

View page history


There are 32 changes. View first change.

 h1. Spring Web Flow 2.3.2 and ICEfaces 3.x
  
 {attachments:booking-icefaces.zip}
  
 h3. About Spring Web Flow
  
 [Spring Web Flow|http://static.springsource.org/spring-webflow/docs/2.3.x/reference/html/index.html|Spring Web Flow 2.3 Documentation] is a library that extends Spring MVC to user defined "controllers using a domain-specific-language." Web Flow is appropriately used when several actions need to be performed in order for a greater action to be performed (booking a hotel, or a flight for instance).
  
 h3. About This Tutorial
  
 This tutorial borrows heavily on existing JSF tutorials for Spring Web Flow, and is an evolution of the tutorial for [integrating ICEfaces 3.x with Spring Web Flow 2.3.1|ICE:Spring Web Flow 2.3.1].
  
  
 The purpose of this tutorial is to demonstrate how application developers can use both Spring Webflow 2.3.2 and ICEfaces 3.x in the same application. Both technologies leverage the Servlet API. Understanding how the various parts of the web.xml file are organized to accomodate both frameworks is essential to understanding this tutorial and being able to extend it to meet your own requirements.
  
 h3.
  
 This tutorial uses Spring Web Flow 2.3.2, Spring Security 3.1+, Spring Core 3.2.1, JSF 2.1.21 and ICEfaces 3.3.0 (3.0.1 and above work). Additional libraries are needed to support these frameworks and have been listed as dependencies in the tutorial's pom.xml file.
  
  
 h3. Tutorial Use Case
  
 The simple business case for this tutorial is the Spring Web Flow standard "booking application". Users can search for hotels and, after authenticating, book a room. Authenticated users can also review their bookings. This tutorial borrows heavily from the Spring Web Flow sample booking application.
  
 h3. Issue with null ViewId and WindowID
  
  
 There is an issue with the Spring implementation of the Lifecycle that affects the operation of ICEfaces. The ICEfaces BridgeSetup class uses a JSF PhaseListener to restore certain scope variables into the request map. Spring webflow applications, by default, use a POST->Redirect->GET pattern for navigation, but during the subsequent GET operation, the Spring lifecycle implementation does not appear to be executing the phase listeners during the RESTORE_VIEW phase. Side effects of this problem are the following symptoms:
  
 1) Console messages of the following form:
  
 {quote}SEVERE - Missing view ID attribute. Request map cleared prematurely.
  
 SEVERE - Missing window ID attribute. Request map cleared prematurely.
 {quote}2) NPE trying to rewrite a null viewID into the document:
  
  
 java.lang.NullPointerException
 at org.icefaces.impl.util.DOMUtils.isWhitespaceText(DOMUtils.java:394)
 at org.icefaces.impl.util.DOMUtils.printNode(DOMUtils.java:351)
 at org.icefaces.impl.util.DOMUtils.printNode(DOMUtils.java:355)
 at org.icefaces.impl.util.DOMUtils.printNode(DOMUtils.java:355)
  
 While the booking tutorial does not take advantage of windowScope, it is clear that functionality is reduced. There is a solution, and that is to use a WebflowListener to duplicate the missing PhaseListener functionality. The spring-booking-tutorial uses the com.icesoft.spring.security.WebflowListener (which is an instance of FlowExecutionListenerAdapter) class to this end. You can download a copy of this WebflowListener directly from the link above, and it is also contained and configured in the tutorial. Configuration of the WebflowListener is shown in the *webflow-config.xml* configuration section below.
  
  
  
 h3. Building the Tutorial WAR
  
 Follow the [general instructions for building any of the tutorials|ICE:Tutorials#Setup].
 ----
 {panel}
  
{panel}
  
  ----
 h3. Configuration Areas
  
 There isn't a perfect delineation between the Spring Web MVC, Web Flow and Security configurations, but they are mostly separated into their own separate files, with each requiring some entries in the _web.xml_.
  
  
 # [Configure _web.xml_ for Spring (Core, Web Flow, and Security)|#web_xml]
 # [Configure _web-application-config.xml_|#web_application_config_xml]
 # [Configure _webflow-config.xml_|#webflow_config_xml]
 # [Configure _webmvc-config.xml_|#webmvc_config_xml]
 # [Configure _security-config.xml_|#security_config_xml]
 # [Configure _data-access-config.xml_|#data_access_config_xml]
  
 {anchor:web_xml}
  
 h3. Part 1: Configure _web.xml_ for Spring
  
 The _web.xml_ below provides configuration parameters for JSF, filter configurations for Spring security, Spring and JSF Servlet declarations and listeners.
 {code:xml|title=web.xml}<?xml version="1.0" encoding="UTF-8"?>
 <web-app version="2.5"
  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-app_2_5.xsd" >
  
  <!-- Enables JSF debug output during development -->
   <context-param>
  <param-name>javax.faces.PROJECT_STAGE</param-name>
  <param-value>Development</param-value>
  </context-param>
  
  <!-- Best practice with ICEfaces -->
  <context-param>
  <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
  <param-value>server</param-value>
  </context-param>
  
  <!-- Causes Facelets to refresh templates during development -->
  <context-param>
  <param-name>javax.faces.FACELETS_REFRESH_PERIOD</param-name>
  <param-value>1</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>
  
  <!-- 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>
  
  <!-- Declare Spring Security Facelets tag library -->
  <context-param>
  <param-name>javax.faces.FACELETS_LIBRARIES</param-name>
  <param-value>/WEB-INF/springsecurity.taglib.xml</param-value>
  </context-param>
  
  <!-- Set the ACE theme. When not set, it defaults to sam -->
  <!--
  <context-param>
  <param-name>org.icefaces.ace.theme</param-name>
  <param-value>le-frog</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>
  
  <!-- Enables Spring Security -->
  <filter>
  <filter-name>springSecurityFilterChain</filter-name>
  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  </filter>
  
  <filter-mapping>
  <filter-name>springSecurityFilterChain</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>
  
  <!-- 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>2</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>
  
  <!-- Faces Servlet -->
  <servlet>
  <servlet-name>Faces Servlet</servlet-name>
  <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
  </servlet>
  
  <!-- Faces Servlet Mapping -->
  <servlet-mapping>
  <servlet-name>Faces Servlet</servlet-name>
  <url-pattern>*.jsf</url-pattern>
  </servlet-mapping>
  
  <!-- Resolve the missing PNG entry issue on old servers such as WAS7 -->
  <mime-mapping>
  <extension>png</extension>
  <mime-type>image/png</mime-type>
  </mime-mapping>
  
  <!-- Welcome File Configuration -->
  <welcome-file-list>
  <welcome-file>index.html</welcome-file>
  </welcome-file-list>
  
 </web-app>
 {code}
 
 {anchor:web_application_config_xml}
  
 h3. Part 2: Configure _web-application-config.xml_
  
 The master configuration file for Spring, as referenced in the _web.xml_ by the contextConfigLocation context-param. This file imports all the other Spring configuration files, to better separate the separate concerns into smaller more manageable files, for webmvc, webflow, database, security.
  
 {code:xml|title=/WEB-INF/config/web-application-config.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"
  xsi:schemaLocation="
  http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/context
  http://www.springframework.org/schema/context/spring-context.xsd">
  
  <!-- Scans for application @Components to deploy -->
  <context:component-scan base-package="org.springframework.webflow.samples.booking" />
  
  <!-- Imports the configurations of the different infrastructure systems of the application -->
  <import resource="webmvc-config.xml" />
  <import resource="webflow-config.xml" />
  <import resource="data-access-config.xml" />
  <import resource="security-config.xml" />
 </beans>
 {code}
 
 {anchor:webflow_config_xml}
  
h3. Part 3: Configure Spring Web Flow
  h3. Part 3: Configure _webflow-config.xml_
  
h3. Part 3: Configure Spring Web Flow
  This step provides an overview of what needs to go into the Spring Web Flow configuration file to integrate well with ICEfaces 3.x. The configuration directs Spring Web Flow to look in the /WEB-INF/flows/**/*-flow.xml pattern for web flow declarations. The tutorial application has two of these files which manage the *main* use case of hostel searching and selection, and the *booking* case for creating a reservation. There are three FlowExecutionListener declarations: FlowFacesContextLifecycleListener, for managing the FacesContext; SecurityFlowExecutionListener, for security integration; and our WebflowListener, for JSF viewId and ICEfaces windowId management.
  
This step provides an overview of what needs to go into the Spring Web Flow configuration file to integrate well with ICEfaces 3. The configuration directs Spring Web Flow to look in the /WEB-INF/flows/\**/*\-flow.xml pattern for web flow declarations. The tutorial application has two of these files which manage the main use case (create reservation) and the secondary use case (booking management). Note also the definition and references to the ICEfaces webflow listener.
  
 {code:xml|title=/WEB-INF/config/webflow-config.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.xsd
  http://www.springframework.org/schema/webflow-config http://www.springframework.org/schema/webflow-config/spring-webflow-config.xsd
  http://www.springframework.org/schema/faces http://www.springframework.org/schema/faces/spring-faces.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="facesContextListener"/>
 <webflow:listener ref="securityFlowExecutionListener" />
 <webflow:listener ref="icefacesFlowListener" />
   </webflow:flow-execution-listeners>
  </webflow:flow-executor>
  <!-- Executes flows: the central entry point into the Spring Web Flow system -->
 <webflow:flow-executor id="flowExecutor">
  <webflow:flow-execution-listeners>
 <webflow:listener ref="facesContextListener"/>
 <webflow:listener ref="securityFlowExecutionListener" />
 <webflow:listener ref="icefacesFlowListener" />
 </webflow:flow-execution-listeners>
  </webflow:flow-executor>
  
<!-- The registry of executable flow definitions -->
 <webflow:flow-registry id="flowRegistry" flow-builder-services="facesFlowBuilderServices" base-path="/WEB-INF/flows">
 <webflow:flow-location-pattern value="/**/*-flow.xml" />
  </webflow:flow-registry>
  <!-- The registry of executable flow definitions -->
 <webflow:flow-registry id="flowRegistry" flow-builder-services="facesFlowBuilderServices" base-path="/WEB-INF/flows">
 <webflow:flow-location-pattern value="/**/*-flow.xml" />
  </webflow:flow-registry>
  
<!-- Configures the Spring Web Flow JSF integration -->
 <faces:flow-builder-services id="facesFlowBuilderServices" development="true" />
  <!-- Configures the Spring Web Flow JSF integration -->
 <faces:flow-builder-services id="facesFlowBuilderServices" development="true" />
  
<!-- Installs a listener that creates and releases the FacesContext for each request. -->
 <bean id="facesContextListener" class="org.springframework.faces.webflow.FlowFacesContextLifecycleListener"/>
  <!-- Installs a listener that creates and releases the FacesContext for each request. -->
 <bean id="facesContextListener" class="org.springframework.faces.webflow.FlowFacesContextLifecycleListener"/>
  
<!-- Installs a listener to apply Spring Security authorities -->
 <bean id="securityFlowExecutionListener" class="org.springframework.webflow.security.SecurityFlowExecutionListener" />
  <!-- Installs a listener to apply Spring Security authorities -->
 <bean id="securityFlowExecutionListener" class="org.springframework.webflow.security.SecurityFlowExecutionListener" />
  
<!-- Define the ICEFaces webflow listener to restore window Scope -->
 <bean id="icefacesFlowListener" class="com.icesoft.spring.security.WebflowListener" />
   <!-- Set the viewId and windowId after a POST-Redirect-GET -->
 <bean id="icefacesFlowListener" class="com.icesoft.spring.security.WebflowListener" />
 </beans>
 {code}
{anchor:step4}
  
h3. Part 4: Configure Spring Security
  {anchor:webmvc_config_xml}
  
Configuring Spring Security to work properly with ICEfaces 2 requires configurations not included in other JSF-based Spring Webflow tutorials. This is due to the design decision to leverage Spring Security's configurable "redirectStrategy" property.
 In the file below we explicitly configure the redirect strategy used by Spring Security to send AJAX redirects if the request is AJAX driven (contains an AJAX header).
  h3. Part 4: Configure _webmvc-config.xml_
  
{code}
 <?xml version="1.0" encoding="UTF-8"?>
 <beans:beans xmlns="http://www.springframework.org/schema/security"
  xmlns:beans="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
  http://www.springframework.org/schema/security
  http://www.springframework.org/schema/security/spring-security-3.0.xsd">
  This configuration file includes the basic setup of resolving request paths to flow and view definitions, and resolving their Facelet view definition files. It declares the relevant JSF view and controller objects from Spring MVC, and JSF flow handler from Spring Web Flow.
  
<!-- Configure Spring Security -->
  <http auto-config="false"
  use-expressions="true"
  access-denied-page="/spring/login">
  
<form-login login-page="/spring/login"
  login-processing-url="/spring/loginProcess"
  default-target-url="/spring/main" always-use-default-target="true"
  authentication-failure-url="/spring/login?login_error=1" />
  {code:xml|title=/WEB-INF/config/webmvc-config.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:faces="http://www.springframework.org/schema/faces"
  xsi:schemaLocation="
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/faces http://www.springframework.org/schema/faces/spring-faces.xsd">
  
<!-- When using custom filters, please make sure the positions do not conflict with default filters.
  Alternatively you can disable the default filters by removing the corresponding child elements from
  http and avoiding the use of http auto-config='true'. -->
   <faces:resources />
  
<custom-filter ref="exceptionTranslationFilter" before="FILTER_SECURITY_INTERCEPTOR" />
   <!-- Maps request paths to flows in the flowRegistry; e.g. a path of /hotels/booking looks for a flow with id "hotels/booking" -->
  <bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping">
  <property name="order" value="1"/>
  <property name="flowRegistry" ref="flowRegistry" />
  <property name="defaultHandler">
  <!-- If no flow match, map path to a view to render; e.g. the "/intro" path would map to the view named "intro" -->
  <bean class="org.springframework.web.servlet.mvc.UrlFilenameViewController" />
  </property>
  </bean>
  
<!-- Maps logical view names to Facelet templates in /WEB-INF (e.g. 'search' to '/WEB-INF/search.xhtml' -->
  <bean id="faceletsViewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
  <property name="viewClass" value="org.springframework.faces.mvc.JsfView"/>
  <property name="prefix" value="/WEB-INF/" />
  <property name="suffix" value=".xhtml" />
  </bean>
  
<logout logout-url="/spring/logout" logout-success-url="/spring/logoutSuccess"/>
  <intercept-url pattern="/secure" method="POST" access="hasRole('ROLE_SUPERVISOR')"/>
  </http>
   <!-- Dispatches requests mapped to org.springframework.web.servlet.mvc.Controller implementations -->
  <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
  
<!--
  Define local authentication provider, a real app would use an external provider (JDBC, LDAP, CAS, etc)
   <!-- Dispatches requests mapped to flows to FlowHandler implementations -->
  <bean class="org.springframework.faces.webflow.JsfFlowHandlerAdapter">
  <property name="flowExecutor" ref="flowExecutor" />
  </bean>
 </beans>
 {code}
  
usernames/passwords are:
  keith/melbourne
  erwin/leuven
  jeremy/atlanta
  scott/rochester
  -->
  <authentication-manager>
  <authentication-provider>
  <password-encoder hash="md5"/>
  <user-service>
  <user name="keith" password="417c7382b16c395bc25b5da1398cf076"
  authorities="ROLE_USER, ROLE_SUPERVISOR"/>
  <user name="erwin" password="12430911a8af075c6f41c6976af22b09"
  authorities="ROLE_USER, ROLE_SUPERVISOR"/>
  <user name="jeremy" password="57c6cbff0d421449be820763f03139eb" authorities="ROLE_USER"/>
  <user name="scott" password="942f2339bf50796de535a384f0d1af3e" authorities="ROLE_USER"/>
  </user-service>
  </authentication-provider>
  </authentication-manager>
  {anchor:security_config_xml}
  
h3. Part 5: Configure _security-config.xml_
  
<beans:bean id="sessionManagementFilter"
  class="org.springframework.security.web.session.SessionManagementFilter">
  <beans:constructor-arg name="securityContextRepository" ref="httpSessionSecurityContextRepository" />
  <beans:property name="invalidSessionUrl" value="/auth/sessionExpired.jsf" />
  <beans:property name="redirectStrategy" ref="jsfRedirectStrategy" />
  </beans:bean>
  Configuring Spring Security to work properly with ICEfaces 2 requires configurations not included in other JSF-based Spring Webflow tutorials. This is due to the design decision to leverage Spring Security's configurable "redirectStrategy" property.
 In the file below we explicitly configure the redirect strategy used by Spring Security to send AJAX redirects if the request is AJAX driven (contains an AJAX header).
  
{code:xml|title=/WEB-INF/config/security-config.xml}<?xml version="1.0" encoding="UTF-8"?>
 ...
 ...
 {code}
  
<!-- http://static.springsource.org/spring-security/site/docs/3.1.x/reference/core-web-filters.html -->
  <beans:bean id="exceptionTranslationFilter" class="org.springframework.security.web.access.ExceptionTranslationFilter">
  <beans:property name="accessDeniedHandler" ref="jsfAccessDeniedHandler"/>
  <beans:property name="authenticationEntryPoint" ref="authenticationEntryPoint"/>
  </beans:bean>
  {anchor:data_access_config_xml}
  
<beans:bean id="jsfAccessDeniedHandler" class="com.icesoft.spring.security.JsfAccessDeniedHandler">
  <beans:property name="loginPath" value="/spring/login" />
  <beans:property name="contextRelative" value="true" />
  </beans:bean>
  h3. Part 6: Configure _data-access-config.xml_
  
<beans:bean id="authenticationEntryPoint" class="com.icesoft.spring.security.JsfLoginUrlAuthenticationEntryPoint">
  <beans:property name="loginFormUrl" value="/spring/login"/>
  <beans:property name="redirectStrategy" ref="jsfRedirectStrategy" />
  </beans:bean >
  xxx
 xxx
  
  
  <beans:bean id="jsfRedirectStrategy" class="com.icesoft.spring.security.JsfRedirectStrategy"/>
  <beans:bean id="httpSessionSecurityContextRepository" class="org.springframework.security.web.context.HttpSessionSecurityContextRepository"/>
  
 </beans:beans>
  {code:xml|title=/WEB-INF/config/data-access-config.xml}<?xml version="1.0" encoding="UTF-8"?>
 ...
 ...
 {code}
  
 h3. Resources
  
 [Spring Web Flow 2.3 Documentation|http://static.springsource.org/spring-webflow/docs/2.3.x/reference/html/index.html|Spring Web Flow 2.3 Documentation]

© Copyright 2017 ICEsoft Technologies Canada Corp.