Spring Faces
The main focus of the Spring Web Flow framework is to deliver the infrastructure
to describe the page fl ow of a web application. The fl ow itself is a very important
element of a web application, because it describes its structure, particularly the
structure of the implemented business use cases. But besides the fl ow which is only
in the background, the user of your application is interested in the graphical user
interface (GUI). Therefore, we need a solution of how to provide a rich user interface
to the users. One framework which offers components is JavaServer Faces (JSF).
With the release of Spring Web Flow 2, an integration module to connect these two
technologies has been introduced. The name of the module is Spring Faces. This
chapter starts with the description of the configuration of the integration. Following
this, the usage of the Spring Faces module is shown. This chapter is no introduction
to the JavaServer Faces technology. It is only a description about the integration
of Spring Web Flow 2 with JSF. If you have never previously worked with JSF,
please refer to the JSF reference to gain knowledge about the essential concepts
of JavaServer Faces.
JavaServer Faces (JSF)—a brief introduction
The JavaServer Faces (JSF) technology is a web application framework
with the goal to make the development of user interfaces for a web
application (based on Java EE) easier. JSF uses a component-based
approach with an own lifecycle model, instead of a request-driven
approach used by traditional MVC web frameworks. The version 1.0
of JSF is specified inside JSR (Java Specification Request) 127
(http://jcp.org/en/jsr/detail?id=127).
Enabling Spring Faces support
To use the Spring Faces module, you have to add some configuration to your
application. The diagram below depicts the single configuration blocks. These
blocks are described in this chapter.
The first step in the configuration is to configure the JSF framework itself. That
is done in the deployment descriptor of the web application—web.xml. The
servlet has to be loaded at the startup of the application. This is done with the
<load-on-startup>1</load-on-startup> element.
<!-- Initialization of the JSF implementation. The Servlet is 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>Faces Servlet</servlet-name>
<url-pattern>*.faces</url-pattern>
</servlet-mapping>
For the work with the JavaServer Faces, there are two important classes.
These are the javax.faces.webapp.FacesServlet and the
javax.faces.context.FacesContext classes.
You can think of FacesServlet as the core base of each JSF application.
Sometimes that servlet is called an infrastructure servlet. It is important
to mention that each JSF application in one web container has its own
instance of the FacesServlet class. This means that an infrastructure
servlet cannot be shared between many web applications on the same
JEE web container.
FacesContext is the data container which encapsulates all information
that is necessary around the current request.
For the usage of Spring Faces, it is important to know that
FacesServlet is only used to instantiate the framework. A further
usage inside Spring Faces is not done.
To be able to use the components from Spring Faces library, it's required to use
Facelets instead of JSP. Therefore, we have to configure that mechanism.
If you are interested in reading more about the Facelets technology,
visit the Facelets homepage from java.net with the following URL:
https://facelets.dev.java.net. A good introduction inside the
Facelets technology is the http://www.ibm.com/developerworks/
java/library/j-facelets/ article, too.
The configuration process is done inside the deployment descriptor of your web
application—web.xml. The following sample shows the configuration inside the
mentioned file.
<context-param>
<param-name>javax.faces.DEFAULT_SUFFIX</param-name>
<param-value>.xhtml</param-value>
</context-param>
As you can see in the above code, the configuration parameter is done with a context
parameter. The name of the parameter is javax.faces.DEFAULT_SUFFIX. The value
for that context parameter is .xhtml.
Inside the Facelets technology
To present the separate views inside a JSF context, you need a specific view handler
technology. One of those technologies is the well-known JavaServer Pages (JSP)
technology. Facelets are an alternative for the JSP inside the JSF context. Instead,
to define the views in JSP syntax, you will use XML. The pages are created
using XHTML.
The Facelets technology offers the following features:
- A template mechanism, similar to the mechanism which is known from the
Tiles framework
- The composition of components based on other components.
- Custom logic tags
- Expression functions
- With the Facelets technology, it's possible to use HTML for your pages.
Therefore, it's easy to create the pages and view them directly in a browser,
because you don't need an application server between the processes of
designing a page
- The possibility to create libraries of your components
The following sample shows a sample XHTML page which uses the component
aliasing mechanism of the Facelets technology.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.
com/jsf/html">
<body>
<form jsfc="h:form">
<span jsfc="h:outputText" value="Welcome to our page: #{user.name}"
disabled="#{empty user}" />
<input type="text" jsfc="h:inputText" value="#{bean.theProperty}"/>
<input type="submit" jsfc="h:commandButton" value="OK"
action="#{bean.doIt}" />
</form>
</body>
</html>
The sample code snippet above uses the mentioned expression language (for
example, the #{user.name} expression accesses the name property from the
user instance) of the JSF technology to access the data.
What is component aliasing
One of the mentioned features of the Facelets technology is that it is
possible to view a page directly in a browser without that the page is
running inside a JEE container environment. This is possible through the
component aliasing feature. With this feature, you can use normal HTML
elements, for example an input element. Additionally, you can refer to
the component which is used behind the scenes with the jsfc attribute.
An example for that is <input type="text" jsfc="h:inputText"
value="#{bean.theProperty}" /> . If you open this inside a
browser, the normal input element is used. If you use it inside your
application, the h:inputText element of the component library is used.
The ResourceServlet
One main part of the JSF framework are the components for the GUI. These
components often consist of many files besides the class files. If you use many of
these components, the problem of handling these files arises. To solve this problem,
the files such as JavaScript and CSS (Cascading Style Sheets) can be delivered inside
the JAR archive of the component.
If you deliver the file inside the JAR file, you can organize the components
in one file and therefore it is easier for the deployment and maintenance of
your component library.
Regardless of the framework you use, the result is HTML. The resources inside the
HTML pages are required as URL. For that, we need a way to access these resources
inside the archive with the HTTP protocol. To solve that problem, there is a servlet
with the name ResourceServlet (package org.springframework.js.resource).
The Servlet can deliver the following resources:
- resources which are available inside the web application (for example, CSS files)
- resources inside a JAR archive
The configuration of the Servlet inside web.xml is shown below.
<servlet>
<servlet-name>Resource Servlet</servlet-name>
<servlet-class>org.springframework.js.resource.ResourceServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Resource Servlet</servlet-name>
<url-pattern>/resources/*</url-pattern>
</servlet-mapping>
It is important that you use the correct url-pattern inside servlet-mapping. As
you can see in the sample above, you have to use /resources/*. If a component
does not work (from the Spring Faces components), first check if you have the correct
mapping for the Servlet. All resources in the context of Spring Faces should be
retrieved through this Servlet. The base URL is /resources.
Internals of the ResourceServlet
ResourceServlet can only be accessed via a GET request. The ResourceServlet
servlet implements only the GET method. Therefore, it's not possible to serve POST
requests. Before we describe the separate steps, we want to show you the complete
process, illustrated in the diagram below.
For a better understanding, we choose an example for the explanation of the
mechanism which is shown in the previous diagram. Let us assume that we have
registered the ResourcesServlet as mentioned before and we request a resource
by the following sample URL: http://localhost:8080/ flowtrac-web-jsf/
resources/css/test1.css..
How to request more than one resource with one request
First, you can specify the appended parameter. The value of the
parameter is the path to the resource you want to retrieve. An example
for that is the following URL: http://localhost:8080/ flowtracweb-
jsf/resources/css/test1.css?appended=/css/test2.
css. If you want to specify more than one resource, you can use the
delimiter comma inside the value for the appended parameter. A
simple example for that mechanism is the following URL: http://
localhost:8080/ flowtrac-web-jsf/resources/css/test1.
css?appended=/css/test2.css, http://localhost:8080/
flowtrac-web-jsf/resources/css/test1.css?appended=/
css/test3.css. Additionally, it is possible to use the comma delimiter
inside the PathInfo. For example: http://localhost:8080/
flowtrac-web-jsf/resources/css/test1.css,/css/test2.
css. It is important to mention that if one resource of the requested
resources is not available, none of the requested resources is delivered.
This mechanism can be used to deliver more than one CSS in one request.
From the view of development, it can make sense to modularize your
CSS files to get more maintainable CSS files. With that concept, the client
gets one CSS, instead of many CSS files. From the view of performance
optimization, it is better to have as few requests for rendering a page as
possible. Therefore, it makes sense to combine the CSS files of a page.
Internally, the files are written in the same sequence as they are requested.
To understand how a resource is addressed, we separate the sample URL
into the specific parts. The example URL is a URL on a local servlet container
which has an HTTP connector at port 8080. See the following diagram for
the mentioned separation.
The table below describes the five sections of the URL that are shown in the
previous diagram.
| request url |
request url is the complete URL. You can retrieve it with the help of the
getRequestURL method from HttpServletRequest. |
| request uri |
This is the URL without the server information. You can retrieve it with
the help of the getRequestURI method from HttpServletRequest. |
| context path |
The name of the servlet context. You can retrieve it with the help of the
getContextPath method from HttpServletRequest. |
| servlet path |
The name of the servlet. You can retrieve it with the help of the
getServletPath method from HttpServletRequest. |
| path info |
path info is the part of the URL after the name of the servlet and
before the first parameter begins. In other words, before the query sign
starts. The reason for that parameter type is to have the possibility to
provide a servlet (or a CGI script) with a complete absolute filename.
You can retrieve it with the help of the getPathInfo method from
HttpServletRequest. |
Step 1: Collect the requested resources
The GET method of ResourceServlet calls the internal getRequestResourceURLs
method , which fetches the path info and appends the value from the appended
parameter. As a delimiter, a comma is used. At the end, the complete string is
separated to an array.
Step 2: Check whether the path is allowed or not
The first step for each resource is to check whether the specified path is allowed
to be retrieved or not, because it is not appropriate to allow the deliverance of
the class or library files inside the secured WEB-INF directory. That check is done
through the internal method called isAllowed of ResourceServlet. At first, it is
validated whether the path is a protected path or not. The protected path is /?WEBINF/.*.
That means everything inside the /WEB-INF directory can not be delivered
with ResourceServlet. The protected path (stored inside private final variable
protectedPath) can not be changed.
With the protection of the WEB-INF directory, it's ensured that there will
be a web application, which is compliant to the Servlet specification. In
that specification, all content inside the WEB-INF directory cannot be
accessed. With the protectedPath variable, it is ensured that there is no
possibility to directly address a resource. If you want to change that, you
have to write your own ResourceServlet, an extension of the existing
servlet does not help in that case.
The next check is whether the specified path is inside the list of allowed paths
(stored inside the allowedResourcePaths array. The array is implemented as a
private instance variable of the String[] type). The following paths are allowed
in the default implementation:
- /**/*.css
- /**/*.gif
- /**/*.ico
- /**/*.jpeg
- /**/*.jpg
- /**/*.js
- /**/*.png
- META-INF/**/*.css
- META-INF/**/*.gif
- META-INF/**/*.ico,
- META-INF/**/*.jpeg
- META-INF/**/*.jpg
- META-INF/**/*.js
- META-INF/**/*.png•
The paths are in Ant-style (http://ant.apache.org) path patterns. The paths
are checked through an implementation of the PathMatcher class (package
org.springframework.util).
The class which implements the Ant-Style is the AntPathMatcher class
inside the same package as the interface. Both are inside the Spring Core
framework. You can use it without the other parts of the Spring framework
(maybe in your own classes to have an ant-like resource selecting, too). You
only need a dependency to the spring-core-x.x.x.jar library (in the
case of version 2.5.4, spring-core-2.5.4.jar).
If you want to allow other parameters, there is a public method called
setAllowedResourcePaths. ResourceServlet, which extends from
HttpServletBean (package org.springframework.web.servlet). This servlet
implements the opportunity to configure a servlet in a Spring way. That means for
each init parameter inside the web.xml file, a setter method on the servlet is called.
In our case, if you have an init parameter allowedPaths, the setAllowedPaths
method is called. See an example for the configuration below.
<servlet>
<servlet-name>Resource Servlet</servlet-name>
<servlet-class>org.springframework.js.resource.ResourceServlet</servlet-class>
<load-on-startup>0</load-on-startup>
<init-param>
<param-name>allowedResourcePaths</param-name>
<param-value>/**/*.png,/**/*.css</param-value>
</init-param>
</servlet>
In most of the cases, you want to specify more than one value for the
allowedResourcePaths parameter. Therefore, the usage of a comma to separate
the values is preferred. Unfortunately, the default implementation does not convert
that value into an array. If you want to use a comma as a delimiter for the values,
you have to manually register StringArrayPropertyEditor inside the org.
springframework.beans.propertyeditors package. An easy way to implement
this is to overwrite the initBeanWrapper method of ResourceServlet.
@Override
protected void initBeanWrapper(BeanWrapper bw) throws
BeansException {
bw.registerCustomEditor(String[].class, new
StringArrayPropertyEditor());
}
Finally, we want to show how to use such a servlet inside the web deployment
descriptor of your web application. Just assume that the servlet has the name
ResourceServlet and is inside the flowtrac.swf.extension.resources
sample package. The configuration is done as shown below.
<servlet>
<servlet-name>Resource Servlet</servlet-name>
<servlet-class>flowtrac.swf.extension.resources.
ResourceServlet</servlet-class>
<load-on-startup>0</load-on-startup>
<init-param>
<param-name>allowedResourcePaths</param-name>
<param-value>/**/*.tss,/**/*.css</param-value>
</init-param>
</servlet>
Just remember: If the resource is not allowed, the isAllowed method
returns false and the servlet delivers no resource for the actual request.
Step 3: Try to load from the servlet context
The next step after the positive check on allowance is to try to load the resource
within the servlet context. This is done with the getServletContext().getResou
rce(resourcePath) expression. Do not confuse the load from the servlet context
with a load from the classpath. With that method, you only get resources which are
reachable directly through a filesystem access. There is no search done. That means
the specified resourcePath is relative to the base directory of the web application.
Step 4: Load from the classpath
I f the resource cannot be found, the META-INF path (that prevents from delivering
classes which are on the classpath) is prefixed to the path of the resource. The
next step is to try to load the resource with the classloader. This is done with the
ClassUtils.getDefaultClassLoader().getResource(resourcePath) helper
method. The ClassUtils utility class is inside the org.springframework.util
package of the spring-core library. If no resource is found, the method returns
null and no resource is delivered by the actual request.
Step 5: Stream the resources
Each resource is now written as a separate byte stream.
Remember if you request more than one resource, the resources are only
delivered if all of the resources could be found from ResourceServlet.
If one of the requested resources could not be found (or not allowed),
none of the requested resources is delivered.
For the usage of ResourceServlet, dependency to the org.springframework.js
library is necessary.
Configuration of the application context
The next step is to configure the application context which is used for the Spring Web
Flow project. This is done in the deployment descriptor of the web application. We
use the same technique as in the samples in the chapters before. For our example, we
choose the name web-application.config.xml in the /WEB-INF/config directory.
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/config/web-application-config.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.
ContextLoaderListener</listener-class>
</listener>
The configuration of web.xml is now complete. For the simple purpose of an
overview, we repeat the configuration steps below:
- Configure the Resources Servlet
- Configure contextConfigLocation
- Configure the Faces Servlet
- Configure faces.DEFAULT_SUFFIX
After we have configured the used file for the configuration of the application
context, it is time to configure the application context itself. For that case, we have
to add an additional XSD file (http://www.springframework.org/schema/faces/
spring-faces-2.0.xsd). Therefore, the header of the file is extended with the
definition of that grammar under the accessor faces.
<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-2.5.xsd
http://www.springframework.org/schema/webflow-config
http://www.springframework.org/schema/webflow-config/
spring-webflow-config-2.0.xsd
http://www.springframework.org/schema/faces
http://www.springframework.org/schema/faces/
spring-faces-2.0.xsd">
The integration of JSF into Spring Web Flow is done through the configuration of a
special parser which creates builders for the integration. To register that parser, the
following line is added to the application context configuration file:
<faces:flow-builder-services id="facesFlowBuilderServices" />
In the case of the usage of the faces: prefix in the XML configuration file, the
FacesNamespaceHandler class (package: org.springframework.faces.config)
is used to handle the tags. The class is extended from NamespaceHandlerSupport
(package: org.springframework.beans.factory.xml) and implements only the
init method. The implementation of the method is shown below.
public void init() {
registerBeanDefinitionParser("flow-builder-services", new
FacesFlowBuilderServicesBeanDefinitionParser());
}
As you can see, the method creates an instance of
FacesFlowBuilderServicesBeanDefinitionParser, which resides inside
the org.springframework.faces.config package. Besides the definition
of facesFlowBuilderServices, it is important to provide that instance to
flow-registry. The instance can be provided with the help of the
flow-builder-services attribute of the flow-registry.
<webflow:flow-registry id="flowRegistry" flow-builder-services="facesF
lowBuilderServices">
<webflow:flow-location path="/WEB-INF/issue/add/add.xml" />
</webflow:flow-registry>
W ith the registration of facesFlowBuilderServices, some other default
classes for the JSF integration are instantiated. It is possible to change the
default behavior. To better understand what is customizable, we look at the
FacesFlowBuilderServicesBeanDefinitionParser class. The class defines
some internal constants which are used through the process of parsing the
XML configuration file. The following diagram shows the class diagram of
FacesFlowBuilderServicesBeanDefinitionParser.
There are five important attributes that you can configure in the faces:flowbuilder-
services tag. These attributes infl uence the behavior of the application.
Therefore, it is important to understand the meaning of the attributes. The attributes
are evaluated through FacesFlowBuilderServicesBeanDefinitionParser. The
diagram below shows the sequence of the evaluation of the attributes. The evaluation
of the attributes is done in four or five steps. The number of steps depends on the
value of the enable-managed-beans attribute. The value is of the Boolean type.
Step 1: Evaluation of the enable-managed-beans attribute.
Step 2: If the enable-managed-beans attribute is set to false, the
expression-parser value is evaluated.
Step 3: Evaluate the formatter-registry attribute.
Step 4: Evaluate the conversion-service attribute.
Step 5: Evaluate the view-factory-creator attribute.
The doParse internal method is called from the
AbstractSingleBeanDefinitionParser class. The doParse method has to
be provided with an instance of the BeanDefinitionBuilder class inside the
org.springframework.beans.factory.support package. That instance is created
inside the parseInternal method of AbstractSingleBeanDefinitionParser.
The instance is created with the genericBeanDefinition static method of the
BeanDefinitionBuilder class.
After the evaluation sequence, we describe the meaning of the attributes.
| Name of the attribute |
Description |
| enable-managed-beans |
JSF uses the concept of managed beans. In the context of the
Spring framework, there is a concept of Spring beans. If you
want to use the managed beans facility of JSF, you have to
provide the enable-managed-beans attribute. The attribute
is a boolean attribute. If you set that attribute to true, the
JsfManagedBeanAwareELExpressionParser class (package: org.
springframework.faces.webflow) is used for the evaluation of the
referenced beans inside the fl ow. Otherwise, the expression-parser
attribute is evaluated.
The default value for that attribute is false.
|
| expression-parser |
If the enable-managed-beans attribute is not set or set to
false, the expression-parser attribute is evaluated. With
that attribute, you can provide a specific parser of the expression
which is used inside the fl ow. If the value is not set, a new instance
of the WebFlowELExpressionParser class (package: org.
springframework.webflow.expression.el) is created.
That instance is created with an ExpressionFactory by
using the createExpressionFactory static method of the
DefaultExpressionFactoryUtils class. The default expression
factory is org.jboss.el.ExpressionFactoryImpl.
|
| formatter-registry |
A formatter registry is the class which creates or provides the formatter
for a specific class. With a formatter from the org.springframework.
binding.format.Formatter type, you can format a specific class for
the output. Additionally, a formatter provides a method called parse
to create the specific class. If you do not provide that attribute, a default
formatter registry is created with the getSharedInstance method of
the DefaultFormatterRegistry class.
That registry has formatters for the following classes: Integer (for
numbers, a NumberFormatter inside the org.springframework.
binding.format.formatters package is used), Long, Short,
Float, Double, Byte, BigInteger, BigDecimal, Boolean (for
Boolean, a BooleanFormatter inside the org.springframework.
binding.format.formatters package is registered) and Date (for
Date, a DateFormatter inside the org.springframework.binding.
format.formatters package is registered).
DefaultFormatterRegistry extends the
GenericFormatterRegistry class from the org.springframework.
binding.format.registry package. If you want to implement your
own formatter registry, it could be a choice to extend from that class.
|
| conversion-service |
A conversion service is responsible to provide instances of
ConversionExecutor (package: org.springframework.
binding.convert) for a specific class. A conversion service is from
type ConversionService (package: org.springframework.
binding.convert). A conversion service converts a source object
into a specific target object. If no value for the conversion-service
attribute is provided, an instance of the FacesConversionService
class (package: org.springframework.faces.model.converter)
is registered. The class extends DefaultConversionService. If
you implement your own conversion service, you could extend from
GenericConversionService (package: org.springframework.
binding.convert.service). This default implementation
registers the following four converters: TextToClass (the
FacesConversionService class registers on the TextToClass
converter an alias for the DataModel class from the javax.model.
DataModel. TextToClass package converts a piece of text to a class.),
TextToBoolean, TextToLabeledEnum and TextToNumber.
|
| view-factory-creator |
A view factory creator is responsible to provide a view factory. A
view factory creator is from type ViewFactoryCreator (package:
org.springframework.webflow.engine.builder). Such a
ViewFactoryCreator is responsible for creating instances from
type ViewFactory (package: org.springframework.webflow.
execution). If the value for the view-factory-creator attribute
is not provided, an instance of the JsfViewFactoryCreator class
(package: org.springframework.faces.webflow) is registered.
That class creates instances of JsfViewFactory (package: org.
springframework.faces.webflow).
|
The next step is the configuration of JSF itself. This is done in faces-config.xml,
which can be located inside the WEB-INF directory.
<?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>
<!-- Enables Facelets -->
<view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
</application>
</faces-config>
|