Chrysalis Model: JavaBeans

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

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

JavaBean MetaData

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 for the JavaBean package.

public class Item {
  private Long itemId;
  private String name;
  private int stock;

  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; }

  // 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" />
  </class>
  
  <!-- Other JavaBean configuration ... -->

</config>

Chrysalis use the org.chwf.filter classes to manage metadata attributes.

Well Known Attributes

The Chrysalis framework defines a number of "well-known" attributes used elsewhere in the framework.

Name Type Description
label String The label for the field. Defaults to the field name.
datatype String The field's logical data type. This may be different from its Java type.
readonly boolean Whether the field is read-only. This is useful for calculated fields.
required boolean Whether the field is required (must be filled in).
max number The field's maximum value (numeric types only). This value is included in the allowed range.
min number The field's minimum value (numeric types only). This value is included in the allowed range.
maxlength int The field's maximum length (string types only). This value is included in the allowed range.
minlength int The field's minimum length (string types only). This value is included in the allowed range.
scale int The number of digits after the decimal place (decimal types only).
options Map 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 well-known attributes can be define as attribute values for the <property> tag in the chrysalis.xml configuration file. The one exception is the options list, which is described below.

Options List

An options list specifies a list of legal values for a property, and text descriptions of those values. The lists are used to generate select lists in the user interface. Each list is configured in the chrysalis.xml by specifying the key and text for each option. Each key must be unique, and match the property type.

<property name="creditCardType">
  <option key="1" text="Visa" />
  <option key="2" text="Mastercard" />
  <option key="3" text="American Express" />
  <option key="4" text="Discover" />
</property>

The <jhtml:input> tag will generate a select list for this field.

<select name="creditCardType">
  <option value="1">Visa</option>
  <option value="2">Mastercard</option>
  <option value="3">American Express</option>
  <option value="4">Discover</option>
</select>

You can customize how option lists are loaded by defining a custom org.chwf.plugin.OptionLister class.

JavaBean Properties and Logical Datatypes

Like controller method parameters, Chrysalis JavaBean properties are divided into simple and complex types:

  • A Simple type fits into a single form field and has a converter in the org.chwf.converter package. This includes all primitive types, primitive object wrappers, strings and dates. You can define new simple types by creating new converters.
  • A Complex (JavaBean) type is anything else, including other JavaBeans, arrays and collections. Chrysalis will not manage complex properties.

You can define new datatypes for simple types. For example, you could define a PhoneNumber class to represent US phone numbers, stored as long values for better memory efficieny.

public class PhoneNumber {
  private final long phoneNumber;
  
  public PhoneNumber(long number) {
    this.phoneNumber = number;
  }
  
  public long getLongValue() {
    return this.phoneNumber;
  }
  
  public String toString() {
    int areaCode = (int) this.phoneNumber / 10000000;
    int prefix = (int) ((this.phoneNumber - areaCode * 10000000) / 10000);
    int suffix = (int) this.phoneNumber - areaCode * 10000000 - prefix * 10000;
    if (areaCode > 0) {
      return "(" + areaCode + ") " + prefix + "-" + suffix;
    } else {
      return prefix + "-" + suffix;
    }
  }
}

You can define a new converter for the new datatype.

public class PhoneNumberConverter extends Converter {

  public Object parse(String value) throws ConversionException {
    try {
      StringBuffer buffer = new StringBuffer();
      char[] chars = value.toCharArray();
      for (int i=0; i<chars.length; i++) {
        if (Character.isDigit(chars[i])) {
          buffer.append(chars[i]);
        }
      }
      return new PhoneNumber(Long.parseLong(buffer.toString()));
    } catch (Exception ex) {
      throw new ConversionException(ex);
    }
  }

  public String format(Object value) {
    return String.valueOf(value);
  }
}

Chrysalis also allows "logical" datatype that are different from the actual Java type of a field. For example, a double property might represent a currency amount. To use logical datatypes:

  • The logical datatype must have a converter.
  • The parsed value of this converter must match the Java datatype for the property.
  • The logical datatype must be specified in the JavaBean packages chrysalis.xml configuration file.
<!-- chrysalis.xml for JavaBean package -->
<config>
  <class name="Item">
    <property name="name" maxlength="20" />
    <property name="stock" min="0" max="1000" />
    <property name="price" datatype="Currency" />
  </class>
  
  <!-- Other JavaBean configuration ... -->

</config>

The org.chwf.converter.basic.CurrencyConverter is an example of a converter for a logical datatype.

Validations

The primary use of JavaBean metadata is to generate validation logic. The same validations can be used in both the client (in JavaScript) and the server.

Client-side validations are generated by the logic in the <jutil:input> tag, and works in conjunction with the FormValidation.js library.

<jutil:input object="item" property="stock" />

Server-side validations are handled by the Validator class. The validate method of this class will throw a ValidationException if the value assigned to the field violates the validation rules. For new classes, this validation logic should be invoked internally in the business object's setter methods.

public class Item implements Serializable {
  private static Validator validator = Validator.findValidator(Item.class);
  
  private int stock;
  
  public void setStock(int stock) throws ValidationException {
    validator.validate(this, "stock", stock);
    this.stock = stock;
  }
  
  // Other logic ...
}

If the class in question is a legacy class that does not perform its own validation logic, you can specify that Chrysalis will invoke the validation logic external to the class (in the BeanFilter), by setting the external-validation configuration attribute for the class to "true":

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

</config>

The Chrysalis JavaBean metadata will only handle simple, single-field validations. Complex or multi-field validations will require custom logic (Java in the JavaBeans, JavaScript in the UI). In Java, you can implement this custom validation logic by creating custom Validator subclasses, as documented in the javadoc for the Validator class.