Making GWT Remote Procedure Calls
Join the DZone community and get the full member experience.
Join For Free[img_assist|nid=3421|title=|desc=|link=url|url=http://www.manning.com/affiliate/idevaffiliate.php?id|align=left|width=208|height=388]GWT enables client applications to communicate with server resources through its own RPC and object-serialization mechanism. This communication process involves each side of the conversation implementing a very straightforward GWT service interface and sending/receiving special GWT serialized data. The server side exposes resources, and the client side invokes those resources asynchronously.
GWT intentionally keeps things very basic and optimizes the translation of data from Java to JavaScript and vice versa. While this mechanism is really easy to work with and to understand, it's a GWTism. That is to say, GWT does not use a standard or existing idiomatic approach no XML-RPC, SOAP, or REST is involved. But despite using its own approach, GWT can also communicate with existing services in additional ways to facilitate integration, such as using plain XML over HTTP (POX), or JavaScript Object Notation (JSON). These are additional methods, though; the native mechanism for GWT client-server communication is GWT RPC.
Starting the HelloServer project
We'll use GWT RPC in a sample "Hello Server" application, aptly named HelloServer. We'll first define the data we want to pass across the wire, then create a server endpoint, which outlines a method that uses that data, and finally implement a client to complete the process. Figure 1 shows what our completed HelloServer application will look like.
[img_assist|nid=4706|title=|desc=|link=none|align=none|width=252|height=229]
Figure 1 The HelloServer sample application demonstrating GWT server communications. The last line on the screen shows the server's response to the user's input.
We'll use the GWT ApplicationCreator utility to create our basic project, but we'll also use the GWT ProjectCreator utility to make Eclipse project files. After we use ApplicationCreator and ProjectCreator, we'll build the example using Eclipse. The ProjectCreator usage options are shown here and described in table 1.
ProjectCreator [-ant projectName] [-eclipse projectName] [-out dir] [-overwrite] [-ignore]
Table 1 ProjectCreator parameters
Parameter | Description |
---|---|
-ant | Generates an Ant build file to compile source (.ant.xml will be appended) |
-eclipse | Generates an Eclipse project |
-out | The directory to which output files will be written (defaults to the current directory) |
-overwrite | Overwrites any existing files |
-ignore | Ignores any existing files; does not overwrite them |
Let's start by creating our application with ApplicationCreator, and then obtaining our Eclipse project configuration with ProjectCreator, as follows:
mkdir [PROJECT_HOME]
cd [PROJECT_HOME]
[GWT_HOME]/ApplicationCreator \
com.manning.gwtip.helloserver.client.HelloServer
[GWT_HOME]/ProjectCreator -eclipse HelloServer
Running ApplicationCreator and ProjectCreator as shown will create your default project template files and will create Eclipse-centric .project and .classpath files. From there, you can open Eclipse and use the File > Import > Existing Projects Into Workspace feature to import the HelloServer project. Once you have the HelloServer project in your Eclipse Navigator, you should see the standard GWT layout within it. Figure 2 displays the imported HelloServer project in the Eclipse Resource perspective.
[img_assist|nid=4707|title=|desc=|link=none|align=none|width=192|height=160]
Figure 2 The HelloServer project layout in the Eclipse Resource perspective after ProjectCreator has generated the Eclipse project
If you execute HelloServer-shell, you'll invoke GWTShell and you'll see the standard template, "Click Me - Hello World", which every default GWT project starts with. With the basic project in place, we're ready to move on to implementing HelloServer. We'll start by defining our simple data model and looking at GWT's serialization mechanism.
Defining GWT serializable data
The first class we need to create is a simple data class, which we'll call Person. Before our Person objects can be passed from a GWT client application to an RPC service, they must be marked with either the com.google.gwt.user.client.rpc.IsSerializable or the java.io.Serializable interface. This is conceptually analogous to regular Java serialization, but in this case it’s used in a manner specific to GWT. Also as in regular serialization, GWT will honor the transient modifier on class properties, allowing them to be omitted if they are not themselves serializable.
Note
While IsSerializable doesn't define any methods, it's imperative that your IsSerializable implementations declare a no-arguments constructor.
The IsSerializable interface is both important and problematic. It's important because it gives the GWT compiler better information on what classes need to support serialization. It's problematic because it introduces a GWT-specific dependency into model classes, as you can see in listing 1, the Person class. While this is not a big problem if you're working entirely within the realm of your GWT application, it could quickly become a deal breaker if you wish to share object models with other Java projects in your organization.
Listing 1 The Person data object class
package com.manning.gwtip.helloserver.client;
import com.google.gwt.user.client.rpc.IsSerializable;
public class Person implements IsSerializable{
public String name;
public String address;
public Person(){
this(null, null);
}
public Person(String name, String address ) {
super();
this.name = name;
this.address = address;
}
}
Because of this the GWT-dependency of IsSerializable, GWT 1.4 added support so that java.io.Serializable could be used in place of IsSerializable. This allows these two marker interfaces to act interchangeably in a GWT context. Generally, this helps to ensure that model classes can be created without direct GWT dependencies. Yet, it's important to understand that such model objects still need to be otherwise GWT translatable. That means they need no-argument constructors, and they cannot use Java 5 language features (for now).
Also, it's important to understand that the GWT implementation of java.io.Serializable, though convenient, is just a marker interface meaning the same thing as IsSerializable this is not the same as actual Java serialization support. The documentation for the GWT version of java.io.Serializable puts it this way: "public interface Serializable: Provided for interoperability; RPC treats this interface synonymously with IsSerializable. The Java serialization protocol is explicitly not supported."
The bottom line is that you can use either IsSerializable or Serializable, and they mean exactly the same thing to GWT this class is RPC translatable. The GWT Serializable emulation can help you avoid the GWT-specific IsSerializable dependency, but your model classes are still limited to what is possible with GWT. Because of this, we'll use the IsSerializable marker to make the association explicit. Keep in mind, though, that in real life you may be better off using Serializable as long as you're disciplined enough to remember the implicit GWT limitations. We want to create our simple service infrastructure to illustrate the GWT RPC basics.
Creating RPC services
GWT includes a GWT RPC package for enabling communications with server resources. Constructing an RPC service entails building two interfaces and a service implementation.
You begin by creating a synchronous service interface, which extends the GWT RemoteService interface and defines the methods your service exposes. Next, you create an asynchronous interface based on the first synchronous one. This asynchronous interface will have the same name as the synchronous interface, but with an Async suffix. Importantly, the asynchronous interface does not extend RemoteService itself. The asynchronous interface must have all the same methods as the synchronous one, except that each of the asynchronous methods must declare a void return type, throw no exceptions, and have an additional final reference parameter of type AsyncCallback. These two interfaces one synchronous, one asynchronous are the client side of the picture. Finally, you must create a server-side implementation of your client-side synchronous RemoteService interface. This must extend the GWT RemoteServiceServlet class.
These three parts, the synchronous client service interface, asynchronous client service interface, and server implementation service servlet, are the backbone of GWT RPC. Table 2 restates these RPC components for reference.
Table 2 Components involved in creating a GWT RPC service
Required interface | Extension | Purpose |
---|---|---|
MyService | RemoteService | Client side. Synchronous interface, used internally by GWT. |
MyServiceAsync | None | Client side. Asynchronous interface which, by convention, backs the synchronous interface. It must have the same name with an Async suffix, must declare void return type on all methods, must throw no exceptions, and must include AsyncCallback as the last parameter in all methods. |
MyServiceImpl | RemoteServiceServlet | Server side. An implementation of the client-side synchronous interface, which by convention will be accessible in the client through the asynchronous interface. |
In our example, we'll add one more element to the mix in order to decouple our implementation just a bit and make things more flexible. We're going to put our server-side RemoteService implementation in a separate class, apart from our RemoteServiceServlet implementation. This could be done in a single step we could have a server-side implementation that both implements RemoteService and extends RemoteServiceServlet in one fell swoop. However, we'll separate these two as a best practice, because in a larger project you may want to use the service implementation outside of the context of your GWT classes. With the RemoteServiceServlet separated, you’re free to implement the RemoteService interface class itself in a Spring bean, an Enterprise Java Bean (EJB), or even through a SOAP service. Figure 3 reinforces these points and also shows the structure we'll use for our HelloServer example.
[img_assist|nid=4708|title=|desc=|link=none|align=none|width=373|height=272]
Figure 3 GWT RPC class diagram for HelloServer. Notice that the RemoteServiceAsync class is not directly related to our service implementation or servlet; it's associated by convention.
It's important to remember that all of the classes you use as arguments or returns from the methods defined by your RemoteService interface must be GWT-serializable, as we discussed in the Defining GWT serializable data section. In addition, your remote interface and all the data you wish to serialize must be part of your source path so that GWTCompiler finds these resources and creates the appropriate JavaScript versions. Getting into the RPC code, we'll start with the client-side synchronous interface, HelloService.java, which is displayed in listing 2.
Listing 2 HelloService.java
package com.manning.gwtip.helloserver.client;
import com.google.gwt.user.client.rpc.RemoteService;
public interface HelloService extends RemoteService {
String sayHello(Person p);
}
Next, we need to create our client-side asynchronous interface, which is almost identical to the synchronous one with the previously noted exceptions (Async suffix, void return type, AsyncCallback as a parameter the callback will be used to return the value). Note that both of these client-side interfaces are in the default source path, the .client package. Listing 3 shows our HelloServiceAsync.java interface.
Listing 3 HelloServiceAsync.java
package com.manning.gwtip.helloserver.client;
import com.google.gwt.user.client.rpc.AsyncCallback;
public interface HelloServiceAsync {
void sayHello(Person p, AsyncCallback callback);
}
Last, we need to create the server-side implementation of our client-side RemoteService interface. Our service implementation is just going to be a plain old Java object (POJO), though again you could use many different techniques at this point. This code for HelloServiceImpl.java is shown in listing 4.
Listing 4 HelloServiceImpl.java
package com.manning.gwtip.helloserver.server;
import com.manning.gwtip.helloserver.client.HelloService;
import com.manning.gwtip.helloserver.client.Person;
public class HelloServiceImpl implements HelloService {
public HelloServiceImpl() {
}
public String sayHello(Person p) {
return "Hello " + p.name + ". How is the weather at " + p.address +
"?";
}
}
Note that even though we created two client-side interfaces, one synchronous and one asynchronous, GWT doesn't support synchronous communications with your client application. You'll never use the synchronous one. The reason for this is browser-related, and technical, and you might be familiar with it if you have done Ajax work in the past. The XMLHttpRequest object is asynchronous; however, the JavaScript interpreter in many browsers is a single execution thread. This means that if you tried to send a request and "spin" while waiting for the callback event to execute, you'd spin forever. The callback event wouldn't fire while the JavaScript interpreter was spinning.
Now we have three classes, but we're not quite done yet. The final piece is our RemoteServiceServlet implementation. This is the actual servlet that the web application exposes, and with which the client-side classes communicate. Listing 5 shows the code for our service servlet, HelloServlet.java.
Listing 5 HelloServlet.java
package com.manning.gwtip.helloserver.server;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.manning.gwtip.helloserver.client.HelloService;
import com.manning.gwtip.helloserver.client.Person;
public class HelloServlet
extends RemoteServiceServlet
implements HelloService {
private HelloService impl = new HelloServiceImpl();
public HelloServlet() {
super();
}
public String sayHello(Person p) {
return impl.sayHello(p);
}
}
This completes our service: two simple interfaces on the client, one implementation class on the server, and a service servlet to host and invoke the server-side implementation. Now that we have a basic service, we'll explore the options available from the RemoteServiceServlet class.
Expanding on RemoteServiceServlet
While we're wrapping our implementation, there are certain features provided by the servlet specification we might want. Indeed, we might want to do a lot of things to a request before it goes to the actual service implementation. The RemoteServiceServlet includes several methods you can call from within your servlet to access these features. The important ones are outlined in table 3.
Table 3 Selected methods of the RemoteServiceServlet
Method | Function |
---|---|
getThreadLocalRequest() | Called from a service method to get the HttpServletRequest, and HttpSession objects. It can be used for server-side state with session or customizing responses. |
getThreadLocalResponse() | Called from a service method to get the HttpServletResponse object. It can be used to customize the response, such as for setting custom headers. |
onBeforeRequestDeserialized(String) | Called before the request objects are deserialized, with the serialization payload as an argument. |
onAfterResponseSerialized(String) | Called before the response serialized object is returned to the client, with the serialized payload as an argument. |
shouldCompressResponse(HttpServletRequest, HttpServletResponse, String) | Called to determine whether Gzip compression should be used on the response. The default behavior is true if the client accepts it and the response is greater than 256 bytes. |
processCall(String) | Called to deserialize incoming payloads, call the appropriate service method, and return a string response payload. |
The most important of the methods in table 3 are the two ThreadLocal methods, which let you access the session state. For instance, if you were proxying the calls into the RPC service to a SOAP service, you could check the session and authenticate the user with the SOAP service on the first call, using the user information available in the HttpServletRequest object. From there, you could store the connect stub for the user in the Session object, giving each user their own instance of the actual business object.
NOTE
We're skipping over service security in general, for the sake of simplicity. However, it should be kept in mind the old saw, "never trust the client," still applies.
You might not need the other methods listed in table 3 often, but they can be useful. It's important to remember that the Google serialization classes are available to you in your application in the form of the com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader and ServerSerializationStreamWriter. If, for instance, you wish to change the service state based on information contained in the client request, you can overload the methods in table 3 and inspect the message to the server beforehand. Unfortunately these methods don't provide you with the option to filter or modify the serialization payload. To do that, you'd need to overload the entire processCall() method and then make your changes before invoking the superclass method. All of this takes place on the server, but we also want to make calls from the client.
Calling the server from the client
In order to invoke our RPC services on the client, we need to get the asynchronous service interface from the static GWT.create() method, bind it to a relative URL for our HelloServlet, create a callback handler, and make our call. Listing 6 demonstrates this directly within the EntryPoint class, HelloServer. This class, now in our Eclipse project, was initially created by the ApplicationCreator utility. We have entirely replaced what was in the default HelloServer.java file with our code here. (For the purposes of this example, we're doing things directly in the entry point. We're not trying to develop with reuse in mind.)
Listing 6 HelloServer.java
public class HelloServer implements com.google.gwt.core.client.EntryPoint {
private HelloServiceAsync service;
private TextBox name = new TextBox();
private TextBox address = new TextBox();
private Label response = new Label();
private AsyncCallback serviceCallback =
new AsyncCallback() {
public void onSuccess(Object result) {
String string = (String) result;
response.setText(string);
}
public void onFailure(Throwable caught) {
Window.alert("There was an error: " + caught.toString());
}
};
public HelloServer() {
super();
}
public void onModuleLoad() {
service = (HelloServiceAsync)
GWT.create(HelloService.class);
ServiceDefTarget endpoint = (ServiceDefTarget) service;
endpoint.setServiceEntryPoint(
GWT.getModuleBaseURL() +
"/HelloService");
RootPanel root = RootPanel.get();
root.add(new Label("Name"));
root.add(name);
root.add(new Label("Address"));
root.add(address);
Button button = new Button("Hello!",
new ClickListener() {
public void onClick(Widget sender) {
service.sayHello(new Person(name.getText(),
address.getText()), serviceCallback);
}
});
root.add(button);
root.add(response);
}
}
Our HelloServer entry point is intended to wire together our example with UI and event handling. Within it, we create an AsyncCallback object to handle the return values from our service . (This could also have been done anonymously.) Then we use the static GWT.create() method to obtain a runtime reference to our service , and implement a ClickListener to connect the click on Button with the sayHello() service call.
The final step we need to complete is to set up
our module file, HelloServer.gwt.xml, with our servlet mapped to
/HelloService. If we take a look at the existing HelloServer.gwt.xml
file created by ApplicationCreator, we can see a single
Listing 7 HelloServer.gwt.xml module with servlet entry added
<module>
<inherits name='com.google.gwt.user.User'/>
<entry-point
class='com.manning.gwtip.helloserver.client.HelloServer'/>
<servlet path="/HelloService"
class=
"com.manning.gwtip.helloserver.server.HelloServlet"/>
</module>
We now have a complete, working GWT application that makes a call back to the server! While our example is not visually impressive, you should now be familiar with all the moving parts involved in making an asynchronous invocation to server-side code.
To run this example in hosted mode, you can simply invoke GWTShell via the HelloServer-shell shortcut script. (This script was either created when you manually ran ApplicationCreator, if you have been following along and building the project, or is provided with the code for this example on the Manning web site.) When you run the example, the client will call the server, and, under the hood, Java objects are being converted to the GWT wire format and passed into JavaScript. Figure 4 provides a visual overview of the entire RPC process: from the user to the client service interface, across the wire into the remote service servlet, and then to the service implementation and then back again.
[img_assist|nid=4709|title=|desc=|link=none|align=none|width=373|height=260]
Figure 4 An overview of the complete RPC process. Notice that the user's browser lifeline is freed while the service call is executed; it's monopolized again as the call is returned.
Figure 5 shows an example request in hexadecimal format so that you can see the special characters used during a service call invocation. While it's not important for you to have a complete understanding of the hexadecimal values, this demonstrates an important point about the operation of the compiler in relation to the server.
[img_assist|nid=4710|title=|desc=|link=none|align=none|width=585|height=497]
Figure 5 A GWT RPC request in hexadecimal format. Notice that the type information is passed for deserialization on the server, along with the attribute values.
As you can see in figure 5, the class name is the important information used in the transfer and it's followed by the properties and a sequence to identify which values belong to which property. In GWT (and unlike JSON) property names on objects can change from compilation to compilation, but the GWT client will always know the Java class name of the object. Now for the response:
{OK}[1,["Hello John Doe. How is the weather at Anytown, NA, 5555"],0,2]
First, we have the response code of the call to the server, followed by our single return value. Since we're returning a simple string value, it's returned in the native JavaScript form. Once values are returned, they will frequently update the model level of your Ajax application.
Troubleshooting server communication
If you begin getting errors in the form of "Deferred binding result type 'module.client.MyService' is not instantiable" when you start creating GWT RPC services, try the following. First, turn your -logLevel option up on the shell logging console to provide additional clues as to what went wrong. Then, run through this checklist:
- Make sure you're casting your call to GWT.create() to MyServiceAsync and not MyService.
- Make sure your MyService interface extends RemoteService.
- Make sure your return types and arguments all implement IsSerializable or Serializable.
- Make sure the classes used as return types and arguments all have no-args constructors.
- Make sure the return types on methods in your MyServiceAsync class are all void.
In addition to the plumbing of GWT server communications and issues with regard to naming and types, you may also be curious about handling synchronization and multiple outstanding callbacks. Developers familiar with the issues surrounding asynchronous, message-based programming will possibly look at the asynchronous nature of Ajax messaging and overthink the issues of synchronization at the model level in their GWT applications.
One important thing to remember is that all JavaScript on a page is executed within the scope of a single thread. This means that while you might have multiple outstanding callbacks waiting for invocation, only one will be called at a time. Unlike invoking web services via the Java API for XML Web Services (JAX-WS) from a Swing application, there is no need to shift UI changes from the thread invoking the callback to the painting thread, since all JavaScript is executed on the painting thread. To borrow an analogy from Brian Glick (http://www.jroller.com/hifi78/entry/gwt_single_threaded_javascript_multi):
With the single threaded browser environment, I think about a colony of bees. (WARNING: Extremely strained metaphor coming!) The queen bee (the application) can tell the worker bees (XMLHttpRequest), "Go get me food." However, when the bees return, only one can give the food to the queen at a time. The rest have to sit in line and wait. From the bee keeper's standpoint, the environment is multi-threaded. All of these bees are swarming around at the same time. However, we need to look at it from the perspective of the queen, who only has to deal with one worker bee at a time.
That is to say, the problems usually associated with multithreaded programming in Java don't apply. Your Java will always execute in a single thread. You'll never have multiple callbacks executing on the client at the same time. Attributes will never be modified outside of the current call stack. In opposition to the standard Servlet API, your client-side GWT code will be single threaded across all the instances you create in the code; it will be fundamentally static. There is no Swing event-dispatching thread. There are no daemon timers. There is a single execution thread that all code will run in, no matter the callback order.
Opinions expressed by DZone contributors are their own.
Trending
-
How To Become a 10x Dev: An Essential Guide
-
Revolutionizing Algorithmic Trading: The Power of Reinforcement Learning
-
Hyperion Essbase Technical Functionality
-
How To Manage Vulnerabilities in Modern Cloud-Native Applications
Comments