Cache in on faster, more reliable JSPs

Open source OSCache tag library offers an innovative approach to caching JSP code

Caching a JavaServer Pages (JSP) block can give a Web developer freedom to add complex information that would otherwise bring a Web server to its knees. Some examples of that complex information include a report that takes the database several seconds to calculate or a Website's weather section, which includes SOAP calls to a remote server to retrieve the current temperature.

JSPs are able to run complex Java code within tag libraries, making JSP files easier to maintain and more useable by nondevelopers. While many tag libraries are available both commercially and as open source projects, most simply duplicate what could otherwise be written with a small Java scriptlet. Few have found innovative ways to use custom tags that were not readily possible before custom JSP tags existed.

The OSCache tag library, created by the OpenSymphony project, breaks new ground by providing fast in-memory caching within your existing JSPs. While some vendors support various forms of caching, those solutions are vendor specific. OSCache will run on any JSP 1.1-compliant server, and can cache your existing JSP code blocks either for all users or on a per user basis. It also has advanced features for added flexibility, including caching to disk, programmatic flushing of caches, and exception handling. And, as with every venture of the OpenSymphony project, the OSCache code is released freely under an open source license.

In this article, I will walk you through the design of a fictitious auction Website to show how the OSCache tag library works. The fictitious Website will include an administrative page that reports on recent activity, a rich homepage with various types of promotion information, and a special navigation bar that includes each user's open auction activity.

Real-time administrative report

Part of the auction Website includes a real-time administrative report that takes the database server several seconds of CPU time to create. This is important as we can potentially have several managers who want to monitor the system, and we want to avoid rerunning this report. To do this, we will wrap the entire page in an application-scope cache tag that refreshes every hour. Some vendors have comparable features, but this OSCache feature is only the tip of the iceberg.

To keep the examples simple, we won't worry too much about formatting, but do realize that the cache tags could be interspersed with as much HTML as needed. We'll start the page by adding the tag library declaration:

<%@ taglib uri="cachetags" prefix="cache" %>

We'll now wrap the cache around the entire page. This will be the first use of the OSCache tag library. As the cache tag wraps regular JSP code, you can finish debugging your code before enabling the cache. The cache tag defaults to one hour.

<cache:cache>
  .... complex administrative report ....
</cache:cache>

The administrative page is now cached, so if a manager visits the same report within an hour from the time the report was generated, it will see a cached result and the database server will be untouched.

Cached homepage

The auction site's homepage will show how active the site is, as well as promote specific auctions that will expire soon. We'll want to show the number of active auctions, the number of currently logged-in users, a list of auctions that are expiring shortly, and the current time. Each piece of information is accurate for a different period of time. Auctions on the site typically last several days, so we can cache the number of active auctions for six hours. The number of users will change reasonably often, but we will cache this number for 15 minutes at a time. Finally, we want to always display the most current time.

After declaring the cache tag library, we'll start by crudely printing the date with no caching:

It is now <%=new java.util.Date()%>.

Next, we want to show a list of soon-to-expire auctions:

<cache:cache>
<ul>
<%
//Build an Iterator of the most recent auctions
Iterator auctions = ....
while (auctions.hasMore()) {
    Auction auction = (Auction)auctions.next();
    %><li><%=auction%></li%<
}
%>
</ul>
</cache:cache>

Finally, we want to display a count of the number of active auctions. We want to cache this information for six hours, and because the cache tag needs to know the number of seconds to cache the data, we translate six hours to 21,600 seconds:

<cache:cache time="21600">
<%
//Query our database for the number
int auctionCount = ....
%>
Fictitious auction has <%=auctionCount%> active auctions!
</cache>

We've quickly built our homepage, complete with a complex caching system that caches each part of the page in line with how frequently that information changes. We could also put much more on the homepage than normal; ordinarily this would have significantly slowed down the homepage and probably overloaded our database server.

Custom navigation bar

Say, while planning our auction site, we decide we want to show the shopping cart contents below the left-hand navigation bar. We'll show the number of bids and current asking price of each item a user is selling, as well as a list of the items for which the user is currently the top bidder.

We use the session-scope caching feature to build this functionality into the navigation bar. We put this code in the template or include file that is referenced across the site:

<cache:cache key="navbar" scope="session" time="300">
<%
/Retrieve and print the current relevant bid information
%>
</cache:cache>

I've introduced two important attributes here, key and scope. Ordinarily we wouldn't need to set the key attribute because the tag will create its own unique key for that block. However, we are going to want to reference this cached block in another part of this site, so we've defined our own key for this cache. Second, the scope attribute was used to tell the tag that this cache should be cached per user rather than for all users.

You should exercise caution when using session-scope caching. Realize that while you may reduce the load on the server by a factor of 5 or 10 for your complex navigation bar, you will drastically increase the memory requirements per session. Increasing the possible number of concurrent users as a function of CPU is great, but not if it reduces the possible number of concurrent users as a memory function below the CPU restriction.

As I mentioned previously, we wanted to reference that cache block in another part of the site. That is because when a user places a new item up for bid or if that user bids on someone else's item, we'll want to flush the cache so that it will refresh the next time the navigation bar is loaded. While this data could change due to another user's activity, the user will notice and possibly get confused if this list doesn't change when he or she performs an action on the site.

We flush the tag by using one of the supporting tags in the OSCache library, namely the flush tag. We will add the following code in the page that processes a user action that would affect this section:

<cache:flush key="navbar" scope="session" />

The navbar cache block will be refreshed the next time a user accesses it.

Future directions

Now that our sample Website is up and running, in large part thanks to OSCache, we can begin to explore the exception handling capabilities of OSCache. The OSCache library allows you to programmatically display cached results even after time has expired; for instance, when a Java exception is thrown inside the block. While the code is somewhat manual at the moment, with this exception handling in place, you could disconnect your database server from your Web server and your site would continue running.

The JSP 1.2 specification introduced the TryCatchFinally interface that will allow the tag itself to detect and handle Java exceptions. The tag can then incorporate this exception handling code, again making your JSP simple and uncluttered.

The OpenSymphony developers plan to implement other caching mechanisms and a more manageable key system so you can manage RAM and disk space consumed by caches. With these features, you can continue to make your Website faster and more robust.

Conclusion

The OSCache tag library can help make your site richer and improve its performance. Armed with the OSCache tags, you can now eliminate some of the things that slow down your Website, like traffic spikes, problematic database servers, or power failures.

Serge Knystautas is the lead technical guru and Java developer at Loki Technologies. He's been using Java since 1.0 beta (1995) and now contributes on several open source projects, including the Town database API and tag library, and JAMES, the Java Apache Mail Enterprise Server. He also has worked on smaller projects such as JavaWeather, and contributes to the JSR 052 for a standard tag library for JavaServer Pages.

Learn more about this topic