XX Guestbook Automation Tutorial

XX Automation Tutorial
David Moskowitz, President, Infoblazer LLC.
September 2006

© David Moskowitz, 2006. All Rights Reserved.

Introduction

XX Automation provides an application development framework that requires little or no procedural coding to handle typical application tasks. Instead, the application is specified using XML configuration and a Java Object Model. XSL transformations are applied to generate the view content, while CSS is used for presentation and formatting. This approach generally leads to a simpler and more elegant solution that a purely procedural approach. Where the applications needs more than simple CRUD, additional business logic can be easily incorporated into the process.

XX automation uses Hibernate as an object-database mapping layer. On of the main tasks in developing an XX automation application is to develop this Hibernate layer. The Hibernate layer includes the Java object model and the Hibernate configuration files needed to map this model to a relational database. One approach to this task is to annotate the Java model classes using XDoclet tags and automatically generate the Hibernate mapping files using Apache Ant. In addition, Hibernate includes tools that can be used to forward engineer a database (DDL) from Hibernate mapping files. This approach is independent from the XX framework. XX automation simply requires the Hibernate model to be in place.

XX Automation builds upon the XX Foundation. In the Foundation layer, implementation functions are written by the developer that returns XML. XSL transformations are applied to the XML results before display. (See he XX Foundation tutorial or additional XX documentation for more information).In XX automation, the implementation functions are provided for standard CRUD (Create, Retrieve, Update, Delete) operations.

It is the author’s belief that 80% of typical web application development relates to CRUD operations. XX Automation supports these standard operations. Custom, developer-implemented, operations can also be specified. These custom operations can extend the functionality of the standard CRUD operation to take advantage of XX persistence even when a custom implementation is needed.

Developing the guestbook

In order to demonstrate an implementation of the XX framework, a very simple online guestbook application was developed. This application allows web users to leave and view comments posted on a web site.

The fully functional guestbook tutorial demo can be seen at http://xxframework.org.

The complete source code for this tutorial is available as a WAR distribution at http://www.xxframework.org/downloads.shtml.  Installation instructions are also included in this distribution. Also, it is recommended that you read the XX foundation tutorial first, as that provides as basic for XX automation.

Identify Use Cases

The first step of the application development will be to identify the use cases that will be implemented. This tutorial will discuss the following guestbook use cases, representing a small subset of the actual guestbook application. The full application is available online and in distribution format.

  1. Add Comment- Enter a message in the guestbook.
  2. List Comments- List messages. Includes searching and paging functionality.
  3. View Organization List- List organizations
  4. View Organization- View a single organization
  5. Save Organization- Save a single organization
  6. View User List- List users
  7. View Page Notes- View page design notes. Page specific.

Here is a UML Diagram for these Use Cases.

User Case Model

We’ve added one actor to our Use Case diagram: the User. At this point, we have only one type of user, so security and permissions will be ignored for now. We also don’t have any authentication (log in) use cases, so we can assume that all use cases are accessible to the User without the need to log in to the system. Also, note the View Page Notes use case is included in several of the other use cases and is not accessible independently.

Create Object Model

Based on the use cases identified, we create an object oriented class model. In this simple scenario, we only need one or at most two objects, a Message object and possible a Contact object. To make things a little more interesting, we’ll add some additional classes that will serve to illustrate common programming techniques.   The diagram below shows the initial object model.

Logical Model

In the model, a Message can exist without being linked to a Contact. This allows for anonymous message posting.  

We include some additional common class types

  • Organization, linked to the contact
  • Organizationtype, describing the Organization
  • Userrole, indicating the Contact’s security permissions in the application.

We can then create the Java entity model required by the XX Automation Framework. Note that XX contains a set of basic, commonly used, entity types and in many cases the application can simply use these. In this case, we’ll create a Contact object and Message Object.

To create these two objects (and any XX automation entity) we need to extend the XX Framework class org.xxframework.basetypes.BaseRecord. For certain objects, it is possible to extend other XX classes. For example, most applications may need a more elaborate User object than the basic XX version provides. In the guestbook tutorial, the Contact class extends org.xxframework.basetypes.BasePerson.

Here are the Java classes we created.

Java Class Model

Contact Class

Here is the signature for the Contact class.

public class Contact  extends org.xxframework.basetypes.BasePerson implements Serializable {

… getters and Setters here…
}

The rest of the class simply includes getter and setter values for class attributes.

Note that the contact class extends the framework base class org.xxframework.basetypes.BasePerson, which contains common person-related. 
The Contact class must also implement Serializable interface, to be compatible with Hibernate. The rest of the class contains standard attributes along with getter and setter methods.

Message Class

Here is the signature for the Message class

public class Message extends BaseRecord implements Serializable {
... Getters and Setters here ….
       public String getShortmessage() {
              if (message.length() > 100)
                     return message.substring(0, 100);
              else
                     return message;
       }
}

Since this class does not build off any other XX base classes, it must at least inherit from the BaseRecord class. All XX entity classes must do this.

Note, the Message class needs to implement the Serializable interface to conform to Hibernate conventions. Also, we can use the full capabilities of Java to add additional methods, such as getShortmessage, that don’t correspond to a DB field or aren’t involved in a Hibernate mapping. These fields will however, be available to Castor for inclusion in the XML output and subsequent display.

Also, note the inclusion of the addedbyuser attribute, as shown in the UML class diagram. This field will be used to store the System User associated with the Message, generally representing the logged in user. For this example, we won’t join Message to Contact. This will be used to illustrate implementation of simple table operations with no related tables involved. Other classes will illustrate techniques in the case of related object hierarchies.

Hibernate Mappings and DDL

Here is the Hibernate config file. Remember, it’s location within the Java package hierarchy needs to be specified in the application’s web.xml file.

< hibernate-configuration>

       < session-factory>

              < property name =" connection.url "> jdbc:mysql://localhost:3306/guestbook </property>

              < property name =" connection.username "> guestbook_user </property>

              < property name =" connection.password "> password </property>

              < property name =" connection.driver_class "> com.mysql.jdbc.Driver </property>

              < property name =" dialect "> net.sf.hibernate.dialect.MySQLDialect </property>

              < property name =" cglib.use_reflection_optimizer "> false </property>

              < property name =" show_sql "> true </property>

              < property name =" show_sql "> false </property>

 

              < mapping resource =" org/xxframework/website/entity/Message.hbm.xml "/>

              < mapping resource =" org/xxframework/website/entity/Contact.hbm.xml "/>

              < mapping resource =" org/xxframework/website/entity/Baseperson.hbm.xml "/>

              < mapping resource =" org/xxframework/website/entity/Userrole.hbm.xml "/>

              < mapping resource =" org/xxframework/website/entity/Organization.hbm.xml "/>

              < mapping resource =" org/xxframework/website/entity/Organizationtype.hbm.xml "/>

       </ session-factory>

</ hibernate-configuration>

 

 

 

 

 

 

From the class model, we can generate a database schema. For the tutorial, we are using MYSQL 4.1.

Data Model

And finally, here is the Hibernate mapping to link the Java Message classes to the MySQL database. Note that we don’t need to include the addbyuser attribute. We can add that in to the database at a later date when we want to expand the implementation. The mappings for the other classes are available in the source code for this tutorial

 

< hibernate-mapping package =" org.xxframework.guestbook.entity ">

       < class name =" Message " table =" messages ">

              < id name =" id " column =" id " type =" integer ">

                     < generator class =" increment "/>

              </ id>

              < property name =" title " column =" Title " type =" java.lang.String "/>

              < property name =" message " column =" Message " type =" java.lang.String "/>

              < property name =" author " column =" Author " type =" java.lang.String "/>

              < property name =" dateadded " column =" dateAdded " type =" timestamp "/>

              < property name =" datemodified " column =" dateModified " type =" timestamp "/>

              < property name =" modifiedbyname " column =" lastModifiedBy " type =" java.lang.String "/>

              < property name =" addedbyname " column =" addedBy " type =" java.lang.String "/>

       </ class>

</ hibernate-mapping>

Implementation

With the Use Cases specified and the object model built, we can begin the actual implementation.

List Guestbook

 

The following diagram shows the List Guestbook page, as implemented.

 

Let’s first concentrate on the center portion of the screen, which contains the data specific to list messages operation. Additional page elements, such as the menu and login form will be discussed below.

We have four components or sections directly related to the List Guestbook page. Each of these components will be translated into an XX class.

The classes/portal components are the following

Class Description
1 HTML form to add a message
2 Message Filter section
3 Message List section
99 Design notes section

Here is the XX config file. The config file specifies each of the XX implementation class, the resulting JSP page, as well as other XX configuration elements.

Implementation

With the Use Cases specified and the object model built, we can begin the actual implementation.

List Guestbook

The following diagram shows the List Guestbook page, as implemented.

List Guestbook Screen Shot

 Let’s first concentrate on the center portion of the screen, which contains the data specific to list messages operation. Additional page elements, such as the menu and login form will be discussed below.

We have four components or sections directly related to the List Guestbook page. Each of these components will be translated into an XX class.

The classes/portal components are the following

Class Description
1 HTML form to add a message
2 Message Filter section

3

Message List section
99 Design notes section

Here is the XX config file. The config file specifies each of the XX implementation class, the resulting JSP page, as well as other XX configuration elements.

<class id="1" scope="session" use="xsl" applyxsl="true">

       <classname>org.xxframework.control.RecordControl</classname>

       <method>get</method>

       <hb_classname>org.xxframework.searchfilter.Dummy</hb_classname>

       <xsl_file>AddMessage.xsl</xsl_file>

</class>

<class id="2" scope="page" applyxsl="true" use="xsl">

       <classname>org.xxframework.control.RecordControl</classname>

       <method>get</method>

       <hb_classname>org.xxframework.searchfilter.GuestbookFilter</hb_classname>

       <xsl_file>ListGuestbookFilter.xsl</xsl_file>

       <cache_context use="string"/>

</class>

<class id="3" scope="application" applyxsl="true" use="xsl" timeout="20000">

       <classname>org.xxframework.control.RecordControl</classname>

       <method>list</method>

       <hb_classname>org.xxframework.guestbook.entity.Message</hb_classname>

       <xsl_file>ListGuestbook.xsl</xsl_file>

</class>

  <class id="99" scope="application" use="file" applyxsl="false">

       <endpoint>[xmlroot]/../../ListGuestbookNotes.html</endpoint>

       <cache_context use="string">

       </cache_context>

</class>

We will discuss Class 3 in more detail first, as that is the most involved.

Here is the relevant potion of the XX config file with line numbers

1

< class id =" 3 " scope =" application " applyxsl =" true " use =" xsl " >

2

< classname > org.xxframework.control.RecordControl </ classname >

3

< method > list </ method >

4

< hb_classname > org.xxframework.guestbook.entity.Message </ hb_classname >

5

< xsl_file > ListGuestbook.xsl </ xsl_file >

6

< security secure =" false "/>

7

</ class >

Line 1: id is the identifier to the particular class. It must be unique within the config file and will correspond to a section in the JSP file.
Caching scope is set to application level, meaning the page will be full cached once generated. Also, the XML results will be transformed using XSL.
Line2- specified the class that will be responsible for returning XML data to the framework. In this case, we are using the default XX framework implementation class. This class will always be used for XX automation, unless the developer provides a custom implementation..
Line3- the method to be called in the implementation class will be “listdata”.
Line4 – the entity class that will be involved in the data related operations.

So from the preceding configuration, the framework will perform a list operation on the Message class. This will effectively return a set of all Message objects in the database. Filters, maximum returned number of records, and other factors may effect which messages are returned.

The set of Message returned will be converted to XML, using the castor mapping specified in “message.list.castor.xml”. The mapping file name is set by convention and cannot currently be overridden.

Line 5- The transformation ListGuestbook.xsl will be applied to the XML returned from the list application.
Line 6 – No security will be applied to this function. This page will be available to all users, whether they are logged in or not.

The only additional piece to look at is the XSL transformation, ListGuestbook.xsl.

First, we can look at the XML that would be passed into the transformation.

<?xml version="1.0" encoding="UTF-8"?>

< root >

       < list_results class =" org.xxframework.guestbook.entity.Message " edit ="" short_class =" Message ">

              < message >

                     < id > 2 </ id >

                     < author > dave </ author >

                     < title > Please check out the new Forum </ title >

                     < message > Click the link on the left. </ message >

                     < dateadded > 2006-01-16T16:13:58.000-05:00 </ dateadded >

                     < shortmessage > Click the link on the left. </ shortmessage >

              </ message >

              < message >

                     < id > 1 </ id >

                     < author > dave </ author >

                     < title > Hello World </ title >

                     < message > Hello.This is the first message in the guestbook. </ message >

                     < dateadded > 2006-01-16T16:13:20.000-05:00 </ dateadded >

                     < shortmessage > Hello.This is the first message in the guestbook. </ shortmessage >

              </ message >

       </ list_results >

       < request >

              < sort >

                     < dateadded > desc </ dateadded >

              </ sort >

              < filter >

                     < title />

                     < author />

                     < message />

              </ filter >

              < start > 1 </ start >

              < recordcount > 50 </ recordcount >

              < total_records > 2 </ total_records >

       </ request >

       < select_lists />

       < security secure =" false "/>

       < system_user >

              < user >

                     < id > 1 </ id >

                     < firstname > David </ firstname >

                     < lastname > Moskowitz </ lastname >

                     < userid > dave </ userid >

                     < fullname > David Moskowitz </ fullname >

                     < password > ******* </ password >

                     < userrole >

                           < id > 1 </ id >

                           < typename > Administrator </ typename >

                     </ userrole >

              </ user >

       </ system_user >

</ root >

 

Without getting into too much detail at this point, the XML is taken directly from the Message class representation, as generated by the Castor mapping file or, if a mapping doesn’t exist, the default Castor mapping behavior. The set of message elements are wrapped in a “list_results” element.

The request element contains the original web request that requested the current operation. In other words, the XX framework read the request and produced the XML above. Note the filtering and sorting related elements. These are passed in form the “guestbook search box”. Filtering and sorting will be discussed later in this document.

The XSL file used is shown below. Note, in keeping with a CSS oriented approach to separating content from presentation, the XSL transformation simply provides the content needed for the resulting page. Presentation related elements or attributes should be eliminated or limited. Elements should have content related meaning, and not be included to ultimately signify presentational attributes when transformed using CSS. This goal is not always full realizable, but should be worked towards.

<?xml version="1.0" encoding="UTF-8"?>

< xsl:stylesheet version =" 1.0 " xmlns:xsl =" http://www.w3.org/1999/XSL/Transform " xmlns:fo =" http://www.w3.org/1999/XSL/Format ">

       < xsl:output method =" html "/>

       < xsl:include href =" includes.xsl "/>

       < xsl:template match =" /root ">

              < xsl:apply-templates select =" list_results "/>

       </ xsl:template>

       < xsl:template match =" list_results ">

              < div id =" messages ">

                     < div>

                           < h2> Guestbook Entries </h2>

                           < div class =" record_range ">

                                  < xsl:call-template name =" display_record_range ">

                                         < xsl:with-param name =" count ">

                                                < xsl:value-of select =" count(message) "/>

                                         </ xsl:with-param>

                                  </ xsl:call-template>

                           </ div>

                           < xsl:call-template name =" record_navigation_link ">

                                  < xsl:with-param name =" listpage "> /guestbook/ListGuestbook?sort/dateadded=desc </xsl:with-param>

                           </ xsl:call-template>

                     </ div>

                     < table summary =" XX Framework Guestbook Entries " class =" datatable " id =" messages ">

                           < thead>

                                  < tr>

                                         < th id =" id "/>

                                         < th abbr =" Posted " id =" posted "> Posted On </th>

                                         < th id =" author "> Author </th>

                                         < th id =" title "> Title </th>

                                  </ tr>

                           </ thead>

                           < tbody>

                                  < xsl:apply-templates select =" message "/>

                           </ tbody>

                     </ table>

              </ div>

       </ xsl:template>

       < xsl:template match =" message ">

              < input type =" hidden " name =" id ">

                     < xsl:attribute name =" value "><xsl:value-of select =" id "/></xsl:attribute>

              </ input>

              < tr>

                     < th headers =" id ">

                           < xsl:attribute name =" id "><xsl:value-of select =" id "/></xsl:attribute>

                           < a>

                                  < xsl:attribute name =" href "> /guestbook/ShowMessage?id= <xsl:value-of select =" id "/></xsl:attribute>

                                  < xsl:value-of select =" id "/>

                           </ a>

                     </ th>

                     < td>

                           < xsl:attribute name =" headers "><xsl:value-of select =" id "/> posted </xsl:attribute>

                           < xsl:call-template name =" format-date-time ">

                                  < xsl:with-param name =" xsd-date-time " select =" dateadded "/>

                                  < xsl:with-param name =" format " select =" '%m/%e/%Y %i:%M %P' "/>

                           </ xsl:call-template>

                     </ td>

                     < td>

                           < xsl:attribute name =" headers "><xsl:value-of select =" id "/> author </xsl:attribute>

                           < xsl:value-of select =" author "/>

                     </ td>

                     < td>

                           < xsl:attribute name =" headers "><xsl:value-of select =" id "/> title </xsl:attribute>

                           < xsl:value-of select =" title "/>

                     </ td>

              </ tr>

              < tr class =" row_caption ">

                     < td/>

                     < td colspan =" 3 ">

                           < xsl:attribute name =" headers "><xsl:value-of select =" id "/></xsl:attribute>

                           < xsl:value-of select =" shortmessage "/>

                           < xsl:if test =" string-length(shortmessage)= 100 "> &#160; </xsl:if>

                     </ td>