Chrysalis Quick Start

Chrysalis is a lightweight, MVC (Model-View-Controller) web development framework with some twists. This guide is designed to quickly familiarize you with its major features.

To understand this document, you need to know the Java language, basic web programming (HTML and form processing) and be familiar with servlet and JSP development. Web developers (HTML page authors) may wish to begin with the Web Developers Guide instead.

Model-View-Controller

Like most modern web frameworks, Chrysalis is based on the Model-View-Controller (MVC) pattern. MVC is a pattern for user interface development that divides components into three groups:

  • The Model: Components that store business data. In Chrysalis, these are ordinary JavaBeans. Ideally, the Model should not know anything about the web environment.
  • The View: Components that display information to the user. In Chrysalis, these are JSP pages using custom tags. The JSP display information from the JavaBeans.
  • The Controller: Components that react to user input. In a sense, controllers mediate between the model and the view, because they take data entered by the user in the view and insert those values into the model. In Chrysalis these controllers are called "controllers" (subclasses of the Controller class).

The basic work horses in Chrysalis are the controllers, so this document will cover them first.

Controllers

Chrysalis controllers resemble ordinary Java classes and may have as many methods as they need to perform their functions. Controllers:

  • Are in a specific, user-defined package (the controller package).
  • Have names of the form XxxController.
  • Are subclasses of the org.chwf.servlet.Controller class.
  • Have one or more controller methods, which may have method parameters.
  • May use instance variables to store information between method calls.

Controller methods can have any logic necessary to process web applications operations. For example, you could have a "Cart" controller that store shopping cart items in a Map between method invocations. The CartController could controller adding, removing and retrieving items from the cart, and eventually using those items to place an order.

public class CartController extends Controller {
  private Map items = new HashMap();
  
  public void addItem(Long itemId) throws Exception {
    items.put(itemId, Item.load(itemId));
  }

  public void removeItems(Long[] itemId) {
    for (int i=0; i<itemId.length; i++) {
      items.remove(itemId[i]);
    }
  }

  public Collection getItems() {
    return this.items.values();
  }

  public void placeOrder() throws Exception {
    // Use the items collection to create an order
  }
}

Controller methods are targeted by Command URLs inside the web application. Command URLs consist of:

  • The controller class name (omitting the "Controller" suffix)
  • The controller method being invoked
  • The command extension: ".cmd"
  • Request parameter data
Cart.addItem.cmd?itemId=296

The Command URL can appear in hyperlinks or form actions:

<h1>Catalog<h1>

Hat   [<a href="Cart.addItem.cmd?itemId=296">Add to Cart</a>]<br>
Shirt [<a href="Cart.addItem.cmd?itemId=689">Add to Cart</a>]<br>
Shoes [<a href="Cart.addItem.cmd?itemId=806">Add to Cart</a>]

In the example above, each time an [Add to Cart] link is clicked, the specified item id would be passed to the addItem() method of the CartController. The logic in this method adds the correct Item object to the cart's items map. This controller (and therefore its instance variables) is maintained in the user's session, so future additions will be put into the same map.

Other controller methods can be targeted by different Command URLs in the site. If the URL is a form action, the form's input elements (form fields) determine the parameter values submitted to the controller method:

<h1>Shopping Cart</h1>

<form action="Cart.removeItems.cmd">
  Items<br>
  <input type="checkbox" name="itemId" value="296" /> Hat <br>
  <input type="checkbox" name="itemId" value="689" /> Shirt <br>
  <input type="submit" value="Remove Items" />
</form>

<form action="Cart.placeOrder.cmd">
  <input type="submit" value="Place Order" />
</form>

In the example above, the selected checkboxes in the first form determine the parameter values passed to the removeItem() method. In the second form, the placeOrder() method is invoked without any parameters.

There is at most one instance of a given type of controller for a particular user, so all of these operations will interact with the same controller and manipulate the same data.

Controller Configuration and Redirection

The configuration information for individual controllers is in an XML file called chrysalis.xml. This configuration file is loaded from the Java classpath, and therefore should be in the same directory as the controller's class file. This is the com/domain/catalog/controllers/ directory for the sample catalog application:

<!-- chrysalis.xml for controller package -->
<config>
  <class name="CartController">
    <default view="/showCart.jsp" errorpage="/error.jsp" />
    <method name="addItem" parameters="itemId" />
    <method name="removeItems" parameters="itemId" />
    <method name="placeOrder" view="/showOrder.jsp" errorpage="REFERER_PAGE" />
  </class>
  
  <!-- Configuration for other controller classes ... -->

</config>

This configuration file includes:

  • A <class> tag for each controller class in the package.
  • A <method> tag for each controller method.
  • The method parameter names (in a comma-delimited list) for each controller method.
  • The views and errorpages associated with each controller method.
  • Default views and errorpages, which are used if no method-specific pages are defined. Defaults can be defined at the class or package level.

When a controller finishes its processing, it redirects to the specified view, or the error page if an exception is thrown. A controller can also use its setView() method to target a given view page (adding request parameters as needed):

public class CatalogController extends Controller {
  // Other operations ...

  public void editItem(Item item) throws Exception {
    item.save();
    setView("/showItem.jsp", "itemId", item.getItemId());
  }
}

The controller above will redirect to the view URL /showItem.jsp?itemId=##, where ## is the value of the item's itemId property. There is a similar redirection mechanism for error pages.

The Model: JavaBeans

The Chrysalis model consists of ordinary JavaBean business objects. The JavaBean specification states that JavaBeans must:

  • Implement the java.io.Serializable interface.
  • Have a no parameter constructor.
  • Have getter and setter methods for bean properties.

Chrysalis supports the use of any JavaBean, but the framework functions more smoothly if the JavaBeans encode business object metadata in the chrysalis.xml configuration file in the JavaBean package.

public class Item {
  private Long itemId;
  private String name;
  private int stock;
  private double price;
  private Date startDate;

  public Long getItemId()            { return this.itemId; }
  public String getName()            { return this.name;   }
  public void setName(String name)   { this.name = name;   }
  public int getStock()              { return this.stock;  }
  public void setStock(int stock)    { this.stock = stock; }
  public String getPrice()           { return this.price;  }
  public void setPrice(double price) { this.price = price; }
  public Date getStartDate()          { return this.startDate; }
  public void setStartDate(Date date) { this.startDate = date; }

  // Other methods: load(), save(), etc.
}
<!-- chrysalis.xml for JavaBean package -->
<config>
  <class name="Item">
    <property name="name" maxlength="20" />
    <property name="stock" min="0" max="1000" />
    <property name="price" min="0.01" datatype="Currency" />
  </class>
  
  <!-- Other JavaBean configuration ... -->

</config>

If a property is omitted from the configuration file, Chrysalis derives the attributes of that property via reflection, as it does with the "itemId" and "startDate" properties in the example above. The Chrysalis framework defines a number of "well-known" attributes used elsewhere in the framework:

Attribute Meaning
label The label for the field. Defaults to the field name.
datatype The field's logical data type. This may be different from its Java type.
readonly Whether the field is read-only. This is useful for calculated fields.
required Whether the field is required.
max The field's maximum value (numeric types only). This value is included in the allowed range.
min The field's minimum value (numeric types only). This value is included in the allowed range.
maxlength The field's maximum length (string types only). This value is included in the allowed range.
minlength The field's minimum length (string types only). This value is included in the allowed range.
scale The number of digits after the decimal place (decimal types only).
options A map containing a list of options for constrained values. The keys in this map should be the valid values for the property, and the values in the map should be human-readable interpretations of these values. This attribute is intended to generate drop-down lists or select lists.

Some of these values (label, datatype, readonly) are critical to the system, and are derived using reflection when they are not explicitly defined. The JavaBean metadata define in this configuration file controls the field validations and input element format for individual object properties.

The View: JSPs

Chrysalis generates views using JSP, with a library of custom tags to support form generation. 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>
  <jhtml:field property="itemId" />
  <jhtml:field property="name" />
  <jhtml:field property="stock" />
  <jhtml:field property="price" />
  <jhtml:field property="startDate" />
  <tr><td colspan="2"><input type="submit" value="Submit"></td></tr>
</table>
</jhtml:form>

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

For the generated JavaScript validations to work, the page must import and initialize the FormValidation.js library (available in the javascript source directory of Chrysalis). Since this must be done globally, its best to do it in the templates for the site's pages:

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

The <jutil:use> tag loads a JavaBean into the page. It delegates to a getter method in a controller class, which may have any logic necessary to load bean data:

public class CatalogController extends Controller {
  public Item getItem(Long itemId) throws Exception {
    return Item.load(itemId);
  }

  // Other logic ...
}

Controller methods that load bean data are called initializers or controller properties to distinguish them from more general controller methods. Initializers are the only controller methods a JSP can invoke directly.

Controller initializers resemble the getter methods of JavaBean properties, but they may have method parameters which can be used to initialize data. The example getItem() initializer requires a parameter, so the links to JSP pages using that initializer will also need that same parameter:

editItem.jsp?itemId=689

Controllers that Update Beans

The editItem controller method that processes the Catalog.editItem.cmd command could use the form fields (itemId, name, stock, price, startDate) as its method parameters:

public class CatalogController extends Controller {
  public Item getItem(Long itemId) throws Exception {
    return Item.load(itemId);
  }

  public void editItem(Long itemId, String name, int stock,
                   double price, Date startDate) throws Exception {
    Item item = Item.load(itemId);
    item.setName(name);
    item.setStock(stock);
    item.setPrice(price);
    item.setStartDate(startDate);
    item.save();
    setView("/showItem.jsp", "itemId", item.getItemId());
  }
}

Because this editItem form is based on a JavaBean, however, we can define a more sophisticated controller method that uses the Item object itself as the method parameter:

public class CatalogController extends Controller {

  public Item getItem(Long itemId) throws Exception {
    return Item.load(itemId);
  }

  // Form data will be copied into the Item object.
  
  public void editItem(Item item) throws Exception {
    item.save();
    setView("/showItem.jsp", "itemId", item.getItemId());
  }
}

When the editItem() is invoked on the above controller, the Chrysalis engine will perform the following operations:

  1. Call the controller's getItem() initializer method to load the Item object into memory.
  2. Copy request data into the item's properties using its setter methods.
  3. Pass the updated item object to the editItem() method for further processing.

This technique is most useful when beans and forms match each other. If this is not the case, you can always use explicit controller method parameters.

Putting It All Together

Suppose one of your application use cases is "Edit an existing catalog item". You decide that you need:

  • Model: An Item JavaBean to represent item data.
  • View: An editItem.jsp form pre-populated with item data, and a showItem.jsp to display the finished item.
  • Controller: An editItem controller method to process the client request from the editForm.

Item Model

You design your model class first:

public class Item {
  private Long itemId;
  private String name;
  private int stock;
  private double price;
  private Date startDate;

  public Long getItemId()            { return this.itemId; }
  public String getName()            { return this.name;   }
  public void setName(String name)   { this.name = name;   }
  public int getStock()              { return this.stock;  }
  public void setStock(int stock)    { this.stock = stock; }
  public String getPrice()           { return this.price;  }
  public void setPrice(double price) { this.price = price; }
  public Date getStartDate()          { return this.startDate; }
  public void setStartDate(Date date) { this.startDate = date; }

  public static Item load(Long itemId) { /* Load item data */ }
  public void save()                   { /* Save item data */ }
}

You configure your Item bean in the chrysalis.xml file within your JavaBean package:

<!-- chrysalis.xml for JavaBean package -->
<config>
  <class name="Item">
    <property name="name" maxlength="20" />
    <property name="stock" min="0" max="1000" />
    <property name="price" min="0.01" datatype="Currency" />
  </class>
  
  <!-- Other JavaBean configuration ... -->

</config>

Catalog Controller

Your view will not function without a controller to load data, so you may want to create the controller first.

public class CatalogController extends Controller {

  public Item getItem(Long itemId) throws Exception {
    return Item.load(itemId);
  }

  // Form data will be copied into the Item object.
  
  public void editItem(Item item) throws Exception {
    item.save();
    setView("/showItem.jsp", "itemId", item.getItemId());
  }
}

You configure your CatalogController in the chrysalis.xml file within your controller package:

<!-- chrysalis.xml for controller package -->
<config>
  <class name="CatalogController">
    <method name="getItem" parameters="itemId" />
    <method name="editItem" parameters="item" view="/showItem.jsp" />
  </class>
  
  <!-- Other controller configuration ... -->

</config>

The editItem() method will redirect to the showItem.jsp view when it is done. The getItem() method in this case serves two purposes:

  • It loads an item object which is then passed to the editItem() method.
  • It is used by the <jutil:use> tags in the view pages.

Item View

You can have a editItem.jsp view creates the form for editing item data:

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

<h1>Edit Catalog Item</h1>

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

Your showItem.jsp view is similar, except that it prints data rather than creating a form.

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

<h2>Catalog Item</h2>

<table border="1" cellspacing="0" cellpadding="3">
  <tr>
    <td><jhtml:label property="itemId" />:&nbsp;</td>
    <td><jutil:print property="itemId" /></td>
  </tr>
  <tr>
    <td><jhtml:label property="name" />:&nbsp;</td>
    <td><jutil:print property="name" /></td>
  </tr>
  <tr>
    <td><jhtml:label property="stock" />:&nbsp;</td>
    <td><jutil:print property="stock" /></td>
  </tr>
  <tr>
    <td><jhtml:label property="price" />:&nbsp;</td>
    <td><jutil:print property="price" /></td>
  </tr>
  <tr>
    <td><jhtml:label property="startDate" />:&nbsp;</td>
    <td><jutil:print property="startDate" /></td>
  </tr>
</table>

Flow of Execution

The flow of execution is as follows:

1. The user opens the "/editItem.jsp?itemId=###" page. The <jutil:use var="item" controller="Catalog" /> tag invokes the getItem() controller method, passing it the itemId to load item data. The other tags define the form and populate it with item data.

2. The user submits the form to the "Catalog.editItem.cmd" Command URL. This URL invokes the editItem() method. The getItem() method is called first to load item data. Form data is copied to the item object, which is them passed to the editItem() method. The editItem method logic saves the object and specifies the next view.

3. Chrysalis redirects to the "/showItem.jsp?itemId=###" page. The <jutil:use var="item" controller="Catalog" /> tag invokes the getItem() controller method, which loads the (revised) item data. The remaining tags display the results.

More Use Cases

Once one of your item-related use cases is done, the others become much easier. Other item-related operations (Create Item, Delete Items, Show List of Items) can re-use the same Item bean. New controller methods can be added to the existing Catalog controller. You will probably need to create new views, but once your model beans and controllers are in place, views are relatively easy. You may be able to reuse some views as well, such as "showItem.jsp".

Other use cases will likely require expanding your model with additional beans. New controller methods can be added to a single controller if the model objects are closely related; the "CatalogController" could handle all catalog-related use cases. Unrelated function can be put into separate controllers for better modularity, such as a "SecurityController" to handle security issues.

Chrysalis Configuration

The controllers in a Chrysalis application should be in a specific set of packages (the controller packages). In the sample applications, there is only one controller package: com.domain.catalog.controllers package. The controller package and other global configuration information is specified in the WEB-INF/classes/ChrysalisConfig.xml file:

<!-- ChrysalisConfig.xml -->
<config>
  <servlet.engine>
    <controller>
      <package>com.domain.catalog.controllers</package>
      <!-- Other controller packages, as needed -->
    </controller>
  </servlet.engine>
</config>

Configuration for each seperate controller package is located in the chrysalis.xml configuration file in that controller package. Similarly, the configuration for each JavaBean package (bean metadata) goes in the chrysalis.xml configuration file in that JavaBean package.

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 path 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 can also recognizes the ".view" extension for backwards compatibility.

Conclusion

That's it for the major features of Chrysalis. Take a look at the sample applications for more examples. Look at the Java Developers Guide and the Web Developers Guide for additional details.

Flow Diagram for Catalog Application

Note: Since the template.jsp is incorporated in every page, its links are available globally.