Item 53: Separate presentation from processing



Item 53: Separate presentation from processing

Consider the following (simplified) JSP page:






<%@ page language="Java" %>

<%@ page import="java.sql.*, javax.sql.*" %>



<html>

<head><title>Product Search Results</title></head>



<%

Connection conn = application.getAttribute("dbconn");

  // Assume JDBC Connection was stored in ServletContext

String SQL = "SELECT name, sku, stock FROM products " +

             "WHERE name = ? AND stock > 0";

PreparedStatement stmt = conn.prepareStatement(SQL);

stmt.setString(1, request.getParameter("name"));

ResultSet rs = stmt.executeQuery();

ArrayList results = new ArrayList();

while (rs.next())

{

  HashMap hm = new HashMap();



  hm.add("name", rs.getString(1);

  hm.add("sku", rs.getString(2);

  hm.add("stock", rs.getString(3);



  results.add(hm);

}

stmt.close();  // Aggressively release resources; see Item 67

%>



<body>

<h1>Product Search Results</h1>

<%

  if (results.size() == 0)

  {

%>

<b>No items found that match your query; try again?</b>

<%

  }

  else

  {

%>

<table>

  <tr>

    <th>Product Name</th>

    <th>Product SKU</th>

    <th>Stock Count</th>

  </tr>

<%

    for (Iterator iter = results.iterator(); iter.hasNext(); )

    {

      Map current = (Map)iter.next();

%>

  <tr>

    <td><%= current.get("name") %></td>

    <td><%= current.get("sku") %></td>

    <td><%= current.get("stock") %></td>

  </tr>

<%

    }

%>

</table>

<%

  }

%>

</body>



</html>


Spot anything dangerous? If not, don't feel bad—lots of JSP pages are written this way, and this particular style of programming seems to be prevalent, particularly within a lot of articles and books on JSP. In some cases, it's done this way to keep the examples simple, but the danger is that readers won't realize that (or care) when it comes time to get the project shipped, so this style of code gets shoehorned into production.

The danger here is that subtly buried within the JSP page is a business rule—in this case, the idea that only those products that are in stock should be searched as part of the search request. In this case, the rule shows up in the same layer of code used to display the results, a dangerous blending of concerns that should normally be separated.

The first reaction by most J2EE architects and experts, when shown this code, is to suggest that this whole sequence be placed within the business tier, usually by taking the search code and dropping it inside a session bean. Typically, a stateless session bean is the first suggestion, although there is merit in making it a stateful session bean if the search is likely to produce more results than can be seen from a single screen. In that scenario, rather than going back to the database again to retrieve the next set of search results, simply hold the results in the stateful session bean and make only one round-trip (see Item 17) to obtain the rest of the search results.

However, putting this code into a session bean (whether stateful or stateless, it makes no difference) has one serious side effect—by adding a network trip between the JSP code and the actual search code, the latency of the page increases significantly. More importantly, if a stateful session bean is used, we have to store a stateful session bean instance reference in per-user state, meaning we have to use HttpSession (see Item 39). This in turn adds load to the server and requires the use of sessions even if the rest of the Web application doesn't need it. In addition, we have to make conscious decisions regarding the transactional affinity of the session bean's SQL execution (see Item 31); typically, since this data isn't being modified frequently, we can run this query at a low isolation level, but remember that EJB doesn't allow for varying the isolation level of its queries—therefore we will probably need to run under a transaction and contention (see Item 29) will result.

In those situations, it's common to want to bypass the EJB tier altogether and execute this query directly against the database, so we can control the isolation level of the transaction. This is known in some quarters as the Fast Lane pattern. Unfortunately, doing so brings us back to the original problem again: this is where code with embedded rules creeps in. As described in Item 3, even though we're trying to bypass the EJB layer to avoid round-trips and minimize contention, we don't necessarily want to put this code directly in the presentation code itself.

It's fair to ask why this is so bad—after all, in many cases, it's not like putting this code in some kind of Model or Bean class will help simplify the actual SQL being executed, and won't that just add another layer of method calls, thereby hurting the performance of the page as a whole? Some people argue that doing so helps preserve the details of the SQL schema being used behind the page, but let's be real—how often will the database schema itself change over the lifetime of the application?

Frequently, as a matter of fact. As it turns out, it's not uncommon to need database schema changes, particularly as the project rolls from one phase to the next. New requirements often mean at least a partial redesign of the storage schema: extending tables to include new information, splitting tables apart to accommodate better organization and/or normalization (or denormalization, when performance calls for it), and in some cases renaming tables and/or columns to better reflect the data being stored.

More importantly, however, directly embedding these business rules in the presentation code frequently leads to violations of what's called the Once- and-Only-Once Rule: a particular snippet of code should appear exactly once within the codebase. In this case, the violation would occur anywhere another product search would be conducted—perhaps in conjunction with a customer search—and the business rule "only search for products with stock on hand," represented by the stock > 0 predicate in the SQL statement, would have to be manually replicated in that search code. Then later, when the business decides to search for products without stock on hand, this SQL clause must be found and removed in both places. The same will be true, of course, if the database schema changes and inventory is suddenly stored in a separate table—now all queries that implemented this business rule must be updated to reflect the new schema.

As a result, then, create a layer of code (see Item 3) that keeps the presentation code ignorant of the actual details of doing the product search; when used from a JSP page, frequently this layer can be a tag handler in a tag library, but often a simple Java class using static methods to encapsulate the query itself can work just as well:






public class Queries

{

  public static List productSearchQuery(String pname)

  {

    // Same JDBC logic as before

  }

}


This makes using it from a JSP page look like so:






<% page language="Java" %>



<html>

<head><title>Product Search Results</title></head>



<%

  List results =

    Queries.productSearchQuery(request.getParameter("name");

%>



<body>

<h1>Product Search Results</h1>

<%

  if (results.size() == 0)

  {

%>

<b>No items found that match your query; try again?</b>

<%

  }

  else

  {

%>

<table>

  <tr>

    <th>Product Name</th>

    <th>Product SKU</th>

    <th>Stock Count</th>

  </tr>

<%

    for (Iterator iter = results.iterator(); iter.hasNext(); )

    {

      Map current = (Map)iter.next();

%>

  <tr>

    <td><%= current.get("name") %></td>

    <td><%= current.get("sku") %></td>

    <td><%= current.get("stock") %></td>

  </tr>

<%

    }

%>

</table>

<%

  }

%>

</body>



</html>


A side benefit of keeping presentation and processing logic separate comes from the inherent opportunities for parallel development—by creating this layer and keeping the presentation designer ignorant of the actual details of the query, the presentation designer can use mock objects that hand back faked data to test the presentation layer. This helps decouple the schedule so that the presentation can go before the users as quickly as possible to ensure they understand what they're getting. This also means that, in the case of JSP pages using tag libraries to provide the layering, the presentation designer can be a nonprogrammer versed instead in graphic design and layout, ensuring that your Web application doesn't show up in a rogues gallery of badly designed Web pages.

It's an arguable fact that the second JSP example presented earlier, even with the Queries abstraction, still isn't "clean" or well abstracted. There's a host of code on the page, such as the decision block to determine whether any rows were returned, not to mention numerous JSP scriptlet blocks, mostly to handle the looping through the ResultSet returned from the query. Also, the query itself is processed within the JSP page, which some will argue shouldn't happen here, since JSPs are principally geared toward allowing nonprogrammers (like page designers and graphic artists familiar with HTML or simple scripting) the ability to write dynamic pages. Instead, the argument goes, the query should be carried out in a servlet that forwards to this JSP to display the results: programmers write the servlets, page designers write the JSPs, and thus we have a well-factored separation of concerns.

This idea has become so prevalent that Sun gave it a name, the Model 2 architecture, as part of the JSP 0.92 Specification. (This rather original name came from the fact that it was the second of three suggested usage patterns for servlets and JSP.) Because Model 2 is somewhat lacking as a descriptive term, this has since been amended to be called the Model-View-Controller architecture and is well documented in other writings. In fact, an entire open-source library, Struts, hosted at the Jakarta Apache Web site (http://jakarta.apache.org), has grown up around formalizing this Model 2 architecture into a framework. Other projects use a custom presentation language based on templates rather than JSP. The core idea, however, is always the same: keep presentation (JSP) separate from processing (servlets) and also from domain-related objects (beans).