Dan is an open source advocate, community catalyst, author and speaker. He's currently pursuing these interests as a Principal Software Engineer at Red Hat. In that role, he serves as a JBoss Community liaison, contributes to several JBoss Community projects, including Arquillian, ShrinkWrap, Seam 3 / DeltaSpike and JBoss Forge, and participates in the JCP on behalf of Red Hat. Dan is the author Seam in Action (Manning, 2008), writes for IBM developerWorks, NFJS magazine and JAXenter and is an internationally recognized speaker. He's presented at major software conference series including JavaOne, Devoxx, NFJS, JAX and Jazoon. After a long conference day, you'll likely find Dan enjoying tech talk with fellow community members over a Belgian Trappist beer. Dan has posted 5 posts at DZone. You can read more from them at their website. View Full User Profile

JSF 2 GETs Bookmarkable URLs

10.29.2009
| 78805 views |
  • submit to reddit

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>
<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>
The similiarities end there. View parameters go above and beyond this simple assignment by providing:

  • 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.

Published at DZone with permission of its author, Dan Allen.

(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:

    <f:view>
        <f:metadata>
            <f:viewParam name="id" value="#{actorManager.id}"/>
        </f:metadata>
    </f:view>

 On storing the id I already fetch (type safe) the corresponding entity:

abstract public class AbstractManager<T extends Element> implements Serializable {
    private Class<T> entityClass;

    public AbstractManager(Class<T> entityClass) {
        this.entityClass = entityClass;
    }
..
    protected T current;

    abstract protected T newElement();
..
    private Long id;
    public Long getId() {return id;}
    /** Note side-effect of loading current object, too, iff id is not null.
     *  Designed for use from <f:viewParam param="id" value="#{manager.id}"/> on view.xhtml entry.
     *
     * Visit: http://java.dzone.com/articles/bookmarkability-jsf-2?page=0,0
     */
    public void setId(Long id) {
        if (id != null) {
            this.id = id;
            T found = (T)getAbstractFacade().find(id);
            if (found == null) {
                String $error = "No object with id("+id+") found for class "+getManagedClassName();
                JsfUtil.addErrorMessage($error);
            }
            //else
            current = found; //! Always set in any case, otherwise get false view of previous current
        }
    }
..
}

 I tried creating a reusable component with the metadata so I could pass in my manager, but it failed:

    <composite:interface>
        <composite:attribute name="manager" required="true"/>
    </composite:interface>

    <composite:implementation>
    <f:view>
        <f:metadata>
            <f:viewParam name="id" value="#{cc.attrs.manager.id}"/>
        </f:metadata>
    </f:view>
    </composite:implementation>

 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.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.