Improving JSP Serving Time for an ATG Application

Improving the performance of the JSPs that serve your HTML pages is the first step in improving the overall site performance. The user’s browser can not start rendering the page or requesting the secondary media. Also the faster the page request is completed, the sooner you have a thread free to handle the next request.

There are two parts to this: first, the time it takes the JSP servlet to generate the HTML response, and secondly the time it takes to transmit that HTML response back to the user’s browser.

Caching content sections

The easiest way to reduce the time it takes for the JSP servlet to generate the response is by reducing the amount of dynamic content on the page. Or more precisely by reducing the amount of real-time or unique individual content on the page.

The Cache droplet is THE most under-utilized ATG droplet.

The Cache droplet caches the rendered output of the contents of the oparam based on a content key (such as category, user gender, logged in/logged out state, etc..) for a configured period of time. This can be very useful for things like navigation menus dynamically built based on the catalog. The catalog won’t change too often, so this dynamically generated menu can be safely cached for hours. Or for some or all of a category or product page, when you set the key to the category id or product id.

Look at your pages and evaluate what parts of the page don’t change that frequently. Even if you can only cache the page or block for five minutes, that can be a huge performance win.

Read on for more….

I’ll talk about things like repository caching and other related topics when I dive into the server side of performance tuning your ATG application in a future post.

Reduce CPU utilization in page rendering

You’re also going to want to look at potential CPU draining elements of your pages. Think about what has to happen to render the page. Don’t use RQLQueryForEach if you have the Repository Id you want, just use the RepositoryLookup droplet. Don’t loop through collections if you don’t have to. Think through it. If you have to do something computationally expensive, don’t do it twice. If you need the same data later just store the first result in a param or JSP variable.

Reduce the size of the HTML

Now that we’ve reduced the CPU time need to render the JSP, we need to reduce the amount of time it takes to transmit to the end user. First, we want to shrink the generated HTML. The smaller the output HTML is, the less time it will take to transfer across the wire to the end user. Also, you want to keep the HTML output under 100k, as Google does not index page content over the first 100k (that’s 100k ungzipped). Also, the smaller the HTML is, the quicker the browser can parse it out into the DOM tree.

Don’t use inline JavaScript or CSS styles in your JSP. Extract them out into separate JavaScript and CSS files. This reduces the size of the output HTML, and increases the amount of content that the browser can cache locally (more on this in my future post on secondary media).

Simplify the html structure as much as you can. Don’t use more tags than you need. I’ve seen an amazing amount of useless nested divs on pages.

Move comments from HTML comments to JSP comments (if you can). Most comments shouldn’t be seen by end users anyhow.

Reduce spaces. This is huge. JSPs (especially ones heavy in ATG droplets) tend to generate a huge amount of extraneous spaces. Part of it is that you generally want to format the JSP files using spaces and tabs to make it easy to see the tag hierarchy structure. It makes development and maintenance much easier. Unfortunately these linefeeds, tabs, and spaces get multiplied by iterative or recursive droplets, and other outputs.

For instance the ATG home page source has 431 lines. 103 are blank. The BestBuy televisions page has 2519 lines and 902 are blank. If you take that BestBuy page, and remove the extra blank lines, comments, and inline JavaScript and CSS, it goes from 98k to 80k. That’s a 20% size reduction, or a 20% transfer speed improvement.

If you’re using JBoss the Tomcat engine has an option to strip out extraneous whitespace. It doesn’t remove all of it, but it helps with multiple blank lines very well. In the tomcat55.sar/conf/web.xml file add the following init param to the <servlet-name>jsp</servlet-name> section:

[fusion_builder_container hundred_percent=”yes” overflow=”visible”][fusion_builder_row][fusion_builder_column type=”1_1″ background_position=”left top” background_color=”” border_size=”” border_color=”” border_style=”solid” spacing=”yes” background_image=”” background_repeat=”no-repeat” padding=”” margin_top=”0px” margin_bottom=”0px” class=”” id=”” animation_type=”” animation_speed=”0.3″ animation_direction=”left” hide_on_mobile=”no” center_content=”no” min_height=”none”][xml]
trimSpaces true

This, and other helpful performance configurations are listed in the Tuning Site Performance section of the Installation and Configuration Guide for JBoss Application Server ATG documentation. You should really read through the whole thing, and generally follow it.

Compressing the HTML response using gzip

Now we’ve reduced the size of the HTML the JSP is outputting. We can further shrink the data we need to send from our server cluster to the end user’s browser, hence making the HTML transfer faster, by compressing the HTML using gzip. Most modern browsers understand gzipped content and decompress it on the fly. Some don’t, and we’ll work around those in a moment.

Assuming you’re using JBoss for your application server and Apache for your web server, you actually have two options for gzipping the content: You can compress it from within JBoss Tomcat, or you can compress it as it passes through the Apache web server.

If you want to compress it within JBoss you can configure Tomcat to do that by editing the tomcat55.sar/conf/server.xml file. Find the connector you are using (the one with port=”8080″ for HTTP, or port=”8009″ if you’re using AJP) and add the following properties to that section:

[/fusion_builder_column][fusion_builder_column type=”1_1″ background_position=”left top” background_color=”” border_size=”” border_color=”” border_style=”solid” spacing=”yes” background_image=”” background_repeat=”no-repeat” padding=”” margin_top=”0px” margin_bottom=”0px” class=”” id=”” animation_type=”” animation_speed=”0.3″ animation_direction=”left” hide_on_mobile=”no” center_content=”no” min_height=”none”][xml]
noCompressionUserAgents=”gozilla, traviata”

That should use gzip compression for output that is html or xml, and is greater than 2k in size.

I prefer to perform the gzip compression in Apache instead of JBoss/Tomcat. Firstly the Apache deflate module is more flexible than the Tomcat option, allowing greater control of compression based on path, mimetype, extension, browser, and more. Second in a three-tier infrastructure, the web servers are typically acting as pass-through proxies, plus handling some caching perhaps (more on this later), but are generally light on CPU utilization, but higher on thread and bandwidth usage. App servers on the other hand are using this CPUs a great deal to actually handle the dynamic application. Gzipping the outgoing content does use some CPU power, so I prefer to have that load be on the web servers who can handle it easily, versus having it impact the application performance.

To make Apache gzip compress the output, you need to enable mod_deflate.

You then need to configure the module using settings similar to this:

[/fusion_builder_column][fusion_builder_column type=”1_1″ background_position=”left top” background_color=”” border_size=”” border_color=”” border_style=”solid” spacing=”yes” background_image=”” background_repeat=”no-repeat” padding=”” margin_top=”0px” margin_bottom=”0px” class=”” id=”” animation_type=”” animation_speed=”0.3″ animation_direction=”left” hide_on_mobile=”no” center_content=”no” min_height=”none”][xml]
# Insert filter
SetOutputFilter DEFLATE

# Netscape 4.x has some problems…
BrowserMatch ^Mozilla/4 gzip-only-text/html

# Netscape 4.06-4.08 have some more problems
BrowserMatch ^Mozilla/4\.0[678] no-gzip

# MSIE masquerades as Netscape, but it is fine
BrowserMatch \bMSIE no-gzip

# NOTE: Due to a bug in mod_setenvif up to Apache 2.0.48
# the above regex won’t work. You can use the following
# workaround to get the desired effect:
BrowserMatch \bMSIE\s7 !no-gzip !gzip-only-text/html

# Don’t compress images
SetEnvIfNoCase Request_URI \
\.(?:gif|jpe?g|png|swf|flv)$ no-gzip dont-vary

# Make sure proxies don’t deliver the wrong content
Header append Vary User-Agent env=!dont-vary

You can read more about this configuration, plus some issues that arise when using mod_disk_cache and mod_deflate together here. You can get around this by only gzipping html output from your JSPs, which you won’t be caching anyhow, however doing that in a clean fashion appears to be tricky. The AddOutputFilterByType directive doesn’t work with proxied content, and the mod_filter based approach for that is only built into Apache 2.1 and 2.2, so if you’re on 2.0 it’s no good. When I have a good solution for Apache 2.0 + ATG, I’ll post about it.

By gzipping the HTML we can reduce the BestBuy page further, from 80k to 14k, an additional 80% reduction. We’ve shrunk the data that has to transfer to the end user’s computer by a total of 85% at this point. This means the end user’s browser can parse the HTML dom tree, display the page, and start loading the secondary media (images, css, js, etc…) 85% faster. It also means your request handling thread is now free that much faster (after doing the actual processing of the JSP servlet) and is available to handle a new request.


So at this point we’ve made the JSP as efficient as possible, used the Cache droplet wherever we could, reduced the size of the output HTML, and gzipped the HTML for faster transmission to the end user. We’ve taken a 98k page and ended up only having to move 14k across the wire.

Next we’ll look at all those secondary media files that your page needs: JavaScript, CSS, images, video, Flash, etc…





13 responses to “Improving JSP Serving Time for an ATG Application”

  1. gux Avatar

    What a coincidence: we have now a requirement where the Cache droplet fits perfectly: a rollover menu where the links are read from a content repository.

    We were thinking on using the cache droplet, and you are right: it’s the most under used ATG droplet ever.

  2. […] Improving JSP Serving Time for an ATG Application […]

  3. Tijomon mathew Avatar
    Tijomon mathew

    very good article ,

    Thanks a lot

  4. Luca Gilardoni Avatar
    Luca Gilardoni

    About the ‘The Cache droplet is THE most under-utilized ATG droplet”; true. However I just fought a bit before realising that
    it won’t work if the cached snippet includes an include … not mentioned anywhere (and I’m not even sure why exactly).

  5. Ben Carlson Avatar

    “The Cache droplet is THE most under-utilized ATG droplet.” – Amen, Devon.

  6. sunil Avatar

    I beleive Best Buy might be using the Akamai Caching. If so, Akamai caches the page and he subsequent request form the end users should be served from Akamai Cache without needing to hit hte customer serer.

    If my understanding is correct then why do we need CacheDroplet?
    Can you please justify or may be correct me if my understanding is wrong?


    1. Devon Avatar


      I’m not sure off-hand how Best Buy is leveraging Akamai, so I can’t speak to that exactly. The majority of ATG customers who are using Akamai, are only using Akamai to cache static assets such as JavaScript, CSS, Images, Flash, etc… and the JSPs are all still served dynamically from the app servers. In this situation the Cache droplet is VERY important.

      There are a couple instances where through advanced Akamai integration and/or heavy use of AJAX to populate dynamic content sections asynchronously, some clients DO cache JSP pages at Akamai – for things like home page, category pages, and even product pages sometimes. If Best Buy is doing that, then the Cache droplet will not matter as much, although it will still help some, since Akamai will make multiple origin pulls for the same resources.

      It really depends on your Akamai integration setup.



      1. Pankaj Avatar

        What to do in case we are caching JSP pages at Akamai – and using form handlers/droplets to populate the jsp. But now all the users see the response from the first user’s request. As first hit’s response get cached and shown to all other users.

        1. Devon Avatar

          You can’t cache pages like that. Unless all the state is kept as query params and akamai caches different versions for different param values, or unless you replace all the dynamic parts with ajax loads (this is pretty typical to allow you to cache the site home pages, basic category pages, etc… while dynamically populating the header to show the “Welcome Devon” and “You have 3 items in your cart”.

          One thing to look at is if Akamai has anything similar to CloudFlare’s Railgun, which only moves the diff of the page across the wire so only the changed bits get pulled from origin, which saves a lot of time.

  7. […] post about Improving JSP Serving Time for an ATG Application was copied and pasted into a new post on the site. There’s no contact info […]

  8. Nitin Verma Avatar
    Nitin Verma

    Hi Devon,

    I am new to ATG, i have installed ATG10.0.1 on my system. I am using Eclipse (Helios).
    Please help me out to create simple ATG App and how to RUN on JBOSS4.2.

    And one more thing when i am importing existing ATG module into Eclipse it’s giving me error “NO Project Found”

    Nitin Verma

    1. Devon Avatar


      that question has no quick answer. Building ATG applications is a major undertaking. I’d recommend starting with the CRS and working through the programmer’s guide.

      ATG Modules != Eclipse Projects. You can’t just import it as a project, as it has no .project file, etc…

  9. siva Avatar

    what is the best of way to improve jsp serving time if we are using endeca & experience manager to serve pages.

Leave a Reply

Your email address will not be published. Required fields are marked *

PHP Code Snippets Powered By :