Skip to main content

Generate Ajax J2EE Web applications with jpa2web

Reuse JPA annotations and create Web user interfaces

Maximo Gurmendez (mgurmendez@users.sourceforge.net), Software Engineer, Freelance Consultant
Maximo Gurmendez
Maximo Gurmendez graduated from the Facultad de Ingenieria, Montevideo, Uruguay. He is a software engineer and a Sun Certified Professional in Java Technologies. Recently, he has been working as a software architect for a large educational institution in Uruguay designing learning platforms and academic management intranets. His research interests include developing groupware and collaborative Web technologies. In addition to authoring jpa2web, Maximo has contributed to other open source initiatives such as MofEditor, CwmPlaza, and Ecademicus Webs. He also enjoys working as a musical director staging musical plays in Montevideo. Maximo has been selected as a Fulbright scholar in 2008 and will pursue a graduate degree in the U.S. in 2008.

Summary:  Learn about, try, and contribute to a new open source tool — jpa2web — which generates J2EE Ajax-based Web applications from JPA-annotated beans. Using the ZK framework, the applications generated by this tool allow your users to add, delete, search, modify, and interconnect instances of database-synchronized objects in a friendly, Ajax-based Web user interface.

Date:  15 Jan 2008
Level:  Intermediate
Activity:  3674 views

What is jpa2web?

Readily available tools like Hibernate (see Resources) have dramatically reduced the impedance created between Java objects and their database storage; specially, the ease with which Java classes can now be annotated to specify the way objects should be persisted. Developers are freed from the onerous task of writing up database integration codes. Hibernate solves the persistence issue; however, Web pages need to be created to handle these elements. A typical scenario for a medium-sized Web application can proceed something like this: The developer starts by coding the Plain Old Java Objects (POJOs) that represent a particular domain model, and then proceeds to create the different transactions and the Web user interface. A subset of the elements of the model will frequently involve non-transactional data. Customers, clients, countries, locations, employees, and companies are typical elements of a business model that are maintained by a few operators.

Why not generate the Web presentation layer to create, add, list, delete, and search these elements from the annotated beans? And why not make this presentation a friendly Ajax experience? These are the main goals of the jpa2web tool, which has the following process flow:

  • Input: POJO-annotated beans (and optional templates).
  • Output: An Ajax Web application to handle and persist the elements of the model.
  • Technologies used: FreeMarker + ZK + Hibernate (see Resources for links to more information on these technologies).

About ZK

ZK is an open source, Ajax Web framework used to create a rich user interface for Web applications, with little programming and no JavaScript code necessary. With ZK, you can design Web applications much as if they were desktop applications. ZK takes care of the client and server Ajax processing. All that needs to be done is to specify the user interface by creating simple XML files (called zul files) and scripting the event handlers in the language of choice: Java code (compiled), Bean Shell (interpreted Java), Groovy, Ruby, JavaScript, and some others.

The tool uses annotation-driven programming mainly to specify ORM mappings. Many of these annotations can be reused to define the Web interface or to create modifiable prototypes.

The following sections illustrate how beans of different complexities can be mapped to an Ajax Web user interface using jpa2web. Next, you'll get an outline of how the jpa2web algorithm works and some basic instructions to get it working. Finally, the article concludes with a description of the applicability of jpa2web and future enhancements to the tool.

A simple example

A sample domain model is used throughout this article, which might be familiar to some readers. It consists of an adaptation of the sample domain model used in the great book Enterprise JavaBeans, 3.0 by Bill Burke and Richard Monson-Haefel (see Resources for a link). This domain model contains a ship management class Ship.java (see Listing 1), which is the simplest kind of POJO: a class with primitive members.


Listing 1. Ship.java
	  
package com.titan.domain;
import javax.persistence.*;

@Entity
public class Ship implements java.io.Serializable
{
   private int id;
   private String name;
   private double tonnage;

   @Id @GeneratedValue
   public int getId() { return id; }
   public void setId(int id) { this.id = id; }

   public String getName() { return name; }
   public void setName(String name) { this.name = name; }

   public double getTonnage() { return tonnage; }
   public void setTonnage(double tonnage) { this.tonnage = tonnage; }
   
   public String toString() { return name;   }
}
  

If you want to manipulate instances of Ship.java in sync with a database, you need basically two kind of user interfaces: one to add (or edit) ships, and another to list them. The first case will look like a form used to enter the details of the ship (name and tonnage). The second interface will be a listing of existing ships with fields for the user to select and edit (see Figure 1):


Figure 1. Ship form
Ship Form

Figure 1 shows how the form generated by jpa2web looks. Note the three available icons: one to create a new ship, one to list all ships (see Figure 2) and one to remove a persisted ship. The OK button will persist the ship in the database. Observe that because the ID field is a GeneratedValue, it appears as disabled.

Figure 2 shows the second generated Web page that lists existing ships. The user can click on a ship to make the previous form for editing the ship's details appear.


Figure 2. Ship listing
Ship Listing

Classes referencing other classes

A more complex case arises when objects reference other objects from a different class. This is the case with Cabin.java, which has a ManyToOne relationship with Ship.java, as well as simple attributes (see Listing 2):


Listing 2. Cabin.java
	  
package com.titan.domain;
import javax.persistence.*;

@Entity
public class Cabin implements java.io.Serializable
{
   private int id;
   private String name;
   private int bedCount;
   private int deckLevel;
   private Ship ship;

   @Id @GeneratedValue
   public int getId() { return id; }
   public void setId(int id) { this.id = id; }
 
   public String getName() { return name; }
   public void setName(String name) { this.name = name; }

   public int getBedCount() { return bedCount; }
   public void setBedCount(int count) { this.bedCount = count; }

   public int getDeckLevel() { return deckLevel; }
   public void setDeckLevel(int level) { this.deckLevel = level; }

   @ManyToOne
   public Ship getShip() { return ship; }
   public void setShip(Ship ship) { this.ship = ship; }
   
   public String toString() {
                  return name;
   }
}

  

The jpa2web tool generates a form similar to that of Ship.java, but with a way to connect a given cabin to a particular ship. This is done by a button that opens a modal for the user to select the desired ship. Figure 3 shows the generated form and Figure 4 shows how the modal dialog opens while selecting the ship. ZK's ease with handling modal dialogs makes it a really convenient technology to use.


Figure 3. Cabin form
Cabin Form

Figure 4. Choosing the ship
Choosing the ship

Classes with higher multiplicities

Let's look at a more complex example now. What about classes that have different multiplicities? This is the case of the Customer.java class (see Listing 3), which simultaneously holds OneToOne, ManyToMany, and OneToMany relationships with other classes (namely CreditCard, Phone, and Reservation, respectively).


Listing 3. Customer.java
	  
package com.titan.domain;
import javax.persistence.*;
@Entity
public class Customer implements java.io.Serializable
{
   private int id;
   private String firstName;
   private String lastName;
   private boolean hasGoodCredit;

   private Address address;
   private Collection<Phone> phoneNumbers = new ArrayList<Phone>();
   private CreditCard creditCard;
   private Collection<Reservation> reservations = 
     new ArrayList<Reservation>();

   @Id @GeneratedValue
   public int getId() { return id; }
   public void setId(int id) { this.id = id; }

   public String getFirstName() { return firstName; }
   public void setFirstName(String firstName) { this.firstName = firstName; }

   public String getLastName() { return lastName; }
   public void setLastName(String lastName) { this.lastName = lastName; }

   public boolean getHasGoodCredit() { return hasGoodCredit; }
   public void setHasGoodCredit(boolean flag) { hasGoodCredit = flag; }

   @OneToOne(cascade={CascadeType.ALL})
   public Address getAddress() { return address; }
   public void setAddress(Address address) { this.address = address; }

   @OneToOne(cascade={CascadeType.ALL})
   @IndexColumn(name="INDEX_COL_CC")
   public CreditCard getCreditCard() { return creditCard; }
   public void setCreditCard(CreditCard card) { creditCard = card; }

   @OneToMany(cascade={CascadeType.ALL},fetch=FetchType.EAGER)
   @IndexColumn(name="INDEX_COL_PHON")
   public Collection<Phone>getPhoneNumbers() { return phoneNumbers; }
   public void setPhoneNumbers(Collection<Phone> phones) { 
       this.phoneNumbers = phones; 
   }

   @ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.EAGER)
   @IndexColumn(name="INDEX_COL_CUS")
   public Collection<Reservation> getReservations() { return reservations; }
   public void setReservations(Collection<Reservation> reservations) { 
       this.reservations = reservations; 
   }
   
   public String toString() {
                  return getLastName()+","+getFirstName();
   }
}
  

Figure 5 shows how the class in Listing 3 is rendered. This page has different areas, which we will examine individually.


Figure 5. Customer form
Customer Form

Let's take a look at the how the CreditCard relationship is rendered.

Customer-CreditCard: OneToOne

This case is similar to the one seen in the previous section with Cabin.java and Ship.java (ManyToOne), wherein a button can open a modal form. However, because the relationship in the case of Customer-CreditCard is OneToOne, opening a dialog to select an existing instance will not be suitable because each credit card number is exclusively associated to a particular customer. Instead, a CreditCard form dialog will open where the complete details of a new credit card are entered (see Figure 6):


Figure 6. Completing the CreditCard details
Completing the Credit Card details

Customer-Phone: OneToMany

Customer-Phone generates a grid where the user enters new phone number details. Pressing the Add Row button creates a new row to enter a new phone number.

Customer-Reservation: ManyToMany

The Add.. button adds a new reservation to the list box collecting the selected reservations with a chooser modal dialog. Figure 7 shows the completed form with sample phone numbers and reservations added:


Figure 7. Completing the customer phone details
Completing the customer phone details

Decorating the windows

At this point, you might not like the look of the generated windows. Two main problems are that the field labels appear as Java property names, and the order in which they appear looks messy. You can address these appearance problems with the jpa2web tool's custom annotations. Adding the MemberField annotations to the Cabin class (Listing 4), for instance, will render a better look to the form (see Figure 8). Compare the decorated version with the raw one shown in Figure 3. The titles now include meaningful names, and the order of the fields now makes more sense.


Figure 8. Decorated cabin form
Decorated Cabin Form

Listing 4. Decorating annotations in Cabin.java
	
@Entity
public class Cabin implements java.io.Serializable
{
   @Id @GeneratedValue @MemberField(order=1,showName="ID")
   public int getId() { return id; }
 
   public String getName() { return name; } @MemberField(order=2,showName="Cabin Name")

   @MemberField(order=2,showName="Number of Beds")
   public int getBedCount() { return bedCount; }

   @MemberField(order=3,showName="Deck Level")
   public int getDeckLevel() { return deckLevel; }

   @ManyToOne @MemberField(showName="Ship")
   public Ship getShip() { return ship; }
   
....
}

Other decorating features include the ability to generate combo boxes instead of text boxes when there is a series of predefined values a given field can take. This can also be done using the MemberField annotation. Many other annotations can be defined to customize the look and feel of the generated windows.

Download jpa2web from sourceforge

The jpa2web application in action

Now that you understand the characteristics of the jpa2web application, it's time to take a look at it in action. Follow these steps to get the software working with Apache Tomcat:

  1. Download jpa2web from Sourceforge.
  2. Extract the distribution to a folder on your disk (referred to as [dir] from now on).
  3. Download Tomcat from the Apache site and install it on a folder (referred to as [tomcatdir] from now on).
  4. Copy the JAR files from [dir]/zklibs and [dir]/jboss_libs to [tomcatdir]/lib.
  5. Download your preferred JDBC driver and copy the .jar file into [tomcatdir]/lib.
  6. Write your domain model source files (JPA-annotated beans) under the [dir]/modelsrc folder.
  7. Modify the [dir]/templates/hibernate-cfg.xml file to connect to the database (see the following code for an example):

<hibernate-configuration>
<session-factory name="thefactory">
	<property name="hibernate.connection.driver_class">
		net.sourceforge.jtds.jdbc.Driver
	</property>
	<property name="hibernate.dialect">
		org.hibernate.dialect.SQLServerDialect
	</property>
	<property name="hibernate.hbm2ddl.auto">update</property>
	<property name="hibernate.connection.url">
		jdbc:jtds:sqlserver://127.0.0.1:1433/test</property>
	<property name="hibernate.connection.username">sa</property>
	<property name="hibernate.connection.password">*</property>
	<property name="hibernate.max_fetch_depth">0</property>
	<#list windows as win><mapping class="${win.mainClass.name}" />
	</#list>
</session-factory>
</hibernate-configuration>

  1. Modify the [rootdir]/jpa2web-loader.xml file to contain the packages of files you are interested in generating.
  2. Modify the paths to Tomcat in the build-tomcat.xml ANT file and execute the default target: $ ant -buildfile build-tomcat.xml .
  3. Ready! A WAR folder is generated with the Web application in the [dir]/dist folder and is also copied to the webapps dir of Tomcat. You can access the application at: http://localhost:8080/sampleapp.war (or similar).

The generator process

Although it's beyond the scope of this article to explain in detail how the jpa2web generator works, here is a summary of the main ideas and method behind the generator engine.

The input is simple: a series of JPA-annotated classes. These source files are read and an internal representation — called a loader — is built of the beans and the relationships among them. In particular, jpa2web only implements an EntityLoader (which reads JPA annotations), but other loaders could be implemented (for example, to read from XML-mapping configuration files). This structure is then used to feed the generator, which also takes as input a series of FreeMarker templates (see Resources for more information on FreeMarker templates). The use of templates makes jpa2web very flexible in the way the windows will be generated. Figure 9 shows the stages of the process. The output of the generator is a WAR folder with a working Web application containing a window for each of the annotated beans found, as well as an index to choose among them.


Figure 9. Generation process
Generation Process

The simplified pseudo-code for the generator is as follows:


The generator algorithm
	
for each bean in the model:
  -create a form window called fully.classified.className.zul:
    for each simple field
      if (field is String) render textbox
      if (field is int/byte/short) render integerbox
      if (field is boolean) render checkbox
      if (field is double/float) render doublebox/floatbox
    for each OneToOne member:
      render a button that will open a form to create an instance of the destiny class
    for each ManyToOne member:
      render a button that will open a list to choose an instance of the destiny class
    for each OneToMany member:
      render a table with the details of the destiny class and buttons
         to add and delete rows
    for each ManyToMany member:
      render a listbox with the elements mapped, 
      and a button that will open a list to choose an instance of the destiny class
  -create a window called list.fully.classified.className.zul 
  which lists all of the instances
  -add the annotated class to the mapping area of hibernate.cfg.xml
  -add links to a menu bar with the two windows generated for this bean


The above algorithm is just one way the mapping can be made, there are various other alternatives and different solutions will work in different scenarios. The generation process itself could be adapted at runtime with further annotations.

Conclusions

Several other tools are available that generate Web application code based on some kind of input, and many are complete implementations of model-driven architecture (MDA). The jpa2web tool should be considered as a lightweight annotation-driven code generation tool for developers rather than an complete implementation of MDA.

Probably the most popular tool for generating Web application code based on user input is AndroMDA, which can generate applications that use different frameworks such as Struts, JSR, and Spring. AndroMDA receives the model as an input (typically in UML/XMI format) and generates the application given some basic directions.

Developers must deal with several issues to develop a generator that could be used in larger deployments. Models with complex directional graphs formed from the class model will obviously not be supported by jpa2web, especially if complex circular references exist. At the time of this writing, jpa2web does not support OneToMany and ManyToMany inside a OneToMany relationship (although this implementation is viable and could be included in the jpa2web roadmap). Nevertheless, further research must be done on how to generate the windows when an intricate model is presented. Probably the most evident situation where jpa2web would not be suitable is when creating Web applications that are more transactional in nature and where user interfaces do not map directly to the class model.

Regardless of its limitations, jpa2web is a useful tool in many scenarios. You can use it to quickly generate the Web interfaces for non-transactional elements just by having the annotated beans. You can also use it for testing by creating the necessary instances of objects in a database, thus avoiding verbose entity creation scripts. The output of jpa2web must be seen as a developing prototype rather than a final application. The generated code is relatively simple and modifying its behavior, as well as adding validations and extra processing, is easily understood. For larger-scale applications, however, the applicability of jpa2web is more limited in terms of transactions, security, concurrency, and multitier restrictions, but it can be integrated with a bit of extra work.

Future planned enhancements include supporting more complex domain models in terms of the interconnection between the elements, adding custom annotations to better specify the generated user interface, and the possibility of generating code from other Ajax toolkits such as Google's GWT, OpenLaszo, or Echo2. The input of the generator could also be something to extend, and maybe an XMI file could be used to enter a model. If you're interested in extending jpa2web or further exploring its capabilities, feel free to contact the author.


Resources

Learn

Get products and technologies

Discuss

About the author

Maximo Gurmendez

Maximo Gurmendez graduated from the Facultad de Ingenieria, Montevideo, Uruguay. He is a software engineer and a Sun Certified Professional in Java Technologies. Recently, he has been working as a software architect for a large educational institution in Uruguay designing learning platforms and academic management intranets. His research interests include developing groupware and collaborative Web technologies. In addition to authoring jpa2web, Maximo has contributed to other open source initiatives such as MofEditor, CwmPlaza, and Ecademicus Webs. He also enjoys working as a musical director staging musical plays in Montevideo. Maximo has been selected as a Fulbright scholar in 2008 and will pursue a graduate degree in the U.S. in 2008.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development, Java technology, XML
ArticleID=281678
ArticleTitle=Generate Ajax J2EE Web applications with jpa2web
publish-date=01152008
author1-email=mgurmendez@users.sourceforge.net
author1-email-cc=ruterbo@us.ibm.com

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Special offers