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
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
Creating Services
public interface MyService extends RemoteService {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 String myMethod(String s);
}
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 calledcom.example.cal.client.SpellingServiceAsync
. The asynchronous interface must be in the same package and have the same name, but with the suffixAsync
. - 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);
Implementing Services
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 theweb.xml
configuration. See ServiceDefTarget for more information.Actually Making a Call
- Instantiate the service interface using
GWT.create()
. - Specify a service entry point URL for the service proxy using ServiceDefTarget.
- Create an asynchronous callback object to be notified when the RPC has completed.
- Make the call.
Example
Suppose you want to call a method on a service interface defined as follows:public interface MyEmailService extends RemoteService {Its corresponding asynchronous interface will look like this:
void emptyMyInbox(String username, String password);
}
public interface MyEmailServiceAsync {The client-side call will look like this:
void emptyMyInbox(String username, String password,
AsyncCallback callback);
}
public void menuCommandEmptyInbox() {It is safe to cache the instantiated service proxy to avoid creating it for subsequent calls.
// (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);
}
Serializable Types
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
, ordouble
; - is
String
,Date
, or a primitive wrapper such asCharacter
,Byte
,Short
,Integer
,Long
,Boolean
,Float
, orDouble
; - 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- 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
- all non-
final
, non-transient
instance fields are themselves serializable, and - it has a
public
default (zero argument) constructor
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 asjava.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 {Note that there is no need to specify the name of the field in the
/**
* 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() {
}
}
@gwt.typeArgs
declaration since it can be inferred. Similarly, to annotate parameters and return types:
public interface MyService extends RemoteService {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.
/**
* 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);
}
Handling Exceptions
Checked Exceptions
Service interface methods supportthrows
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 calledInvocationException
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
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.
Architectural Perspectives
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:
Post a Comment