A multi-tiered Java application with GWT

This article was originally published in VSJ, which is now part of Developer Fusion.
Most corporate data are still maintained in relational database servers that are accessible only over the internal Intranet. Providing secured, controlled, highly interactive, real-time mobile access to some of this back-office data is often in the dreams of many application system architects. You can fulfil this dream today with a little bit of ingenuity using standard, freely available Java-based technology. This article shows you how to expose back office relational data for access via mobile browsers using a combination of Google Web Toolkit (GWT), Asynchronous JavaScript and XML (AJAX), GWT-RPC (GWT-Remote Procedure Calls), Java RMI (Remote Method Invocation), the Spring framework, and Hibernate JPA (Java Persistence API). Together, these technologies enable you to construct a data pipeline from the back office to the mobile work-force that is modular, maintainable, and highly adaptive to changes in requirements. Some of the more difficult decisions that must be made when selecting technology for such pipelines are also discussed in the article. Working through the example, you’ll get hands-on experience shuttling data through three tiers of servers to browsers running on mobile devices.

Data flow through multiple tiers

The Google Web Toolkit gives Java developers an easy route to create interactive AJAX web applications. Using a specialized compiler to translate Java user interface code into JavaScript code, GWT allows developers to use the Java programming language for all parts of the application – from the user interface, through the business logic, down to the data access code. Figure 1 shows the typical architecture of a GWT based system.

Figure 1
Figure 1: A GWT based client server system

The system depicted in Figure 1 is described fully in the November 2007 VSJ article, Simple RPC with GWT. It is a web application that displays a list of contacts. If you click on one of the contacts, more details pops up as a fly-in details panel. The topology here is a simple client server configuration. The client is a mobile device running a browser that is JavaScript enabled; the server is a Tomcat server. The GWT code, compiled into JavaScript, handles the user interface and uses GWT-RPC to make asynchronous call to a servlet running in the Tomcat server. The contact information is hard coded within the servlet, and transferred to the calling JavaScript client over the network. Finally, the user interface code displays the contact information as a graphical list on the mobile device. While this illustrates how GWT-RPC can be used to fetch data asynchronously over the Internet, it is not indicative of typical deployment. Figure 2 shows an extension of the architecture into a more realistic multiple tier one.

Figure 2
Figure 2: A typical multi-tiered server system

A More realistic deployment

The system depicted in Figure 2 has the following interesting properties:
  • a back-office server running a business logic component
  • data is stored in a relational database in the back office
  • the servlet in this case is just a front-end proxy that manages the mobile user interfaces (potentially multiple users) and retrieves data from the business logic tier
  • for a larger system, you can increase the capacity of the system by independently adjusting the number of front-end servers, and/or the number of back office servers
  • there is no direct access to the data source possible from the mobile client, it can only access the subset of data presented by the business logic component
This scenario corresponds to system topology often found in production deployment. Figure 3 illustrates another view of the same topology, showing how the front-end Tomcat driver can sit between the Internet firewall and the internal Intranet router/firewall (firewalls are shown for reference only, this article does not cover configuration of firewalls); this area is sometimes referred to as the “no man’s land” in security configurations. Figure 3 also shows the protocols and technologies used in the various tier. The following is a summary:

Figure 3
Figure 3: A typical corporate deployment of multi-tiered server system

Users have mobile devices running a browser supporting JavaScript and AJAX. These browsers talk to the Tomcat server over the Internet, using the HTTP protocol. The code that is executed on the browser is loaded over the Internet as part of a web page. The code is JavaScript code, compiled from Java source code using GWT. This code presents the user interface, and also fetches data asynchronously over the Internet, via the Tomcat server. Data fetch is performed through the network using the GWT RPC mechanism.

On the Tomcat server, the server portion of the GWT RPC mechanism executes. This code runs as a standard Java EE Servlet on the Tomcat server. This code is at JDK 1.4 level because GWT currently only supports JDK 1.4 level. When an HTTP request arrives for contact information, the servlet makes calls to the business logic server in the back office for the actual contact list. This is done via standard RMI. Java 5’s RMI backwards compatibility enables the JDK 1.4 servlet to communicate over the network with the business logic server (running on Java 5). Of course, it is also possible to use other remoting technologies to communicate between the web tier and the business logic tier – such as Web Services or CORBA. However, the use of RMI keeps the details of interfacing straightforward and keeps us focused on the integration activities.

The business logic server itself is hosted in a Java 5 (or Java 6) virtual machine. It acts as an RMI-based server that accepts network requests from the servlet. The components running on this server communicate directly with a Relational Database Management System (RDBMS). Here, the RDBMS is a MySQL server in the back office data tier. In a production scenario, the business logic may request data from multiple data sources and may also process or manipulate the data before sending it as a response to the servlet. One can easily visualize a Java 2 Enterprise Edition (J2EE) server here hosting Enterprise Java Bean components (EJBs). Instead, to keep things componentized, lightweight, and maintainable, the Spring 2 framework is used (see the November 2006 VSJ article, Wiring your way to a maintainable future, for more information on the Spring 2 framework. More specifically, this code uses Spring 2’s lightweight container, its support for RMI remoting, and persistence support through Java Persistence API (JPA). The business logic component accesses the MySQL server via JDBC. However, the JDBC code is generated by Object to Relational Mapping (ORM) using the Hibernate JPA component. The Spring 2 container can support any JPA component implementation, our decision to use the Hibernate implementation is an arbitrary one. In production, you may need to use Oracle, BEA, IBM, or another vendor’s JPA depending on the server that your back office data resides on. All of the code in this tier is written in Java, and takes advantage of the support for annotation and generics found in Java 5 and beyond.

Now that you understand the architecture of the system that we are going to build, it is time to download all the required software.

Downloading the components

It is assumed that you have GWT 1.4 (or later) installed and running. If not, you need to download GWT.

There is no need to download Tomcat separately, as we will be using the embedded Tomcat that is included with the GWT distribution.

In addition, this article uses the Eclipse IDE to build and run the applications. The Europa edition of Eclipse (actually 3.3) is used for this article. Download the latest version of Eclipse, selecting the distribution for Java EE developer.

The Spring 2 Framework is used at the business logic layer. Download the latest revision of Spring 2 (make sure you download the distribution with all dependencies, it can save you time hunting for dependencies; Spring 2.0.7 is used in testing the code here).

The Hibernate JPA provider (consisting of Hibernate Annotation and Hibernate EntityManger) is included as a dependency of the Spring 2 download, and you do not have to download them separately. For more information on these components, see the Hibernate site.

The JavaAssist library is a dependency of the Hibernate provider, you can download the latest version (version 3.6 is used to test code in this article).

The relational database used here is MySQL. If you do not already have MySQL installed, download it.

The version used in this article is 5.0.27 of the Community Server. You will also need the JDBC driver, MySQL Connector/J. Version 5.1 of this driver is used in this article.

Of course, it is assumed that you have Java 6 installed and running. I used JDK 1.6 udpate 2; but any later version should work just fine.

Loading the projects into Eclipse

The source code distribution for this article includes two Eclipse projects:
  1. GWTFlyer – the GWT GUI and the front-end servlet
  2. ContactServer – the backend data fetching servlet
You can unzip the projects into a directory, then bring the projects into Eclipse using the menu selection: File –> Import –> Existing Projects into Workspace.

Next, you need add a list of dependent JARs to the Build Path of the projects. Right click on the project name, then select Build Path –> Configure Build Path.

Click the Libraries tab, and then the button Add External JARs… to add the required JARs.

The dependent JARs that you need for the GWTFlyer project is tabulated in Table 1.

Table 1: GWTFlyer dependent JARs
JAR library Origin of library
gwt-dev-windows.jar GWT distribution
gwt-user.jar GWT distribution

The ContactServer project depends on Spring 2, Hibernate, and the MySQL driver. The dependent JARs, all from the Spring 2 distribution unless otherwise indicated, that you need to add for the ContactServer project are shown in Table 2.

Table 2: ContactServer dependent JARs
JAR library Origin of library
spring.jar, spring-mock.jar dist
commons-collections.jar
commons-logging.jar
lib\jakarta-commons
cglib-nodep-2.1_3.jar lib\cglib
antlr-2.7.6.jar lib\antlr
jta.jar lib\j2ee
log4j.jar lib\log4j
junit.jar lib\junit
hibernate3.jar
hibernate-entitymanager.jar
hibernate-commons-annotations.jar
hibernate-entitymanager.jar
jboss-archive-browsing.jar
lib\hibernate
persistence.jar lib\jpa
jaxen-1.1-beta-7.jar, dom4j-1.6.1.jar lib\dom4j
javaassist.jar JavaAssist distribution
mysql-connector-java-5.1.5-bin.jar MySQL Connector-J distribution
You should be able to build the projects once these dependent libraries are added. Right click on the project name, and then select Build Project.

The projects include three Eclipse launch configurations to ease the execution and testing of the code. These launch configurations are described in Table 3.

Table 3: Launch configurations
Configuration Description
GWTFlyer Located in the GWTFlyer.launch file. Runs the GWTShell class and the embedded Tomcat server. This is the standard GWT host mode of operation, starts the GWT application is a hosted browser. This configuration starts both the browser and the servlet.
RunService Located in the RunService.launch file. This runs the class named ContactAccessService, which is a standalone RMI server. The hibernate.hbm2ddl.auto system property is set to validate in this configuration to prevent the ORM mapper from deleting the table being mapped.
SeedData Located in the SeedData.launch file. Starts the RunThisToSeedData class. This class creates the MySQL tables and seeds it with three rows of contact data. This code is kept simple through the use of JPA and Hibernate generated DDL. The hibernate.hbm2ddl.auto system property is set to create in order to create the required relational database tables.

To start any of these configurations, select from menu Run –>Open Run Dialog… Then look for the configurations on the left panel.

Invoking RMI from the GWT RPC Servlet

In the GWTFlyer project, the GWT RPC servlet class is named ContactManagerImpl. This class accepts asynchronous HTTP requests from the GWT code running on the mobile browser, and sends RMI requests to the business logic server for Contacts information. The code for ContactManagerImpl is shown here.
public class ContactManagerImpl
	extends RemoteServiceServlet implements
	ContactManager {
	public ContactManagerImpl() {
	}
	public Contact[] getContacts() {
		Contact[] retval = null;
		XContact[] contList = null;
		try {
			boolean needToReset = false;
			if (System.getSecurityManager()
				== null) {
				System.setSecurityManager(
					new CustomSecurityManager());
				needToReset = true;
			}
			RemoteContactAccess remote =
				(RemoteContactAccess)
				Naming.lookup(
"rmi://localhost:1199/ContactService");
			contList = remote.getContacts();
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		if (contList != null) {
			int contSize = contList.length;
			retval = new Contact[contSize];
			for (int i = 0;
				i < contList.length; i++) {
				XContact cnt = contList[i];
				retval[i] = new Contact(
					cnt.getFirstName(),
					cnt.getLastName(),
					cnt.getPhoneNumber(),
					cnt.getAddress1(),
					cnt.getAddress2(),
					cnt.getPhotoFile());
			}
		}
		return retval;
	}
}

A custom RMI security manager

In this code, the getContacts() method is called by the GWT RPC machinery when an async call arrives from the browser. Note that a CustomSecurityManager is installed before making the RMI call. This is a standard requirement for RMI clients – a SecurityManager must be installed. Normally, one would install a stringent instance of RMISecurityManger, and then explicitly tweak each individual access permission. If you just install an RMISecurityManager, however, the GWT RPC support runtime will throw security exceptions when introspecting for class information. To keep things simple for this article, a highly permissive CustomSecurityManager is created. You can find the code for this security manager in CustomSecurityManager.java. The purpose is to enable RMI client functionality without disabling the GWT RPC runtime support.

Using a data-exchange class for RMI

Another point to note in the ContactManagerImpl code is the use of a data-exchange class called XContact. Instead of using the Contact class in the client code, or the Contact class from the business logic server, a new data-exchange class is created containing essentially the same fields. This is done on purpose to decouple the requirements of GWT on the client’s version of the Contact class, from the Java 5 annotation requirements of JPA and Hibernate on the server version of Contact. The only tradeoff is the need to explicitly transfer data to and from XContact instances. This approach also enables the client and the server’s Contact class to evolve independently unencumbered by each other’s inheritance hierarchies. This benefit is evident when you see that the remote RMI method has no type dependency on client nor server code.

The RMI interface

The remotely invoked method is part of the uk.co.vsj.gwtjpa.service.RemoteContactAccess interface. This interface, along with the data-exchange XContact class, is defined and accessible from both projects. The following code is from RemoteContactAccess:
import java.rmi.Remote;
import java.rmi.RemoteException;
import uk.co.vsj.gwtjpa.xchange.XContact;
public interface RemoteContactAccess
	extends Remote {
	XContact [] getContacts()
		throws RemoteException;
}
To obtain a remote instance of RemoteContactAccess, the lookup is performed against the RMI registry. The URL used for lookup is “rmi://localhost:1199/ContactService”.

A RemoteContactAccess instance must be previously bound, in the RMI registry, to the name ContactService (this is done by the server code, you will see this shortly). If your business logic server is on another machine (or port), you need to modify this URL to reflect it.

The remote getContacts() method is called by ContactManagerImpl synchronously to fetch contact information from the RMI server. The implementation of getContacts() on the RMI server must fetch the data from the relational database and return them.

Fetching contacts using the remote method

In the ContactServer project, the implementation of the getContacts() method can be located in the uk.co.vsj.gwtjpa.service.ContactAccessService class and is shown here:
public class ContactAccessService
	implements RemoteContactAccess {
	private ContactRepository repo;
	public void setRepo(ContactRepository
		repo) {
		this.repo = repo;
	}
	public XContact[] getContacts()
		throws RemoteException {
		List <Contact> contList =
			repo.retrieveAllContacts();
		return
			convertToXContacts(contList);
	}
...
Counter to expectation, note how simple the code of getContacts() is. First, the convertToXContacts() method is a simple data-exchange method that copies the list of Contact to an array of XContact and returns them. This avoids two compatibility issues between JDK 1.4 and Java 5 classes:
  • the use of generics in contList – List <Contact>
  • the use of annotations in Contact
Secondly, the list of contacts is retrieved from MySQL using just a simple call to the contact repository:
List <Contact> contList =
	repo.retrieveAllContacts();
You can track the retieveAllContacts() method to the uk.co.vsj.gwtjpa.repos.ContactJpaRepository class. In this class, the method is implemented as:
public List<Contact>
	retrieveAllContacts() {
	Query q = em.createNamedQuery(
		"allContacts");
	return q.getResultList();
}
You may question where repo, the instance of ContactJpaRepository comes from. Actually, it is nowhere to be found in the code. This is because the instance is created by the Spring 2 container and injected into the ContactAccessService bean. You will see this Spring “wiring” a little later when the Spring configuration file is examined.

JPA annotations to persistence-enable a contact

A JPA named query is used in the retieveAllContacts() method to obtain all contacts in the database. Of course, in a production scenario, you’ll need to limit the number of contacts that are returned.

To see the actual named query, take a look at the uk.co.vsj.gwtjpa.pojos.Contact class itself. It is partially reproduced in the following listing, showing the named query and the JPA entity annotations:

package uk.co.vsj.gwtjpa.pojos;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
@Entity
@NamedQueries( {
@NamedQuery(name = "getByLastname",
	query="select a from Contact a where
	a.lastName = ?1"),
@NamedQuery(name = "allContacts",
	query = "select a from Contact a")
})
public class Contact {
	@Id
	@Column(insertable=false,
		updatable = false)
	@GeneratedValue
	private int id;
	private String firstName;
	private String lastName;
	private String phoneNumber;
	private String address1;
	private String address2;
	private String photoFile;
	public Contact(String firstName, String
		lastName, String phoneNumber,
		String address1, String address2,
		String photoFile) {
		super();
		this.firstName = firstName;
		this.lastName = lastName;
		this.phoneNumber = phoneNumber;
		this.address1 = address1;
		this.address2 = address2;
		this.photoFile = photoFile;
	}
	public String getFirstName() {
		return firstName;
	}
...
}
The red annotations in the preceding code show the actual object query that is performed. This query is written in Java Persistence Query Langauge (JPQL). For more information on how to use JPQL, check out the persistence document of the JSR-220 Enterprise Java Bean 3.0 specifications.

The green annotations in the preceding code tell the JPA provider how to persist a Contact POJO. In fact, when the above JPA annotation is translated into MySQL data definition language (DDL) statements, the following relational database schema will be created:

CREATE TABLE vsjdb.contact (
	id int(11) NOT NULL auto_increment,
	address1 varchar(255) default NULL,
	address2 varchar(255) default NULL,
	firstName varchar(255) default NULL,
	lastName varchar(255) default NULL,
	phoneNumber varchar(255) default NULL,
	photoFile varchar(255) default NULL,
	PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Of course, you would want to tune the length of the fields generated in a production environment, and can do that with additional JPA annotations. See the JSR-220 persistence specification or the VSJ Spring/JPA article (November 2006) for more details of the options.

Wiring the beans together with Spring 2

I mentioned earlier that an instance of uk.co.vsj.gwtjpa.repos.ContactJpaRepository is injected into the ContactAccessService by the Spring 2 container. Now let’s take a look at the configuration file. You can find the file named context.xml in the META-INF directory of the ContactServer project. The following is the listing of context.xml:
<?xml version="1.0" encoding="UTF-8"?>
	<beans xmlns="http://www.
		springframework.org/schema/beans"
		xmlns:xsi="http://www.w3.org/
		2001/XMLSchema-instance"
		xmlns:aop="http://www.
		springframework.org/schema/aop"
		xmlns:p="http://www.
		springframework.org/schema/p"
		xmlns:tx="http://www.
		springframework.org/schema/tx"
		xsi:schemaLocation="http://www.
		springframework.org/schema/beans
		http://www.springframework.org/
		schema/beans/spring-beans-2.0.xsd
		http://www.springframework.org/
		schema/tx
		http://www.springframework.org/
		schema/tx/spring-tx-2.0.xsd
		http://www.springframework.org/
		schema/aop
		http://www.springframework.org/
		schema/aop/spring-aop-2.0.xsd"
	>
	<tx:annotation-driven />
	<bean class="org.springframework.
		dao.annotation.
PersistenceExceptionTranslationPostProcessor" />
	<bean class="org.springframework.orm.
		jpa.support.
PersistenceAnnotationBeanPostProcessor" />
	<bean id="entityManagerFactory"
	class="org.springframework.orm.jpa.
LocalContainerEntityManagerFactoryBean">
		<property name="dataSource"
			ref="dataSource" />
		<property name="jpaVendorAdapter">
			<bean
		class="org.springframework.orm.jpa.
			vendor.HibernateJpaVendorAdapter">
				<property name="database"
					value="MYSQL" />
			</bean>
		</property>
	</bean>
	<bean id="dataSource" class="org.
		springframework.jdbc.datasource.
		DriverManagerDataSource">
		<property name="driverClassName"
				value="com.mysql.jdbc.Driver" />
		<property name="url"
value="jdbc:mysql://localhost/vsjdb" />
		<property name="username"
			value="dbuser" />
		<property name="password"
			value="pass" />
	</bean>
	<bean id="transactionManager"
		class="org.springframework.orm.
		jpa.JpaTransactionManager">
		<property
			name="entityManagerFactory"
			ref="entityManagerFactory" />
		<property name="dataSource"
			ref="dataSource" />
	</bean>
	<bean id="contactRepo"
		class="uk.co.vsj.gwtjpa.repos.
		ContactJpaRepository" />
	<bean id="contactAccessService"
		class="uk.co.vsj.gwtjpa.service.
		ContactAccessService">
		<property name="repo"
			ref="contactRepo"/>
	</bean>
	<bean id="dataSeeder"
		class="uk.co.vsj.gwtjpa.service.
		RunThisToSeedData">
		<property name="repo"
			ref="contactRepo"/>
	</bean>
	<bean class="org.springframework.
		remoting.rmi.RmiServiceExporter">
		<property name="serviceName"
			value="ContactService"/>
		<property name="service"
			ref="contactAccessService"/>
		<property name="serviceInterface"
			value="uk.co.vsj.gwtjpa.service.
			RemoteContactAccess"/>
		<property name="registryPort"
			value="1199"/>
	</bean>
</beans>
In the preceding Spring 2 context configuration file, the green code shows how the Hibernate JPA entity manager is configured to use a MySQL data source. In this case, it is assumed that the table is created in a database named vsjdb, and the user dbuser with password pass has full access to the database.

The red code shows how an instance of the ContactJpaRepository bean is created (named contactRepo). Then an instance of ContactAccessService is created (named contactAccessService), and its repo property is wired with contactRepo. This tells the Spring 2 container to use setter injection (calling the setRepo() method) to wire the contactRepo bean into the ContactAccessService.

The blue code creates an instance of the Spring 2 RMI remoting RMIServiceExporter bean. This creates binds the contactAccessService bean to the RMI registry as “ContactService”. The registry is configured to run on port 1199. With this configuration, the business logic server is fully configured, and the service can be accessed at the URL “rmi://localhost:1199/ContactService”.

The main() method of the ContactAccessService class loads the Spring 2 configuration file via the ClassPathXmlApplicationContext class in the Spring API, and explicitly obtains an instance of the contactAccessService.

Testing the system

To test the system, you need to do the following:
  1. Make sure the MySQL server is installed and running, the vsjdb database must be available and accessible by the user named dbuser
  2. In Eclipse, seed data by running the SeedData configuration (select menu Run->Open Run Dialog…, then select SeedData on the left and click the Run button)
  3. In Eclipse, start the business logic server by running the RunService configuration
  4. Finally run the GWTFlyer configuration in Eclipse
You should see the GWTFlyer user interface displayed on the GWT hosted mode browser. You can also cut the URL in the GWT browser and paste it into a Firefox browser to run the application in Firefox. Figure 4 shows Firefox running the user interface, displaying the data fetched from the MySQL database via the business logic server and the Tomcat GWT-RPC server.

Figure 4
Figure 4: GWT user interface running in the Firefox browser

Conclusions

GWT together with GWT-RPC is great for creating interactive web-based user interfaces and data retrieval mechanisms over the Internet. This architecture can be extended to access business logic and data stored in back office servers. In this article, the GWT-RPC server is extended to access a business logic server via RMI remoting. The business logic server itself accesses backend MySQL relational database data with the help of the Spring framework and a JPA $provider from Hibernate. Using these widely available open source technologies, quickly creating modular and flexible multi-tiered data systems need no longer be a dream.


Sing Li has been writing software, and writing about software, for twenty plus years. His specialties include scalable distributed computing systems and peer-to-peer technologies. He now spends a lot of time working with open source Java technologies. Sing’s most recent publications include Professional Geronimo and Professional Apache Tomcat from Wrox Press.

You might also like...

Comments

About the author

Sing Li United States

Sing Li has been writing software, and writing about software for twenty plus years. His specialities include scalable distributed computing systems and peer-to-peer technologies. He now spends ...

Interested in writing for us? Find out more.

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“UNIX is basically a simple operating system, but you have to be a genius to understand the simplicity.” - Dennis Ritchie