An Alternative JSF Navigation Handler

By , 7 September 2008

An Alternative JSF Navigation Handler

Two of the things that frustrate me about the stock JSF navigation handler are 1) I find it cumbersome to have navigation rules externalised in faces-config.xml; and 2) there is little or no possibility for reuse in XML. The CRUD pattern we use was spawning lines and lines of almost identical configuration for navigation.

Naturally the only solution to these problems is to swear a lot, post angry messages online and denounce the JCP as a disturbed, malevolent oligarchy led by individuals with as much grip on reality as a gecko has on teflon. But... I refrained.

Instead, I implemented this little NavigationHandler which allows you to shortcut the navigation rules by specifying a view id directly as an outcome:

An Alternative JSF Navigation Handler
<h:commandButton actionListener="${bean.update}" action="view.xhtml"/>

The action can still be a method expression which returns the view id.

Here is the implementation, which you can also find in Furnace Webcore 1.4:

/**
 * An extension to the default navigation handler which allows us to
 * specify viewIds directly as the outcome. There is no support for 
 * redirection, however if you need this you can specify a viewId 
 * which simply contains a web:redirect tag.
 */
public class WebcoreNavigationHandler extends NavigationHandlerImpl {
    private Logger log = Logger.getLogger(getClass().getName());
    
    /** 
     * Default constructor.
     */
    public WebcoreNavigationHandler() {
        super();
    }
    
    
    /**
     * Override the default navigation handler to check for viewIds in
     * the outcome string. This simply looks for the .xhtml extension.
     */
    @Override
    public void handleNavigation(FacesContext context, String fromAction,
                                 String outcome) {
        if (outcome != null && outcome.endsWith(".xhtml")) {
            
            // canonicalize path relative to current view
            String dir = "/";
            if (!outcome.startsWith("/")) {
                dir = context.getViewRoot().getViewId();
                dir = dir.substring(0, dir.lastIndexOf("/"));
            }
            try {
                File file = new File(dir, outcome);
                outcome = file.getCanonicalPath();
            } catch (IOException ioe) {
                log.severe("Error canonicalizing " + outcome);
                return;
            }
            
            // set the specified view
            ViewHandler views = context.getApplication().getViewHandler();
            UIViewRoot view = views.createView(context, outcome);
            context.setViewRoot(view);
        } else {
            super.handleNavigation(context, fromAction, outcome);
        }
    }
}

You need to configure the WebcoreNavigationHandler in your faces-config.xml (this is already done for you in the Furnace jar):

<application>
  <navigation-handler>au.com.ninthavenue.webcore.application.WebcoreNavigationHandler</navigation-handler>
</application>

Hope you find it useful :)

About Roger Keays

An Alternative JSF Navigation Handler

Roger Keays is an artist, an engineer, and a student of life. He has no fixed address and has left footprints on 40-something different countries around the world. Roger is addicted to surfing. His other interests are music, psychology, languages, the proper use of semicolons, and finding good food.

Leave a Comment

Please visit https://rogerkeays.com/blog/an-alternative-jsf-navigation-handler to add your comments.

Comment posted by: , 14 years ago

Jeu pc, tu n'as pas laissé un lien pour ton blog... Je voudrais bien le lire.

Comment posted by: jeu pc, 14 years ago

wrote a post about that in my blog, but unfortunately the post was written in french (because I am from french)), but I believe that you can understand the idea behind of state navigation if you read the source code.

Comment posted by: Rafael Ponte, 15 years ago

Great post Roger!

When you say "[...] The CRUD pattern we use was spawning lines and lines of almost identical configuration for navigation." is an "old-school" way to develop pages with JSF actually, because JSF (and a lot of components set) allows us write pages using state navigation and AJAX instead of page navigation "old-way", then isn't necessary configure faces-config.xml with rules navigation, mainly for CRUD pages.

Using state navigation instead of page navigation you getting several advantages, as better performance, as minor cpu overhead, GUI really more rich, better use of the bandwidth and others.

I wrote a post about that in my blog, but unfortunately the post was written in portuguese (because I am brazilian :)), but I believe that you can understand the idea behind of state navigation if you read the source code.

It's an interesting post, could translate the its title as "Using AJAX with JSF on efficient way",
http://www.rponte.com.br/2008/04/10/utilizando-ajax-com-jsf-de-maneira-eficiente/

Thank you!