Printable Version Printable Version

Developing GWT-RPC application with a Database support

Jun 25th, 2010 | By admin | Category: GWT, Web Technologies

This post is a step-by-step guide to build an application using GWT-RPC . Just to make it more realistic , I have used a back-end database to store the records. I have used MySql as a database and Tomcat 6.0 as a deployment web server. If you are interested to have a look at the code and then the post , it can be downloaded from here. Let me start with a small but specific description of GWT-RPC.

What is GWT-RPC ?

Google Web Tool-kit (GWT) provides couple of different ways to communicate with the server using HTTP. GWT-RPC is a framework to make a transparent server-side call where GWT takes care of all other low level details , like , object serialization etc.
When your application running in the browser , it needs to interact with the server using a Remote Procedure Call (RPC).GWT provides an RPC mechanism based on Java Servlets to provide access to server side. GWT uses deferred binding to serialize objects across the network. You can read more on GWT-RPC from here.

The application : User Creation Application

The funny thing is , My application creates User alone. Yes , it doesn’t do anything else other than creating user and storing them to the database. But , the important thing is , it uses GWT-RPC and gives enough scope of learning to build an application using GWT-RPC or GWT. My create user screen look like ,


Create User Screen

Create User Screen

Setup details :

  • Launch your favorite Java IDE. I have used eclipse which can be downloaded from here. If you are not so comfortable with eclipse , you can learn it from here.
  • Configure the GWT plug-in for eclipse. Learn how , here.
  • Download the project from here and extract into a folder of your choice.
  • Download the MySQL Connector/J and copy the jar to the /war/WEB-INF/lib directory.
  • Import the extracted project to your eclipse.

Once the project is imported to eclipse , the project structure should be like following :


Project Directory Structure

Project Directory Structure

Now we are all set to go. GWT-RPC uses servlets to communicate with the server-side. GWT provides a servlet called , RemoteServiceServlet. This is a base servlet class for your RPC service implementations. It helps you to automatically deserializes incoming requests from the client and serializes outgoing responses for client/server RPCs.

You should do the following to set your GWT-RPC interface ,

  • Create a service interface – Create an interface for your service that extends RemoteService and lists all RPC methods.
  • Create a service implementation – Create a class to implement the server-side logic. The class should extends RemoteServiceServlet and implements the interface you created in the above step.
  • Create an asynchronous interface to your service to be called from the client-side code.

Create a service interface :
Our Create User program requires at least two service methods . One is to check if the user is already exist in the database and other one is to Save the User into the databse.

import com.etechguide.project.gwtrpcdb.client.exception.UserException;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

/**
 * The client side stub for the RPC service.
 */
@RemoteServiceRelativePath("user")
public interface UserService extends RemoteService {
	 String createUser(User user) throws UserException;
	 boolean checkUserExist(String userName) throws UserException;

}

I have defined both the methods in my service interface. The service interface is annotated with the annotation , @RemoteServiceRelativePath(”user”). This annotation associates a RemoteService with the relative path(/user). we will use this relative path(/user) in the web.xml file while defining the servlet.

Create a service implementation :
This class should extends RemoteServiceServlet and implements the UserService interface . As this implementation class extends RemoteServiceServlet , it is a servlet class by itself.

/**
 * The server side implementation of the RPC service.
 */
@SuppressWarnings("serial")
public class UserServiceImpl extends RemoteServiceServlet implements
		UserService {

	UserDao userDao = new UserDao();
	@Override
	public String createUser(User user) throws UserException {
		String retVal = "true";
		if (!Validator.isValidName(user.getUserName())) {
			throw new UserException(
					"User name must be at least 5 characters long");
		}
		try {
			userDao.createUser(user);
		} catch (ClassNotFoundException e) {
			retVal = "false";
			throw new UserException(e.getLocalizedMessage());
		} catch (SQLException e) {
			retVal = "false";
			throw new UserException(e.getLocalizedMessage());
		}

		return retVal;
	}

	@Override
	public boolean checkUserExist(String userName) throws UserException {
		return userDao.checkUserExist(userName);
	}
}

The implementation class , UserServiceImpl uses the Data Access Object (DAO) to call the data base related methods. The DAO (UserDao.java) would look like ,

public class UserDao {

	Connection connection = null;
	Statement stmt = null;

	public void createUser(User user) throws ClassNotFoundException,
                     SQLException{
		String query = null;
		StringBuilder queryBuilder = null;
		queryBuilder = new StringBuilder();
		queryBuilder.append("INSERT INTO user(userName,password,");
		queryBuilder.append("emailId,address)");
		queryBuilder.append(" VALUES( ");
		queryBuilder.append("'"+user.getUserName()+"'");
		queryBuilder.append(",");
		queryBuilder.append("'"+user.getPassword()+"'");
		queryBuilder.append(",");
		queryBuilder.append("'"+user.getEmailId()+"'");
		queryBuilder.append(",");
		queryBuilder.append("'"+user.getAddress()+"'");
		queryBuilder.append(" )");

		query = queryBuilder.toString();
		System.out.println("query from createUser = " + query);
		System.out.println("user to be created ... " + user.toString());
		connection = DbConnection.createConnection();
		stmt = connection.createStatement();
		stmt.executeUpdate(query);
		DbConnection.closeConnection(connection, stmt);
	}

	public boolean checkUserExist(String userName) throws UserException {
		boolean isExist = false;
		String query = null;
		StringBuilder queryBuilder = null;
		ResultSet rs = null;
	    int rowCount = -1;
		queryBuilder = new StringBuilder();
		queryBuilder.append("SELECT count(*) FROM user WHERE userName= '");
		queryBuilder.append(userName);
		queryBuilder.append("'");
		query = queryBuilder.toString();
		System.out.println("query from checkUserExist = " + query);
		try {
			connection = DbConnection.createConnection();
			stmt = connection.createStatement();
			rs = stmt.executeQuery(query);
			rs.next();
			rowCount = rs.getInt(1);
			if(rowCount > 0){
				isExist = true;
			}
		} catch (ClassNotFoundException e) {
			throw new UserException(e.getLocalizedMessage());
		} catch (SQLException e) {
			throw new UserException(e.getLocalizedMessage());
		}finally{
			try {
				DbConnection.closeConnection(connection, stmt,rs);
			} catch (SQLException e) {
				throw new UserException(e.getLocalizedMessage());
			}
		}
		return isExist;
	}

}

So , as of now the story is , you have User Service and the Implementation of that. The Implementation is nothing but a servlet. We call all the database related methods from the implementation class. As it is a servlet and we are building a web application , we must declare this servlet in the web.xml file which is the main deployment descriptor file for any web application. The web.xml file is like ,

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>

  <!-- Servlets -->
  <servlet>
    <servlet-name>userServlet</servlet-name>
    <servlet-class>
    	com.etechguide.project.gwtrpcdb.server.UserServiceImpl
    </servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>userServlet</servlet-name>
    <url-pattern>/gwtrpcdb/user</url-pattern>
  </servlet-mapping>

  <!-- Default page to serve -->
  <welcome-file-list>
    <welcome-file>GwtRpcDB.html</welcome-file>
  </welcome-file-list>

</web-app>

Look at the url-pattern , it’s the MODULE_NAME/user which we have already specified with RemoteServiceRelativePath in the User Service. Now it is the time to write the asynchronous interface so that the client can use the services created by us.

Create an asynchronous interface :

import com.google.gwt.user.client.rpc.AsyncCallback;

public interface UserServiceAsync {
	void createUser(User user, AsyncCallback<String> callback)
			throws IllegalArgumentException;

	void checkUserExist(String userName, AsyncCallback<Boolean> callback);
}

You should follow certain rules while creating the asynchronous interface.

  • The interface name should be the actual service name suffix with the word ‘Async’
  • The interface should have the similar methods that the actual service has with some exceptions.
    1. The method should not have any return type other than void.
    2. The methods should have an extra parameter at the end( AsyncCallback<String> callback). Here the <String> is the return type of the method in the actual service(User Service).
  • The methods in the async interface are allowed to throw Run-time Exceptions only.

Lets call out services from a client side code. Have a look at the following code.

/**
   * Create a remote service proxy to talk to the server-side User service.
   */
	private final UserServiceAsync userService = GWT
			.create(UserService.class);

userService.createUser(user,
						new AsyncCallback<String>() {
							@Override
							public void onFailure(Throwable caught) {
								// Show the RPC error message to the user
								Window.alert(caught.getLocalizedMessage());

							}
							@Override
							public void onSuccess(String result) {

								if(result.equals("true")){
									Window.alert("User Created Successfully");
								}else{
									Window.alert("User Creation failed");
								}
								clearAll();

							}

						});

nameField.addBlurHandler(new BlurHandler() {
			@Override
			public void onBlur(BlurEvent event) {
				final String userName = nameField.getText();
				if(userName != null
                               && userName.length() >0){
					userService.checkUserExist(userName,
                                                new AsyncCallback<Boolean>() {
						@Override
						public void onFailure(Throwable caught) {
							Window.alert(caught.getLocalizedMessage());

						}
						@Override
						public void onSuccess(Boolean result) {
							if(result.booleanValue()){
								errorLabel.setText("");
								errorLabel.setText(userName+" taken");
								errorLabel.setStyleName("errorLabel");
							}else{
								errorLabel.setText("");
								errorLabel.setText(userName+" available");
								errorLabel.setStyleName("successLabel");
							}
						}
					});
				}
			}
		});

First we create a remote service proxy to talk to the server side user service. Once the proxy is created we give a call to the service methods. We create an anonymous callback class instance and pass to the service methods. AsyncCallback interface has two methods , onSuccess() and inFailure(). Once a request is success and completed , the onSuccess() method gets called. If there is a failure , onFailure() method gets called. One thing to note here , onSuccess() method’s parameter’s type should be same of the service’s called method’s parameter type.

Thats all !! Download the project and start exploring more. Please feel free to write comments if you like it or would like to see any improvement on this post.

  • Share/Save/Bookmark
Tags: , , ,

2 comments
Leave a comment »

  1. I found your interesting article I want the analysis to better understand the GWT and RPC. A big thank you for this work.

  2. Consider using an ORM tool. That will speed development and reduce development and maintenance efforts.

    Thanks.

Leave Comment