The Java EE 5 Tutorial

A Template Tag Library

A template provides a way to separate the common elements that are part of each screen from the elements that change with each screen of an application. Putting all the common elements together into one file makes it easier to maintain and enforce a consistent look and feel in all the screens. It also makes development of individual screens easier because the designer can focus on portions of a screen that are specific to that screen while the template takes care of the common portions.

The template is a JSP page that has placeholders for the parts that need to change with each screen. Each of these placeholders is referred to as a parameter of the template. For example, a simple template might include a title parameter for the top of the generated screen and a body parameter to refer to a JSP page for the custom content of the screen.

The template uses a set of nested tags (definition, screen, and parameter) to define a table of screen definitions and uses an insert tag to insert parameters from a screen definition into a specific application screen.

JSP Pages

The template for the Duke’s Bookstore example, tut-install/javaeetutorial5/examples/web/bookstore3/web/template/template.jsp, is shown next. This page includes a JSP page that creates the screen definition and then uses the insert tag to insert parameters from the definition into the application screen.

<%@ taglib uri="/tutorial-template" prefix="tt" %>
<%@ page errorPage="/template/errorinclude.jsp" %>
<%@ include file="/template/screendefinitions.jsp" %>
<html>
<head>
<title>
<tt:insert definition="bookstore" parameter="title"/>
</title>
</head>
<body  bgcolor="#FFFFFF">
  <tt:insert definition="bookstore" parameter="banner"/>
<tt:insert definition="bookstore" parameter="body"/>
<center><em>Copyright &copy; 2004 Sun Microsystems, Inc. </em></center>
</body>
</html>

The tut-install/javaeetutorial5/examples/web/bookstore3/web/template/screendefinitions.jspf page creates a definition for the screen specified by the request attribute javax.servlet.forward.servlet_path:

<tt:definition name="bookstore"
screen="${requestScope
    [’javax.servlet.forward.servlet_path’]}">
    <tt:screen id="/bookstore">
    <tt:parameter name="title" value="Duke’s Bookstore"
        direct="true"/>
    <tt:parameter name="banner" value="/template/banner.jsp"
        direct="false"/>
    <tt:parameter name="body" value="/bookstore.jsp"
        direct="false"/>
    </tt:screen>
    <tt:screen id="/bookcatalog">
    <tt:parameter name="title" direct="true">
      <jsp:attribute name="value" >
        <fmt:message key="TitleBookCatalog"/>
      </jsp:attribute>
    </tt:parameter>
    <tt:parameter name="banner" value="/template/banner.jsp"
        direct="false"/>
        <tt:parameter name="body" value="/bookcatalog.jsp"
        direct="false"/>
    </tt:screen>
    ...
</tt:definition>

The template is instantiated by the Dispatcher servlet. Dispatcher first gets the requested screen. Dispatcher performs business logic and updates model objects based on the requested screen. For example, if the requested screen is /bookcatalog, Dispatcher determines whether a book is being added to the cart based on the value of the Add request parameter. It sets the price of the book if it’s on sale, and then adds the book to the cart. Finally, the servlet dispatches the request to template.jsp:

public class Dispatcher extends HttpServlet {
    @Resource
    UserTransaction utx;

    public void doGet(HttpServletRequest request,
        HttpServletResponse response) {
        String bookId = null;
        Book book = null;
        String clear = null;
        BookDBAO bookDBAO =
            (BookDBAO)getServletContext().
                getAttribute("bookDBAO");
        HttpSession session = request.getSession();
        String selectedScreen = request.getServletPath();
        ShoppingCart cart = (ShoppingCart)session.
            getAttribute("cart");
        if (cart == null) {
            cart = new ShoppingCart();
            session.setAttribute("cart", cart);
        }
        if (selectedScreen.equals("/bookcatalog")) {
            bookId = request.getParameter("Add");
            if (!bookId.equals("")) {
                try {
                    book = bookDBAO.getBook(bookId);
                    if ( book.getOnSale() ) {
                        double sale = book.getPrice() * .85;
                        Float salePrice = new Float(sale);
                        book.setPrice(salePrice.floatValue());
                    }
                    cart.add(bookId, book);
                } catch (BookNotFoundException ex) {
                    // not possible
                }
            }
        } else if (selectedScreen.equals("/bookshowcart")) {
            bookId =request.getParameter("Remove");
            if (bookId != null) {
                cart.remove(bookId);
            }
             clear = request.getParameter("Clear");
            if (clear != null && clear.equals("clear")) {
                cart.clear();
            }
        } else if (selectedScreen.equals("/bookreceipt")) {
        // Update the inventory
            try {
                utx.begin();
                bookDBAO.buyBooks(cart);
                utx.commit();
            } catch (Exception ex) {
                try {
                    utx.rollback();
                    request.getRequestDispatcher(
                        "/bookordererror.jsp").
                        forward(request, response);
                } catch(Exception e) {
                        System.out.println(
                            "Rollback failed: "+e.getMessage());
                        e.printStackTrace();
                }
            }
        }
        try {
            request.
                getRequestDispatcher(
                "/template/template.jsp").
                forward(request, response);
        } catch(Exception ex) {
            ex.printStackTrace();
        }
    }

    public void doPost(HttpServletRequest request,
        HttpServletResponse response) {
             request.setAttribute("selectedScreen",
            request.getServletPath());
        try {
            request.
                getRequestDispatcher(
                "/template/template.jsp").
                forward(request, response);
        } catch(Exception ex) {
            ex.printStackTrace();
        }
    }
}

Tag Handlers

The template tag library contains four tag handlers (DefinitionTag, ScreenTag, ParameterTag, and InsertTag) that demonstrate the use of cooperating tags. DefinitionTag, ScreenTag, and ParameterTag constitute a set of nested tag handlers that share private objects. DefinitionTag creates a public object named bookstore that is used by InsertTag.

In doTag, tut-install/javaeetutorial5/examples/web/bookstore3/src/java/com/sun/bookstore3/template/DefinitionTag.java creates a private object named screens that contains a hash table of screen definitions. A screen definition consists of a screen identifier and a set of parameters associated with the screen. These parameters are loaded when the body of the definition tag, which contains nested screen and parameter tags, is invoked. DefinitionTag creates a public object of class tut-install/javaeetutorial5/examples/web/bookstore3/src/java/com/sun/bookstore3/template/Definition.java, selects a screen definition from the screens object based on the URL passed in the request, and uses this screen definition to initialize a public Definition object.

public int doTag() {
    try {
        screens = new HashMap();
        getJspBody().invoke(null);
        Definition definition = new Definition();
        PageContext context = (PageContext)getJspContext();
        ArrayList params = (ArrayList) screens.get(screenId);
        Iterator ir = null;
        if (params != null) {
            ir = params.iterator();
            while (ir.hasNext())
                definition.setParam((Parameter)ir.next());
        // put the definition in the page context
        context.setAttribute(definitionName, definition,
             context.APPLICATION_SCOPE);
        }
    }

The table of screen definitions is filled in by ScreenTag and ParameterTag from text provided as attributes to these tags. Table 8–14 shows the contents of the screen definitions hash table for the Duke’s Bookstore application.

Table 8–14 Screen Definitions

Screen ID 

Title 

Banner 

Body 

/bookstore

Duke’s Bookstore

/banner.jsp

/bookstore.jsp

/bookcatalog

Book Catalog

/banner.jsp

/bookcatalog.jsp

/bookdetails

Book Description

/banner.jsp

/bookdetails.jsp

/bookshowcart

Shopping Cart

/banner.jsp

/bookshowcart.jsp

/bookcashier

Cashier

/banner.jsp

/bookcashier.jsp

/bookreceipt

Receipt

/banner.jsp

/bookreceipt.jsp

If the URL passed in the request is /bookstore, the Definition object contains the items from the first row of Table 8–14 (see Table 8–15).

Table 8–15 Definition Object Contents for URL /bookstore

Title 

Banner 

Body 

Duke’s Bookstore

/banner.jsp

/bookstore.jsp

The parameters for the URL /bookstore are shown in Table 8–16. The parameters specify that the value of the title parameter, Duke’s Bookstore, should be inserted directly into the output stream, but the values of banner and body should be included dynamically.

Table 8–16 Parameters for the URL /bookstore

Parameter Name 

Parameter Value 

isDirect

title

Duke’s Bookstore

true

banner

/banner.jsp

false

body

/bookstore.jsp

false

tut-install/javaeetutorial5/examples/web/bookstore3/src/java/com/sun/bookstore3/template/InsertTag.java inserts parameters of the screen definition into the response. The doTag method retrieves the definition object from the page context and then inserts the parameter value. If the parameter is direct, it is directly inserted into the response; otherwise, the request is sent to the parameter, and the response is dynamically included into the overall response.

public void doTag() throws JspTagException {    
    Definition definition = null;
    Parameter parameter = null;
    boolean directInclude = false;
    PageContext context = (PageContext)getJspContext();

        // get the definition from the page context
    definition = (Definition)context.getAttribute(
        definitionName, context.APPLICATION_SCOPE);
        // get the parameter
    if (parameterName != null && definition != null)
        parameter = (Parameter)
            definition.getParam(parameterName);

    if (parameter != null)
        directInclude = parameter.isDirect();

    try {
        // if parameter is direct, print to out
        if (directInclude && parameter  != null)
            context.getOut().print(parameter.getValue());
        // if parameter is indirect,
                 include results of dispatching to page
         else {
            if ((parameter != null) &&
                 (parameter.getValue() !=  null))
            context.include(parameter.getValue());
        }
    } catch (Exception ex) {
          throw new JspTagException(ex.getMessage());
    }
}