My experiments with SiteMesh got me thinking about how best to separate out breadcrumb like navigation from the core application content. Strictly speaking I mean Homeward Path navigation rather than the form of breadcrumb navigation that essentially gives you an onscreen potted browser history.

I have seen numerous approaches to this kind of navigation. The custom JSP tag libraries for this kind of thing that I have seen have never felt quite right. In my travels I recently discovered something called JATO (also known as Sun Java System Application Framework). JATO seems to have been around for a long while but I hadn't seen it before because it appears that it was only distributed as part of the Sun ONE Application Server (formerly iPlanet Application Server). JATO seems to be a MVC framework that existed before the JSTL, Struts, JSF and Spring era.

One of the examples in the JATO sample application is of a Static Breadcrumb method.

I liked the example but not the implementation. So I thought to myself, I can do better than that! What I have ended up with is a very simple and consequently quite satisfying solution. Using the navigationSample.xml from the JATO sample application, I have achieved the same result through a fairly simple XSL transformation. This is achieved with bog standard JSTL without the need to write any new tag libraries. You could easily extend the navigation xml sitemap to include more link information and if you were using SiteMesh you could also take advantage of any "Where Am I?" information passed from your content pages.

Here is JATO's navigationSample.xml as used in the JATO sample application.

<?xml version="1.0" encoding="UTF-8"?>
<category name="Performing Arts" id="0">
        <category name="Acting" id="2">
                <category name="Actors" id="20">
                        <category name="Buster Keaton" id="200"/>
                        <category name="Charlie Chaplin" id="201"/>
                        <category name="WC Fields" id="202"/>
                </category>
                <category name="Actresses" id="21">
                        <category name="Mae West" id="210"/>
                        <category name="Bette Davis" id="211"/>
                        <category name="Marlene Dietrich" id="212"/>
                </category>
                <category name="Companies" id="22"/>
        </category>
        <category name="Circus Arts" id="3">
                <category name="Acrobatics" id="30">
                        <category name="Anti-Gravity" id="300"/>
                        <category name="Human Design" id="301"/>
                        <category name="Trixy's Arco Page" id="302"/>
                </category>
                <category name="Clowning" id="31">
                        <category name="Rodeo Clowns" id="310">
                                <category name="One Eyed Jack" id="3100"/>
                                <category name="Cool Nite Rodeo" id="3101"/>
                        </category>
                        <category name="Creepy Clowns" id="311">
                                <category name="Creepy Clown Gallery" id="3110"/>
                                <category name="Ghost Town Clowns" id="3111"/>
                        </category>
                        <category name="Anti-Clowns" id="312">
                                <category name="The No Clown Zone" id="3120"/>
                                <category name="The Anti-Clown Page" id="3121"/>
                        </category>
                </category>
        </category>
</category>

Here is my simple JSP which currently does the XML/XSL transformation and passes any appropriate parameters into the XSL. It could of cource be optimized for performance, e.g. caching the XSL or XML in memory.

<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%><%--
--%><%@taglib uri="http://java.sun.com/jsp/jstl/xml" prefix="x"%><%--
--%><c:import url="navigationSample.xml" var="xml"/><%--
--%><c:import url="navigation.xsl" var="xsl"/><%--
--%><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title></title>
        <style type="text/css">
            body {
            font-family: helvetica;
            font-size: 0.8em;
            }
        </style>
    </head>
    <body>
        <c:choose><%--
        --%><c:when test="${empty param.id}"><%--
        --%><x:transform xml="${xml}" xslt="${xsl}"/><%--
        --%></c:when><%--
        --%><c:otherwise><%--
        --%><x:transform xml="${xml}" xslt="${xsl}"><%--
        --%><x:param name="id" value="${param.id}"/><%--
        --%></x:transform><%--
        --%></c:otherwise><%--
        --%></c:choose>
    </body>
</html>

Here is my navigation.xsl

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml"  version="1.0" omit-xml-declaration="yes" />
    <xsl:param name="id" select="'0'"/>
    <xsl:strip-space elements="category"/>

    <xsl:template match="/">
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="category[@id = $id]">
        <xsl:for-each select="ancestor::category">
            <a>
                <xsl:attribute name="href">?id=<xsl:value-of select="@id"/></xsl:attribute>
                <xsl:value-of select="@name"/>
            </a> &gt;
        </xsl:for-each>         
        <a>
            <xsl:attribute name="href">?id=<xsl:value-of select="@id"/></xsl:attribute>
            <xsl:value-of select="@name"/>
        </a>
        <xsl:if test="count(category) &gt; 0">
            <ol>
                <xsl:for-each select="category">                 
                    <li>
                        <a>
                            <xsl:attribute name="href">?id=<xsl:value-of select="@id"/></xsl:attribute>
                            <xsl:value-of select="@name"/>
                        </a> 
                    </li>
                </xsl:for-each>
            </ol>               
        </xsl:if>
    </xsl:template>

</xsl:stylesheet>

And that is all you need, horribly simple eh? I hope so.

Technically there is nothing stopping us doing XSL transformations for navigation in the client. This emergent navigation seems to be the direction that Web 2.0 applications are going. Although what stops me doing this now is all the cross browser XSL transformation issues and accessibility issues of needing to support non-javascript driven solutions on the client.

SiteMesh is one of those tools that has made me stop and re-evaluate how I write web applications (a paradigm shift if you like). It is basically a servlet filter that allows you to decorate web pages with header, footer and navigational adornments after your application produces the output. I saw it described somewhere as AOP for the web. Something about the way that SiteMesh works feels very right to me at the moment. SiteMesh focuses the mind on creating applications in such a way that they become a very good fit with the direction where Web application development are currently heading (even if you're not actually planning to use SiteMesh in those applications).

SiteMesh is not Tiles

I have, on some limited occasions, used Tiles with Struts applications. There is something about Tiles that seems over elaborate. I'm sure there are numerous cases where Tiles does things that you can't do with SiteMesh. However, I like SiteMesh precisely because it is such a simple idea.

Separating presentation from content

What is really nice about SiteMesh is that it really focuses the mind on completely separating presentation from content (even the navigational aspects). This seems all the more relevant in the current climate of Web 2.0 and portlet environments. Web 2.0/AJAX style applications often devolve user interface elements to the client side. Portlets also advocate the final responsibility for user interface to the portal.

Context Specific Rendering

I like the idea that based on the contents of a cookie the same application could be rendered entirely differently. I could have university department specific interfaces without the need to actually change the underlying application once it has been written to accommodate SiteMesh decoration.

To use the description often quoted to me about CMS systems, with SiteMesh the content becomes the sandwich filling with the bread of the sandwich (headers, footers, navigation) added via SiteMesh.

Using SiteMesh with Struts Bridge Portlets

What I also like is the idea that since it works via a filter, I can take advantage of this in the portlet environment. Struts Bridge applications are Struts applications that can run as standalone applications and simultaneously as portlets. Essentially, if I wrote a Struts Bridge based application then using SiteMesh it could have header, footer and navigational elements in the "standalone" view and without the need to change anything these would disappear in the portlet rendering (this is because portlets are not effected by servlet filters).

Passing "Where Am I?" content through to SiteMesh

In order for SiteMesh to add context specific navigation it needs to know something about where it has been invoked. Since SiteMesh works as a servlet filter, it can only extract this information from some aspect of the page. SiteMesh has access to a pages' URL, URL parameters and cookies, so it can use these as sources of information. SiteMesh decorators can also access information from the contents of a HTML page (although the specifics elements that you can access appear deliberately quite limited). SiteMesh can access the <head>, any <meta> elements specified in the <head> and the <body> content. I think it would pollute and overcomplicate SiteMesh if it had full HTML DOM browsing facilities, restricting it to a limited set of information makes it simpler and in my view better. Therefore if you add a meta data element to the header of the content page this can be used to render the context aware navigation (all this machine readable meta data sounds a bit semantic web-ish, doesn't it?). So with a bit of effort you can create context aware navigation menus. This got me thinking about how best to separate breadcrumb (homeward path) style navigation, I will talk more about this in my next entry.

See also:
  1. Dynamic Navigation using SiteMesh
  2. Dependency Injection with SiteMesh