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

Fluent Navigation in JSF 2

11.02.2009
| 108356 views |
  • submit to reddit

Anticipating the user's next move

Up to this point, the navigation handler only comes into play on a postback. Since user interface events trigger a "postback" to the current view, as mentioned earlier, the navigation handler kicks in after the Invoke Application phase to route the user to the next view. JSF 2 introduces a completely new use of the navigation handler by evaluating the navigation rules during the Render Response phase. This render-time evaluation is known as preemptive (or predetermined) navigation.
 

Preemptive navigation

The spec defines preemptive navigation as a mechanism for determining the target URL at Render Response, typically for a hyperlink component. The current view ID and specified outcome are used to determine the target view ID, which is then translated into a bookmarkable URL and used as the hyperlink's target. This process happens, of course, before the user has activated the component (i.e., click on the hyperlink). In fact, the user may never activate the component. The idea is to marry the declarative (or implicit) navigation model with the support for generating bookmarkable links.

Based on what was just described, you should now understand why you declare the target view ID in an attribute named outcome on the new bookmarkable component tags (and why those components inherit from a component class named UIOutcomeTarget). You are not targeting a view ID directly, but rather a navigation outcome which may be interpreted as a view ID if the matching falls through to implicit navigation.

Let's consider an example. Assume that you want to create a link to the home page of the application. You could define the link using one the new bookmarkable link component:

<h:link outcome="home" value="Home"/>

This definition would match the following navigation case if it existed:

<navigation-rule>
<from-view-id>*</from-view-id>
<navigation-case>
<from-outcome>home</from-outcome>
<to-view-id>/home.xhtml</to-view-id>
</navigation-case>
</navigation-rule>


Of course, with implicit navigation available, this navigation case would be redundant. We could exclude it and the result would be the same.
<a href="/app/home.jsf">Home</a>
But if the target view ID depends on the context, such as the user's credentials, you might choose to reintroduce the navigation case to leverage conditional logic as we did earlier. In either case, the key is that the target view ID is not hard-coded in the template.

As it turns out, you've already been using preemptive navigation when you explored bookmarkability in the last article. But there's a critical part of preemptive navigation that we haven't yet fully explored: the assembly of the query string. As it turns out, this topic also applies to redirect navigation rules. In a sense, preemptive navigation has the same semantics as redirect navigation rules because both produces URL that lead to a non-faces request. The only difference is that a bookmarkable URL is a deferred request, whereas a redirect happens immediately. In both cases, the payload in the query string is an essential part of the URLs identity.  

Building the query string

As a result of the new GET support in JSF 2, there are now a plethora of ways to tack on values to the query string. Options can collide when heading into the navigation funnel. What comes out on the other side? There's a simple conflict resolution algorithm to find out. Each parameter source is given a precedence. When a conflict occurs, meaning two sources define the same parameter name, the parameter from the source with the highest precedence is used.

The query string parameters are sourced using the following order of precedence, from highest to lowest:

  1. Implicit query string parameter (e.g., /blog.xhtml?id=3)
  2. View parameter (defined in the <f:metadata> of the target view ID)
  3. Nested <f:param> in UIOutcomeTarget (e.g., <h:link>) or UICommand component (e.g., <h:commandLink>)
  4. Nested <view-param> within the navigation case <redirect> element in faces-config.xml

Granted, this appears to be a lot of options. Don't worry, we'll walk you through the cases in which you would use each option in this article. We recommend you choose a single style of providing navigation parameters that best suits your architecture and keep the others in the back of your mind, so that when an edge case comes up, you can tap into their power.

In the last article, you learned that you can use view parameters to let JSF manage the query string for you. Instead of using view parameters, you could just tack on the query string yourself when building a link to a blog entry.
<h:link outcome="entry?id=#{blog.entryId}" value="Permalink"/
This is really a prototyping case because tooling is going to struggle to locate and visualize parameters like this, and it makes refactoring difficult. It's far cleaner, and really the recommended approach, to use a nested parameter tag:

<h:link outcome="entry" value="Permalink">
<f:param name="id" value="#{blog.entryId}"/>
</h:link>

You could even abstract the parameter away from the view and define it in the navigation case instead, but it again it presents a challenge to tooling:

<h:link outcome="permalink" value="Permalink"/>

<navigation-case>
<from-outcome>permalink</from-outcome>
<to-view-id>/entry.xhtml?id=#{blog.entryId}</to-view-id>
</navigation-case>

A nested <view-param> would also work here, especially if you want to centralized your parameters.

In terms of navigation, the most important point to emphasize here is that you can finally add query string parameters to a redirect URL in the navigation rules. This need likely appears in your existing applications. No longer do you have to resort to using the programmatic API to issue a redirect with a query string payload. Let's consider the case of posting a comment to an entry. This example demonstrates the case when you are submitting a form and want to redirect to a bookmarkable page which displays the result of submitting the form:

<navigation-case>
<from-action>#{commentHandler.post}</from-action>
<to-view-id>/entry.xhtml</to-view-id>
<redirect>
<view-param>
<param-name>id</param-name>
<param-value>#{blog.entryId}</param-value>
</view-param>
</redirect>
</navigation-case>


Note: Don't confuse <view-param> with a UIViewParameter. Think of it more as a redirect parameter (the tag should probably be called <redirect-param> not <view-param>, something to address in JSF 2.1).

There are now plenty of options to pass the user along with the right information. But the spec can't cover everything. That's why you can now query the navigation rule base at runtime to do with it what you like.
 

Peeking into the navigation cases

You've now seen a number of ways in which the navigation cases themselves have become more dynamic. Regardless of how dynamic they are, the fact remains that once you ship the application off for deployment, the navigation cases that you defined in faces-config.xml are set in stone. That's no longer the case in JSF 2. A new navigation handler interface, named ConfigurableNavigationhandler, has been introduced that allows you to query and make live modifications to the registered NavigationCase objects.

Not that you necessarily want to make changes in production. Having a configurable navigation rule set means that you can incorporate a custom configuration scheme such as a DSL or even a fluent, type-safe navigation model from which rules can be discovered at deployment time. In short, the navigation rule set is pluggable, and it's up to you what to plug into it.

NavigationCase is the model that represents a navigation case in the JSF API. When JSF starts up, the navigation cases are read from the JSF descriptor, encapsulated into NavigationCase objects and registered with the ConfigurableNavigationHandler. You can retrieve one of the registered NavigationCase objects by the action expression signature and logical outcome under which it is registered.

NavigationCase case = navigationHandler.getNavigationCase(
facesContext, "#{commandBoard.post}", "success");

You can also access the complete navigation rule set as a Map<String, Set<NavigationCase>>, where the keys are the <from-view-id> values.

Map<String, Set<NavigationCase>> cases =
navigationHandler.getNavigationCases();

You can use this map to register your own navigation cases dynamically. For example, a framework might read an alternative navigation descriptor (such as Seam's pages descriptor) and contribute additional navigation cases. With an individual NavigationCase object in hand, you can either read its properties or use it to create an action or redirect URL, perhaps to feed into your own navigation handler. There are a lot of possiblities here.
The slightly awkward part is how you reference this new API (ConfigurableNavigationHandler). The default NavigationHandler implementation in a standard JSF implementation must implement this interface. But you still have to cast to it when you retrieve it from the Application object, as follows:

ConfigurableNavigationHandler nh =
(ConfigurableNavigationHandler) FacesContext.getCurrentInstance()
.getApplication().getNavigationHandler();

Obviously, something to revisit in JSF 2.1. Once you get a handle on it, the navigation model is your oyster. You can define new ways to navigate or use it to generate bookmarkable URLs in your own style.

Forging ahead

The JSF navigation model had the right idea in spirit, but lacked a couple of elements that would allow it to truly realize loose coupling, it's required use slowed down prototyping, and you had no control to query or modify the navigation rule set at runtime. Your going to find that in JSF 2, the navigation system is much more flexible. You could argue that it finally accomplishes its original goals.

For prototype applications, you can get navigation working without touching the faces-config.xml descriptor with implicit navigation. Just use a view ID, with or without an extension, as the logical outcome and away you go. As the application matures, you can establish a clean separation between JSF and your transactional tier by using conditional navigation to select a navigation case. You can trim the number of navigation cases by defining the target view ID as a value expression and having JSF resolve the target view ID from a navigation helper bean. If the design of your application calls for bookmarkable support, you can leverage the navigation handler in its new role to produce bookmarkable URLs at render time.

In JSF 2, it's a lot easier to route the user around the application. While that may be good for some applications, other applications never advance the user beyond a single page. These single page applications transform in place using Ajax and partial page updates. The next article in this series will open your eyes to how well Ajax and JSF fit together, and what new Ajax innovations made their way into the spec.
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

Adam Warski replied on Wed, 2010/03/10 - 6:06am

Unless I'm doing something wrong the element can't have any children or attributes, and you mention two:

  • view-param
  • include-view-params

Are you referring to some future version of JSF, or were these two removed from the final spec? Is there a way to include the view parameters after a POST?

Adam

Adam Warski replied on Wed, 2010/03/10 - 6:14am

Also, the ?faces-redirect=true parameter doesn't seem to have any effect.

Adam Warski replied on Wed, 2010/03/10 - 6:26am

Moreover any parameters included in seem to be stripped which leaves me with a good question on how to redirect after a POST to a page which has the parameters :).

Liezel Jandayan replied on Mon, 2012/05/21 - 9:52pm

First of all, you pass the string 'block' as an argument to the method render. In that method, that string is treated as an object.-James P. Stuckey

Jorge Muñoz replied on Sun, 2013/03/17 - 5:55pm

Great article covering JSF 2.0 navigation system. This has helped me a lot differentiating when each option is most appropiate and the idea of abstracting the navigation on a helper bean is very clever.Thanks for posting this

Tayo Koleosho replied on Mon, 2013/04/08 - 10:23am

 This is implicitly flawed navigation. Is there anything that stops me from navigating directly to /client/home.xhtml, completely bypassing the `if`

Comment viewing options

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