Remote Procedure Calls :~ provided in GWT to connect with the server

Remote Procedure Calls


An easy-to-use RPC mechanism for passing Java objects to and from a server over standard HTTP.

Contents

  • Overview
    An easy-to-use RPC mechanism for passing Java objects to and from a server over standard HTTP.
  • RPC Plumbing Diagram
    Diagram of the RPC plumbing.
  • Creating Services
    How to build a service interface from scratch.
  • Implementing Services
    Implement your service interface as a servlet.
  • Actually Making a Call
    How to actually make a remote procedure call from the client
  • Serializable Types
    Using GWT's automatic serialization well.
  • Handling Exceptions
    Handle exceptions due to failed calls or thrown from the server.
  • Getting Used to Asynchronous Calls
    Asynchronous calls are tricky at first, but ultimately your users will thank you.
  • Architectural Perspectives
    Contrasting a couple of approaches to implementing services.

Overview

A fundamental difference between GWT applications and traditional HTML web applications is that GWT applications do not need to fetch new HTML pages while they execute. Because GWT-enhanced pages actually run more like applications within the browser, there is no need to request new HTML from the server to make user interface updates. However, like all client/server applications, GWT applications usually do need to fetch data from the server as they execute. The mechanism for interacting with a server across a network is called making a remote procedure call (RPC), also sometimes referred to as a server call. GWT RPC makes it easy for the client and server to pass Java objects back and forth over HTTP.

When used properly, RPCs give you the opportunity to move all of your UI logic to the client, resulting in greatly improved performance, reduced bandwidth, reduced web server load, and a pleasantly fluid user experience.

The server-side code that gets invoked from the client is often referred to as a service, so the act of making a remote procedure call is sometimes referred to as invoking a service. To be clear, though, the term service in this context isn't the same as the more general "web service" concept. In particular, GWT services are not related to the Simple Object Access Protocol (SOAP).

RPC Plumbing Diagram

This section outlines the moving parts required to invoke a service. Each service has a small family of helper interfaces and classes. Some of these classes, such as the service proxy, are automatically generated behind the scenes and you generally will never realize they exist. The pattern for helper classes is identical for every service that you implement, so it is a good idea to spend a few moments to familiarize yourself with the terminology and purpose of each layer in server call processing. If you are familiar with traditional remote procedure call (RPC) mechanisms, you will recognize most of this terminology already.

[Image]

Creating Services

To develop a new service interface, begin by creating a client-side Java interface that extends the RemoteService tag interface.
public interface MyService extends RemoteService {
public String myMethod(String s);
}
This synchronous interface is the definitive version of your service's specification. Any implementation of this service on the server-side must extend RemoteServiceServlet and implement this service interface.
public class MyServiceImpl extends RemoteServiceServlet implements
MyService {

public String myMethod(String s) {
// Do something interesting with 's' here on the server.
return s;
}

}

Asynchronous Interfaces

Before you can actually attempt to make a remote call from the client, you must create another interface, an asynchronous one, based on your original service interface. Continuing with the example above...
interface MyServiceAsync {
public void myMethod(String s, AsyncCallback callback);
}

The nature of asynchronous method calls requires the caller to pass in a callback object that can be notified when an asynchronous call completes, since by definition the caller cannot be blocked until the call completes. For the same reason, asynchronous methods do not have return types; they must always return void. After an asynchronous call is made, all communication back to the caller is via the passed-in callback object.

The relationship between a service interface and its asynchronous counterpart is straightforward:
  • If a service interface is called com.example.cal.client.SpellingService, then the asynchronous interface must be called com.example.cal.client.SpellingServiceAsync. The asynchronous interface must be in the same package and have the same name, but with the suffix Async.
  • For each method in your service interface,
    public ReturnType methodName(ParamType1 param1, ParamType2 param2);
    an asynchronous sibling method should be defined that looks like this:
    public void methodName(ParamType1 param1, ParamType2 param2,
    AsyncCallback callback);

See AsyncCallback for additional details on how to implement an asynchronous callback.

Implementing Services

Every service ultimately needs to perform some processing to order to respond to client requests. Such server-side processing occurs in the service implementation, which is based on the well-known servlet architecture.

A service implementation must extend RemoteServiceServlet and must implement the associated service interface. Note that the service implementation does not implement the asynchronous version of the service interface.

Every service implementation is ultimately a servlet, but rather than extending HttpServlet, it extends RemoteServiceServlet instead. RemoteServiceServlet automatically handles serialization and invoking the intended method in your service implementation.

Testing Services During Development

To automatically load your service implementation, use the tag within your module XML. The GWT development shell includes an embedded version of Tomcat which acts as a development-time servlet container for testing.

Deploying Services Into Production

In production, you can use any servlet container that is appropriate for your application. You need only to ensure that the client code is configured to invoke the service using the URL to which your servlet is mapped by the web.xml configuration. See ServiceDefTarget for more information.

Actually Making a Call

The process of making an RPC from the client always involves the exact same steps.
  1. Instantiate the service interface using GWT.create().
  2. Specify a service entry point URL for the service proxy using ServiceDefTarget.
  3. Create an asynchronous callback object to be notified when the RPC has completed.
  4. Make the call.

Example

Suppose you want to call a method on a service interface defined as follows:
public interface MyEmailService extends RemoteService {
void emptyMyInbox(String username, String password);
}
Its corresponding asynchronous interface will look like this:
public interface MyEmailServiceAsync {
void emptyMyInbox(String username, String password,
AsyncCallback callback);
}
The client-side call will look like this:
public void menuCommandEmptyInbox() {
// (1) Create the client proxy. Note that although you are creating the
// service interface proper, you cast the result to the asynchronous
// version of
// the interface. The cast is always safe because the generated proxy
// implements the asynchronous interface automatically.
//
MyEmailServiceAsync emailService = (MyEmailServiceAsync) GWT.create(MyEmailService.class);

// (2) Specify the URL at which our service implementation is running.
// Note that the target URL must reside on the same domain and port from
// which the host page was served.
//
ServiceDefTarget endpoint = (ServiceDefTarget) emailService;
String moduleRelativeURL = GWT.getModuleBaseURL() + "email";
endpoint.setServiceEntryPoint(moduleRelativeURL);

// (3) Create an asynchronous callback to handle the result.
//
AsyncCallback callback = new AsyncCallback() {
public void onSuccess(Object result) {
// do some UI stuff to show success
}

public void onFailure(Throwable caught) {
// do some UI stuff to show failure
}
};

// (4) Make the call. Control flow will continue immediately and later
// 'callback' will be invoked when the RPC completes.
//
emailService.emptyMyInbox(fUsername, fPassword, callback);
}
It is safe to cache the instantiated service proxy to avoid creating it for subsequent calls.

Serializable Types

Method parameters and return types must be serializable, which means they must conform to certain restrictions. GWT tries really hard to make serialization as painless as possible, so while the rules regarding serialization are subtle, in practice the behavior becomes intuitive very quickly.

A type is serializable and can be used in a service interface if it

  • is primitive, such as char, byte, short, int, long, boolean, float, or double;
  • is String, Date, or a primitive wrapper such as Character, Byte, Short, Integer, Long, Boolean, Float, or Double;
  • is an array of serializable types (including other serializable arrays);
  • is a serializable user-defined class; or
  • has at least one serializable subclass

Serializable User-defined Classes

A user-defined class is serializable if
  1. it is assignable to IsSerializable or Serializable, either because it directly implements one of these interfaces or because it derives from a superclass that does
  2. all non-final, non-transient instance fields are themselves serializable, and
  3. it has a public default (zero argument) constructor
The transient keyword is honored, so values in transient fields are not exchanged during RPCs. Fields that are declared final are also not exchanged during RPCs, so they should generally be marked transient as well.

Polymorphism

GWT RPC supports polymorphic parameters and return types. To make the best use of polymorphism, however, you should still try to be as specific as your design allows when defining service interfaces. Increased specificity allows the compiler to do a better job of removing unnecessary code when it optimizes your application for size reduction.

Type Arguments

Collection classes such as java.util.Set and java.util.List are tricky because they operate in terms of Object instances. To make collections serializable, you should specify the particular type of objects they are expected to contain. This requires you to use the special Javadoc annotation @gwt.typeArgs. Defining an item type for a collection means that you will ensure that the collection only ever contains objects of that item type or a subclass thereof. This hint is necessary so that the GWT proxy generator can create efficient code. Adding an object to a collection that violates its asserted item type will lead to undefined behavior.

To annotate fields of collection type in a serializable user-defined class:

public class MyClass implements IsSerializable {
/**
* This field is a Set that must always contain Strings.
*
* @gwt.typeArgs
*/
public Set setOfStrings;

/**
* This field is a Map that must always contain Strings as its keys and
* values.
*
* @gwt.typeArgs
*/
public Map mapOfStringToString;

/**
* Default Constructor. The Default Constructor's explicit declaration
* is required for a serializable class.
*/
public MyClass() {
}
}
Note that there is no need to specify the name of the field in the @gwt.typeArgs declaration since it can be inferred.

Similarly, to annotate parameters and return types:

public interface MyService extends RemoteService {
/**
* The first annotation indicates that the parameter named 'c' is a List
* that will only contain Integer objects. The second annotation
* indicates that the returned List will only contain String objects
* (notice there is no need for a name, since it is a return value).
*
* @gwt.typeArgs c
* @gwt.typeArgs
*/
List reverseListAndConvertToStrings(List c);
}
Note that parameter annotations must include the name of the parameter they are annotating in addition to the collection item type, while return type annotations do not.
Tip
Although the terminology is very similar, GWT's concept of "serializable" is different than serialization based on the standard Java interface Serializable. All references to serialization are referring to the GWT concept as defined above.

Handling Exceptions

Making RPCs opens up the possibility of a variety of errors. Networks fail, servers crash, and problems occur while processing a server call. GWT lets you handle these conditions in terms of Java exceptions. RPC-related exceptions fall into two categories.

Checked Exceptions

Service interface methods support throws declarations to indicate which exceptions may be thrown back to the client from a service implementation. Callers should implement AsyncCallback.onFailure(Throwable) to check for any exceptions specified in the service interface.

Unexpected Exceptions

InvocationException

An RPC may not reach the service implementation at all. This can happen for many reasons: the network may be disconnected, a DNS server might not be available, the HTTP server might not be listening, and so on. In this case, an InvocationException is passed to your implementation of AsyncCallback.onFailure(Throwable). The class is called InvocationException because the problem was with the invocation attempt itself rather than with the service implementation itself.

An RPC can also fail with an invocation exception if the call does reach the server, but an undeclared exception occurs during normal processing of the call. There are many reasons such a situation could arise: a necessary server resource, such as a database, might be unavailable, a NullPointerException could be thrown due to a bug in the service implementation, and so on. In these cases, a InvocationException is thrown in application code.

IncompatibleRemoteServiceException

Another type of failure can be caused by an incompatibility between the client and the server. This most commonly occurs when a change to a service implementation is deployed to a server but out-of-date clients are still active. For more details please see IncompatibleRemoteServiceException.

When the client code receives an IncompatibleRemoteServiceException, it should ultimately attempt to refresh the browser in order to pick up the latest client.

Getting Used to Asynchronous Calls

Asynchronous RPC isn't the simplest thing in the world, but it does allow you to achieve true parallelism in your application, even without multi-threading.

For example, suppose your application displays a large table containing many widgets. Constructing and laying out all those widgets can be time consuming. At the same time, you need to fetch data from the server to display inside the table. This is a perfect reason to use asynchronous calls. Initiate an asynchronous call to request the data immediately before you begin constructing your table and its widgets. While the server is fetching the required data, the browser is executing your user interface code. When the client finally receives the data from the server, the table has been constructed and laid out, and the data is ready to be displayed.

To give you an idea of how effective this technique can be, suppose that building the table takes 1 second and fetching the data takes 1 second. If you make the server call synchronously, the whole process will require at least 2 seconds. But if you fetch the data asynchronously, the whole process still takes just 1 second, even though you are doing 2 seconds' worth of work.

The hardest thing to get used to about asynchronous calls is that the calls are non-blocking. However, Java inner classes go a long way toward making this manageable.

Tip
The AsyncCallback interface is the key interface you'll extend to handle RPC responses.

Architectural Perspectives

There are various ways to approach services within your application architecture. Understand first of all that GWT services are not intended to replace J2EE servers, nor are they intended to provide a public web services (e.g. SOAP) layer for your application. GWT RPCs, fundamentally, are simply a method of "getting from the client to the server." In other words, you use RPCs to accomplish tasks that are part of your application but that cannot be done on the client computer.

Architecturally, you can make use of RPC two alternative ways. The difference is a matter of taste and of the architectural needs of your application.

The first and most straightforward way to think of service definitions is to treat them as your application's entire back end. From this perspective, client-side code is your "front end" and all service code that runs on the server is "back end." If you take this approach, your service implementations would tend to be more general-purpose APIs that are not tightly coupled to one specific application. Your service definitions would likely directly access databases through JDBC or Hibernate or even files in the server's file system. For many applications, this view is appropriate, and it can be very efficient because it reduces the number of tiers.

In more complex, multi-tiered architectures, your GWT service definitions could simply be lightweight gateways that call through to back-end server environments such as J2EE servers. From this perspective, your services can be viewed of as the "server half" of your application's user interface. Instead of being general-purpose, services are created for the specific needs of your user interface. Your services become the "front end" to the "back end" classes that are written by stitching together calls to a more general-purpose back-end layer of services, implemented, for example, as a cluster of J2EE servers. This kind of architecture is appropriate if you require your back-end services to run on a physically separate computer from your HTTP server.

0 comments: