views:

49

answers:

1

Hi all,

This is a follow-up to the 'overhead of jsp include' question below:

http://stackoverflow.com/questions/1479218/jsp-performance-using-jspinclude

In our application, developers have 'modularized' jsp fragments by heavy use of "jsp:includes" for "common" jsp code repeated throughout the application.

Pros

The pro's are as follows:

  • it's DRY-- we define the jsp fragment once. This is a big help when you need to change some html and don't need to find/replace/search/destroy.

  • it's fairly easy to follow: you clearly pass in the parameters. When you edit the 'included' page, you 'know what you're getting', i.e. vs. some 'global variables' declared in the 'including/calling' page.

Cons

  • performance overhead of the additional request

Questions

So as a follow-on:

  • how much performance overhead does a 'jsp:include' incur? It's not obvious from the tomcat code (though you see it does a whole lot more than would an inline call). Also in profiling the app I've never requestDispatcher.include() or invoke() methods appears as hotspots.
  • could someone point as to where precisely lies the bulk of the overhead? (i.e. method X in class Y) Or whether it's just all the "little stuff" (e.g. setting attributes or object creation and subsequent GC) that happens with each request?
  • what are the alternatives? (AFAIK @include and jsp:include. anything else?)
  • (Stupid bonus question) why couldn't the servlet engine 'include' the jsp at compile time, i.e like an 'inline macro with parameters' so that we developers could get the clarity of 'jsp:include' and the peformance of '@include'.

I've wondered about this last question for a while. I've used code generation tools in a past life and never quite understood the lack of options for including jsp fragments.

For the benefit of the reader I've included the tomcat 'applicationDispatcher.invoke()' method (tomcat 5.5. sorry if it's dated). For clarity, I've pruned the exception handling.

thanks in advance

will

    private void invoke(ServletRequest request, ServletResponse response,
        State state) throws IOException, ServletException {

    // Checking to see if the context classloader is the current context
    // classloader. If it's not, we're saving it, and setting the context
    // classloader to the Context classloader
    ClassLoader oldCCL = Thread.currentThread().getContextClassLoader();
    ClassLoader contextClassLoader = context.getLoader().getClassLoader();

    if (oldCCL != contextClassLoader) {
        Thread.currentThread().setContextClassLoader(contextClassLoader);
    } else {
        oldCCL = null;
    }

    // Initialize local variables we may need
    HttpServletResponse hresponse = (HttpServletResponse) response;
    Servlet servlet = null;
    IOException ioException = null;
    ServletException servletException = null;
    RuntimeException runtimeException = null;
    boolean unavailable = false;

    // Check for the servlet being marked unavailable
    if (wrapper.isUnavailable()) {
        wrapper.getLogger().warn(
                sm.getString("applicationDispatcher.isUnavailable", 
                wrapper.getName()));
        long available = wrapper.getAvailable();
        if ((available > 0L) && (available < Long.MAX_VALUE))
            hresponse.setDateHeader("Retry-After", available);
        hresponse.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, sm
                .getString("applicationDispatcher.isUnavailable", wrapper
                        .getName()));
        unavailable = true;
    }

    // Allocate a servlet instance to process this request
    try {
        if (!unavailable) {
            servlet = wrapper.allocate();
        }
    }
   ...exception handling here....

    // Get the FilterChain Here
    ApplicationFilterFactory factory = ApplicationFilterFactory.getInstance();
    ApplicationFilterChain filterChain = factory.createFilterChain(request,
                                                            wrapper,servlet);
    // Call the service() method for the allocated servlet instance
    try {
        String jspFile = wrapper.getJspFile();
        if (jspFile != null)
            request.setAttribute(Globals.JSP_FILE_ATTR, jspFile);
        else
            request.removeAttribute(Globals.JSP_FILE_ATTR);
        support.fireInstanceEvent(InstanceEvent.BEFORE_DISPATCH_EVENT,
                                  servlet, request, response);
        // for includes/forwards
        if ((servlet != null) && (filterChain != null)) {
           filterChain.doFilter(request, response);
         }
        // Servlet Service Method is called by the FilterChain
        request.removeAttribute(Globals.JSP_FILE_ATTR);
        support.fireInstanceEvent(InstanceEvent.AFTER_DISPATCH_EVENT,
                                  servlet, request, response);
    }
   ...exception handling here....

    // Release the filter chain (if any) for this request
    try {
        if (filterChain != null)
            filterChain.release();
    }
   ...exception handling here....        

    // Deallocate the allocated servlet instance
    try {
        if (servlet != null) {
            wrapper.deallocate(servlet);
        }
    }
   ...exception handling here....

    // Reset the old context class loader
    if (oldCCL != null)
        Thread.currentThread().setContextClassLoader(oldCCL);

    // Unwrap request/response if needed
    // See Bugzilla 30949
    unwrapRequest(state);
    unwrapResponse(state);

    // Rethrow an exception if one was thrown by the invoked servlet
    if (ioException != null)
        throw ioException;
    if (servletException != null)
        throw servletException;
    if (runtimeException != null)
        throw runtimeException;

}
+1  A: 

If you've profiled the app, then you've really answered your own question - if there's no measurable performance hit for using <jsp:include>, then it's not worth worrying about. Internally, Tomcat will construct a new HttpServletRequest and associated gubbins, but it's probably smart enough to do keep it lightweight. The lesson is to not assume there's a performance problem with Feature X until you've actually observed it.

A good alternative to <jsp:include> is JSP 2.0 tag files. These allow you to encapsulate reusable content, like with a <jsp:include>, but with a clearly defined interface for the fragment, and without incurring the overhead (however small that may be) of <jsp:include>. I much prefer them, I think it's a more elegant approach.

(Bonus Answer) There is an include mechanism that inlines: <%@ include file="x.jsp" %>. This performs a compile-time inlining of the included content. You have to be careful with that one, though, since if you change the content of x.jsp at runtime, the "host" page won't be recompiled.

skaffman