Serve Web content in static document form to reflect data state Every modern commercial Web application employs a technology that lets the Web server assemble content dynamically upon the client’s request. The reason is obvious: With business data changing so rapidly, simply not enough hands exist on Earth to manually update the content on the Web and corporate intranets. Thus, numerous servlets, CGI scripts, JSPs (JavaServer Pages), and ASPs (Active Server Pages) continuously process client requests to generate responses on the fly. This is called dynamic content publishing, a well-known concept with well-known pros and cons:Pros:Represents the current data stateIs automatic; requires no human operatorCons: Requires extra request processing; therefore, poor response timeCreates processing overhead on the serverAt the same time, whenever possible, the same commercial Web applications serve static content. Documents like articles, photos, corporate reports, disclaimers, and so on represent static data, and therefore, don’t need to be generated when the client requests them. This data exists in file form and is generally created only once. This is called static content publishing and has been around since the Internet’s inception. Its strong and weak points contrast with those of dynamic content publishing:Pros:Has fastest response timeAdds no processing overhead on the serverCons: Does not represent data’s current stateIs not automatic; requires human operator for content updatesFor some time, I pondered how to combine the strengths of these two approaches while avoiding their weaknesses. I wondered: Is there such a thing as semistatic or semidynamic content publishing? And then I discovered …Event-driven Web content publishingMy event-driven content publishing solution is simple, and I’ll explain it with a familiar example.Most developers are familiar with the idea of personal homepage-style Web applications that serve static content. Once a notable event occurs in the author’s life, she simply opens her favorite Web-authoring tool, modifies or adds the content, and FTPs it to the Web server. Please note that she actually modifies the content upon an event taking place. In between events, the content is static; hence, the term event-driven. Remove the weak human factor from the above example and you get a powerful publishing concept. Its core lies in the assumption that in between data modification (or state change) events, the data is static. Therefore, you can present the data in static Web content. Upon the data modification event, this static content automatically repopulates with new data and serves as such until the next event. In comparison, traditional dynamic content publishing appears request-driven.Let’s examine the event-driven content publishing concept:Pros: Represents data’s current stateHas fastest response time of static contentReduces processing overhead on the serverIs automatic; requires no human operatorEasily integrates with existing Web applicationsAs wonderful as it looks, the approach is no silver bullet. Its applicability heavily depends on the ratio between the frequency of data modification events and the Web client’s requests. For example, if the events occur more frequently than the client’s requests, this approach creates even more overhead on the server than request-driven publishing. Imagine a rarely visited Webpage that displays current server time with millisecond precision. Use the event-driven approach to publish such a page and your server will die long before the first curious client can enjoy the fruits of your labor.Another constraint: You must be absolutely certain you handle every data modification event regardless of its origin. For example, you might have a table in the relational database whose records can be updated by the Web clients as well as the SQL console operator and the batch process. If you handle just Web-originated events and miss the others, you may soon find your content outdated.Finally, this publishing approach is not applicable with variable data selection criteria. Consider online banking where clients may request account statements for unpredictable time periods. You can service such requests with traditional request-driven publishing only. To summarize, the approach’s cons include:Susceptible to the event-frequency/request-frequency ratio. To be applicable, the ratio must be less than or equal to 1.Requires handling all data modification events regardless of origin.Supports queries with fixed criteria only.If your application is not subject to these constraints, then the benefits of event-driven Web content publishing approach are obvious. Design overviewBefore I walk you through the simple proof-of-concept implementation, I need to cover the approach’s design pattern. Basically, you can break its functionality down to the following logical components: Data source. Maintains the data’s state. It may be a relational database, an entity bean, a JavaBean, a flat file, a timer, a temperature measuring device, or so on.Content generator. Retrieves the data from the data source and embeds it into the Web client-formatted content. This component’s implementation has no constraints. Choose whatever technology better suits your business (or yourself!): Java Servlet API, JSP, ASP, server-side JavaScript, Tcl, Perl, the list goes on. You may even implement it as part of the deployed Web application without any modifications! You should not directly expose the content generator to the Web clients; otherwise, you’ll end up with a request-driven content publishing implementation.Event source. Recognizes a data state change in the data source. It then notifies the event handler component synchronously or asynchronously. The event source must be able to track data modifications regardless of their origin. For example, if you maintain the data in a relational database, you would use a table trigger for the most robust event source implementation. The event source may act as an entry point for the Web-originated data modification requests.Event handler. Is the heart of event-driven content publishing. Once notified by the event source, it retrieves the dynamically populated content from the content generator and saves it to the file, normally in HTML or XML format. Without the right tools, implementing the event handler could be a challenge. Some people use proprietary template engines to accomplish a similar task, but you really only need JSP technology to succeed.Static content. Is produced by the content generator, intercepted by the event handler, and captured into a file or file set. It is directly exposed to the Web clients.Figure 1 is a high-level sequence diagram that can help you visualize the dynamics of a typical event-driven content publishing scenario.The actual implementation may blur the boundaries between these components. You may combine the event source and event handler in one physical component. In addition, some components may carry extra responsibilities. For example, if the content generator produces content in XML document form, the event handler might need to transform it into HTML using an XSLT processor. For learning purposes, we will stick to a simple scenario.Take to the pollsWe will use JSPs to implement event-driven content publishing. The following exercise assumes you are familiar with JSP basics. Download the complete source code for this example in Resources. Our proof-of-concept Web application collects opinions in a simple poll. The static HTML page shows the current poll results. You may request this page directly for poll-monitoring purposes or indirectly by casting your vote. Listing 1’s simple JavaBean acts as the data-source component. As you’ll see, the bean maintains the poll’s state in the instance variables, so the poll data is available as long as the bean’s instance exists:Listing 1. Poll.javapackage org.climbstairs.beans; /** * This class maintains the state of the multiple choice poll. * The number of choices is configurable. * @author Victor Okunev */ public class Poll implements java.io.Serializable { private long aChoices[]; private long voteCounter; /** * Sets the maximum number of choices for this poll. * This value must be set prior to calling any beans methods. */ public void setSize(int size) { aChoices = new long[size]; } /** * Registers the vote with given choice number. */ public void setVote(int choice) { validate(choice); aChoices[choice]++; voteCounter++; } /** * Returns total number of votes for a given choice number. */ public long getVoteCount(int choice) { validate(choice); return aChoices[choice]; } /** * Returns total number of votes for all choices. */ public long getVoteCount() { return voteCounter; } private void validate(int choice) { if( aChoices == null ) { throw new IllegalArgumentException("The size property has to be initialized first"); } // Validate the choice. if( choice < 0 || choice >= aChoices.length ) { throw new IllegalArgumentException("The choice number is out of valid range"); } } } The Poll bean supports polls with a configurable number of choices. Normally, you would use a constructor to initialize that number, but JSP uses a default (no argument) constructor to instantiate the beans. Therefore, this bean has a property size that we must initialize before using the bean. Listing 2’s content generator implementation does that immediately upon creating the bean: Listing 2. generator.jsp<%@ page import="org.climbstairs.beans.Poll" %> <jsp:useBean id="poll" class="org.climbstairs.beans.Poll" scope="application"> <!-- Initialize the poll size after the bean is created --> <jsp:setProperty name="poll" property="size" value="5"/> </jsp:useBean> <html> <head><title>The current poll results</title></head> <body> What is your favorite color? <form action="esource.jsp" method="POST"> <input type="radio" name="vote" value="0" checked> Red [<%= poll.getVoteCount(0)%>] <br> <input type="radio" name="vote" value="1"> Green [<%= poll.getVoteCount(1)%>] <br> <input type="radio" name="vote" value="2"> Blue [<%= poll.getVoteCount(2)%>] <br> <input type="radio" name="vote" value="3"> White [<%= poll.getVoteCount(3)%>] <br> <input type="radio" name="vote" value="4"> Black [<%= poll.getVoteCount(4)%>] Total votes: <%= poll.getVoteCount()%> <input type="submit" value="Vote!"> </form> </body> </html> Please note the application scope of the bean instance: once created, it will exist as long as the Web application does.You may call generator.jsp directly for testing purposes. In fact, this component is neutral to the content publishing approach for which we will use it. The HTML form’s action attribute points to Listing 3’s esource.jsp, which implements the event source component: Listing 3. esource.jsp<%@ page import="org.climbstairs.beans.Poll" %> <jsp:useBean id="poll" class="org.climbstairs.beans.Poll" scope="application"> <!-- Initialize the poll size after the bean is created --> <jsp:setProperty name="poll" property="size" value="5"/> </jsp:useBean> <!-- Collect the submitted vote --> <jsp:setProperty name="poll" property="*"/> <!-- Pass control to the event handler --> <jsp:forward page="handler.jsp"/> This page changes the data’s state in the data source and passes control to Listing 4’s event-handler component implementation: Listing 4. handler.jsp<%@ taglib uri="/WEB-INF/tlds/mytaglib.tld" prefix="tlb" %> <!-- Capture the generated content into the file --> <tlb:file fileName="index.html"> <jsp:include page="generator.jsp"/> </tlb:file> <% response.sendRedirect("index.html"); %> Things get tricky here. This JSP uses an advanced custom tag that processes its body and captures the resulting content into a specified file. It literally wraps around the content source and intercepts its output. This source can be another JSP, servlet, or static file from the same Web application.So far, in this application standard, JSP tags suffice. JSP technology provides a mechanism for encapsulating nonstandard functionality in custom tags, which are JSP extensions. This is a powerful concept, which opens the doors for commercial and noncommercial tag library developers. The custom tag implementation is called the tag handler. Listing 5 shows the tag handler specifically built to support event-driven content publishing: Listing 5. SaveToFileTag.javapackage org.climbstairs.tags; import java.io.*; import javax.servlet.http.*; import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; /** * This tag writes the content of its body into the specified file. * @author Victor Okunev */ public class SaveToFileTag extends BodyTagSupport { private String relativeUrlPath; private FileWriter out; /** * Sets a mandatory file name attribute. * If the relativeUrlPath begins with a "/" then the URL specified * is calculated relative to the DOCROOT of the ServletContext for this JSP. * If the path does not begin with a "/" then the URL specified is calculated * relative to the URL of the request that was mapped to the calling JSP. */ public void setFileName(String relativeUrlPath) { this.relativeUrlPath = relativeUrlPath; } /** * Opens up a file. */ public void doInitBody() throws JspException { String fullName = null; if( relativeUrlPath.startsWith("/") ) { fullName = pageContext.getServletContext().getRealPath("/") + File.separator + relativeUrlPath.substring(1).replace('/', File.separatorChar); } else { fullName = new java.io.File(pageContext.getServletContext().getRealPath(((HttpServletRequest)pa geContext.getRequest()).getServletPath())).getParent() + File.separator + relativeUrlPath.replace('/', File.separatorChar); } try { out = new FileWriter(fullName); } catch (IOException ex) { throw new JspException(ex); } } /** * Writes body content into the file. */ public int doAfterBody() throws JspException { try { getBodyContent().writeOut(out); } catch (IOException ex) { throw new JspException(ex); } return SKIP_BODY; } /** * Flushes the data, closes the file. */ public int doEndTag() throws JspException { try { out.flush(); out.close(); } catch (IOException ex) { throw new JspException(ex); } return EVAL_PAGE; } } The most challenging part about this tag’s implementation is translating a page-relative or context-relative URI (Uniform Resource Identifier) path of the destination file to the physical location. Please note that this algorithm must also work on any platform.To deploy, a description contained in the XML document called Tag Library Descriptor file must accompany every custom tag. Listing 6’s mytaglib.tld file describes just one tag; you can use it to describe the whole tag library: Listing 6. mytaglib.tld<?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/j2ee/dtd/web-jsptaglibrary_1_2.dtd"> <taglib> <tlib-version>1.0</tlib-version> <jsp-version>1.2</jsp-version> <short-name>tlb</short-name> <description> A simple tag library </description> <!-- A SaveToFileTag --> <tag> <name>file</name> <tag-class>org.climbstairs.tags.SaveToFileTag</tag-class> <body-content>JSP</body-content> <description> Saves the body content into the specified page-relative or context-relative file location </description> <attribute> <name>fileName</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> </tag> </taglib> At this point, we have everything we need to deploy our Web application. The deployment process is specific to every servlet container, but all containers compatible with the Servlet API Specification (2.2 or later) must accept a Web Archive (WAR) file. This archive’s directory structure is defined in these specifications as well.Once the application first deploys, you request the generator.jsp to dynamically build static index.html with the poll form. If you don’t have a decent servlet container, you can play with the application at the following URL: https://www.climbstairs.org/examples/poll/index.html. Figure 2 shows the generated document in the browser.Figure 2. Generated document displaySelect your favorite color and press the Vote! button. Then check the returned document’s name; it should be index.html. Cast a few more votes and you’ll notice every new vote regenerates the otherwise static document.An auspicious eventEvent-driven Web content publishing shines when data is relatively static, relative to the frequency of client requests. When you apply this concept to commercial high-traffic Web applications, you can significantly improve applications’ vital characteristics in scalability and response time. Using JSPs helps implement the approach in a highly modular and efficient way. Web DevelopmentJava