|
Spring Web Flow Integration
The Spring Framework is a full-stack Java/JEE application framework focusing on increased development productivity while improving application testability and quality. For more information on Spring Framework, see http://www.springframework.org.
The easiest way to produce a working ICEfaces/Spring Web Flow application is to check out the demonstration application available from http://anonsvn.icefaces.org/repo/projects/swf-booking-icefaces/trunk/swf-booking-icefaces. The build process is explained in the readme.txt included with the project. This application is based on the original Spring Booking demonstration application, and consists of a simple Web Flow example coupled with ICEfaces components. Using the build mechanism in place from the Spring examples allows Ivy to resolve any JAR dependencies.
Generally speaking, Spring Web Flow applications are about managing state change and managing navigation through the web application. ICEfaces can certainly work in this environment, but ICEfaces brings Ajax technology to web applications simply, without exposure to verbose JavaScript. Using partial submits, you can increase the functionality of a given page and expose more business logic without the need for the same amount of cumbersome full-page navigation states in the application. Any single page can now be enhanced with features, such as autocomplete, with values fetched from the Server, or server-based business rules for calculating intermediate costs.
The good news is that requests to the server made by ICEfaces components don't change or affect the state of the current Web Flow State as long as the interaction doesn't return a navigation result that is the same as the result defined for the Spring Web Flow. This means that you can add a component to the page and go about increasing functionality without worrying about inadvertently changing the application's behavior.
Primarily, the changes to a Spring application to work with ICEfaces consist of:
- Changing the default Spring variable resolver to the following, as per the
<variable-resolver>org.springframework.web.jsf.DelegatingVariableResolver</variable-resolver>. This allows defining beans defined in a Web Flow to be accessed during a JSF lifecycle. The opposite, defining beans used by Spring in the faces-config file, defeats other aspects of Spring configuration.- Changing web.xml to point to ICEfaces Servlets, and additionally defining a listener that allows access to Session scoped beans. This is required because the entrance into the JSF lifecycle is done through ICEfaces servlets, and not the Spring DispatcherServlet.
- Changing the JSP pages to include the ICEfaces taglib definitions and optionally using ICEfaces components to enhance page functionality.
There are some concepts necessary for correctly configuring the always-redirect-on-pause configuration parameter. The Spring resource is an excellent starting point: http://www.ervacon.com/products/swf/tips/tip4.html
There are certain applications that can benefit from ICEFaces AJAX navigation. In this type of configuration, interaction is done via POST requests, but the response contains the updates to the page. If navigation occurs, then the new page contents are fully rendered in the response and redirects are not necessary. We don't recommend this type of interaction for applications where duplicate POSTing of details is an issue but it does have applications.
The downside to setting the always-redirect-on-pause setting to false in ICEFaces is that without redirects, the Flow execution key isn't inserted into the URL in the nav bar. This means that reloading the page will restart the Flow.
The default for Spring Web Flow is to consider always-redirect-on-pause=true. Add the following always-redirect-on-pause attribute to webflow-config.xml for ICEFaces POST->Response behavior:
<!-- Executes flows: the central entry point into the Spring Web Flow system--> <webflow:flow-executor id="flowExecutor"> <webflow:flow-execution-attributes> <webflow:always-redirect-on-pause value="false" /> </webflow:flow-execution-attributes> <webflow:flow-execution-listeners> <webflow:listener ref="jpaFlowExecutionListener" /> <webflow:listener ref="securityFlowExecutionListener" /> </webflow:flow-execution-listeners>The following is the web.xml file from the downloadable example application:
<?xml version="1.0" encoding="ISO-8859-1"?> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"> <!-- 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> <!-- 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> <!-- 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> <!-- 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> <servlet> <servlet-name>SpringWebFlowInstantiationServlet</servlet-name> <servlet-class> com.icesoft.faces.webapp.http.servlet.SpringWebFlowInstantiationServlet </servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/config/web-application-config.xml </param-value> </init-param> <load-on-startup> 1 </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>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>/pages/*</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>*.faces</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> <!-- Icesoft specific config --> <!-- Specifies to the ICEfaces framework whether to support multiple views of a single application from the same browser. When running in a Portlet environment, this parameter must be set to true. --> <context-param> <param-name>com.icesoft.faces.concurrentDOMViews</param-name> <param-value>true</param-value> </context-param> <context-param> <param-name>javax.faces.STATE_SAVING_METHOD</param-name> <param-value>server</param-value> </context-param> <context-param> <param-name>com.icesoft.faces.synchronousUpdate</param-name> <param-value>false</param-value> </context-param> <context-param> <param-name>com.sun.faces.validateXml</param-name> <param-value>true</param-value> </context-param> <context-param> <param-name>com.prime.facestrace.DISABLE_TRACE</param-name> <param-value>true</param-value> </context-param> <context-param> <param-name>com.icesoft.faces.standardRequestScope</param-name> <param-value>true</param-value> </context-param> <context-param> <param-name>com.icesoft.faces.uploadDirectory</param-name> <param-value>upload</param-value> </context-param> <listener> <listener-class> com.icesoft.faces.util.event.servlet.ContextEventRepeater </listener-class> </listener> <listener> <listener-class> com.sun.faces.application.WebappLifecycleListener </listener-class> </listener> <!-- file upload Servlet --> <servlet> <servlet-name>uploadServlet</servlet-name> <servlet-class> com.icesoft.faces.component.inputfile.FileUploadServlet </servlet-class> <load-on-startup> 1 </load-on-startup> </servlet> <servlet> <servlet-name>Persistent Faces Servlet</servlet-name> <servlet-class> com.icesoft.faces.webapp.xmlhttp.PersistentFacesServlet </servlet-class> <load-on-startup> 1 </load-on-startup> </servlet> <servlet> <servlet-name>Blocking Servlet</servlet-name> <servlet-class> com.icesoft.faces.webapp.xmlhttp.BlockingServlet </servlet-class> <load-on-startup> 1 </load-on-startup> </servlet> <servlet-mapping> <servlet-name>Persistent Faces Servlet</servlet-name> <url-pattern>*.xhtml</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>Persistent Faces Servlet</servlet-name> <url-pattern>/xmlhttp/*</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>Persistent Faces Servlet</servlet-name> <url-pattern>/spring/*</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>Blocking Servlet</servlet-name> <url-pattern>/block/*</url-pattern> </servlet-mapping> <session-config> <session-timeout>30</session-timeout> </session-config> </web-app>
- ICEfaces servlets handle all requests from the browser.
- Synchronous request handling with concurrent DOM views are disabled.
<?xml version="1.0"?> <!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN" "http://java.sun.com/dtd/web-facesconfig_1_0.dtd"> <faces-config> <application> <view-handler> com.sun.facelets.FaceletViewHandler </view-handler> <view-handler> com.icesoft.faces.facelets.D2DFaceletViewHandler </view-handler> <variable-resolver> org.springframework.web.jsf.DelegatingVariableResolver </variable-resolver> </application> </faces-config>In this file, be sure to have the appropriate JSF variable resolver specified. See the Spring Framework 2.1 reference Section 15.3, Integrating with JavaServer Faces.
Using server-initiated rendering can cause problems with regard to Web Flow states. When a server-initiated render operation starts, the Web Flow executor key is retrieved from the UIViewRoot, and the Web Flow state is resumed. This is okay, and works properly, but it does cause problems if the Web Flow has reached a terminal state. In this case, the Web Flow cannot be resumed.
There might be several server-initiated rendering scenarios for your application. Currently, it is difficult to know when a page transition occurs as part of a partial submit; hence, it is difficult to know precisely when to stop the server-initiated rendering if the page transitions to a Web Flow terminal state.
Consider an example of an application with an outputText component displaying the time on the bottom of the page every 5 seconds. This type of component would be easy to add to a footer, so it is included on every page of the application. This would cause problems in the Web Flow sellitems demonstration application because when the application winds up in the shipping cost summary page, the Web Flow has reached a terminal state. If the ticking clock is still updating at this time, this will cause exceptions when it tries to restore the Web Flow as part of the server-initiated rendering pass.
The DisposableBean interface (and the deprecated ViewListener interface) allow the server-initiated rendering code to know when the user leaves a particular view. For more information on the DisposableBean interface, refer to The DisposableBean Interface.
In Spring Web Flow 2.0.5, there is a known issue with ICEFaces ViewHandler delegation. To use ICEFaces with Spring Web Flow 2.0.5, use the following faces-config.xml file contents:
<application> <view-handler> com.sun.facelets.FaceletViewHandler </view-handler> <view-handler> com.icesoft.faces.facelets.D2DFaceletViewHandler </view-handler> <view-handler> org.springframework.faces.webflow.FlowViewHandler </view-handler> <variable-resolver> org.springframework.web.jsf.DelegatingVariableResolver </variable-resolver> </application>This will cause two instances of the FlowViewHandler to be instantiated, but they will not collide.
Copyright 2005-2009. ICEsoft Technologies, Inc. |