Xoetrope
View

Screenshots •  Videos •  Demos •  Documentation •  Tutorials •  Articles •  Knowledge Base •  Zone


POJO support

POJOs or Plain Old Java Objects support has been added in XUI 3.0. The new support for using POJOs as a backing for the XUI data model means that POJOs can be bound to user interface components just as easily as static data or database data. The POJO model may be home grown or generated with tools such as JDO or Hibernate. Most of the configuration takes place automatically via reflection and little needs to be done other than pointing XUI at the root of the POJO model.

The following steps show how to build a two-tier XUI application which uses Hibernate POJOs as its data model.

Setting the data source

The XML entry, describing the data source object tasked with loading and adapting POJOs to the XUI model, needs to be set in the file pointed to by the ModelData startup property (datasets.xml by default). The aformentioned XML entry can look like this:

 
 
Listing 1 - setting POJO data source
 
 
<Datasources>
  <context class="auction.persistence.PersistenceController"/>
  ...
  <DataSource filename="pojos.xml" name="Pojos"
	type="net.xoetrope.optional.data.pojo.XPersistentPojoDataSource"/>
  ...
<Datasources>
 
 

In the listing above the filename attribute points to a file containing model data POJOs configuration.

Setting POJO model root node

The data model POJOs configuration file must include the <root.../> XML entry pointing to a class whose instance will be attached to the XUI data model and will serve as a "POJOs repository". The listing below shows the sample entry.

 
 
Listing 2 - setting the root POJO
 
 
<Pojos>
  ...
  <root id="pojo" class="xui.ce.model.PojoBase">
    <param class="String" value="pojo base object"/>
  </root>
  ...
</Pojos>
 
 

The class attribute points to a class which will be instantiated and attached to the XUI data model via XModel object, the constructor attributes are defined in the <param.../> entries. The id attribute of the <root .... /> entry defines the data model path at which the XModel object wrapping the root POJO will be located.

Accessing POJO model nodes

The general rule of accessing POJOs in the data model is: if the XModel node wrapping a given POJO is located at the "parentPath" path and defines property named p1, (via accessors: getP1(), setP1(...)) then the XModel node wrapping the value of this property is located at "parentPath/p1" path. The actual value of the property can be obtained by invoking the get() method on the encapsulating XModel node. The source of the root class being used in the example is:

 
 
Listing 3 - the source of the root POJO
 
 
package xui.ce.model;

import auction.dao.DAOFactory;

public class PojoBase
{    
  private String pojoBaseName;
  
  public PojoBase( String name )
  {
    pojoBaseName = name;
  }
  
  /**
   * Gets the hibernate data access object factory, all DAOs
   * being used in the application can be otained from it.
   */
  public Object getHibernateDAOFactory()
  {            
    return DAOFactory.instance( DAOFactory.HIBERNATE );
  }
    
  public String getId()
  {
    return pojoBaseName;
  }
}
 
 

As can be seen the class defines hibernateDAOFactory property, its value will be located at the "pojo/hibernateDAOFactory" path, or to be more acurrate, the wrapping model node will be located at the mentioned path.

Overriding POJO properties

POJO properties can be overriden, via the data model POJO's configuration file (pojos.xml in the example).The listing below shows the sample overloading.

 
 
Listing 4 - overriding POJO properties
 
 
<Pojos>
  ...
  <pojo id="categoryDAO" class="auction.dao.hibernate.CategoryDAOHibernate">
    <property id="rootCategories" getter="all(Boolean:true)"/>
    <property id="allCategories" getter="all()"/>
  </pojo>
  ...
</Pojos>
 
 

Each <pojo.../> entry defines how individual class is overriden. While the id attribute is not being used at the moment, the class attribute specifies a class whose properties are customized. <property..../> entries define accessors that should be used to get/set property value specified by the id attribute. With the definition as in the listing above, evaluating the model path "categoryDAO/rootCategories" would result in invocation of the getAll(true) method on the CategoryDAOHibernate object stored at the "categoryDAO" path.

Transient properties

In the data model's POJO configuration file a special type of properties, named transient properties, can be defined. The listing below shows the sample definition.

 
 
Listing 5 - defining transient properties
 
 
<Pojos>
  ...
  <pojo id="categoryDAO" class="auction.dao.hibernate.CategoryDAOHibernate">
	  <transient_property id="selectedCategory"
		class="auction.model.Category"/>
  </pojo>
  ...
</Pojos>
 
 

The <transient_property..../> entries define an additional accessor-less properties which serve as a place to store POJOs in the data model. Invoking the set(arg)/get() method on a model node wrapping such a property will simply store/return the POJO, neither a getter nor setter will be invoked on the parent model node. The reason for this is to provide the ability to declaratively define POJO properties, that may be useful in an application as a part of the data model, but are not connected with a business logic which the POJO's native implementation is intended to define. For example, an application may want to display the details of the selected Category object when the selection is made in the list of all categories. In such a scenario, the model node associated with the selected category would be defined as a transient property. The listing below is an example of this.

 
 
Listing 6 - transient property binding in a page xml source
 
 
<XPage class="mypackage.EditCategories">

  <Components>
    <List name="allCategoriesList" ..../>
    <Edit name="selectedCategoryName" ... />
    <List name="subCategoriesList".../>
  <Components/>
	
  <Data>
    <Bind target="allCategoriesList"
		source="categoryDAO/allCategories" attrib="name"/>
    <Bind target="selectedCategoryName"
		source="categoryDAO/selectedCategory/name"/>
    <Bind target="subCategoriesList"
		source="categoryDAO/selectedCategory/childCategories"
		attrib="name"/>
  <Data/>
	
  <Events>
    <Event method="categoryListSelectionChanged"
		target="allCategoriesList"
		type="ListSelectionHandler"/>
  </Events>
	
<XPage/>
 
 

 
 
Listing 7 - list selection handler updating components bound to the transient property
 
 
public EditCategories extends XPage
{
  ...
  // invoked whenever the selection on allCategoriesList is changed
  public categoryListSelectionChanged()
  {
    // get selected category model from the list
    Category category = getSelectedCategory( allCategoriesList );
    // put selected category to the transient property model
    rootModel.get( "categoryDAO/selectedCategory" ).set( category );
    // update the UI
    updateBoundComponentValues();
  }
  ...
}
 
 

Setting ORM specific configuration

It is possible to use ORM (i.e Hibernate, JPA) POJOs as a data model. This requires some addiction configuration. Following the DAO pattern, the first thing that has to be done is to create a set of Data Access Object that will serve as a layer between the POJOs and the persistent store. Each of these objects has to be declared in the configuration file(pojo.xml). The sample configuration can look like this.

 
 
Listing 8 - declaring DAOs
 
 
<Pojos>
  ...
  <pojo id="userDAO" class="auction.dao.hibernate.UserDAOHibernate"
	dao="true"/>			   	
  <pojo id="categoryDAO"
	class="auction.dao.hibernate.CategoryDAOHibernate"
	dao="true"/>			   
  ...
</Pojos>
 
 

The declaration from the listing above requires that before accessing (via accessor methods) any of the properties belonging to either UserDAOHibernate or CategoryDAOHibernate instances, the session "bound" to this property should be opened. This is being handled automatically via the Persistence Controller whose implementation will be described later.

The next thing that needs to be added to the POJO model configuration file is the definition of lazily initialized POJO properties which means properties whose values are being retrieved from the persistent store "on demand". This kind of properties are supported by either Native Hibernate and JPA ORMs. The listing below shows the sample configuration.

 
 
Listing 9 - lazy initialized properties configuration
 
 
<Pojos>
  ...
  <pojo id="book" class="auction.model.Book">	
    <property id="categories" lazy="true"/>
    <property id="bids" lazy="true"/>
  </pojo>
  ...
</Pojos>
 
 

This configuration specifies that two things have to be done before accessing any of the listed properties (before invoking getter methods). First: the session bound to the given property has to be opened, and second: the parent object (in this case: an instance of Book class) must be in the "persistent" state. All this is handled by data model and persistent controller which transparently begin and end sessions (database transactions) when the UI is updated, bindings are evaluated, etc.
When the Native Hibernate serves as an ORM framework this configuration is retrieved from the mapping files (specified in the "hibernate.cfg.xml" file), so there is no need to provide it explicitly.

Setting up the Persistence Controller

The next thing is to create and setup the Persistent Controller object which must implement the XPersistenceController interface including methods tasked with beginning/ending sessions, saving/removing entity objects, etc. This can be done with the Native Hibernate as well as with the JPA ORMs. The sample implementataion of one of the interface's methods "persist( Object o )" is shown below.

 
 
Listing 11 - the Native Hibernate implementation of the "persist" method.
 
 
import org.hibernate.*;

public class HibernatePersistenceController
{
  ...
	
  /**
   * Makes the specified object persistent.
   * @param object the entity to be saved.
   */
  public void persist( Object object )
  {
    Session session = HibernateUtil.getSessionFactory().
    getCurrentSession();    
    session.saveOrUpdate( object );
  }
	
  ...
}
 
 

 
 
Listing 11 - the JPA implementation of the "persist" method.
 
 
import javax.persistence.*;

public class JPAPersistenceController
{
  ...
  private EntityManager entityManager;  
	
  /**
   * Sets the entity manager that should be used
   * by this controller.
   * @param em the entity manager to be used
   */
  public void setEntityManager( EntityManager em )
  {
    entityManager = em;
  }
	
  /**
   * Makes the specified object persistent.
   * @param object the entity to be saved.
   */
  public void persist( Object object )
  {
    entityManager.merge( object );
  }    

  ...
}
 
 

The last thing is to add an entry pointing to a class that implements XPersistenceController to the POJO model configuration file, as shown in the listing below.

 
 
Listing 12 - setting the Persistence Controller
 
 
<Pojos>
  ...
  <context class="xui.ce.HibernatePersistenceController"/>
  ...
</Pojos>