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
| 75290 views |
  • submit to reddit

Describing view metadata with UI components

There are two important benefits to defining the metadata within the view template. First, it circumvents introducing yet another XML file with its own schema that developers would have to learn. More importantly, it allows us to reuse the UI component infrastructure to define behavior, such as registering a custom converter or validator, or to extract common view parameters into an include template.

Since we're using UI components to describe the view metadata, then it makes sense to treat the UIViewParameter like any other input component. In fact, it extends UIInput. That allows us to register custom converters and validators on a UIViewParameter without any special reservations. Here's an example:

<f:viewParam name="id" value="#{blog.entryId}">
<f:validateLongRange minimum="1"/>
</f:viewParam>

Note: Later in this series you'll learn that like input components, view parameters can enforce constraints defined by Bean Validation annotations (or XML), making the explicit validation tags such as this unnecessary.

But there is one caveat to embedding the view metadata in the template. Without special provisions, extracting the metadata would require building the entire view (i.e., UI component tree). Not only would this be expensive and unnecessary if the intent is not to render the view, it could also have side effects. When the component tree is built, value expressions in Facelets tag handlers get evaluated, potentially altering the state of the system.

To prevent these counteractions, the view metadata facet is given special treatment in the specification. Specifically, it must be possible to be extract and built it separately from the rest of the component tree. Earlier, I mentioned that view parameters are only available in Facelets, and not JSP, because of an executive decision. There's also a technical reason why view parameters rely on Facelets. Only Facelets can provide the necessary separation between template parsing and component tree construction that allows a template fragment to be processed in isolation. The result of this operation is a genuine UI component tree, represented by UIViewRoot, that contains only the view metadata facet and its children. For all intents and purposes, it's as though the view template only contained this one child element.

Using the following logic, it's possible to retrieve the metadata for an arbitrary view at any point in time. This data mining will come in to play later when we talk about view parameter propagation.

String viewId = "/your_view_id.xhtml"
FacesContext ctx = FacesContext.getCurrentInstance();
ViewDeclarationLanguage vdl = ctx.getApplication().getViewHandler()
.getViewDeclarationLanguage(ctx, viewId);
ViewMetadata viewMetadata = vdl.getViewMetadata(ctx, viewId);
UIViewRoot viewRoot = viewMetadata.createMetadataView(ctx);
UIComponent metadataFacet = viewRoot.getFacet(UIViewRoot.METADATA_FACET_NAME);
At this point you could retrieve the UIViewParameter components, which are children of the facet, to perhaps access the view parameter mappings. More likely, though, you'll be looking for your own custom components so you can execute custom behavior before the view is rendered (e.g., view actions).

The extraction of the view metadata is very clever because, while it only builds a partial view, it still honors Facelets compositions. That means you can put your metadata into a common template and include it. Using some creative arrangement, you can apply common metadata to a pattern of views. Here's an example:

<f:view>
<f:metadata>
<ui:include src="WEB-INF/fragments/common_view_metadata.xhtml"/>
...
</f:metadata>
...
</f:view>

You've learned that defining a view metadata facet provides the following services for JSF:

  • Arbitrarily complex metadata, which can reuse existing component infrastructure
  • Metadata is kept with the view, or in a shared template, instead of in an external XML file
  • Can be extracted and processed without any side effects (idempotent)
  • Common metadata declarations can be shared across multiple views

Now that you are well versed in the view metadata facet, it's time to work out a concrete example of view parameters in practice. We'll look at how to enforce preconditions and load data on an initial request using information from the query string. Then you'll learn how that information gets propagated as the user navigates to other views.

Weaving parameters into the life cycle

This article has alluded several times to the use case of loading a blog entry from a URL by passing the value of the id parameter to our managed bean on an initial request. Let's allow this scenario to play out. Here's the URL the user might request coming into the site:

http://domain/blog/entry.jsf?id=9

We'll start by asking what we do with the value once it is assigned to the entryId property of the blog managed bean. One approach is to load the entry lazily as soon as it's referenced in the UI.
<h2>#{blog.blogEntry.title}</h2>
<p>#{blog.blogEntry.content}</p>
Here's what the managed bean would look like to support this approach:

@ManagedBean(name = "blog")
public class Blog {
private Long entryId;
private BlogEntry blogEntry;

public Long getEntryId() { return entryId; }
public void setEntryId(Long entryId) { this.entryId = entryId; }

public BlogEntry getBlogEntry() {
if (blogEntry == null) {
blogEntry = blogRepository.findEntry(entryId);
}

return blogEntry;
}
}

Of course, it doesn't make any sense to display an entry without an id (and could even lead to a NullPointerException). So we should really make the id request parameter required. We'll also add a message if it is missing.

<f:metadata>
<f:viewParam name="id" value="#{blog.entryId}" required="true" requiredMessage="No entry specified.">
<f:validateLongRange minimum="1"/>
</f:viewParam>
...
</f:metadata>

In the case a required request parameter is missing, you can display the error message using the <h:messages> tag. Conversion and validation failures are recorded as global messages since there's no view element with which to associate.
<h:messages globalOnly="true"/>
But these preconditions still don't stop the view from being rendered if a request parameter is missing or invalid. What we need is a way to execute an initialization method that parallels an action invocation on a postback. That would allow us to get everything sorted before the user sees a response.

View initialization

While view parameters provide the processing steps from retrieving the request value to updating the model, they do not furnish the action invocation and navigation steps that are part of the faces request life cyle. That means you have to fall back to lazy loading the data as the view is being rendered (i.e., encoded). You are also missing a definitive point to fine tune the UI component tree programmatically before it's encoded. Fortunately, another new feature in JSF 2, system events, makes it possible to perform a series of initialization steps before view rendering begins.

Systems events notify registered listeners of interesting transition points in the JSF life cycle at a much finer-grained level than phase listeners. In particular, we are interested in the PreRenderViewEvent, which is fired immediately after the component tree is built (but not yet rendered). If the word "registered" evokes dreadful memories of XML descriptors, fear not. Observing the event we are interested in is just a matter of appending one or more <f:event> elements to the view metadata facet.

The <f:event> tag has two required attributes, type and listener. The type attribute is the name of the event to observe derived by removing the Event suffix from the end of the event class name and decaptializing the result. We are only interested in one event, preRenderView. The listener attribute is a method binding expression pointing to either a no-arguments method or a method that accepts a SystemEvent.

<f:metadata>
...
<f:event type="preRenderView" listener="#{blog.loadEntry}"/>
</f:metadata>

We can use this method to retrieve the blog entry before the view is rendered.

public void loadEntry() {
blogEntry = blogRepository.findEntry(entryId);
}

If the entry cannot be found, you could add conditional logic to the view to display an error message:
<c:if test="#{blog.blogEntry eq null}">
The blog entry you requested does not exist.
</c:if>

Ideally, it would be better not to display the view at all. You can force a navigation to occur using the NavigationHandler API.
public void loadEntry() {
try {
blogEntry = blogRepository.findEntry(entryId);
} catch (NoSuchEntryException e) {
FacesContext ctx = FacesContext.getCurrentInstance();
ctx.getApplication().getNavigationHandler()
.handleNavigation(ctx, "#{blog.loadEntry}", "invalid");
}
}

The only problem is that the listener method is going to be invoked even if the view parameter could not be successfully converted, validated and assigned to the model property. Once again, there's a JSF 2 feature to the rescue. You can use the new isValidationFailed() method on FacesContext to check whether a conversion or validation failure occurred while processing the view parameters.
public void loadEntry() {
FacesContext ctx = FacesContext.getCurrentInstance();
if (ctx.isValidationFailed()) {
ctx.getApplication().getNavigationHandler()
.handleNavigation(ctx, "#{blog.loadEntry}", "invalid");
return;
}

// load entry
}

So far we have dealt with a trivial string to long conversion. But view parameters allow you to represent more complex data, as long as you have a converter that can marshal the value from (and to) a string. Let's assume that we want to allow the user to look at blog entries that fall within a range of dates. The before and after dates can be encoded into the URL as follows:

/entries.jsf?after=2007-12-31&before=2009-01-01


Those values can then be converted to Date objects using the <f:convertDateTime> converter tag and assigned to Date properties on a managed bean as follows:
<f:metadata>
<f:viewParam name="after" value="#{blog.afterDateFiler}">
<f:convertDateTime type="date" pattern="yyyy-MM-dd"/>
</f:viewParam>
<f:viewParam name="before" value="#{blog.beforeDateFiler}">
<f:convertDateTime type="date" pattern="yyyy-MM-dd"/>
</f:viewParam>
<f:event type="preRenderView" listener="#{blog.loadEntries}"/>
</f:metadata>


We again use a PreRenderViewEvent listener to load the data before the page is rendered, in this case filtering the collection of blog entries to be displayed.

Emulating the behavior of an action-oriented framework, which the previous examples have demonstrated, is one use of the PreRenderViewEvent. Another is to act as a life cycle callback for programmatically creating or tweaking the UI component tree after it is "inflated" from the view template. Perhaps you want to build part of the tree dynamically from a data structure. Accomplishing this in JSF would require "binding" a bean property to an existing UI component, declared using an EL value expression in the binding attribute of the tag. But this approach is really ugly because you have to put the tree-appending logic in either the JavaBean property getter or setter, depending on whether the view is being created or restored. The PreRenderViewEvent offers a much more definitive and self-documentating hook.

As you've seen, it's finally possible to respond to a bookmarkable URL in JSF (without pain or brittle code). But, up to this point, all we've done is take, take, take. For bookmarkable support to be complete, we need to be able to create bookmarkable URLs. That brings us to the topic of parameter propagation.

Push the parameters on

If view parameters were only capable of accepting data sent through the query string of the URL, even considering the built-in conversion and validation they provide, they really wouldn't be all that helpful. What makes them so compelling is that they are bi-directional, meaning they are also propagated to subsequent requests, and rather transparently. The subsequent request may be a faces request, which targets the current view, or a non-faces request to a view that has view parameters, which translates into a bookmarkable URL. A request for a bookmarkable URL can come from either a link in the page or a redirect navigation event. We'll look at how view parameters get propagated in all of these cases in this section.

Saved by the component tree

Let's return to the blog entry view and consider what happens if we have a comment form below the post. The comment form might be defined as follows:
<h:form>
<h:inputText value="#{comment.name}" required="true"/>
<h:inputTextarea value="#{comment.text}" required="true"/>
<h:commandButton action="#{commentHandler.post}" value="Post"/>
</h:form>

Notice that there is no reference to the id of the blog entry in this form. Assuming that the blog entry is not stored in session scope (or a third-party conversation), how will the handler know which entry the comment should be linked? This is where view parameter propagation blends with component tree state saving.

When encoding of the view (i.e., rendering) is complete, the view parameter values are tucked away in the saved state of the UI component tree. When the component tree is restored on a postback, such as when the comment form is submitted, the saved view parameter values are applied to the model. This allows view parameters to tie in nicely with the existing design of JSF. The initial state supplied to the view parameters by the URL can be maintained as long as the user interacts with the view (e.g., triggers faces requests through user interface events). You can think of view parameters as an elegant replacement for hidden form fields in this case.

If the user bookmarked the URL after posting a comment, however, the reference to the blog entry would be lost. That's because after a POST request, the browser location does not contain a query string. Here's what the user would see:

http://domain/blog/entry.jsf

If we are following best practices, we'll want to implement the Post/Redirect/Get pattern anyway. That gives us a opportunity to repopulate the query string of the URL. In the past, this would have required an explicit call to the redirect() method of ExternalContext inside the action method.
FacesContext.getCurrentInstance().getExternalContext().redirect("/entry.jsf?id=" + blog.getEntryId());

This explicit (and intrusive) call was necessary because the navigation case did not provide any way to append parameters to the query string. Now, view parameters can take care of this for us. We can tell JSF to encode the view parameters of the target view ID into the redirect URL by enabling the include-redirect-params attribute on the <redirect> element.
<navigation-rule>
<from-view-id>/entry.xhtml</from-view-id>
<navigation-case>
<from-action>#{commentHandler.post}</from-action>
<to-view-id>#{view.viewId}</to-view-id>
<redirect include-view-params="true"/>
</navigation-case>
</navigation-rule>

We'll get into navigation more in the next article in this series. Let's talk about those regular old hyperlinks in the page. We want those to be bookmarkable as well. That means the state needs to be encoded into the URL they point to. Once again, view parameters come into play.

Bookmarkable links

Let's now assume we want to create a bookmarkable link (permalink) to the current blog entry. You can link directly to another JSF view using the outcome attribute of the new hyperlink-producing component tags, <h:link> and <h:button>. These component tags are represented by the component class javax.faces.component.UIOutcomeTarget. (The reason the attribute is named outcome and not viewId will be explained in the next article. For now, just know that the value of the outcome attribute can be a view ID). Both of these component tags support encoding the view parameters into the query string of the URL as signaled by the includeViewParams attribute. Here's how the permalink is defined:
<h:link outcome="/entry.xhtml" includeViewParams="true" value="Permalink"/>
The default value of includeViewParams is false. Since it's set to true, the view parameters are read in reverse and appended to the query string of the link. Here's the HTML that this component tag generated, assuming an entry id of 9:
<a href="/blog/entry.jsf?id=9">Permalink</a>

The link is produced using the new getBookmarkableURL() method on the ViewHandler API. This method calls through to the encodeBookmarkableURL() on the ExternalContext API to have the session ID token tacked on, if necessary. These methods complement the getRedirectURL() and encodeRedirectURL() methods on ViewHandler and ExternalContext, respectively. In a servlet environment, the implementations happen to be the same, but the extra methods serve as both a statement of intent and an extension point for environments where a link URL and a redirect URL are handled differently, such as a portlet.

Notice that the context path of the application (/blog) is prepended to the path, the extension is changed from the view suffix (.xhtml) to the JSF servlet mapping (.jsf) and the query string contains the name and value of the view parameter read from the model. If you had used an <h:outputLink> tag, you would have had to do all of these things manually. That's exactly why the EG felt it was necessary to introduce this component.

We can do one better. If the outcome attribute is absent, the current view ID is assumed. So we can shorten the tag to this:
<h:link includeViewParams="true" value="Permalink"/>

If you want the link to appear as a button, you can use the <h:button> component tag instead.
<h:button includeViewParams="true" value="Permalink"/>

However, note that JavaScript is required in this case to update the browser location when the button is clicked, as you can see from the generated HTML:

<button onclick="window.location.href="/blog/entry.jsf?id=9";">Permalink</button>

View parameters come in especially handy when the number of parameters to keep track of increases. For instance, let's consider the case when a user is searching for entries using a query string in a particular category and wants to paginate through the results. In this case, we are dealing with at least three parameters:
<f:metadata>
<f:viewParam name="query" value="#{blog.searchString}"/>
<f:viewParam name="category" value="#{blog.category}"/>
<f:viewParam name="page" value="#{blog.page}"/>
</f:metadata>

Yet the link to these search results still remains as simple as the permalink to an entry:

<h:link includeViewParams="true" value="Refresh query"/>

This component tag will produce HTML similar to this:

<a href="/blog/entries.jsf?category=JSF&query=features&page=2">Refresh</a>

What if we want to link back to the previous page? In that case, we cannot allow the view parameter named page be automatically written into the query string since that will just link us to the current page. We need an override. Fortunately, it's easy to override an encoded view parameter. You simply use the standard <f:param> tag, just as you would if you were defining a new query string parameter:
<h:link includeViewParams="true" value="Previous">
<f:param name="page" value="#{blog.page - 1}"/>
</h:link>

View parameters that are encoded into links to the current view ID are pretty intuitive. Where things get tricky is when we use view parameters on a link to a different view ID. This requires putting on your thinking cap and doing some reasoning.

View parameter handoff

When a request is made for a URL, and in turn a JSF view ID, the view parameters defined in that view are used to map request parameters to model properties. But when the view parameters are encoded into a bookmarkable URL, the mappings are read from the target view ID. That's why it's especially important to be able to extract the view metadata from a template without having building a full component tree, as mentioned earlier. Otherwise, you would end up building a component tree for every view that is linked to in the current view. That would be very costly.

Let's consider a use case. Suppose that we want to create a link from the search results to a single entry. We would define the link as follows:

<ui:repeat var="_entry" value="#{blog.entries}">
<h2>#{_entry.title}</h2>
<p>#{_entry.excerpt}</p>
<p>
<h:link outcome="/entry.xhtml" includeViewParams="true" value="View entry">
<f:param name="id" value="#{_entry.id}"/>
</h:link>
</p>
</ui:repeat>


The question to ask yourself is this. "Are the search string, category and page offset included in the URL for the entry?". I hope you said "No". The reason is because when the URL for the entry link is built, the component tag reads the view parameter mappings defined in the /entry.xhtml template. The only parameter mapped in that template is entry id. In order to preserve the filter vector, the view parameters defined in the /entries.xhtml view need to also be in the /entry.xhtml template. Aha! Since these are shared view parameters, we should define them in a common template:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core">
<f:viewParam name="page" value="#{blog.page}"/>
<f:viewParam name="query" value="#{blog.searchString}"/>
<f:viewParam name="category" value="#{blog.category}"/>
</ui:composition>

We can then include that template in each view that needs to preserve these view parameters:
<f:view>
<f:metadata>
<ui:include src="/WEB-INF/fragments/common_view_params.xhtml"/>
<f:viewParam name="id" value="#{blog.entryId}"/>
...
</f:metadata>
</f:view>

Keep in mind that if the user navigates to the entry after performing a search, the URL for the entry shown in the browser's location bar will now contain the filter vector. But if you want the user to be able to return to the search filter (without using the back button), that's what you want. You can always provide a simple permalink to bookmark just the entry.

Even though you are now defining view parameters in each of the views, that doesn't mean the URL will become littered with empty query string parameters when they are not in use. View parameters are only encoded (i.e., added to the query string) if the value is not null. Otherwise, there is no trace of the view parameter.

You have now learned how view parameters are propagated during a postback, on a redirect and into a bookmarkable URL. The main benefit of this process is that it is transparent. You don't have to worry about each and every request parameter that comprises the state in the query string of the URL. Instead, JSF interprets the view parameter metadata defined in the template of the target view and automatically appends those name/value pairs to the URL when you activate this feature.

Bookmark it

View parameters serve as an alternative to storing state in the UI component tree, provide a starting point for the application, help integrate with legacy applications, assert preconditions of views, make views bookmarkable, and, with help of the new UIOutcomeTarget components or the enhancement to the redirect navigation case, produce links to those bookmarkable views.

This article began by introducing you to the view metadata facet, which is a general facility for defining a view's metamodel that reuses the existing UI component infrastructure. You learned that view parameters and PreRenderViewEvent listeners are the first standard implementations of view metadata. You saw how the combination of these two features allow you to capture initial state from URL query string, validate preconditions and load data for a view all before the view is rendered. Finally, you learned how view parameter values are propagated to subsequent requests.

This series continues by taking a deeper look at the navigation enhancements made in JSF 2 and explaining how those changes tie into the bookmarkability that you learned about in this article. So bookmark this series and check back again soon!
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.