1

I have a web application that uses Spring MVC and jsp pages. In one of my jsp files I have a loop that iterates over a Map<String, Object> and renders each entries value using escapeXml(toString):

<c:forEach var="attr" items="${attributes}">
  <ns:entry name='${attr.key}' value='${fn:escapeXml(attr.value)}' />
</c:forEach>

This is obviously not a very good solution as it steals the toString for its purposes, and couples the markup to the model. I want to avoid this by checking a specific folder to see if there exists a .jspf file with the same name as the entries key, and if so, use that fragment to render it with the toString approach as a fallback:

<c:if test="! ${fn:includeJspf(pageContext, '/WEB-INF/attributes/', ${attr.key}>
  <c:forEach var="attr" items="${attributes}">
    <ns:entry name='${attr.key}' value='${fn:escapeXml(attr.value)}' />
  </c:forEach>
</c:if>

And my includeJspf function is defined:

public static boolean includeJspf( PageContext pageContext, String folderPath, String jspfName ) {
    String relativePath = folderPath + "/" + jspfName + ".jspf";
    if ( new File( pageContext.getServletContext().getRealPath( relativePath ) ).exists() ) {
        try {
            pageContext.getRequest().setAttribute( "parentPageContext", pageContext );
            pageContext.include( relativePath );
        }
        catch ( ServletException | IOException e ) {
            logger.warn( "Unable to include fragment [{}]: {}", relativePath, e.getMessage() );
        }
        return true;
    }
    return false;
}

And my jspf

<%@ page session="false" contentType="application/xml; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<c:set var="name"
  value="${requestScope['parentPageContext'].getAttribute('attr').key}" />
<c:set var="value"
  value="${requestScope['parentPageContext'].getAttribute('attr').value}" />

<myobj:${fn:escapeXml(name)} value=${fn:escapeXml(value.value)} />

This fails thusly:

:30: parser error : StartTag: invalid element name
                    <%@ page session="false" contentType="application/xml; chars
...

But... If I change the extension to jsp, everything works perfectly. I assume I am missing something simple like configuration in web.xml, but have been unable to find any documentation to explain this. Any advice?

Lucas
  • 14,227
  • 9
  • 74
  • 124

1 Answers1

0

In tomcat (I'm not sure about other servlet containers) you do need to add a .jspf mapping to the web.xml:

<servlet-mapping>
    <servlet-name>{jspservlet-name}</servlet-name>
    ...
    <url-pattern>*.jsp</url-pattern>
    <url-pattern>*.jspf</url-pattern>
</servlet-mapping>

(SO source: jsp include doesn't work for jspf files under tomcat 8 but works under the jetty - jsp:include and a programmatic include function the same I think)

However, there is some discussion about the desirability of using fragments for runtime/dynamic inclusions: here on coderanch and here on oracle.

This is a recommendation about it, but not the spec:

Use the .jsp extension for top-level pages, dynamically included pages, and pages that are forwarded to—pages that are translatable on their own.

Community
  • 1
  • 1
ljgw
  • 2,751
  • 1
  • 20
  • 39
  • Great answer and thanks for the resources. It makes sense. Being dynamically included they are not part of the _including_ page as it is compiled. So, in keeping with the _reasonable_ recommendations from your links, I think I will continue to use the `jsp` extension due to their dynamic nature... – Lucas Aug 28 '15 at 14:38