JSF 2 GETs Bookmarkable URLs
JSR-314: JavaServer Faces (JSF) 2.0 demonstrates a strong evolution of the JSF framework driven by de facto standards that emerged out of the JSF community and participating vendor's products. This article, the second installment in a series covering JSF 2.0 features contributed by Red Hat, or which Red Hat participated in extensively, covers the new features that bring formalized GET support to a framework traditionally rooted in POST requests. The primary building blocks of this support are view parameters and a pair of UI components that produce bookmarkable hyperlinks. Both features incubated in Seam and, therefore, should be familiar to any Seam developer. They are also features for which the JSF community has passionately pleaded.
Author's Note: Many thanks to Pete Muir, who played a pivotal role as technical editor of this series.
Read the other parts in this article series:
Part 1 - JSF 2: Seam's Other Avenue to Standardization
Part 2 - JSF 2 GETs Bookmarkable URLs
Part 3 - Fluent Navigation in JSF 2
Part 4 - Ajax and JSF, Joined At Last
Part 5 - Introducing JSF 2 Client Behaviors
Every user session must start somewhere. JSF was designed with the expectation that the user always begins on a launch view. This view captures initial state and allows the user to indicate which action to invoke by triggering a UI event, such as clicking a button. For instance, to view a mortgage loan, the user might enter its id into a text box and then click the "Lookup" button.
The assumption that this scenario is the norm is surprising since
it overlooks that fact that the web was founded on the concept of a
hyperlink. A hyperlink points to a resource (URI), which may already
contain the original state and intent, such as to view a mortgage loan
summary. There's no need to bother the user with a launch view in
this case.
While hyperlinks are most often used in web sites, they apply to web applications as well (see this blog entry
for a discussion about the difference between a web site and a web
application). Hyperlinks support reuse by serving as an exchange
language in composite applications. One application can link to a
resource in another application in lieu of having to duplicate its
functionality. In fact, the request may be coming from a legacy
application that isn't even web-based. In that case, you'll likely be
plopping the user into the web application somewhere in the middle. As
it turns out, this situation is quite common. When you visit a blog, do
you start on the search screen to find an entry to read? Not likely.
More times than not, you click on a link to view a specific blog entry.
The point to take away from this discussion is that initial requests
(referred to as non-faces requests in JSF) can be just as important as
form submissions (faces requests), whether in a web site or web
application. In the past, JSF has struggled to support the scenario
cited above, placing much more emphasize on faces requests. JSF 2
rectifies this imbalance by introducing view parameters and
hyperlink-producing UI components. View parameters allow the
application to respond to a resource request by baking formal
processing of request parameters into the JSF life cycle for both GET
and POST requests. View parameters are not limited to consuming data.
They are bi-directional. JSF 2 can propagate the data captured by view
parameters when generating bookmarkable URLs, with complementary
behavior for redirect URLs produced by redirect navigation cases.
We'll start by examining view parameters, how they are defined and
how they are worked into the JSF life cycle. You'll then discover how
they work in tandem with the new hyperlink-producing components and the
navigation handler to bring "bookmarkable" support to JSF.
Introducing view parameters
The API documentation describes a view parameter, represented by the javax.faces.component.UIViewParameter component class, as a declarative binding between a request parameter and a model property. The binding to the model property is expressed using an EL value expression (e.g., #{blog.entryId}). If the expression is omitted, the request parameter is bound instead to a request-scoped variable with the same name.
Here's a simple example of a view parameter that maps the value of a request parameter named id to the JavaBean-style property named entryId on a managed bean named blog.
<f:viewParam name="id" value="#{blog.entryId}"/>
Assuming the entryId
property on the blog managed bean is of type long, a value of 9 will be
assigned to the property when the following URL is requested:http://domain/blog/entry.jsf?id=9
But wait, there's more! The value of the request parameter is first converted and validated before being assigned to the model property. This behavior should sound familiar. That's because it mirrors the processing of form input bindings on a faces request. In a way, view parameters turn the query string into an alternative form submission. And like form inputs, view parameters are also processed during faces requests. The complete view parameter life cycle is covered later when we look at view parameter propagation
Before going any further, it's important to point out that view parameters are only available when using the new View Declaration Language (VDL), a standardized version of Facelets. The primary reason is because the JSR-314 EG agreed that no new features should be made to support JSP since it's deprecated as a view handler in JSF 2.
Perhaps you are thinking...
Isn't this already possible?
If you are savvy JSF developer, you're perhaps aware that it's already possible to map a request parameter value to a model property. The assignment is declared by referencing an element of the #{param} map in a <managed-property> element of a managed bean declaration. For instance, you could alternatively map the id request parameter to the blog managed bean in faces-config.xml as follows:
<managed-bean>The similiarities end there. View parameters go above and beyond this simple assignment by providing:
<managed-bean-name>blog</managed-bean-name>
<managed-bean-class>com.acme.Blog</managed-bean-class>
<managed-property>
<property-name>entryId</property-name>
<value>#{param['id']}</value>
</managed-property>
</managed-bean>
- View-oriented granularity (the property mapping in the managed bean definition is global to the application)
- Custom converters and/or validators (along with failure messages)
- Bi-directionality
It's hard to say which feature is the most important, but bi-directionality is certainly the most unique. Since view parameters are a mapping to a JavaBean-style property, the value can be read from the property and propagated to the next request using either the query string or the UI component tree state (depending on the type of request). You are going to find out how useful this bi-directionality can be later on in the article.
Suffice to say, while the property mapping in the managed bean definition works, it's pretty anemic. View parameters are far more adequate and robust in contrast. Pertaining to the topic in this article, view parameters are the key to bringing bookmarkable support to JSF. And since bookmarks link to specific views, so must view parameters.
It's all in the view
As you may have guessed, view parameters are view-oriented. That means
they somehow need to be associated with one or more views (as opposed
to being linked to a managed bean, for instance). Up to this point,
however, there was no facility for associating extensible,
non-rendering metadata with a JSF view. So the EG first had to find a
place within the UI component tree to stick metadata like view
parameters. That led to the introduction of the metadata facet of UIViewRoot.
The next section will introduce this new facet and how it's used to
host view parameters for a particular view, or even a set of views.
Then we get into how view parameters get processed in the JSF life
cycle.The view metadata facet
View parameters provide information about how request parameters should
be handled when a view is either requested or linked to. The view
parameters are not rendered themselves. Therefore, we say that they are
part of the view's metamodel and described using metadata. So the
question is, "Where should this metadata live?"
It turns out that a JSF view, which is represented at the root by the javax.faces.component.UIViewRoot
component class, already accommodates some metadata. Currently, this
metadata consists of string values to define settings such as the
locale, render kit, and content type of the view, and method
expressions that designate view-specific phase observers. For example:
<f:view locale="en_US" afterPhase="#{myBean.afterPhaseListener}">
...
</f:view>While values can be assigned to these metadata properties explicitly in Java code, more often they are assigned declaratively using corresponding attributes of the <f:view> component tag. But neither UIViewRoot or it's component tag <f:view> can accommodate complex metadata--that is, metadata which cannot be described by a single attribute. That's were the view metadata facet comes in.
The view metadata facet is a reserved facet of UIViewRoot, named javax_faces_metadata, that can hold an arbitrarily complex branch of UI components that provide additional metadata for a view. Facets are special because they are ignored by a UI component tree traversal, requiring an imperative request to step into one of them. This aspect makes a facet an ideal candidate for tucking away some metadata for the view that can be accessed on demand.
The view metadata facet looks like any other facet in the UI component tree. It must be declared as a direct descendant of <f:view> within a view template as follows:
<f:view>
<f:facet name="javax_faces_metadata">
...
</f:facet>
...
</f:view>
Note: If you are using Facelets, you may not be familiar with the <f:view> tag since it's optional in Facelets. When you add it to your template, it must be the outer-most component tag, but it does not have to be the root of the document.
Since the view metadata facet is a built-in facet, and is expected to be heavily used, the alias tag <f:metadata> was introduced as a shorthand for the formal facet definition shown above:
<f:view>
<f:metadata>
...
</f:metadata>
...
</f:view>
We now have a place in the UI component tree to store metadata pertaining to the view. But why define the metadata in the view template? It's all about reuse and consistency.
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)





Comments
Darren Kelly replied on Tue, 2010/09/21 - 8:23pm
I'm delighted to have found this excellent article after a long search for guidance on bookmarkable URLs for JSF2.
I had started a project migrating from the automatically generated "JSF from entity" pages, which leverages a commandLink strategy tracking selection from a DataModel, which is sufficient for pages with one column with similar entities, but as soon as I tried to extend it to provide pages with many links for multiple, heterogeneous entities I ran into trouble using commandLinks.
I struggled, after much online research, for hours yesterday trying to decide whether to go the way of passing an id via a URL, and decided to sleep on it. This morning I googled this article and within hours I was able to migrate a quite complex application to use viewParam and id query parameters throughout.
Many thanks to Dan Allen.
I have a small question. I find myself repeating the following code at the top of each view.xhtml page for each entity type, which has its own folder after a pattern. Each entity has a specific managedbean manager which extends a generic AbstractManager. For example, for entity Actor in /actor/view.xhtml I have:
On storing the id I already fetch (type safe) the corresponding entity:
abstract public class AbstractManager<T extends Element> implements Serializable {I tried creating a reusable component with the metadata so I could pass in my manager, but it failed:
<f:viewParam name="id" value="#{cc.attrs.manager.id}"/>This would be called from every view.xhtml for every entity type:
<util:setid manager="#{actorManager}"/>
But it fails silently.
Any idea how I can save some coding and encapsulate the strategy in a resuable way ? I can't pass in a parameter (my Manager class) into a ui:include.