Rendering JSP Versus Static HTML Completed With AJAX
Most web applications are combinations of dynamically-generated and later dynamically-managed HTML. That is to say that a server-side rendering of HTML, through the likes of JSP or ASP or PHP, is combined with client-side manipulation using JavaScript, or a JavaScript framework like jQuery or Prototype or Dojo, to provide robust web experiences. There are many places in web applications to provide compromise or balance between performance and dynamic content.
In most of these applications, most of the HTML pages provided aren’t stored in a “static” form, but are generated as the result of a request. For our discussion, we’ll use JSP, although the techniques discussed don’t depend on that technology. This request-to-render requires time in the request processing to gather and format the data into HTML, which often tips that balance toward the dynamic content and away from performance. In many cases, it can be frustrating for users, and can even lead to loss of business.
There are ways of providing performance-driven delivery of the HTML content, and still maintaining on-demand content. Advanced preparation of the least-changing parts of web apps, even if they are generally dynamically generated, can provide huge boosts. We like to think that our applications need all of that on-the-fly generation, but if we look more carefully, we can find lots of places where this is just not true.
Consider, as an example, a part of a website that is responsible for rendering catalog pages. In our most frequently used patterns, we have a skeleton of a catalog page that is filled with portions of our JSP (or ASP or PHP or whatever) that fetch the images and text and even stock information and possibly customer reviews. These pages are queued off of requests that are perhaps keyed on inventory or SKU numbers that then provide the information necessary to grab the correct records needed to complete the page. The request finishes by generating the HTML, complete with image links and text.
Re-think the process a little bit, and consider generating that HTML at the time the catalog entries are entered or updated, rather than as they are requested. Using whatever back-end tool to create the inventory items, or to update the text or images, triggers a process that creates an HTML page that contains the image links and appropriate text. It could even do this for several languages, if desired. The search indexes and page links are all fed information that leads to the static page, which still might be named with clues like the inventory number or SKU. These static pages are retrieved incredibly fast, and can even be cached by web servers in front of the application servers, or by huge caching services like Akamai.
The little bits that require truly dynamic information would be left out of the HTML, and instead would be filled-in after (or as) the page loads. This would load the “live” things like the current price or stock availability; the rolling feed of costumer feedback and ratings; and even offers or specials, discounts, and “also purchased” suggestions. By providing indicators in the pre-rendered HTML, like named <div>, and providing keys in-line using JavaScript or providing values in HTML attributes, the browser would load these elements and the user experience wouldn’t change, except for the lean toward performance, without a loss of dynamic content.
There is a downside to this method. It does require JavaScript to be enabled on the client. The JSP rendering HTML doesn’t require any JavaScript to work, and well-written JSP can behave quite responsively. For web applications that are already relying on JavaScript for client-side functionality, this is a small leap, but for applications that handle all of the user interactions completely on the server, this can be a giant leap.
This technique relies on using cacheable, or at least fast-to-deliver, static HTML. Let’s start there and make some examples of this process. Here’s a simple JSP that isn’t cacheable.
<html> <head> <title>Session ID</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> </head> <body> Your session ID is <span id="sessionId"><em><%= request.getSession(true).getId() %></em></span>. </body> </html>
Because the session is injected in the rendering of the HTML, it must be run for every request. For one visitor this may render as this HTML.
<html> <head> <title>Session ID</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> </head> <body> Your session ID is <span id="sessionId"><em>ABC</em></span>. </body> </html>
For another visitor, or the same visitor later, this may render as this HTML.
<html> <head> <title>Session ID</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> </head> <body> Your session ID is <span id="sessionId"><em>123</em></span>. </body> </html>
There are ways to make this provide some hints of expiration that might allow a browser to cache the page, but it still needs to be run for every visitor. Further, what happens if the browser respects an expiration that outlasts a session? If the expiration is shorter than a session time-out the user still re-requests and re-renders the HTML. Of course, for this trivial example those are all silly considerations.
Consider instead a static HTML page that uses post-loading queries to set that data. The required elements to complete this task are potentially three or four times as numerous, and potentially more difficult to maintain, but if the desire is a solid, cacheable HTML page with some dynamic elements, it’s what must be done. Well, one way it must be done; the way outlined here.
Let’s look at our simple session ID page. Put that JSP in our application, named sessionId.jsp (so the following examples will work). In a real-world application the JSP would probably be loaded in a context (looks like a path to the browser), and the HTML would be in another so that a web server acting as a front-end could tell them apart, serving the HTML and forwarding the JSP requests. We’ll use jQuery to shorten the examples, but any JavaScript or framework should be capable of the same. Add the following static HTML to our application and call it sessionId.html instead.
<html> <head> <title>Session ID</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script> <script type="text/javascript"> $(function() { $("#sessionId").load('sessionId.jsp #sessionId em'); }); </script> </head> <body> Your session ID is <span id="sessionId"><em>loading</em></span>. </body> </html>
This HTML is completely cacheable. It will be quickly loaded by the browser, and will at every open be loaded from the browser cache if possible. By using the Google CDN version of jQuery, we leverage the browser cache again, as well as off-loading standard libraries to be served by someone else. The little bit of JavaScript in our page runs after the page load is complete. It uses jQuery AJAX calls to retrieve our other JSP (which could be pared down to have less content, but this one is pretty trivial, so we can leave it as is), and parses the dynamic value out and places it into the web page. In the end the browser is displaying a page that looks the same as the one rendered by our original JSP.
All right, for the purists, the JSP could be pared down to the bare minimum to meet our JavaScript’s needs:
<span id="sessionId"><em><%= request.getSession(true).getId() %></em></span>
It could even be reduced to just return a simple string of text containing the session ID and the JavaScript could be changed to not look for our named element. We could also change the result from HTML formatted to another, perhaps JSON or XML, as would be necessary to contain the data. The myriad options gives us more flexibility than we have time to discuss in this little blurb.
Since this request for the data is very fast to render, in most browsers, the user probably won’t ever see the loading message in their browser window. Still, every user gets the same HTML, and every user gets their specific dynamic content.
What about more complicated JSP?
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <html> <head> <title>Complicated</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> </head> <body> <div id="wrapper"> <div id="header"> <div id="logo"> <img src="<c:url value='/images/logo.png'/>" style="border: none;" /> </div> <div id="userInfo"> Your session ID is <span id="sessionId"><%=request.getSession(true).getId()%></span>. </div> </div> <div id="content">Lorem Ipsum...</div> <div id="footer"> <c:if test="${empty visitors}"> <c:set var="visitors" value="0" scope="application" /> </c:if> <c:set var="visitors" value="${1 + visitors}" /> Visitor ${visitors} </div> </div> </body> </html>
This is not an entirely uncommon page style, brevity and content notwithstanding. It has some layout. There’s a runtime generated URL used to nab a logo image from the /images folder relative to the application. There’s our simple session ID look up (which would probably display the users’ username or other information in a real application). Here we’ve put some static content (the “Lorem Ipsum” bit), but in a real-world application this would probably also be dynamically generated. Finally we have a little counter to show how many times the page has loaded since the application started.
Again, the problem we’re trying to solve is that because the values are injected into the HTML by the JSP at render time, the server generates a different result every time. Further, in addition to dealing with the data, the server has to jump in and out of the HTML as it renders. Think of a more real-world example where these header and content and footer areas are done by various JSP includes or forwards instead. All of that work adds up.
Consider changing the HTML to be more of a template, and like our session ID example, gather the information from a post-load event.
<html> <head> <title>Complicated</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script> <script type="text/javascript"> $(function() { $.get("complexJson.jsp", function(data) { $("#sessionId em").html(data.sessionId); $("#footer").html("Visitor " + data.visitorCount); }, "json"); }); </script> </head> <body> <div id="wrapper"> <div id="header"> <div id="logo"> <img src="/images/logo.png" style="border: none;" /> </div> <div id="userInfo"> Your session ID is <span id="sessionId"><em>loading</em></span>. </div> </div> <div id="content">Lorem Ipsum...</div> <div id="footer"></div> </div> </body> </html>
As discussed in our mention of the catalog before, the HTML should be rendered ahead of time with as much of the “static” data already in place; this will result in more HTML pages, but the speed of delivering static content is what we’re after. We would ensure the URL for the logo was correct, and the content (the “Lorem Ipsum” bit) would be in place. Only the “live” data would be left for the call to handle. With this in mind, we’ve left the placeholders for our content, but removed any initial content, or put indicators that content is to come.
For our “live” data, the little bit of JavaScript calls for some JSON data and then sets the HTML according to the results. Here we can create our small JSON-generating JSP to answer the call.
<%@ page language="java" contentType="application/json" pageEncoding="UTF-8" trimDirectiveWhitespaces="true"%> <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <c:if test="${empty visitors}"> <c:set var="visitors" value="0" scope="application" /> </c:if> <c:set var="visitors" value="${1 + visitors}" /> { "sessionId" : "${pageContext.session.id}", "visitorCount" : "${visitors}" }
Again, we’re playing with some pretty trivial data here, but this shows how our result can be configured to be pretty small, allowing for fast results for our live data. We’re spared anything more than the trival formatting of a JSON object, and in a larger application we probably would have used a library for that effort and moved this work into a Servlet or perhaps a Spring Controller or Struts Action. Of course, this JSON could have been generated by any number of scripting or programming efforts.
If necessary or desired, we could have broken the requests into separate AJAX calls. This allows flexibility in data gathering as well as allowing smaller data sections to fill in without waiting for larger data sets to be processed. For example, we could have filled in our header and footer separately by calling our other example JSP for the session ID and our grab JSON for the visitor count.
$("#sessionId").load("sessionId.jsp #sessionId em"); $.get("complexJson.jsp", function(data) { $("#footer").html("Visitor " + data.visitor); });
These trivial examples give some insight to the potential. Imagine a site with hundreds or thousands of catalog or “brochure” pages, where the bulk of the content doesn’t frequently change, but where small parts contain “live” data. These sites could pre-render their pages using techniques such as those shown here, and off-load a ton of server load wasted generating nearly identical HTML. The dynamic content would be completed post-load, and the user experience can be greatly enhanced.
This loading of data after the HTML arrives is used around the web by service giants, including Google (specifically Google+), Facebook and Twitter. Visit one of those sites (logged-in, of course) and scroll to the bottom; you’ll find that as you approach the bottom similar requests are made and the page content is extended. All of this is done very quickly and often with large amounts of data.
A potential downside has to do with some of the site decorations and things like navigation menus. Imagine a case where a series of web pages is pre-rendered, each within a section of a menu contained in the HTML. If another page is added to the same section of the menu then one technique to ensure they are all correct would require all of those pages to be re-rendered to include the changed page. Static content, like that found in footers, might have the same issue when rendered into every page in advance. Every page needs to be re-rendered or otherwise updated when these elements change.
Of course, those bits could likewise be loaded with the same technique. If the content for those parts of the web page were contained in a static HTML page, they would both load very quickly for everyone (size is a consideration), and they would be cached by the browser as well.
The objective here is to show that large numbers of pages with mostly static content can be generated and cached for improved performance. Intelligent and careful use of JavaScript can enhance the user experience by providing not only the client-side functionality, but by completing some of the content.
great blog post explaning jsp vs ajax for rendering data, one question to @Jeff Warren ,what is best practice for data randering business logic security is mejor concern,in my web application i am using jsp as well as ajax for initial loding of data i am storing some data in session(on servlet) and while loding jsp i am assigning this values to some js varible ,after loading page further communication is done through ajax request as per examp per filte option fetching data for a specific search!