From Documentation

Jump to: navigation, search




  • Author
    Simon Massey, AVP, Marsh, The world's leading insurance broker and strategic risk advisor
  • Date
    November 3, 2008
  • Version
    Applicable to ZK 3.5.0 and later.


Contents

Background

In this article we will introduce a refactored version of the ZK ToDo sample application using a Spring JPA database backend. The sample application "ZkToDo2" demonstrates the MVC approach documented by the MVC Patterns smalltalk. The MVC approach allows you to centralize application logic in one or more classes resulting in code which is easier to maintain over time.

The Spring and JPA configuration used in this article are adapted from the persistence add-on of the Loom framework. My thanks go to Ignacio Coloma and his collaborators on Loom for their well documented and discussed JPA code.

Why Spring & JPA?

Data access within J2EE has had a long and colourful evolution. Seasoned programmers will have used a number of different persistence frameworks and techniques over the years. JPA has lately become a popular standard for Object Relational Mapping supported by a broad range of both open-source and commercial vendors. The Spring Frameworks support for JPA provides easy control over how different JPA vendor offerings can be configured and used within your application.


Obtaining & Building The Sample Code

Typically setting up an environment to try out J2EE sample code can be an in-depth and time consuming experience. The ZkToDo2 sample code aims to be self installing to make it easy to run, debug and explore the running code. All you need is the Maven build system (a powerful successor to Ant) or a Maven plug-in for your chosen IDE. Maven will download all of the dependant jars, compile the code, test the code and will run a Servlet container with an embedded database with a single command. The steps to checkout, build, run and debug the application using Eclipse with the m2eclipse Maven plug-in are here. Alternatively if you would like to build and run the application on the command-line using only a stock Maven distribution then follow the instructions here.


Domain Object & Database Bindings

Here is an extract of the domain class and it's mapping into the database:

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "REMINDER")
public class Reminder {
	@Id @GeneratedValue
	@Column(name = "EVENT_ID")
	private Long id;
	
	@Column(name = "NAME")
	private String name;
	
	@Column(name = "PRIORITY")
	private int priority;
	
	@Column(name = "DATE")
	private Date date;
 ...


The database binding annotations used within the class are from the javax.persistence package. This means that they will work with any JPA implementation such as EclipseLink, Hibernate, OpenJPA or TopLink to name just a few. The state held in each domain object will be persisted to the database by the BasicDao class detailed below. First we will look at the user interface and its controller class that displays and updates the reminder objects stored within the database.


The User Interface Controller

The user interface of ZkToDo2 is defined in a single file zktodo_a.zul file that is very similar to the original:

Zktodo2.png

<?xml version="1.0" encoding="UTF-8"?>
<zk xmlns="http://www.zkoss.org/2005/zul">
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
<window title="To do list" width="640px" border="normal" apply="${toDoControllerV1}">
<listbox id="list" multiple="true" rows="4">
 <listhead>
	<listheader label="Item" />
	<listheader label="Priority" width="80px" />
	<listheader label="Opened" width="90px" />
 </listhead>
 </listbox>
 <vbox>
	<hbox>
	Item:<textbox id="name" cols="40" constraint="no empty"/>
	Priority:<intbox id="priority" cols="1" constraint="no empty"/>
	Date:<datebox id="date" cols="14" constraint="no empty"/>
	</hbox>
	<hbox>
	<button id="add" label="Add" width="36px" height="24px"/>
	<button id="update" label="Update" width="46px" height="24px"/>
	<button id="delete" label="Delete" width="46px" height="24px"/>
	</hbox>
 </vbox> 
</window>
</zk>


The rendering behaviours and event handlers of the original ToDo application have been removed. The screen file defines an inert page with no application state or behaviours. These are to be provided by our controller class. What is new is that the window tag has an "apply" attribute set to "${toDoControllerV1}". Spring is named as a variable resolver near the top of the page. The introduction of the spring variable resolver gives complete integration with the spring framework. All we need to do is define beans within Spring's configuration and we can reference this beans by name within our ZUL file. The toDoControllerV1 bean is defined by this entry within the file spring-config.xml:

	<bean id="toDoControllerV1" class="org.zkforge.zktodo2.ZkToDoControllerV1" 
	p:reminderService-ref="reminderService" scope="prototype"
	/>

The class of the toDoControllerV1 bean is ZKToDoControllerV1. The scope of the bean is "prototype". This means that Spring will instantiate a new bean each time ZK asks for a bean of that name. This occurs each time the zul page is loaded. The controller configuration specifies that it requires a "reminderService". The reminderService bean is defined elsewhere within the configuration file. Here is an extract of the controller class:


public class ZkToDoControllerV1 extends GenericForwardComposer implements
	ListitemRenderer {

	public void doAfterCompose(Component comp) throws Exception {
		super.doAfterCompose(comp);
		// more initialization logic
	}

	protected Textbox name;
	protected Intbox priority;
	protected Datebox date;
	protected Listbox list;
	protected ListModelList listModelList;

	public void onClick$add(org.zkoss.zk.ui.event.Event e) {
		 // ...
	}
	public void onClick$update(org.zkoss.zk.ui.event.Event e) {
		 // ...
	}

	public void onClick$delete(org.zkoss.zk.ui.event.Event e) {
		// ...
	}

	public void render(Listitem listItem, Object data) throws Exception {
		// ...
	}
}


ZkToDoControllerV1 subclasses GenericForwardComposer and overrides it's doAfterCompose method. ZK passes the Window component of the user interface into the doAfterCompose method. It does this as the toDoControllerV1 bean was named in the "apply" attribute of the Window object within the zul file. The method first calls super.doAfterCompose to automatically wire our controller to the user interface inside the window object. The autowiring is done via ZK matching the names and types within our Java file to the XML elements and IDs defined within our ZUL file.

If we compare the Java and ZUL listings above we find matches for "name", "priority", "data" and "list". In addition the types within the Java code match the components defined within the ZUL file. ZK will inject references to the named screen components into our controller class. The Java listing also defines three event handlers. The signatures of the event handlers are similar to the standard ZK onClick event handler. The method names start with "onClick" and end with the ID of a button defined within the ZUL file. ZK will bind the three controller methods as event handlers on the corresponding button components.

Our controller class implements the ListitemRenderer interface. Within the doAfterCompose method the controller sets itself as the listItemRenderer of the Listbox defined within the ZUL page. In this way our controller can define the logic of how to transfer state from our database bound objects into UI state.

Lets take a more detailed look at two of the controller class methods:

 protected Reminder selectedReminder = null;
	
	public void doAfterCompose(Component comp) throws Exception {
		super.doAfterCompose(comp);
		listModelList = new ListModelList();
		List<Reminder> reminders = reminderService.findAll();
		listModelList.addAll(reminders);
		list.setModel(listModelList);
		list.setItemRenderer(this);
		list.addEventListener("onSelect", new EventListener(){
			public void onEvent(Event e) throws Exception {
				int index = list.getSelectedIndex();
				selectedReminder = (Reminder) listModelList.get(index);
				date.setValue(selectedReminder.getDate());
				priority.setValue(selectedReminder.getPriority());
				name.setValue(selectedReminder.getName());
				return;
		}});
	}

	public void render(Listitem listItem, Object data) throws Exception {
		Reminder reminder = (Reminder) data;
		new Listcell(reminder.getName()).setParent(listItem);
		new Listcell(reminder.getPriority()+"").setParent(listItem);
		new Listcell(dateFormat.format(reminder.getDate())).setParent(listItem);
	}

Within the doAfterCompose method the controller configures the initial state of our page. If there are objects within the database they must be rendered into the listbox. To load objects from the database our controller invokes findAll on the rendererService bean. It then adds the objects loaded from the database into a ListModelList. The ListModelList is then set on the Listbox. This binds our database data to the user interface control.

Our controller class implements the ListitemRenderer interface. Within the doAfterCompose method the controller sets itself as the listItemRenderer of the Listbox defined within the ZUL page. In this way our controller can define the logic of how to transfer state from our database bound objects into UI state. For each item within the ListModelList ZK calls our render method passing both the model object and the Listitem instantiated for the current model object. Our controller code then creates Listcell components that are rendered into the current Listitem using the setParent method of the abstract Component class.

The last act of the controller initialization logic is to bind an onSelect EventListener onto the Listbox. This event handler resolves the corresponding database object within the ListModelList using the selectedIndex value of the Listbox. This object is set as the selectedReminder member variable. The update and delete event handlers make use of selectedReminder. The onSelect event handler also sets the textbox, intbox and databox values to match the state of the selected database object.

The Service & Data Layers

The layering of the UI controller bean, service bean and data access bean is defined within the Spring configuration file as follows:

<bean id="basicDao" class="org.zkforge.zktodo2.BasicDao" />

<bean id="reminderService" class="org.zkforge.zktodo2.ReminderService" 
	p:basicDao-ref="basicDao"
/>

<bean id="toDoControllerV1" class="org.zkforge.zktodo2.ZkToDoControllerV1" 
	p:reminderService-ref="reminderService" scope="prototype"
/>


The controller is provided with a service bean. The service bean is provided with a data access bean. The service bean has default scope so it will be a singleton object that will be injected into each controller object instantiated for each loaded ZUL page. The controller class makes use of the service to list database objects as outlined above. It also uses the service object within each of the EventListeners bound onto the buttons defined within the ZUL page. The EventListeners bound to the buttons provide logic to create, update or delete database objects.


Here is an extract of the ReminderService class:

public class ReminderService {

	@Transactional(readOnly=true)
	public List<Reminder> findAll(){
		List<?> events = this.basicDao.findAll(Reminder.class);
		return(List<Reminder>) events;
	}
	
	@Transactional(readOnly=false,propagation = Propagation.REQUIRED)
	public void persistEvent(Reminder reminder){
		this.basicDao.persist(reminder);
	}

	@Transactional(readOnly=false,propagation = Propagation.REQUIRED)
	public void deleteEvent(Reminder reminder) throws EntityNotFoundException {
		this.basicDao.remove(Reminder.class, reminder.getId());
	}

	@Transactional(readOnly=false,propagation = Propagation.REQUIRED)
	public void mergeEvent(Reminder reminder) throws EntityNotFoundException {
		this.basicDao.merge(reminder);
	}
}


The reminderService has Spring transaction annotations to define the transactional semantics of the business logic. The sample application is very simple and does not define many database operations within each service method. In a larger system that must perform a set of data operations in an atomic manner the use transactions would be of greater importance to ensure data integrity. The transaction annotations are activated by a single line in the spring-config.xml file:

<tx:annotation-driven />

This tx annotation tag does not name a specific transaction manager to use. So by convention Spring will use a bean called transactionManager to honour the transaction annotations of the service bean. We may wish to change the transaction manager and data source between running JUnit tests within an IDE and deployment to an application server. So these beans are separated out into a file called dataSourceContext.xml.

The final part of the spring configuration is the JPA EntityManagerFactory:

<bean id="entityManagerFactory"
	class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
	p:persistence-xml-location="classpath:META-INF/persistence.xml"
	p:data-source-ref="dataSource"
>
	<property name="jpaVendorAdapter">
	<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
	p:showSql="true"
	p:generateDdl="true">
	</bean>
	</property>
	<property name="jpaProperties">
	<value>
	hibernate.ejb.naming_strategy=org.hibernate.cfg.ImprovedNamingStrategy
	hibernate.dialect=
	hibernate.hbm2ddl.auto=
	</value>
	</property>
</bean>

<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />


The configuration defines a Hibernate JPA EntityManagerFactory that uses our dataSource bean defined within dataSourceContext.xml. The use of PersistenceAnnotationBeanPostProcessor provides the glue between our JPA entity manager and our BasicDao bean. Our BasicDao bean uses persistence annotations:

@Repository
public class BasicDao {

 private EntityManager entityManager;

 @PersistenceContext
 public void setEntityManager(EntityManager entityManager) {
	 this.entityManager = entityManager;
 }

 ...


The @PersistanceContext annotation will cause Spring to inject an EntiyManager taken from the EntityManagerFactory into our BasicDao bean.


The BasicDao class has been taken from the Loom framework source code along with the Spring+JPA configuration outlined above. The pagination support of the class has been removed as it was not required for this sample application. It is recommended that readers study the original class and surrounding packages which provide richer behaviour than was needed to implement zktodo2. The methods of the BasicDao that perform the list, add, update and delete operations are shown below. They delegate straight through to the EntityManager class obtained from the EntityManagerFactory configured above:

	// Called to list a objects of a given type within the database
	public <T> List<T> findAll(Class<T> clazz) {
		return (List<T>)find("from " + clazz.getName());
	}
	// Called from findAll above
	public <T> T find(Class<T> clazz, Serializable id) throws EntityNotFoundException {
		T result = entityManager.find(clazz, id);
		if (result == null) {
		throw new EntityNotFoundException(clazz, id);
		}
		return result;
	}
	// Called to delete an object from the database
	public <T> T remove(Class<T> clazz, Serializable id) throws EntityNotFoundException {
		T instance = find(clazz, id);
		entityManager.remove(instance);
		return instance;
	}
	// Called to add an object to the database
	public void persist(Object entity) {
		entityManager.persist(entity);
	}
	// Called to update an object within the database
	public <T> T merge(T entity) {
		return entityManager.merge(entity);
	}


Improving The Design With An Explicit Model Class

In the design discussed so far there is no explicit class representing the Model of the application. It is the set of Reminder domain objects referenced by the Controller class that represent the state of system. If the application is large with multiple screens presented to the user then it is a good ideal to have a focused Controller per window or page rather than use a single large Controller class. When multiple Controllers are used to fulfil overlapping Use Cases the design will be improved by creating an explicit Model class. Each Controller class can then work directly with the Model class which encapsulates the state and behaviour of the business layers of the system. The model class can utilize the service layer of the application to sync it's state with the database.

An explicit model type for the sample application is here with the implementation here. The interface is shown below:

package org.zkforge.zktodo2;

import java.util.List;

public interface ZkToDoModel {

	public abstract void deleteEvent(Reminder reminder)
			throws EntityNotFoundException;

	public abstract List<Reminder> findAll();

	public abstract void mergeEvent(Reminder reminder)
			throws EntityNotFoundException;

	public abstract void persistEvent(Reminder reminder);

	//used by selectedItem="@{controller.selectedReminder}" and others
	public abstract Reminder getSelectedReminder();

	//used by selectedItem="@{controller.selectedReminder}" and others
	public abstract void setSelectedReminder(Reminder reminder);

	//used by model="@{controller.reminders}"
	public abstract List<Reminder> getReminders();

}

Notice that the Model class holds the "selected reminder" which is the reminder that the current user is editing. Here are the spring bean configuration for the Model class which is passed into a simplified controller class that delegates to the Model class:

	<bean id="toDoModel" class="org.zkforge.zktodo2.ZkToDoModelImpl" 
			p:reminderService-ref="reminderService" scope="session"/>

	<bean id="toDoControllerV2" class="org.zkforge.zktodo2.ZkToDoControllerV2" 
		p:toDoModel-ref="toDoModel" scope="prototype"
	/>

Notice that the toDoModel is given "session" scope. This means that Spring will instantiate and bind a single bean into the users HttpSession and return that same bean each time that it is referenced. The toDoControllerV2 class has "prototype" scope which means that a new bean will be instantiated each time that ZK applies that bean onto to the ZUL user interface. As the Model holds the reference to the users "selected reminder" multiple screens with multiple Controllers can interact with the single selected reminder held by the Model.

Improving The Design With ZK Databindings

Databindings are declarative bindings. Within the application the JPA Annotations provide a declarative binding between our POJOs and the database tables. The ZK databindings feature provides declarative binding between POJOs and UI Components. A modified version of the ZUL screen which uses databindings is zktodo_b.zul. The markup for this revised screen is shown below:

<?xml version="1.0" encoding="UTF-8"?>
<zk xmlns="http://www.zkoss.org/2005/zul">
<?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" ?>
<?variable-resolver class="org.zkoss.zkplus.spring.DelegatingVariableResolver"?>
<window title="To do list" width="640px" border="normal" apply="${toDoControllerV2}">
<listbox id="list" multiple="true" rows="4" model="@{toDoModel.reminders}" selectedItem="@{toDoModel.selectedReminder}">
	<listhead>
		<listheader label="Item" />
		<listheader label="Priority" width="80px" />
		<listheader label="Opened" width="90px" />
	</listhead>
	<listitem self="@{each=reminder}">
		<listcell label="@{reminder.name}"/>
		<listcell label="@{reminder.priority}"/>
		<listcell label="@{reminder.date, converter='org.zkforge.zktodo2.DateFormatConverter'}"/>
	</listitem>
</listbox>
<vbox>
	<hbox>
	 Item:<textbox id="name" cols="40" constraint="no empty" value="@{toDoModel.selectedReminder.name}"/>
	 Priority:<intbox id="priority" cols="1" constraint="no empty" value="@{toDoModel.selectedReminder.priority}"/>
	 Date:<datebox id="date" cols="14" constraint="no empty" value="@{toDoModel.selectedReminder.date}"/>
	</hbox>
	<hbox>
		 <button id="add" label="Add" width="36px" height="24px"/>
		 <button id="update" label="Update" width="46px" height="24px"/>
		 <button id="delete" label="Delete" width="46px" height="24px"/>
	</hbox>
</vbox> 
</window>
</zk>

In the listing above the page has been given an "init" instruction referencing the AnnotateDataBinderInit class. Preprocessing the page with AnnotateDataBinderInit causes the zul properties containing "@{binding}" declarative bindings to be bound onto the application POJOs. The databindings for the Listbox initially reads from "@{toDoModel.reminders}" and writes to "@{toDoModel.selectedReminder}" when an item is selected from the list. ZK resolves these bindings to the methods "toDoModel.getReminders()" and "toDoModel.setSelectedReminder(...)". As there is no "toDoModel" ID defined within the page ZK will ask Spring for the bean with that name. Below the Listbox the selected reminder is rendered into the edit area using bindings such as "@{model.selectedReminder.name}". ZK resolves this to the "getName()" method of the selected reminder object. Collectively the databindings show how to render the initial state of the screen and how to write UI input from the edit area into the selected POJO.

With the databindings the controller no longer needs any logic to initialize the screen state. If we compare the original ZKToDoControllerV1 documented above with the newer ZkToDoControllerV2 we see that the new version is 30 lines of code smaller. This is because the is now no need to override the doAfterCompose method to initialize the page.

Summary

In this article we introduced the ZkToDo2 sample application. The application makes use of the ZK support class GenericForwardComposer which provides explicit support for the Model-View-Controller pattern. The user interface defined in the ZUL file was wired to the controller class of the application automatically by matching elements and IDs within the ZUL file to member variable names and types within the controller class.

Integration with the Spring framework was demonstrated using the ZK support class DelegatingVariableResolver. Spring was used to create application layering through configuration using its support for the Inversion-Of-Control pattern. Logical layers for the UI controller beans, a singleton service bean and a singleton data access bean were configured via Spring. These layers provide a clear separation of concerns between the classes of the application. Spring support for transaction management was used to configure the database transaction boundaries of the service bean. Spring support for JPA was used to connect an EntityManager to the data access bean that was injected into the service bean.

The ZkToDo2 sample application was then further enhanced with the introduction of an explicit Model class and ZK databindings. The introduction of an explicit model class provides for better encapsulation of the application business logic to facilitate code re-use between controllers. The introduction of ZK databindings removed much of the boiler plate code required to initialize the UI and update application POJOs with user input.

References

  • The ZK ToDo sample application on ZK Forge from which the screen for the ZKToDo2 sample code was derived
  • The ZK FusionInvoker allows you to bind more than one Controller object at a time onto your Page. So rather using a controller per screen (1-to-1), or a larger controller for multiple screens (1-to-many) you can now choose to bind multiple controllers onto a single composite screen (many-to-1). This flexibility lets you choose to use aggregation to re-use a set of of narrowly focused controllers between screens within a more complex user interface


Ruler.gif


The ZK DevGuide states "ZK doesn't enforce developers to use MVC or other design patterns. Whether to use them is the developer's choice"


Ruler.gif


Simon Massey is Technical Design Authority of a pan-european risk trading platform for Marsh (London, UK) that is built on top of ZK and other leading opensource technologies. He was previously Technical Lead on three production systems at Marsh that use ZK since Q3 2006. Not having enough of ZK in his day job he is also a developer of the ZKFoodToGo, zlora and zktodo2 projects for ZK.forge which is okay as his girlfriend debugs his public sample apps.




Copyright © Simon Massey. This article is licensed under GNU Free Documentation License.
You got stuck here?
Let us know why!
For questions please use the forum