Chrysalis View: JSP

Chrysalis views consists of JSP together with a library of custom tags supporting application flow control and HTML form generation. Many of these tags support automatic form generation for JavaBeans. A typical Chrysalis form might be rendered as follows:

<jutil:use var="item" controller="Catalog" />

<h1>Edit catalog item</h1>

<jhtml:form action="Catalog.editItem.cmd">
<table>
  <tr>
    <td><jhtml:label property="itemId" />: </td>
    <td><jhtml:input property="itemId" /></td>
  </tr>
  <tr>
    <td><jhtml:label property="name" />: </td>
    <td><jhtml:input property="name" /></td>
  </tr>
  <tr>
    <td><jhtml:label property="stock" />: </td>
    <td><jhtml:input property="stock" /></td>
  </tr>
  <tr>
    <td></td>
    <td><input type="submit" value="Submit"></td>
  </tr>
</table>
</jhtml:form>

The <jhtml:form> and <jhtml:input> tags generate the correct HTML code necessary to render an input form for the bean. This includes pre-populating fields with bean values as well as generated JavaScript validation code based on bean property attributes (as described above).

<h1>Edit catalog item</h1>
<form action="Catalog.editItem.cmd" method="post">
<table>
  <tr>
    <td>Item Id: </td>
    <td>1098<input type="hidden" value="1098" name="itemId"></td>
  </tr>
  <tr>
    <td>Name: </td>
    <td><input value="Hat" name="name" size="20"></td>
  </tr>
  <tr>
    <td>Stock: </td>
    <td><input value="16" name="stock" size="20"></td>
  </tr>
  <tr>
    <td></td>
    <td><input type="submit" value="Submit"></td>
  </tr>
</table>
</form>

For forms generated as two-column tables, more compact view logic is possible using the <jhtml:field> tag:

<jutil:use var="item" controller="Catalog" />

<h1>Edit catalog item</h1>

<jhtml:form action="Catalog.editItem.cmd">
  <jhtml:field property="itemId" />
  <jhtml:field property="name" />
  <jhtml:field property="stock" />
  <tr>
    <td></td>
    <td><input type="submit" value="Submit"></td>
  </tr>
</table>
</jhtml:form>

Form Validation JavaScript

For generated JavaScript validations to work, the page must import and initialize the FormValidation.js library (in the javascript subdirectory of the sample application). Since this must be done globally, its best to do it in the templates for the site's pages. Consult the documentation for the FormValidation.js library for additional details on this library.

<!-- In template.jsp for the sample application -->
<script src="/catalog/javascript/FormValidation.js"></script>
<body onload="initFormValidation()">

If for some reason you want to generate your own form validation logic, you can disable the automatic generation of JavaScript validation by setting the validations attribute of the <jhtml:form> to "false".

<jhtml:form action="Catalog.editItem.cmd" validations="false">

Tag Logic

Chrysalis custom tags support limited tag logic in a JSP. A typical page will do the following:

  • Load object (JavaBean) data into a page.
  • Use output tags (like <jhtml:input> and <jutil:print>) to print object data.
  • Use logic tags (like <jutil:if> and <jutil:forEach>) for flow control in the page.

There are three ways to load object data into a page:

  • The <jutil:use> tag loads a JavaBean into the page using an initializer method of a controller.
  • The <jutil:new> tag creates a brand new JavaBean.
  • The <jutil:set> tag retrieves a JavaBean property from an existing object and stores it in a local variable.

Each of these tags has a var attribute to specify the variable name in which the object is stored. The object is always stored in the JSP page scope.

<jutil:use var="order" controller="Cart" property="order" />
<jutil:new var="item" type="com.catalog.domain.Item" />
<jutil:set var="lineItems" object="order" property="lineItems" />

The tags above:

  • Retrieve an order object from the cart controller using its getOrder() initializer method.
  • Create a new item object using the no-parameter constructor of the com.domain.catalog.Item class.
  • Retrieves the lineItems collection from the order object using its getLineItems() method.

Objects loaded elsewhere in the page are referenced via the object attributes of other tags. In most cases, the tag will also have a property attribute to indicate the JavaBean property it manipulates.

<jutil:print object="order" property="id" />
<jhtml:label object="item" property="stock" />
<jutil:if object="lineItems" property="empty">
  [Executed if lineItems collection is empty]
</jutil:if>

The tag logic in Chrysalis is deliberately limited, because the view component is the wrong place for complex logic. If you must have more sophisticate logic in the view, you always have the option of using scripting or some other tag library (like JSTL).

Default Objects

Every time an object is stored in a variable via a var attribute, that object becomes the default object. It remains the default object until a new object is defined. For many output tags, the object attribute is optional; the default object is used if no other object is specified. For example, the following code fragments are equivalent.

<jutil:use var="order" controller="Cart" />
<jutil:print property="id" />

<jutil:use var="order" controller="Cart" />
<jutil:print property="id" object="order" />

Default Properties

For tags that have both a var attribute and a property attribute, the property attribute is optional. It defaults to the value of the var attribute. For example, the following code fragments are equivalent.

<jutil:use var="order" controller="Cart" />
<jutil:set var="lineItems" object="order" />

<jutil:use var="order" controller="Cart" property="order" />
<jutil:set var="lineItems" object="order" property="lineItems" />

View Helper JavaBeans

If you need complex logic for a particular JSP view, rather than embedding that logic in the page itself, you should create a helper Java class to support the logic. The helper class should be a JavaBean. To use a helper JavaBean in a page:

  • Define an initializer method in a controller for the helper bean.
  • Load the bean into the page with the <jutil:use> tag.
  • Store any data needed by the page in instance variables of the helper JavaBean with appropriate getter method.
  • Perform any calculations needed on object data in custom getter methods, and retrieve the data in the page as needed.

For example suppose you needed to make complex computations in showOrder.jsp. You could create an OrderHelper like the following:

package com.domain.catalog;

import java.util.Collection;
import java.util.Iterator;

public class OrderHelper {
  private Order order;
  
  /** Constructor */
  public OrderHelper(Order order) {
    this.order = order;
  }
  
  public double getOrder() {
    return this.order;
  }
  
  public double getOrderTotal() {
    double total = 0;
    Collection lineItems = order.getLineItems();
    Iterator i = lineItems.iterator();
    while (i.hasNext()) {
      LineItem item = (LineItem) i.next();
      total += item.getAmount() * item.getPrice();
    }
    return total;
  }
  
  public boolean isHighPriceOrder() throws Exception {
    return (getOrderTotal() > 100);
  }
  
  // Other methods ...
}

The initializer for the OrderHelper might resemble the following:

public class CartController {
  private Order currentOrder;

  public double getOrderHelper() {
    return new OrderHelper(this.currentOrder);
  }
  
  // Other methods ...
}

You could then use the OrderHelper in your view like your business objects:

<jutil:use var="orderHelper" controller="Cart" />

Order total:
<jutil:print object="orderHelper" property="orderTotal" />

<jutil:if object="orderHelper" property="highPriceOrder">
  You qualify for free shipping!
</jutil:if object="orderHelper">

<jutil:set var="order" object="orderHelper" />
[Print other order data using order object]

This approach has many advantages:

  • Reduces ugly scripting in the page.
  • Page logic is in a Java object where it is easier to unit test.
  • Page logic can be reused in multiple JSP.

If correctly written, the view helper JavaBeans will make no reference to Chrysalis or the Servlet API. Therefore, they can be reused in other UI. Better still, important logic in the view helpers can be migrated into the business object layer (e.g. moving the getOrderTotal() from OrderHelper to Order).

Servlet 2.2 View URLs

Most of Chrysalis is based on the older Servlet 2.2/JSP 1.1 standard. Chrysalis does use one feature of the Servlet 2.3+ API: filters. Chrysalis needs to perform some preprocessing before displaying JSP pages, and filters are the best way to do this.

To support the older standard, Chrysalis provides a special FilterServlet that invokes Servlet 2.3 filters in Servlet 2.2 containers. The FilterServlet requires a URL pattern, and this pattern cannot be *.jsp. View URLs for Servlet 2.2 containers should use the ".view" extension: /showItem.view?itemId=##.

Unfortunately, this means that for Servlet 2.2 containers, the URLs used to invoke the view and the file name of the page will not be the same.

  • URL: *.view. Use this extension for any URL that the browser is aware of (hyperlinks, form actions, etc.).
  • File Name: *.jsp. Use this extension for any URL known only to the server (includes, templates, error pages, etc.).

Though awkward, this mechanism does allow Chrysalis to support older servlet engines that only comply with the Servlet 2.2 standard. The Servlet 2.3 version of Chrysalis also recognizes the ".view" extension for backwards compatibility.

The sample catalog application uses the ".view" extension to demonstrate when it should and should not be used.

Tag Library Documentation

Additional information about Chrysalis JSP logic can be found in the documentation for the Chrysalis Tag Libraries.