JSF IntegrationIntroduction
Spring Faces is Spring's JSF integration module that simplifies using JSF with Spring. It lets you use the
JSF UI Component Model with Spring MVC and Spring Web Flow controllers.
Spring Faces also includes a small Facelets component library that provides Ajax and client-side validation
capabilities. This component library builds on Spring Javascript, a Javascript abstraction framework that
integrates Dojo as the underlying UI toolkit.
Spring-centric Integration Approach
Spring Faces combines the strengths of JSF, its UI component model, with the strengths of Spring, its
controller and configuration model. This brings you all the strengths of JSF without any of the weaknesses.
Spring Faces provides a powerful supplement to a number of the standard JSF facilities, including:
managed bean facilityscope managementevent handlingnavigation ruleseasy modularization and packaging of viewscleaner URLsmodel-level validationclient-side validation and UI enhancementAjax partial page updates and full navigationprogressive enhancement and graceful degradation
Using these features will significantly reduce the amount of configuration required in faces-config.xml
while providing a cleaner separation between the view and controller layer and better modularization of your
application's functional responsibilities. These use of these features are outlined in the sections to
follow. As the majority of these features build on the flow definition language of Spring Web Flow, it is
assumed that you have an understanding of the foundations presented in
Defining Flows
.
Configuring web.xml
The first step to using Spring Faces is to route requests to the
DispatcherServlet
in the
web.xml
file. In this example, we map all URLs that begin with
/spring/
to the servlet. The servlet needs to be configured. An
init-param
is used in the servlet to pass the
contextConfigLocation
. This is the location of the Spring configuration for your application.
Spring MVC Dispatcher Servletorg.springframework.web.servlet.DispatcherServletcontextConfigLocation/WEB-INF/web-application-config.xml1Spring MVC Dispatcher Servlet/spring/*]]>
In order for JSF to bootstrap correctly, the
FacesServlet
must be configured in
web.xml
as it normally would even though you generally will not need to route requests through it at all when using
Spring Faces.
Faces Servletjavax.faces.webapp.FacesServlet1Faces Servlet*.faces]]>
When using the Spring Faces components, you also need to configure the Spring JavaScript
ResourceServlet
so that CSS and JavaScript resources may be output correctly by the components. This servlet must be mapped
to /resources/* in order for the URL's rendered by the components to function correctly.
Resource Servletorg.springframework.js.resource.ResourceServlet0Resources Servlet/resources/*]]>
The Spring Faces components require the use of Facelets instead of JSP, so the typical Facelets
configuration must be added as well when using these components.
javax.faces.DEFAULT_SUFFIX.xhtml]]>
Configuring Web Flow to render JSF views
The next step is to configure Web Flow to render JSF views. To do this, in your Spring Web Flow
configuration include the
faces
namespace and link in the faces
flow-builder-services
:
]]>
The faces:flow-builder-services tag also several other defaults appropriate for a JSF environment.
Specifically, the Unified EL is configured as the default Expression Language.
See the swf-booking-faces reference application in the distribution for a complete working example.
Configuring faces-config.xml
The only configuration needed in
faces-config.xml
is specific to the use of Facelets. If you are using JSP and not using the Spring Faces components, you do
not need to add anything specific to Spring Faces to your
faces-config.xmlcom.sun.facelets.FaceletViewHandler
]]>
Replacing the JSF Managed Bean Facility
Spring Faces allows you to completely replace the JSF managed bean facility with a combination of
flow-managed variables and Spring managed beans. It gives you a good deal more control over the lifecycle of
your managed objects with well-defined hooks for initialization and execution of your domain model.
Additionally, since you are presumably already using Spring for your business layer, it reduces the
conceptual overhead of having to maintain two different managed bean models.
In doing pure JSF development, you will quickly find that request scope is not long-lived enough for storing
conversational model objects that drive complex event-driven views. The only available option is to begin
putting things into session scope, with the extra burden of needing to clean the objects up before
progressing to another view or functional area of the application. What is really needed is a managed scope
that is somewhere between request and session scope. Fortunately web flow provides such extended facilities.
Using Flow Variables
The easiest and most natural way to declare and manage the model is through the use of
flow variables
. You can declare these variables at the beginning of the flow:
]]>
and then reference this variable in one of the flow's JSF view templates through EL:]]>
Note that you do not need to prefix the variable with its scope when referencing it from the template
(though you can do so if you need to be more specific). As with standard JSF beans, all available scopes
will be searched for a matching variable, so you could change the scope of the variable in your flow
definition without having to modify the EL expressions that reference it.
You can also define view instance variables that are scoped to the current view and get cleaned up
automatically upon transitioning to another view. This is quite useful with JSF as views are often
constructed to handle multiple in-page events across many requests before transitioning to another view.
To define a view instance variable, you can use the
var
element inside a
view-state
definition:
]]>
Using Scoped Spring Beans
Though defining autowired flow instance variables provides nice modularization and readability,
occasions may arise where you want to utilize the other capabilities of the Spring container such as
AOP. In these cases, you can define a bean in your Spring ApplicationContext and give it a specific web
flow scope:
]]>
The major difference with this approach is that the bean will not be fully initialized until it is first
accessed via an EL expression. This sort of lazy instantiation via EL is quite similar to how JSF
managed beans are typically allocated.
Manipulating The Model
The need to initialize the model before view rendering (such as by loading persistent entities from a
database) is quite common, but JSF by itself does not provide any convenient hooks for such
initialization. The flow definition language provides a natural facility for this through its
Actions
. Spring Faces provides some extra conveniences for converting the outcome of an action into a
JSF-specific data structure. For example:
]]>
This will take the result of the
bookingService.findBookings
method an wrap it in a custom JSF DataModel so that the list can be used in a standard JSF DataTable
component:
0}">
Name
#{booking.hotel.name}
Confirmation number
#{booking.id}
Action
]]>
The custom DataModel provides some extra conveniences such as being serializable for storage beyond
request scope and access to the currently selected row in EL expressions. For example, on postback from
a view where the action event was fired by a component within a DataTable, you can take action on the
selected row's model instance:
]]>
Handling JSF Events With Spring Web Flow
Spring Web Flow allows you to handle JSF action events in a decoupled way, requiring no direct dependencies
in your Java code on JSF API's. In fact, these events can often be handled completely in the flow definiton
language without requiring any custom Java action code at all. This allows for a more agile development
process since the artifacts being manipulated in wiring up events (JSF view templates and SWF flow
definitions) are instantly refreshable without requiring a build and re-deploy of the whole application.
Handling JSF In-page Action Events
A simple but common case in JSF is the need to signal an event that causes manipulation of the model in
some way and then redisplays the same view to reflect the changed state of the model. The flow
definition language has special support for this in the
transition
element.
A good example of this is a table of paged list results. Suppose you want to be able to load and display
only a portion of a large result list, and allow the user to page through the results. The initial
view-state
definition to load and display the list would be:
]]>
You construct a JSF DataTable that displays the current
hotels
list, and then place a "More Results" link below the table:
]]>
This commandLink signals a "next" event from its action attribute. You can then handle the event by
adding to the
view-state
definition:
]]>
Here you handle the "next" event by incrementing the page count on the searchCriteria instance. The
on-render
action is then called again with the updated criteria, which causes the next page of results to be
loaded into the DataModel. The same view is re-rendered since there was no
to
attribute on the
transition
element, and the changes in the model are reflected in the view.
Handling JSF Action Events
The next logical level beyond in-page events are events that require navigation to another view, with
some manipulation of the model along the way. Achieving this with pure JSF would require adding a
navigation rule to faces-config.xml and likely some intermediary Java code in a JSF managed bean (both
tasks requiring a re-deploy). With the flow defintion language, you can handle such a case concisely in
one place in a quite similar way to how in-page events are handled.
Continuing on with our use case of manipulating a paged list of results, suppose we want each row in the
displayed DataTable to contain a link to a detail page for that row instance. You can add a column to
the table containing the following
commandLink
component:
]]>
This raises the "select" event which you can then handle by adding another
transition
element to the existing
view-state
:
]]>
Here the "select" event is handled by pushing the currently selected hotel instance from the DataTable
into flow scope, so that it may be referenced by the "reviewHotel"
view-state
.
Performing Model Validation
JSF provides useful facilities for validating input at field-level before changes are applied to the
model, but when you need to then perform more complex validation at the model-level after the updates
have been applied, you are generally left with having to add more custom code to your JSF action methods
in the managed bean. Validation of this sort is something that is generally a responsibility of the
domain model itself, but it is difficult to get any error messages propagated back to the view without
introducing an undesirable dependency on the JSF API in your domain layer.
With Spring Faces, you can utilize the generic and low-level
MessageContext
in your business code and any messages added there will then be available to the
FacesContext
at render time.
For example, suppose you have a view where the user enters the necessary details to complete a hotel
booking, and you need to ensure the Check In and Check Out dates adhere to a given set of business
rules. You can invoke such model-level validation from a
transition
element:
]]>
Here the "proceed" event is handled by invoking a model-level validation method on the booking instance,
passing the generic
MessageContext
instance so that messages may be recorded. The messages can then be displayed along with any other JSF
messages with the
h:messages
component,
Handling Ajax Events
Spring Faces provides some special
UICommand
components that go beyond the standard JSF components by adding the ability to do Ajax-based partial
view updates. These components degrade gracefully so that the flow will still be fully functional by
falling back to full page refreshes if a user with a less capable browser views the page.
Revisiting the earlier example with the paged table, you can change the "More Results" link to use an
Ajax request by replacing the standard
commandButton
with the Spring Faces version (note that the Spring Faces command components use Ajax by default, but
they can alternately be forced to use a normal form submit by setting ajaxEnabled="false" on the
component):
]]>
This event is handled just as in the non-Ajax case with the
transition
element, but now you will add a special
render
action that specifies which portions of the component tree need to be re-rendered:
]]>
The
fragments="hotels:searchResultsFragment"
is an instruction that will be interpreted at render time, such that only the component with the JSF
clientId "hotels:searchResultsFragment" will be rendered and returned to the client. This fragment will
then be automatically replaced in the page. The
fragments
attribute can be a comma-delimited list of ids, with each id representing the root node of a subtree
(meaning the root node and all of its children) to be rendered. If the "next" event is fired in a
non-Ajax request (i.e., if JavaScript is disabled on the client), the
render
action will be ignored and the full page will be rendered as normal.
In addition to the Spring Faces
commandLink
component, there is a corresponding
commandButton
component with the same functionality. There is also a special
ajaxEvent
component that will raise a JSF action even in response to any client-side DOM event. See the Spring
Faces tag library docs for full details.
An additional built-in feature when using the Spring Faces Ajax components is the ability to have the
response rendered inside a rich modal popup widget by setting
popup="true"
on a
view-state
.
]]>
If the "changeSearchCriteria"
view-state
is reached as the result of an Ajax-request, the result will be rendered into a rich popup. If
JavaScript is unavailable, the request will be processed with a full browser refresh, and the
"changeSearchCriteria" view will be rendered as normal.
Enhancing The User Experience With Rich Web Forms
JSF and Web Flow combine to provide and extensive server-side validation model for your web application, but
excessive roundtrips to the server to execute this validation and return error messages can be a tedious
experience for your users. Spring Faces provides a number of client-side rich validation controls that can
enhance the user experience by applying simple validations that give immediate feedback. Some simple
examples are illustrated below. See the Spring Faces taglib docs for a complete tag reference.
Validating a Text Field
Simple client-side text validation can be applied with the
clientTextValidator
component:
]]>
This will apply client-side required validation to the child
inputText
component, giving the user a clear indicator if the field is left blank.
Validating a Numeric Field
Simple client-side numeric validation can be applied with the
clientNumberValidator
component:
]]>
This will apply client-side validation to the child
inputText
component, giving the user a clear indicator if the field is left blank, is not numeric, or does not
match the given regular expression.
Validating a Date Field
Simple client-side date validation with a rich calendar popup can be applied with the
clientDateValidator
component:
]]>
This will apply client-side validation to the child
inputText
component, giving the user a clear indicator if the field is left blank or is not a valid date.
Preventing an Invalid Form Submission
The
validateAllOnClick
component can be used to intercept the "onclick" event of a child component and suppress the event if
all client-side validations do not pass.
]]>
This will prevent the form from being submitted when the user clicks the "proceed" button if the form is
invalid. When the validations are executed, the user is given clear and immediate indicators of the
problems that need to be corrected.
Third-Party Component Library Integration
Spring Faces strives to be compatible with any third-party JSF component library. By honoring all of the
standard semantics of the JSF specification within the SWF-driven JSF lifecycle, third-party libraries in
general should "just work". The main thing to remember is that configuration in web.xml will change slightly
since Spring Faces requests are not routed through the standard FacesServlet. Typically, anything that is
traditionally mapped to the FacesServlet should be mapped to the Spring DispatcherServlet instead. (You can
also map to both if for example you are migrating a legacy JSF application page-by-page.) In some cases, a
deeper level of integration can be achieved by configuring special flow services that are "aware" of a
particular component library, and these will be noted in the examples to follow.
Rich Faces Integration
To use the Rich Faces component library with Spring Faces, the following filter configuration is needed
in web.xml (in addition to the typical Spring Faces configuration):
RichFaces Filterrichfacesorg.ajax4jsf.FilterrichfacesSpring Web MVC Dispatcher ServletREQUESTFORWARDINCLUDE]]>
For deeper integration (including the ability to have a view with combined use of the Spring Faces Ajax
components and Rich Faces Ajax components), configure the RichFacesAjaxHandler on your FlowController:
]]>
RichFaces Ajax components can be used in conjunction with the
render
tag to render partial fragments on an Ajax request. Instead of embedding the ids of the components to be
re-rendered directly in the view template (as you traditionally do with Rich Faces), you can bind the
reRender
attribute of a RichFaces Ajax component to a special
flowRenderFragments
EL variable. For example, in your view template you can have a fragment that you would potentially like
to re-render in response to a particular event:
Name
#{hotel.name}
Address
#{hotel.address}
]]>
then a RichFaces Ajax
commandLink
to fire the event:
]]>
and then in your flow definition a
transition
to handle the event:
]]>
Apache MyFaces Trinidad Integration
The Apache MyFaces Trinidad library has been tested with the Spring Faces integration and proven to fit
in nicely. Deeper integration to allow the Trinidad components and Spring Faces components to play
well together has not yet been attempted, but Trinidad provides a pretty thorough solution on its own
when used in conjunction with the Spring Faces integration layer.
Typical Trinidad + Spring Faces configuration is as follows in web.xml (in addition to the typical
Spring Faces configuration):
javax.faces.STATE_SAVING_METHODserver
org.apache.myfaces.trinidad.CHANGE_PERSISTENCE
session
org.apache.myfaces.trinidad.ENABLE_QUIRKS_MODE
falseTrinidad Filter
org.apache.myfaces.trinidad.webapp.TrinidadFilter
Trinidad FilterSpring MVC Dispatcher ServletTrinidad Resource Servlet
org.apache.myfaces.trinidad.webapp.ResourceServlet
resources/adf/*
]]>