Using the Command Pattern to Organize RPC Calls in GWT
In his last year’s speech on Google I / O, Ray Rayan told the audience how to properly structure the architecture of more or less large GWT projects. One of his recommendations is to use the Command template to organize RPC services. In this article, I will try to briefly highlight this approach using the simplest GWT application as an example. To dispatch RPC calls, the gwt-dispatch GWT-Dispatch library will be used . I want to warn you right away that this article is a symbiosis, comprehension and compilation of several sources ( GWT-Dispatch Getting Started , GWT MVP Example) Think of it as a quick start guide on how to properly build your GWT applications. All material is developed taking into account that the server implementation of RPC services is also performed in the Java language.
It's no secret that when developing more or less large applications, design patterns (patterns) rush to our aid. Patterns are a kind of recipes for solving specific typical cases. You can start the study and application of design patterns with Patterns on Wiki and go further into relevant books, articles, works, etc.
Briefly, the Command template allows you to implement specific implementations of the Command (Action etc.) interface through a unified interface.
Regarding the GWT RPC, the application of this approach will allow one to have one RPC service call interface (dispatcher) and pass an object of the corresponding action (command) into it.
Connecting the required libraries
So, to implement RPC interaction in a project using commands, we need to connect additional libraries to the project:
GWT-Dispatch - GWT implementation of call manager. This library also provides Action and Result interfaces for organizing your commands and their execution results.
Google Guice - Dependency Injection Framework from Google. Allows you to organize dependency management in server code using the Dependency Injection approach. It is much simpler than the well-known Spring Framework, and therefore works faster. During the implementation of the demo project, Guice also served as a servlet manager and initializing link for the entire server side. But more on that later.
Google GIN - Guice implementation for GWT. Allows you to apply the DI approach in client (read, GWT) code. We obviously will not use it, it is required as a dependency.
To connect these libraries to the project, just put the files gin-1.0.jar, guice-2.0.jar, guice-servlet-2.0.jar and gwt-dispatch-1.0.0.jar in WEB-INF / lib and add them to the Build Path project. The configuration of the GWT module with connected modules looks like this for me:
* This source code was highlighted with Source Code Highlighter.
Organization of a specific team on the GWT side
I will illustrate the creation of a team using the example of creating one RPC call. Its essence will be as simple as a door: send the parameter read from the input field to the server method and get an answer. All. Oh yes, display the received response on the UI. The demo project also contains a call that receives an array of fake DTO objects from the server. Its code will not be considered in this note. If interested, I can provide it additionally in the comments.
To create an RPC command, you need to create a class that implements the Action interface:
package net.pimgwt.client.rpc;
import net.customware.gwt.dispatch.shared.Action;
@SuppressWarnings("serial")
public class SingleRequestAction implements Action {
private String param;
public SingleRequestAction() {
}
public SingleRequestAction(String param) {
this.param = param;
}
public String getParam() {
return this.param;
}
}
* This source code was highlighted with Source Code Highlighter.
As you can see, nothing complicated. This command encapsulates the parameter that will be transmitted to the server. The only interesting point here is the indication that the result of the execution will be an object of the SingleRequestResult class:
package net.pimgwt.client.rpc;
import net.customware.gwt.dispatch.shared.Result;
@SuppressWarnings("serial")
public class SingleRequestResult implements Result {
private String resultMessage;
public SingleRequestResult() { }
public SingleRequestResult(String resultMessage) {
this.resultMessage = resultMessage;
}
public String getResultMessage() {
return this.resultMessage;
}
}
* This source code was highlighted with Source Code Highlighter.
which also encapsulates data that will “arrive” to the client code.
At the moment, preparations on the client side are completed. It's time to take on the roasting of coffee beans that will work on the server. By the way, our server will run on Google App Engine.
Server implementation of RPC services
The GWT-Dispatch library provides tools for organizing dispatchers for both the client and server parts.
We start configuring the server side from the servlet manager that will handle the processing of RPC calls:
* This source code was highlighted with Source Code Highlighter.
Class DispatcherServletModuleheir ServletModule. It rewrites the parent method configureServlets(), which matches the RPC-URL of the servlet manager implementation provided by GWT-Dispatch. By default, the URL that the dispatcher will listen on is constructed using the application_name / dispatch scheme.
We now implement the handler, which will be attached to the command declared on the client side (command SingleRequestAction):
* This source code was highlighted with Source Code Highlighter.
The command handler implements a generic interface, during the parameterization of which the command and its result are indicated. In this case, SingleRequestActionand SingleRequestResult, respectively. The interface ActionHandleralso requires the implementation class to provide methods execute(), getActionType()and rollback()whose names speak for themselves. In the above code, for such a simple command as SingleRequestActionthe rollback action in case of failure, it is simply left empty. Nothing to roll back.
The result of the method execute()is an object SingleRequestResultin which we simply write the response text, which will be transmitted to the caller (client) side.
Well, the methodgetActionType()should return a reference to the class of the command to which the handler is bound. This is necessary so that the dispatcher can correctly call the desired handler, and not some other one.
In addition to directly dispatching and providing Action and Result interfaces, the GWT-Dispatch library also provides integration with Google Guice. This integration allows you to register command handlers in a Guice context:
public class RpcCommandHandlerModule extends ActionHandlerModule {
@Override
protected void configureHandlers() {
bindHandler(SingleRequestHandler.class);
// . . .
}
}
* This source code was highlighted with Source Code Highlighter.
We will connect everything together with the help of a class GuiceServletContextListenerthat will “listen” to what is happening from the outside and react when the request / rpc_command / dispatch comes from the client and starts the handler of the corresponding command:
public class RpcCommandGuiceConfig extends GuiceServletContextListener {
@Override
protected Injector getInjector() {
return Guice.createInjector(new RpcCommandHandlerModule(), new DispatcherServletModule());
}
}
* This source code was highlighted with Source Code Highlighter.
The class is GuiceServletContextListenerprovided by the Guice framework as a means of integrating it with Java Servlets. The above code will perform all the necessary injects (injects) in the right places. Thus, we have the integration chain of GWT-Dispatch and Guice with Servlets closed.
The last step that is needed to make all this play as a single ensemble is to indicate the required listener in the web.xml file and the corresponding request filter:
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
guiceFilter
com.google.inject.servlet.GuiceFilter
guiceFilter
/*
net.pimgwt.server.RpcCommandGuiceConfig
index.html
* This source code was highlighted with Source Code Highlighter.
GuiceFilterconfigured to filter all requests that fall on the server side of the client. Naturally, in the url-param-instruction you can specify your own URL pattern for listening. I won’t say how to do this, these are obvious things and they are not related to the issue under consideration.
The server side is ready. It remains now to associate RPC calls from the client code with the dispatcher.
Dispatching commands in the GWT code
For calls to RPC commands in the GWT code, the interface is responsible DispatchAsync. You can implement this interface as you wish, for example, as a dispatcher who can cache the results obtained earlier. For the demo project, I chose the “boxed” implementation, DefaultDispatchAsyncagain from the delivery of GWT-Dispatch.
Below I will give only a button click handler that initiates an RPC call through the specified interface and displays the result received from the server side:
// . . .
private DispatchAsync rpcDispatcher = new DefaultDispatchAsync();
// . . .
@UiField Button singleValueTestButton;
// . . .
@UiHandler("singleValueTestButton")
public void singleValueButtonClicked(ClickEvent event) {
responseLable.setText("");
rpcDispatcher.execute(new SingleRequestAction(paramTextbox.getText()), new
AsyncCallback() {
@Override
public void onFailure(Throwable caught) {
responseLable.setText("Error occured: " +
caught.getMessage());
}
@Override
public void onSuccess(SingleRequestResult result) {
responseLable.setText(result.getResultMessage());
}
});
}
* This source code was highlighted with Source Code Highlighter.
The main point here is that we pass the initialized command to the dispatcher to send it to the server. In the callback, the result is simply retrieved from the received SingleRequestResponse server: responseLable.setText(result.getResultMessage());
Everything is written, implemented, configured and even works!
Demo project
The screenshot below shows the structure of a demo project in Project Packages panel
If you look at it, we can see that the project is implemented, the RPC is another team . The result of its execution is , which in turn contains a list of objects , which is filled in a loop in the server handler of this command.
Project for live viewing available RPC Command Demo Project
MultiRequestActionMultiRequestResultDummyDTO
Instead of a conclusion
The described RPC interaction approach does not detract from the role of simple RPC calls, which were discussed a little in the article Authorization through the User Service in GWT applications . In some cases, when you have one, a maximum of two calls to the server side in your project, it makes little sense to block the garden from Actions, Result, some Guice and others like them, because it only complicates the code. On the other hand, the use of “correct” OOP code building practices increases its structurability, readability and _add your benefit_.
Moreover, I know of several GWT projects that generally do not contain Java on the server side. So with such a server implementation, it is natural to use some common messaging format, for example, JSON or XML. But that's another story…
I look forward to constructive criticism, wishes and, of course, questions!
Thanks.