Spring Web Flow 2.3.1 and ICEfaces 3.0.1
About Spring Web FlowSpring Web Flow 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). About This TutorialThis tutorial borrows heavily on existing JSF tutorials for Spring Web Flow. Along the lines of JSF 2.1.0-b04 or so, JSF introduced some methods in the FacesContext class whose default implementation was to throw an UnsupportedOperationException.Spring has not (as of this writing) released a new version of Webflow to address these changes, so to combine Spring webflow, JSF, and ICEFaces required an odd concoction of older versions. Furthermore, ICEfaces 3 required a newer version of JSF, meaning ICEfaces 3 wasn't at all compatible with Spring Webflow 2.3.0. Spring Source community has now resolved https://jira.springsource.org/browse/SWF-1467, and a fix is available through Maven using a snapshot build. The pom.xml file included in the download bundle refers to the nightly build of 26 March that has is known to work, but this should be updated to the general release once it's released. The purpose of this tutorial is to demonstrate how application developers can use both Spring Webflow 2.3.1 and ICEfaces 3 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. This tutorial uses Spring Webflow 2.3.1 (prerelease build), Spring Security 3, Spring 3.0.5, JSF 2.1.3 and ICEfaces 3.0.1. Additional libraries are needed to support these frameworks and have been noted in the tutorial's build.xml or pom.xml file. This tutorial also has been tested with the Spring Web Flow 2.2.1 library as well. The JSF version in the attached tutorial is 2.1.3. Tutorial Use CaseThe simple business case for this tutorial 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 heavliy from the Spring Web Flow sample booking application. Issue with NULL ViewId and WindowIDThere 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: 2) NPE trying to rewrite a NULL viewID into the document: java.lang.NullPointerException 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 booking-icefaces 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 Listener directly from the link above, and it is also contained and configured in the tutorial. Configuration of the Listener is shown in the webflow-config.xml configuration section below. Building the Tutorial WarThe booking-icefaces tutorial is located in the icefaces/tutorials directory in the ICEfaces source distribution. It can be built either with ant or maven.
Configuration Areas:
Part 1: Configure Web.xml For SpringThe web.xml below provides (in order) configuration parameters for JSF, filter configurations for spring security, Spring and JSF Servlet declarations and listeners. web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app 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" version="2.5"> <!-- 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>javax.faces.PROJECT_STAGE</param-name> <param-value>Development</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> <!-- 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> <servlet> <servlet-name>Icefaces Resources Servlet</servlet-name> <servlet-class>com.icesoft.faces.webapp.CompatResourceServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <!-- 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> <servlet-mapping> <servlet-name>Icefaces Resources Servlet</servlet-name> <url-pattern>/xmlhttp/*</url-pattern> </servlet-mapping> <!-- 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> </web-app> Part 2: Configure Spring SecurityThis step builds upon the configurations we put into the web.xml file. The two files shown in this step are the general The file immediately below (applicationContext.xml) includes 4-other spring configurations for security, webflow, database, and webmvc. /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" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.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="config/webmvc-config.xml" /> <import resource="config/webflow-config.xml" /> <import resource="config/data-access-config.xml" /> <import resource="config/security-config.xml" /> </beans> Part 3: Configure Spring Web FlowThis 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. /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> <!-- 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" /> <!-- 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" /> <!-- Define the ICEFaces webflow listener to restore window Scope --> <bean id="icefacesFlowListener" class="com.icesoft.spring.security.WebflowListener" /> </beans> Part 4: Configure Spring SecurityConfiguring 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. <?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"> <!-- 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" /> <!-- 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'. --> <custom-filter ref="exceptionTranslationFilter" before="FILTER_SECURITY_INTERCEPTOR" /> <logout logout-url="/spring/logout" logout-success-url="/spring/logoutSuccess"/> <intercept-url pattern="/secure" method="POST" access="hasRole('ROLE_SUPERVISOR')"/> </http> <!-- Define local authentication provider, a real app would use an external provider (JDBC, LDAP, CAS, etc) 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> <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> <!-- 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> <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> <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 > <beans:bean id="jsfRedirectStrategy" class="com.icesoft.spring.security.JsfRedirectStrategy"/> <beans:bean id="httpSessionSecurityContextRepository" class="org.springframework.security.web.context.HttpSessionSecurityContextRepository"/> </beans:beans> Resources |
Spring Web Flow 2.3.1
© Copyright 2018 ICEsoft Technologies Canada Corp.