Systems designed without clear separation between presentation and application logic quickly become chores to maintain. Trivial look-and-feel updates in such applications take days or weeks, and trying to extend such a coupled architecture can introduce unmanageable risks and code that is impossible to unit test. To minimize the possibility of creating such disasters, avoid coupling presentation and application logic through the use of a good templating engine. Maintain clear separation between presentation and application logic from the beginning—be orthogonal. Don't print out HTML, XML, or SQL from Java code, use a templating engine.
The simplest example of templating is Java's MessageFormat
. A simple message, such as
Hello {0}, I speak {1}
, can be
parameterized using the MessageFormat
class. A more complex templating example is found in applications that use
Apache Velocity or FreeMarker to avoid mixing Java with HTML or textual
output. Throughout this spectrum of complexity, the concept of templating
remains the same; a template with references to variables is merged with a
context containing these variables. There are many ways to decouple the
rigors of logic from the prettiness of presentation, and after reading
this chapter, you will have a range of options for different
situations.
This chapter touches upon Apache Velocity, Apache Commons JEXL, and a technology outside of the Apache Software Foundation named FreeMarker. Templating engines are frequently used in web application, and this chapter ends with instructions for integrating these engines into a J2EE web application. Separating HTML or textual content into a separate file allows you to give graphic designers and business users a larger role in customizing and creating content for the enduser. With a templating engine, you can dramatically reduce the time and effort it takes to make simple changes. But, by far, the largest benefit of a templating engine is that it allows programmers to do more programming and graphic designers to do more designing by reducing needless coupling of presentation markup and compiled application logic.
Server-side Java has won a fair amount of attention over the past five years, but developers seldom consider using templating engines when writing a standalone Java application. Consider the following code, which prints a formatted report for a bank customer:
System.out.println( "******************************" ); System.out.println( "******* BANK STATEMENT *******" ); System.out.println( "******************************" ); Account[] accounts = AccountUtil.getAccounts("1232"); double total = 0.0; for( int i = 0; i < accounts.length; i++ ) { System.out.println( "Account: " + accounts[i].getBalance( ) ); total += accounts[i].getBalance( ); } System.out.println( "Total: " + total ); System.out.println( "******************************" );
In this example, textual content is mixed with Java code, and a programmer must be involved in any change to the look and feel of the report. If this report were generated using an external template, a nonprogrammer could be trained to change the report, or the customers could customize the output of this program by modifying a template with straightforward syntax.
Consider another common problem: generating an XML document. There
are seemingly thousands of ways to create an XML document: using DOM or
JDOM to create a Document
object,
serializing an object with Commons Betwixt, and using Hibernate are a
few of these possibilities. Some of these techniques are explained in
Chapter 6, but here is an example of how
not to generate an XML document:
System.out.println("<?xml version=\"1.0\"?>"); System.out.println("<Config>"); System.out.println("<Property name=\"" + name + "\"\n" + "value=\"" + value + "\"/>"); System.out.println("</Config>");
Avoid this practice at all costs. Here are a few red flags from the previous two examples:
Nesting an XML document into a Java class by way of printing
out String
literals is
unreadable. When you need to mentally decode heavily escaped XML
nested in Java, you are asking yourself to do too much at once.
You will also end up with mixed context, an XML element starting
within a pair of parentheses, and ending in another. In this fugue
of forward and back slashes, one misplaced slash produces an
invalid XML document, or one overlooked NullPointerException
causes a
catastrophe. You are asking your brain to compile two strands of
code with intertwined and overlapping syntax. While your compiler
might not complain, your system will be difficult to maintain.
Code that is difficult to comprehend is even more difficult to
maintain.
Escaping characters in String
literals quickly becomes an
annoyance, especially if the strings to be encoded contain back
slashes and double quotes. I accept that the String
c:\temp\blah.txt
, must be written as
c:\\temp\\blah.txt
, but I don't
enjoy doing it. How confusing is a literal \"c:\\temp\\\
"? Avoid this entirely by
using a templating engine or externalizing strings in properties
files.
A previous example printed each account balance in a
for
loop. A common trick in JSP
is to create a while
loop with
JSP scriptlets, surrounding the code to be executed with <% while( i < 4 ) { %>
and
<% } %>
. Templating
languages such as Velocity or even JSP 2.0 with JSTL support
conditional and iterative operations without asking you to mix
languages.
There are other opportunities for using a template in an application. These include code generation, creating SQL scripts, and interacting with an interpreted language via generated scripts. The list continues, and it proves that templating isn't just for making web pages.
Examine the JSP code below (the variable names have been changed to protect the innocent). Take this code and multiply it by 100 or 200 pages. Now, what happens when you want to move from PostgreSQL to Oracle? Do you do a global search and replace on the driver class name and the JDBC URL? Or, even worse, what happens when someone asks you to translate the entire site to Chinese in two weeks? It would be easier to learn Chinese in two weeks than it would be to internationalize this code. The offending JSP is:
<% ResultSet rs; try { Class.forName( "org.postgresql.Driver" ); String dbURL = "jdbc:postgresql://192.168.0.1/dbname"; Connection dbCon = DriverManager.getConnection( dbURL, "system", ""); PreparedStatement ps = dbCon.prepareStatement( "select * from Offer " + "where id = ?" ); ps.setString( 1, request.getAttribute("id") ); rs = ps.executeQuery( ); rs.next( ); } catch( Exception e ) { %><%= Something bad happened, Call Jack (800) 232-2233 %> <% } %> <jsp:include page="header.html" /> Hello <%= rs.getString("name") %>, I would like to inform you of a good offer. <%= rs.getString("offerText") %> There are a few things I would like to tell you. <ul> <% Thing[] things = OfferUtil.getThings( rs.getString("id") ); for( int i = 0; i < things.length; i++ ) { %> <li><%= things[i].getText( )%></li> <% } %> </ul> <jsp:include page="footer.html" />
This is real code from a real system, and it was written by a whole team of programmers who didn't find anything terribly wrong with this style. If you were raised on JSP like this, you might not recognize some of the problems. What is wrong with the previous example? Four different "languages" are combined in one file (Java, SQL, HTML, and JSP); the page starts off with JSP scriptlets, then the example contains Java code that prints out HTML and generates SQL statements. Lastly, this particular JSP page forgets to close the connection it created to the database—something that could easily create a resource leak under a heavy load. Make it simpler, use a templating engine (or upgrade to JSP 2.0) and never write a line of Java in a JSP again.
Velocity, JEXL, and FreeMarker are remedies for the coupling issues demonstrated in the previous examples. Each of these tools can be integrated into any application in a matter of minutes. In this chapter, you will learn techniques for separating presentation logic from behavioral (or business) logic. At the end of the chapter, I will briefly explain how you can integrate all of these utilities into a J2EE web application.