Minimizing JSP scriptlet usage in CQ5 components

Monday, June 8, 2009

Still using scriptlets in your CQ5 JSPs?

CQ5 components tend to rely heavily on JSP scriplet code, despite the availability of tag libraries to handle the logic associated with rendering a component.  I wanted to provide a quick example of how to replace a block of scriptlet code in a JSP by using a Java bean class in conjunction with JSP tags.

We start by looking at our existing code, which is actually the side navigation component from the CITYTECH site:

<%@include file="/apps/citytechinc/components/global.jsp"%><%
%><%@ page import="java.util.Iterator, com.day.cq.wcm.api.Page" %>
<%
    Iterator<Page> childIterator = currentPage.listChildren();

    if ("home".equals(currentPage.getName()) ||
        (!childIterator.hasNext()
        && "home".equals(currentPage.getParent()
            .getName()))) {
        %><cq:include script="blogs.jsp" /><%
    } else  {
        %><ul id="sidenav">
        <%
            if (!childIterator.hasNext()
                && !"home".equals(currentPage.getParent()
                    .getName())) {
                childIterator = currentPage.getParent()
                    .listChildren();
            }

            while (childIterator.hasNext()) {
                final Page child = childIterator.next();
                %>
                <li>
                    <a href="<%= child.getPath() %>.html">
                        <%= child.getNavigationTitle() == null
                            ? child.getName()
                            : child.getNavigationTitle() %>
                    </a>
                   </li><%
            }
        %>
        </ul>
        <%
    }
%>

The logic above is relatively straightforward and can easily be encapsulated into a Java bean. We create this class, SideNavigation.java, as follows:

package com.citytechinc.www.bean;

import java.util.Iterator;

import com.day.cq.wcm.api.Page;

public class SideNavigation {

    private static final String HOMEPAGE_NAME = "home";

    private Page currentPage;

    private boolean homepage;

    public SideNavigation() {

    }

    public Page getCurrentPage() {
        return currentPage;
    }

    public Iterator<Page> getPages() {
        Iterator<Page> childIterator = currentPage.listChildren();

        if (!childIterator.hasNext()
            && !HOMEPAGE_NAME.equals(currentPage.getParent()
                .getName())) {
            childIterator = currentPage.getParent().listChildren();
        }

        return childIterator;
    }

    public boolean isHomepage() {
        return homepage;
    }

    public void setCurrentPage(final Page currentPage) {
        this.currentPage = currentPage;

        setHomepage();
    }

    private void setHomepage() {
        final Iterator<Page> childIterator = currentPage
            .listChildren();

        if (HOMEPAGE_NAME.equals(currentPage.getName())
            || (!childIterator.hasNext()
            && HOMEPAGE_NAME.equals(currentPage.getParent()
                .getName()))) {
            homepage = true;
        } else {
            homepage = false;
        }
    }
}

The class contains a no-argument public constructor in addition to getters and setters for all the relevant properties that our component requires. Unfortunately, we cannot use an immutable class to back the component, as the tag we will use does not support passing arguments to the constructor (and our class needs access to the “currentPage” variable). Once the class is defined, we return to our component to implement the JSP changes.

<%@include file="/libs/wcm/global.jsp"%>
<%@include file="/apps/citytechinc/components/global.jsp"%>

<jsp:useBean id="sideNavigation"
    class="com.citytechinc.www.bean.SideNavigation"/>
<jsp:setProperty name="sideNavigation"
    property="currentPage" value="<%= currentPage %>"/>

<c:choose>
    <c:when test="${sideNavigation.homepage}">
        <cq:include script="blogs.jsp" />
    </c:when>
    <c:otherwise>
        <ul id="sidenav">
            <c:forEach items="${sideNavigation.pages}" var="child">
                <li>
                    <a href="<c:out value="${child.path}"/>.html">
                    <c:choose>
                        <c:when test="${child.navigationTitle == null}">
                            <c:out value="${child.name}"/>
                        </c:when>
                        <c:otherwise>
                            <c:out value="${child.navigationTitle}"/>
                        </c:otherwise>
                    </c:choose>
                    </a>
                </li>
            </c:forEach>
        </ul>
    </c:otherwise>
</c:choose>

In our updated JSP, we employ the jsp:useBean tag to instantiate the bean class. This is followed by the jsp:setProperty tag to attach the aforementioned “currentPage” variable to the bean class (this variable is made available by the inclusion of /libs/wcm/global.jsp). In our updated implementation, this is the only piece of scriplet code that is necessary. From here forward, we use JSTL and EL expressions to handle the conditional and iteration logic from the bean properties.

Advantages:

- Clean, more readable code (eliminate all those indentation/formatting headaches when mixing HTML and Java)
- Easier to implement proper logging and exception handling
- Can be properly debugged and tested

Disadvantages:

- Overhead required for initial implementation (if your project already has several components that use scriptlets)
- The usual criticisms associated with following the bean model rather than using an immutable class — recommended reading here: http://www.javapractices.com/topic/TopicAction.do?Id=29
- Tag nesting may become confusing if the component logic is extremely complex

I’m probably overlooking some other aspects of this discussion, but hopefully this is helpful to anyone looking to streamline their CQ5 components. Thanks for reading my first post!

UPDATE: an alternative to using the jsp:useBean tag and a bean-type class would be constructing an immutable class inside of a c:set tag. Here’s what that would look like:

<c:set var="sideNavigation" value="<%= new SideNavigation(currentPage) %>"/>

Obviously, you would have to update the SideNavigation class to include a one-argument constructor in place of the setCurrentPage() method. This implementation would eliminate the “mutability” disadvantage mentioned above and also saves the trouble of having multiple jsp:setProperty tags if you are passing more than one argument to your component class. For those reasons alone, I prefer this method for most applications.

Top