Thursday, December 8, 2011

GWT RPC (Part 1 of 3) - Synchronous and asynchronous interfaces

GWT is all about creating web applications. And a web application wouldn't be a web application without client/server communication. A mechanism called GWT RPC (remote procedure call) is used for this communication in GWT. In this series of tutorials, I'm going to show you how GWT RPC works by building a simple GWT application that uses the Twitter API to perform tweet searches and retrieve the Twitter privacy policy.

The process involves three steps. I'll be covering step 1 in this blog post.

  1. Create the synchronous and asynchronous interfaces that define each RPC method
  2. Create the server-side implementation of each RPC method
  3. Create the UI (user interface) that the client will use perform the searches

You can download the entire sample project used in this tutorial here.


Synchronous interface

The first thing to do is figure out what kind of communication you need to do with the server. What actions do you need to perform? What data do you need to retrieve? In this case, we need to do two things: perform tweet searches and get the Twitter privacy policy.

These actions are defined in the what's called the synchronous interface. I'll create two RPC methods in my synchronous interface (there's no limit to how many you can define). The interface must extend RemoteService and have a @RemoteServiceRelativePath annotation (which we'll revisit later). It must also be in the client package.

package com.acme.twittersearch.client;

[imports...]

@RemoteServiceRelativePath("search")
public interface TwitterSearchService extends RemoteService {
  List<Tweet> searchTweets(String query) throws IOException, IllegalArgumentException;
  String getPrivacyPolicy() throws IOException;
}

The first method, searchTweets, will take a search query as input and then return a list of tweets in the response. Tweets contain a lot of information (such as an ID, author, and creation date, not to mention the actual tweet message), so I'm going to create a Tweet class that encapsulates all of this information. I'm not going to worry about creating this class right now, though. For now, just assume that the Tweet class holds all the information about a tweet that we need it to.

Also notice how searchTweets throws two exceptions. An IOException will be thrown if there's a problem querying the Twitter API. We'll also have it throw an IllegalArgumentException if the search query is empty. Even though IllegalArgumentException is a runtime exception and doesn't have to be defined in the method's throws clause in order for the Java compilation to pass, GWT requires it to be declared here. Otherwise, it will send a generic "500 error" exception to the client instead of the real exception if that exception is thrown.

The second method, getPrivacyPolicy, doesn't need any parameters. It will return the Twitter privacy policy, which is a block of text that we will hold in a String object. As with searchTweets, it too will throw an IOException if there's a problem calling the Twitter API.

Asynchronous interface

Now that we've defined our RPC methods in the synchronous interface, we're ready to create the asynchronous interface. This interface will contain the exact same methods that are in the synchronous interface, but in a slightly different form:

package com.acme.twittersearch.client;

[imports...]

public interface TwitterSearchServiceAsync {
  void searchTweets(String query, AsyncCallback<List<Tweet>> callback);
  void getPrivacyPolicy(AsyncCallback<String> callback);
}

The interface can be named anything, but I've named it TwitterSearchServiceAsync because the convention is to use the synchronous interface's name with "Async" appended to the end.

As you may have noticed, unlike their counterparts in the synchronous interface, the methods don't return anything. They also take an extra parameter--an AsyncCallback object. This object will be used by the client to handle the response when the response is returned from the server. Since this interface will be used by the client, it must be located in the client package. Also, note that, while the methods in the synchronous interface throw exceptions, the methods in the asynchronous interface do not.

Both of these interfaces must remain in-sync. By that I mean, if one interface changes, the other must also change. For example, if I change the return value of a method in the synchronous interfaces, I must make the same, corresponding change to the asynchronous interface (by changing the generics parameter of the AsyncCallback parameter). The Eclipse GWT plugin is helpful in this regard. If it notices that the two interfaces are not in-sync, it will throw a Java compilation error.


In my next blog post, I'll talk about creating the server-side implementation of the RPC methods.

No comments: