An Implementation of Northwind using The Alchemist

Author: Martyn Cutcher

email martyn@cutthecrap.biz

website www.cutthecrap.biz

The "Northwind" database is used by Microsoft as a teaching example. The aim of this paper is to use this data model as the basis for a demonstration of the Cut The Crap "Alchemist".

What is The Alchemist?

The Alchemist is a system generator. From either a definition file or using its own API, the Alchemist will generate a complete object model implementation. The generated system includes a jar file with the compiled code, javadocs for the generated API, and a war file for instant web deployment in a tomcat server. Since the generated object model uses the Generic Persistent Object model, all Alchemist generated systems are scalably persistent to gigabytes of data and deployable in muti-user contexts.

The Alchemist works by identifying common patterns. These are not simply low-level programming patterns, but ones more akin to overall application structure. How is a system initialized? - or - How is data created?

This paper attempts to de-mystify the patterns used and demonstrate the correspondence between the provided user definitions and the code generated.

The ORM - Object Representation Model

This is a meta-representation of the target model. In other words it describes the properties and object associations of the model.

Why define a new modelling language? Because it is essential to have the freedom to add and remove language features as required. The aim of this language is to support the system generation, nothing more or less.

The ORM File

For the purposes of this paper the Alchemist API will not be addressed, instead we will provide the definition using an ORM file. This is an xml file that will have the following structure:

<orm name='Northwind' package='alchemy.northwind'>
  ...definitions
</orm>

There are just two types of definitions, class definitions and assoc definitions.

A Root System Object

A frequent oversight in most object modelling, is that there is rarely a representation of the Application or System itself.

Suppose, for example, that you wanted to model some business - let's call it Northwind.

The modelling process tends to start by considering all the entities within Northwind. We may have, Customers, Suppliers, Products and Orders to start off with.

We would then come up with some object or entity diagram to show how these things related to each other.

But what about including an object to represent Northwind itself? Surprisingly enough, by introducing such an object as the focus of the overall model, things start to look far more sensible.

Rather than saying "there are Customers", we state that "Northwind has many Customers", etc...

When someone asks "How do I start interacting with this system?", the root object - in this case Northwind defines the initial interactions.

For this reason, a definition for The Alchemist requires a root object, here is the definition:

<class name='Business' isroot='true'>
  <attr label='Name'
        type='java.lang.String'
        islabel='true'
        default='Northwind'/>
</class>

We have defined a class called Business, declared that it is the root class and provided a single Name attribute, that should be used to label any instance and is given the default value "Northwind".

Labelling an Object

Oooops... we have already stumbled over a number of patterns.

In order to display an object to the user in some understandable form it is normal to use some property of the object. The islabel definition attribute indicates this.

...and while we are at it, it is often useful to declare default values for some attributes.

What would The Alchemist make of that?

This is already sufficient for a system to be generated. A client object called NorthwindClient will be generated to support application initialization. This can be created by:

client = new alchemy.northwind.client.NorthwindClient();

Other constructors provide options to store the model using different GPO object managers.

When the client is created, it checks to see if the root object already exists, if not then it creates one.

The client would have a method getBusiness() that would return a reference to the root object.

The root object is an instance of alchemy.northwind.Business and provides methods to both getName and setName, when the Business object was first created it will have set the Name property to the default "Northwind" value as specified in the ORM.

A generated war file could be deployed in a Tomcat server, access to the northwind application would explain that it had been generated by the Alchemist, invite you to inspect the javadocs or to open an inspector that by default would focus on the root Business object. With this inspector you could view and modify the single specified property.

As you start to edit the property a "Submit" button is made visible. At any time you could "Revert" to the state currently stored, or you could "Submit" the update. When submitted you will then see two more buttons "Revert" and "Save", these provide the transaction control. Any number of submits may be made within the same transaction.

Let us continue.....

Type Restrictions and Validation

Take a look at a more complete Business class definition:

<class name='Business' isroot='true'>
  <attr label='Name'
        type='java.lang.String'
        islabel='true'
        default='Northwind'/>
  <attr label='Address'
        type='java.lang.String'
        guihint='rows:3'/>
  <attr label='Postcode'
        type='java.lang.String'
        restriction="postcode"/>
  <attr label='TelNo'
        type='java.lang.String'
        restriction="telno"/>
  <attr label='EMail'
        type='java.lang.String'
        restriction="email"/>
</class>

You will notice that as well as defining several more properties, that two more definition attributes have been introduced.

The guihint attribute is just that, a hint to how the field should be displayed. In this case guihint='rows:3' indicating that any GUI should provide a multi-row editing box of three rows.

More interesting is the restriction attribute. The Alchemist uses an external file that provides aliases for restrictions. Here is a snippet of the file:

<entry alias="postcode" 
  value="regex:^[A-Z]{1,2}[0-9]{1,2}( [0-9][A-Z]{2})?$" 
  example="RG41 1JT"
/>

Yep, that's right, there are a number of different kinds of restrictions, and one of the especially useful ones is a regular expression.

The restrictions.xml file declares several useful aliases and you are able to add new definitions or modify existing definitions as you see fit.

Generally, a restriction is a type restriction, other restrictions might be range:1-10 for a numerical value, or timeslot:hours for a Date type.

The restrictions as defined in The Alchemist model definition file are used to generate meta data that can be exploited by validating user interfaces. Here is a snippet from the generated getMetaSpec method that is used by several generic GUI tools:

public MetaSpec getMetaSpec() { 
  MetaSpec spec = new MetaSpec(this);
  Object lblval = get(m_name);
  if (lblval != null) {
    spec.setLabel(lblval.toString());
  }

  // attributes
  spec.addAttribute(m_name, "Name",
                    get(m_name), String.class, null, null);
  spec.addAttribute(m_address, "Address",
                    get(m_address), String.class, "rows:3", null);
  spec.addAttribute(m_postcode, "Postcode", 
                    get(m_postcode), String.class, null,
                    "regex:^[A-Z]{1,2}[0-9]{1,2}( [0-9][A-Z]{2})?$");
  ...

A key point here is that the meta data that has been used to generate the model is itself available at runtime to tools that know how to process it.

Object Associations

Before we can look at associations we need to define another class. The "Northwind" system requires Customers and Suppliers, however, we'll assume that we have a few minutes time to think about it and recognise that these can both be thought of as Contacts.

<class name='Contact'>
  <attr label='Name'
        type='java.lang.String'
        required='true'
        islabel='true'/>
  <attr label='TelNo'
        type='java.lang.String'
        restriction="telno"/>
  <attr label='Email'
        type='java.lang.String'
        restriction="email"/>
  <attr label='Address'
        type='java.lang.String'
        guihint='rows:3'/>
  <attr label='PostCode'
        type='java.lang.String'
        restriction="postcode"/>
  <attr label='Notes'
        type='java.lang.String'
        guihint='rows:4'/>
</class>

Now we are ready to define an association:

<assoc>
  <one src='Business' owner='true'/>
  <many src='Contact' classified='Name:alpha'/>
</assoc>

Okay, we are saying here that there is a one-to-many association between a Business instance and instances of Contact.

The owner attribute indicates that the Business instance "owns" the Contact instances. From this The Alchemist will generate methods for the Business class to create new instances of the Contact class.

Note though that the Name property has been indicated as required, this means that it must be provided when the object is created, so what method do we see generated for the Business class?

/*************************************************************
 * Create a new contact.
 *  @return the new contact.
 **/
public IContact createContact(String name) { 
  cat.debug("Creating new contact.");
  startNativeTransaction();
  Contact ret = new Contact(getObjectManager(), name);
  addContact(ret);
  commitNativeTransaction();
	
  return ret;
}

We can see from the code that the generated Contact constructor requires a name argument, and the generated createContact method will provide it.

This is precisely what we should expect, we can just ask the root object:

contact = northwind.createContact("Customer One");

This has created the new object, persistently stored it in the datastore and returned a reference to it.

Object Lookup

Furthermore, we have stated that the many Contacts are classified with the value Name:alpha. This indicates that the set of Contacts defined by the one-to-many association is classified by the value of the Name attribute of each Contact. The alpha parameter can be used by user interface tools as an indication of how such a classification should be navigated.

The Alchemist will generate lookup methods on the Business class that access the classification object to find Contacts according to the value of their Name attribute.

...continuing

Okay we'll add in some more classes and associations we need, hopefully you should already be able to understand most of what is declared:

<class name='Product'>
  <attr label='Name'
        type='java.lang.String'
        required='true'
        islabel='true'/>
  <attr label='Price'
        type='java.lang.Double'/>
  <attr label='Description'
        type='java.lang.String'
        guihint='rows:3'/>
</class>
  
<assoc>
  <one src='Business' owner='true'/>
  <many src='Product' classified='Name:alpha'/>
</assoc>

<assoc>
  <one src='Contact' knownas='Supplier'/>
  <many src='Product' classified='Name:alpha'/>
</assoc>

<class name='OrderItem'>
  <attr label='Label'
        type='java.lang.String'
        islabel='true'
        formula="$Product!$Name"/>
  <attr label='UnitPrice'
        type='java.lang.Double'
        formula="$Product!$Price"/>
  <attr label='Quantity'
        type='java.lang.Integer'
        default="1"/>
  <attr label='Price'
        type='java.lang.Double'
        formula="(* $Quantity $UnitPrice)"/>
</class>

<!-- An OrderItem make no sense without a Product -->
<assoc>
  <one src='Product' required='true'/>
  <many src='OrderItem'/>
</assoc>

Whooa! What's all this formula stuff?

The Alchemist now provides support for spreadsheet-type formulas within the object model. These generate GPO formulas - check out the separate whitepaper on GPO Formulas.

The first formula $Product!$Name defines the value of the OrderItem label. The formula indicates that the value of the label is computed by accessing the "Name" property of the associated "Product". If the formula had been $Product$Name then the value of the order item would be updated should ever the product name be changed, while the formula used includes the '!' character, this ensures that the dependency stops at the "Product" link and that the Label of the OrderItem is the same value as when it is created.

The '!' character is also used to define the UnitPrice of the OrderItem, calculating the value at the time it is created.

Lastly the Price is calculated by multiplying the value of the UnitPrice by the Quantity.

Mapping ORM to GPO formulas

The syntax for defining formulas within the ORM file is slightly different from when defining directly in GPO. This is because of the mapping required between the names used in the ORM and the generated names in GPO. Specifically this involves the lookup formula. So $Product for example will generate (-> orderitem/product) and here is a snippet of the generated code for the OrderItem class:

public OrderItem(IGPO _context, IProduct product) { 
  super(_context);
  setProduct(product);

  // Set any default attribute values.
  setQuantity(new Integer("1"));

  // Define any formulas.
  define(m_label, "(-> orderitem/product ! product/name)");
  define(m_unitPrice, "(-> orderitem/product ! product/price)");
  define(m_price, "(* (-> orderitem/quantity) (-> orderitem/unitprice))");

  registerDependencies();

  cat.debug("Constructed.");
}

...continuing

Next up is the Order class:

<class name='Order'>
	<attr label='Date'
	      type='java.util.Date'
	      isLabel='true'
	      restriction='timeslot:day'/>
	<attr label="Price"
	      type='java.lang.Double'
	      guihint='numberformat:###,##0.00'
	      formula="(sum $$OrderItem.$Price)"/>
</class>

This definition has a few new features:

There is a Date attribute that has the declared rstriction timeslot:day, indicating that only the date and not the precise time is of interest.

The Price property defines a numberformat guihint that is used to display the value returned by the formula (sum $$OrderItem.$Price), this is a "set-based" computation and the $$OrderItem indicates the relevant association.

The effect of this formula is to incrementally compute the Price of the Order as OrderItems are added, removed or modified.

<assoc>
  <one src='Business' owner='true'/>
  <many src='Order'/>
</assoc>

<assoc>
  <one src='Order' owner='true'/>
  <many src='OrderItem'/>
</assoc>

<assoc>
  <one src='Contact' knownas='Customer' required='true'/>
  <many src='Order'/>
</assoc>

<assoc>
  <one src='Contact' knownas='DeliveredTo'/>
  <many src='Order' knownas="ReceivedOrder"/>
</assoc>

Is That Really Northwind?

No - it is substantially more than the normal Northwind model.

We have generalized the Northwind Customer and Supplier classes and recognized that an Order is for a Customer Contact, includes Products provided by Supplier Contacts and DeliveredTo a possibly different Contact.

So our Northwind model handles nicely the role of a general distributor where Contacts can be Suppliers, Customers and Recipients.

Here is a snippet of the generated IContact interface, stripped of javadocs for readability:

public IProductIterator getProducts();
public void addProduct(IProduct product);
public void removeProduct(IProduct product);
public IProduct getProductByName(String name);

public IOrderIterator getOrders();
public void addOrder(IOrder order);
public void removeOrder(IOrder order);

public IOrderIterator getReceivedOrders();
public void addReceivedOrder(IOrder receivedOrder);
public void removeReceivedOrder(IOrder receivedOrder);

Note the use of the typed iterators, where for example an IProductIterator provides a nextProduct() method to help in clearer programming. These iterators derive from the Cut The Crap Striterator class that provides useful methods to process and filter such object streams, for more information check out the whitepaper.

Furthermore, we have included formulas that dynamically calculate Order prices and initialize OrderItems.

We also have an API that correctly addresses object creation, so the Business instance will create new Orders - with a required Customer contact:

public IOrder createOrder(IContact customer);

An Order instance will create OrderItem instances with a required Product:

public IOrderItem createOrderItem(IProduct product);

...and so on

In short, we have generated a complete system, not simply some database that allows the data to be stored.

Now you have got this far, take a look at the complete ORM definition file.

What You Get

With this model definition The Alchemist will generate a complete persistent object model - building on the underlying GPO system.

The ant build files will build a deployable zip file including javadocs and jar files.

A recent enhancement, is the generation of a Struts and Tiles based JSP web deployment - war file. When deployed, the Web Application provides a highly functional interface to the generated system. The guihints and restrictions are utilized to maximum effect to provide browser-based validation of user entered text "as they type", and drag-n-drop object associations alongside effective interfaces to navigate the set and lookup structures.

The Web Application also includes user management and login security using standard security techniques.