How to test if <jsp:doBody/> is empty / null in a tag file?

You have three options available to you, each with its pros and cons.

1. <jsp:doBody var="bodyText" />

This is probably the most common and obvious solution.

You execute the body once, store its output in a variable, and check to see if that variable is empty.

<jsp:doBody var="bodyText"/>
<c:choose>
  <c:when test="${not empty fn:trim(bodyText)}">
    ${bodyText}
  </c:when>
  <c:otherwise>
    <!-- Default content here -->
  </c:otherwise>
</c:choose>

Why use ${not empty fn:trim(bodyText)} instead of the simpler condition ${empty bodyText}? Thanks to @ach, who pointed this out, the latter trims whitespace and thus behaves consistently regardless of the trimDirectiveWhitespaces setting.

Pros

  • Being the canonical solution, it is likely the least surprising.

Cons

  • The body always gets executed. You may not want to execute the tag body (and any side effects) just to test for its existence.

  • The entire contents of the tag body get stored in memory. This could be an issue for longer tag bodies.

  • (Disregard this point when using ${not empty fn:trim(bodyText)}. It only applies to ${empty bodyText}.)

    Whether the condition ${empty bodyText} is true or not depends on whether the calling JSP enables trimDirectiveWhitespaces.

    When the tag's body contains nothing but whitespace

    <%@ page trimDirectiveWhitespaces="true" %>
    <my:tag value="a">
    </my:tag>
    

    and the page defines trimDirectiveWhitespaces="true", your tag file will detect that there is no body. If trimDirectiveWhitespaces="false", your tag file will detect that there is a body.

    Note that this only applies to tag files, not simple/classic tag handlers. In the above example, if <my:tag> were defined in a tag handler, the handler would behave as if a body exists regardless of the whitespace setting:

    • For simple tag handler, getJspBody() would be non-null.
    • For a classic tag handler, doAfterBody() would be called.

2. Scriptlet

You can do away with the cons of the first solution by testing for null body with Java code. This solution and the next do exactly that, except this one is simpler and uses a scriptlet.

<c:choose>
  <c:when test="<%= super.getJspBody() != null %>">
    <jsp:doBody />
  </c:when>
  <c:otherwise>
    <!-- My default HTML content here -->
  </c:otherwise>
</c:choose>

The reason this works is that tag files are translated into handlers extending SimpleTagSupport (see javax.servlet.jsp.tagext) and scriptlets are executed within the handler's doTag() method. Thus super.getJspBody() == null works the same as you'd expect in any simple tag handler.

Pros

  • The test for a null body is done without executing the body.

  • The body is not stored in a variable.

Cons

  • Use of scriptlets. (But note, tag files are allowed to use scriptlets regardless of the scripting-invalid configuration for JSP files.)

3. Helper tag

And if you need the functionality of the scriptlet solution without actually using scriptlets, read on.

The idea here is to encapsulate the body null test within a tag handler called from the tag file. The tag handler can get access to the tag file via getParent() and and export the boolean test result as a variable:

class HasBody extends SimpleTag {
  @Override
  public void doTag() throws JspException {
    TagAdapter adapter = (TagAdapter)this.getParent();
    SimpleTagSupport parent = (SimpleTagSupport)adapter.getAdaptee();

    // `getJspBody()` is a protected method, so actually you must use reflection.
    JspFragment body = parent.getJspBody();

    this.getJspContext().setAttribute("hasBody", body == null);
  }
}

Now your tag file can look like this:

<my:hasBody />
<c:choose>
  <c:when test="${hasBody}">
    <jsp:doBody />
  </c:when>
  <c:otherwise>
    <!-- Default content here -->
  </c:otherwise>
</c:choose>

Pros

  • Does not use scriptlets.
  • Functionally equivalent to the scriptlet solution.

Cons

  • Complicated. Requires a separate tag handler just to perform the body null test.
  • Requires use of reflection.

Addendum: Use with Non-Body JSPFragments

@Ryan was wondering about adapting this approach to general JSPFragment attributes. Luckily, because all attributes are directly accessible as variables from EL (whereas the body is not), testing for an empty fragment attribute is trivial:

<%@ attribute name="frag" fragment="true" %>
<c:choose>
  <c:when test="${frag != null}">
    <jsp:invoke fragment="frag" />
  </c:when>
  <c:otherwise>
    <!-- Default content here -->
  </c:otherwise>
</c:choose>

Tags:

Jsp

Jsp Tags