Spring Faces is the home of the new Spring Web Flow + JSF 1.2 integration layer, as well as a number of additional value adds specific to a JSF environment. These value adds include:
Unified EL Integration - A separate implementation of the ExpressionParser from Spring Binding that uses the new Unified EL from JSF 1.2 and JSP 2.1. This allows for JSF users to use the same expression language in their flow definitions as in their JSF views.
Client Side Validator Components - A small set of JSF components that work as "advisors" on regular JSF inputText components. These components make use of the rich validation capabilities of the Dojo javascript framework by default, with an alternate set of tags based on Ext also available.
The Spring Faces module provides strong integration between Spring Web Flow and Java Server Faces (JSF). When used with JSF, Spring Web Flow takes responsibility for view navigation handling and managing model state, adding power and simplicity beyond JSF's default navigation system and object scopes. Plain JSF views and components continue to work just as before, and are able to participate in flows with full access to flow state. In addition, other view technologies such as Facelets continue to plug-in normally.
The JSF integration relies on custom implementations of core JSF artifacts to drive the execution of flows. In addition, it relies on custom ELResolvers to access flow execution attributes from JSF components.
Using Spring Web Flow in a JSF environment does not require any additions to the application's faces-config.xml. The Spring Faces jar just needs to be on the classpath and all of the custom JSF artifacts provided by spring-faces will be picked up by JSF automatically. Ideally, when building new applications from the start with Spring Faces, a faces-config.xml should not be needed at all in preference for having all beans managed by Spring.
The artifacts configured by Spring Faces use Spring to access the Web Flow system configuration. This requires the Spring Web Servlet to be configured in the web.xml deployment descriptor:
<!-- The front controller of the Spring Web application, responsible for handling all application requests --> <servlet> <servlet-name>Spring Web Servlet</servlet-name> <servlet-class>org.springframework.webflow.servlet.SpringWebServlet</servlet-class> <init-param> <param-name>configLocations</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 /spring/* requests to the Spring Web Servlet for handling --> <servlet-mapping> <servlet-name>Spring Web Servlet</servlet-name> <url-pattern>/spring/*</url-pattern> </servlet-mapping>
The application context bootstrapped by the Spring Web Servlet should contain the Web Flow system configuration. The example webflow-config.xml below shows a typical Web Flow configuration for a JSF environment:
<?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:web="http://www.springframework.org/schema/webflow-config"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/webflow-config
http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd">
<!-- Imports the "application-layer" definining business logic and data access services -->
<import resource="application-layer-config.xml"/>
<web:flow-executor id="flowExecutor" flow-registry="flowRegistry">
<web:flow-execution-listeners>
<web:listener ref="jpaFlowExecutionListener" criteria="*"/>
</web:flow-execution-listeners>
</web:flow-executor>
<web:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices">
<web:flow-location path="flow/main/main.xml" />
<web:flow-location path="flow/booking/booking.xml" />
<web:flow-builder class="org.springframework.faces.ui.resource.ResourcesFlowBuilder" />
</web:flow-registry>
<bean id="flowBuilderServices" class="org.springframework.webflow.engine.builder.support.FlowBuilderServices">
<property name="expressionParser">
<bean class="org.springframework.webflow.core.expression.el.WebFlowELExpressionParser">
<constructor-arg >
<bean class="org.jboss.el.ExpressionFactoryImpl"/>
</constructor-arg>
</bean>
</property>
<property name="viewFactoryCreator">
<bean class="org.springframework.faces.webflow.JsfViewFactoryCreator"/>
</property>
</bean>
</beans>
A bean named flowExecutor must be configured and linked with a flow definition registry that contains the flows eligible for execution. Note the flowExecutor bean name is significant, as that is bean name the Web Flow JSF extensions will expect.
Any flow executor property such as the flow execution repository type is configurable here, consistent with the other environments Spring Web Flow supports.
The flowRegistry bean definition shows the registration of two XML based flow definitions, as well as a special java-based FlowBuilder that installs a special flow for serving the javascript and CSS resources needed by the Spring Faces custom JSF components.
The flowBuilderServices provides a number of JSF-specific services to the flowRegistry, including the WebFlowELExpressionParser that allows Web Flow to use the Unified EL for parsing expressions in flow definitions.
Even in an ideal scenario where the SpringWebServlet is handling all incoming requests, the FacesServlet must still be configured in web.xml in order for JSF to bootstrap properly:
<!-- 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> <!-- Mapping for faces initialization --> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.faces</url-pattern> </servlet-mapping>
This configuration also allows for the mixing of legacy pure JSF request handling with the request handling of Spring Web Flow for easier page-by-page migration.
The preferred way of launching a flow from an external system, such as a normal JSF view is by accessing flow definition URLs directly using a bookmark or normal HTML link:
<a href="/spring/main">Go</a>
This link would launch the "main" flow, assuming /spring/* has been mapped to the SpringWebServlet defined within web.xml.
Before going this route, it should be considered whether the same effect can be achieved with a normal REST-ful URL link in combination perhaps in combination with a custom Web Flow action in order to execute specialized logic. If it is an absolute requirement to use a JSF UICommand component, then the recommended approach is to programmatically have JSF forward the request to the flow execution URL from within a JSF ActionListener.
public void myActionListener(ActionEvent event) {
// Execute any required processing and then forward to the flow execution
facesContext.getCurrentInstance().getExternalContext().dispatch("/spring/main/");
facesContext.getCurrentInstance().responseComplete();
}
Flow definitions in a JSF environment are just plain Spring Web Flow definitions:
<?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-1.0.xsd">
<var name="myBean" class="example.ManagedBeanImpl" scope="conversation" />
<start-state idref="displayView" />
<view-state id="displayView" view="myview.jsp">
<transition on="submit" to="prepareNextView"/>
</view-state>
<action-state id="prepareNextView" >
<bean-action bean="myService" method="loadMyModel">
<method-arguments>
<argument expression="#{myBean.foo}"/>
</method-arguments>
</bean-action>
<transition on="success" to="displayNextView"/>
</action-state>
<view-state id="displayNextView" view="mynextview.jsp" />
</flow>
A primary benefit of using JSF is it is a rich UI component framework, and UI components have both data and behavior. As JSF components typically handle data binding and field-level validation behaviors, the actual flow definition logic is often simpler and more focused as a result.
An important difference to note in the above example is the difference in using EL expressions versus Web Flow's traditional OGNL expressions. When using the ELExpressionParser, the chain of configured resolvers will automatically resolve an expression against the correct scope, so the "conversationScope" identifier is optional in the expression when referencing "myBean".
Views selected by view states are specified using paths relative to the current flow definition. In the above example, it is expected that myview.jsp and mynextview.jsp are both located in the same directory as the flow definition.
Views participating in flows are just plain JSF views. They may also incorporate other JSF view technologies such as Facelets and Ajax4JSF.
<f:view>
<h:form id="form">
...
<h:inputText id="propertyName" value="#{someBean.someProperty}"/>
...
<h:commandButton type="submit" value="Next" action="submit"/>
</h:form>
</f:view>
As shown above, there is nothing Spring Web Flow specific here. The flow execution key is automatically tracked by a special UI component in the view root, so there is no need to track it manually. Action outcomes are automatically mapped to Spring Web Flow event identifiers signaled against the current state.
Spring Faces provides some lightweight JSF components that act in an "advisor" role to provide rich client-side validation capabilities to standard inputText components. These can be used in place of server-side JSF validators to provide immediate validation feedback to the end user without the overhead of another fine-grained call to the server. The default implementation of these components use the Dojo javascript library to provide this validation behavior. Dojo was chosen due to their increased attention to accessibility concerns compared to other javascript frameworks. An alternate implementation based on the Ext library is also provided. Though Ext does not address accessibility issues, it can still be attractive for use in internal corporate intranet style applications.
The Spring Faces components are currently provided as Facelets tags. In order to utilize them, the following namespace declaration must be added to the header of a Facelets view template:
xmlns:sf="http://www.springframework.org/tags/faces"
Spring Faces requires the installation of a special flow for loading javascript and CSS resources, as shown in the configuration example. This special stateless flow serves up resources corresponding to URLs such as "/spring/resources/dojo/dojo.js". This flow searches for the corresponding resource as follows:
For convenience, the external javascript libraries that the Spring Faces components depend on are made available in seperate jar files, and will be automatically loaded by the components when needed using the proper resource URLs. Since the resource loading mechanism checks in the web app classpath first it is possible to, for example, override the provided resources with a custom build of the Dojo or Ext library that is optimized for the particular application.
Spring Faces provides three different client-side validator components:
<sf:clientTextValidator> - Provides validation with customizable error messages for text fields.
<sf:clientNumberValidator> - Provides validation and input filtering with customizable error messages for numeric fields.
<sf:clientDateValidator> - Provides validation and a rich popup date picker control with customizable error messages for date fields.
<sf:validateAllOnClick> - When wrapped around a UICommand component such as <h:commandButton> or <h:commandLink> fires all client-side validators when the UICommand component is clicked and prevents the form from being submitted if any of the validations fail.
The validator components must be wrapped around an <h:inputText> component (or any other component that renders an HTML text input). For example, see the following snippet from the Booking sample application:
<sf:clientDateValidator required="true">
<h:inputText id="checkinDate" value="#{booking.checkinDate}" required="true">
<f:convertDateTime pattern="yyyy-MM-dd" timeZone="EST"/>
</h:inputText>
</sf:clientDateValidator>
In general, each of the available validations has a corresponding sensible default error message. The error messages can be overridden via the component's "invalidMessage" attribute. All of the customizable message attributes are value-binding aware so that expressions may be used to bind to keys in the application message bundle if so desired.
Please refer to the javadocs of the component classes to see all of the attributes for the components. More extensive taglib docs will be available with the final release of Spring Web Flow 2.0.
An alternate version of the components based on the Ext library is provided under a separate tag namespace. In order to utilize them, the following namespace declaration must be added to the header of a Facelets view template:
xmlns:sfe="http://www.springframework.org/tags/faces-ext"
The basic behavior of the Ext versions of the components is the same, but the tags have different attributes that correspond with the attributes of the underlying Ext widgets. Please refer to the javadocs of the component classes to see all of the attributes for the components. More extensive taglib docs will be available with the final release of Spring Web Flow 2.0.