Chapter 6. Server Integration

As mentioned in the previous chapter, a web application without any persistent data is really just a static web site. Therefore, we want to introduce you to the ways the Google Web Toolkit can support communication between the client-side browser and the server. Using different methods, we'll show you how the sample application can be extended to support a server-side data provider.

For a developer, the easiest way to implement the communication between a client and a server is to develop as if there were no separation. And with today's communication frameworks, the reality comes pretty close. The details of communicating a message between client and server and vice versa can be abstracted away by frameworks, so you only have to concern yourself with the details at hand. GWT provides communication at two levels of abstraction: GWT RPC and Basic Ajax. Both use asynchronous communication to provide the rich UI experience expected from RIAs. GWT RPC is the more abstracted of the two and allows you to program your communication by calling a method on a Java interface. You are, however, limited to communication to a Java server. Basic Ajax is a bit less abstract, and similar to Ajax communication in JavaScript. You'll have to program each message you want to transfer, but you're free to talk to whatever server-side language as long as it supports Ajax communication. In this chapter, we'll first show you how you can communicate using GWT RPC and then show you the details of Ajax communication.

GWT RPC

With GWT RPC, almost all communication details are abstracted away. In short terms, all you need to do is define an interface with methods you want to be able to call on the server. GWT will generate a message broker for the interface, which only needs the address of the server.

Defining the Interface

The Java interface is used to define a contract for the communication. As the interface is shared between client and server, they both know which method can be called and what's expected in return. Listing 6-1 shows a contract for requesting a list of tasks. As you can see, defining the interface is really straightforward. Each service can have as many methods as necessary. There's no limitation on how many methods a service provides. The GWT RPC procedure will make sure the correct method is called at the server side.

Example 6-1. Interface for a Task Service

public interface TaskService extends RemoteService {
    List<Task> getTasks();
}

For GWT to be able to abstract everything away, there are a couple of conditions that need to be fulfilled by this interface. First of all, as it needs to be available for the client-side application, it needs to be placed inside the client package of your GWT application. Second, it should extend the GWT interface com.google.gwt.user.client.rpc.RemoteService in order for the GWT compiler to understand that it defines an RPC interface. Finally, you can only use classes that GWT knows how to send over a wire. As of GWT version 1.5, a class can be transferred if it matches the following conditions:

  • You can use all the primitives (such as char, byte, int, and double).

  • You can use the wrapper classes (Character, Byte, Integer, and Double).

  • You can use the classes java.lang.String and java.util.Date.

  • You can use an array of supported classes.

  • Any enumeration can be transferred. It will, however, be reduced to the name only. Any member variables won't be serialized and transferred.

  • You can use any serializable user-defined class. A class is considered serializable when it meets the following three conditions:

    • The class or one of its superclasses implements java.io.Serializable.

    • The class has a default constructor (no arguments) or no constructor at all. Since GWT 1.5, the access modifier of the constructor is no longer required to be public.

    • Each member itself is serializable.

There's an exception to the last condition. As GWT takes the transient keyword into consideration, everything marked as transient won't be passed between client and server. Additionally, final variables won't be transported at all, so it's a good idea to mark all final variables as transient by default as an indication of the limitation. Listing 6-2 shows an implementation of the Task class.

Example 6-2. The Task Class Definition

public class Task implements Serializable {
    private Long id;
    private String title;
    private String description;
    /* getters and setters */
    ...
}

In GWT versions prior to 1.5, there was a fourth condition to be taken into consideration. As stated, for an object to be transferred, it needs to be serializable. The transfer of an instance of java.util.Collections or any of its subclasses (List, Set, and Map) creates a problem. As these collections contain by definition instances of java.lang.Object and Object isn't serializable, the collections wouldn't be eligible for serialization. However, by using the Javadoc annotation on the methods using the collections, either as arguments or as return value, the GWT compiler could be instructed on how to serialize the collections. This had to be done using Javadoc annotations, because once again, the client side only supported Java 1.4 features. Listing 6-3 shows what the interface looks like for a simple task service with a method to get all task names.

Example 6-3. A Simple Task Service Using the @gwt.typeArgs Annotation

public interface TaskService extends RemoteService {
    /**
      * @gwt.typeArgs <com.apress.beginninggwt.gwtasks.client.Task>
      */
    List getAllTasks();
}

Since the toolkit reached version 1.5, it now supports Java 5 language features. Among these features is generics. While using generics enables a lot of new features in the Java language, primarily it makes it possible to write stronger typed code. Listings 6-4 and 6-5 show two client-side methods for logging the title of tasks. Listing 6-4 doesn't use generics; Listing 6-5 uses generics.

Example 6-4. logTaskTitles Without Generics

/**
 * @gwt.typeArgs <com.apress.begininggwt.gwtasks.client.Task>
 */
public void logTaskTitles(List tasks) {
    for(int i=0;i<tasks.size();i++) {
        Object o = tasks.get(i);
        if(o instanceof Task) {
            GWT.log(((Task)o).getTitle(), null);
        }
    }
}

Example 6-5. logTaskTitles with Generics

public void logTaskTitles(List<Task> tasks) {
    for(int i=0;i<tasks.size();i++) {
        GWT.log(tasks.get(i).getTitle(), null);
    }
}

Note

Note The GWT.log() method is a special method for logging during hosted mode development. As normal client-side logging doesn't exist in a browser, these statements will be ignored by the Java to JavaScript compiler. In fact, the compiler will optimize the code as much as possible, so the methods from Listings 6-4 and 6-5 won't generate any JavaScript code at all. In hosted mode, however, these statements are executed and the messages will be displayed in the GWT Development Shell.

As you can see, the use of generics in Listing 6-5 makes the code more strongly typed. More strongly typed java code implies less type casting, thus the code becomes cleaner and easier to maintain. Java 5 introduces more features to make the code even cleaner, such as the for-each style for loop, which is also supported in the GWT client side as of version 1.5. Listing 6-6 shows an optimized version of the logTaskTitle method, using all features of Java 5.

Example 6-6. Optimized Version of logTaskTitles

public void logTaskTitles(List<Task> tasks) {
    for(Task task : tasks) {
        GWT.log(task.getTitle(), null);
    }
}

Implementing the Server Side

For the server side of the GWT RPC mechanism, Google uses servlets. Servlets are a part of the Java Enterprise Edition (Java EE) specifications and are used to make dynamic content available on the Web. Usually servlets are used for HTTP communication only, but this is certainly no limitation of the specification. Wikipedia references Jim Driscoll's blog at java.net, where he writes that James Gosling came up with the idea for servlets back in 1995, but pushed it aside for other interests. The first servlet API is dated from 1997, so servlets have been around for quite some time and are considered relatively mature.

A servlet is an extension of a web container, able to receive a request and form an applicable response for it. The most commonly known web containers are Tomcat and Jetty. The web container receives the actual request and passes it on to a servlet based on the configuration of the container. The request is marshaled into a Request object and passed to the servlet. Hence, the servlet has full knowledge of the request's origin and content without the requirement to know how the data is transferred.

A servlet that needs to handle requests from a GWT application extends the com.google.gwt.user.server.rpc.RemoteServiceServlet class and implements your service interface. The RemoteServiceServlet class puts another layer of abstraction on the Servlet class. With the RemoteServiceServlet class, not only is the serialization abstracted away, it even determines the method that needs to be invoked and invokes it. Hence, the only task left to the developer is to implement the methods from the service interface like any other server-side service. Listing 6-7 shows a simple implementation of our TaskService interface.

Example 6-7. A Server-side Implementation of the TaskService

public class TaskServiceImpl extends RemoteServiceServlet implements TaskService {
    public List<Task> getTasks() {
        /* ... retrieve a list of tasks
        List<Task> tasks = ...
        return tasks;
    }
}

As you can see, the code is clean and simple. All complex details about receiving the request, deserializing it, parsing it, serializing the response, and returning it to the client are filtered out into the RemoteServiceServlet.

Implementing the Client Side

The next step is start using the created service. Remember that GWT RPC uses asynchronous communication. To enable the client to use this, we need to define an asynchronous version of the service interface, next to the synchronous one in the package structure. The difference is in the return values. As the client won't block execution on the call to the service, it needs a hook to be able to do something with the result of the call. For this purpose, the asynchronous interface will have a callback hook that gets executed when the call to service method returns. Listing 6-8 shows the asynchronous version of our TaskService from Listing 6-1. Instead of a zero-argument method returning a list of strings, the method gets an argument for the callback and a void return type.

Example 6-8. Asynchronous Interface of TaskService

public interface TaskServiceAsync {
    void getTasks(AsyncCallback asyncCallback);
}
Deferred Binding

Deffered Binding

Now that we've defined our asynchronous interface, we need a way to use it. You might think you need to create a client-side implementation of this interface. Well, you're almost right. You do need an implementation, but it will get generated by the GWT compiler, for good reason. As the client side can run in a lot of different environments, the implementation of the service should be aware of the different environments. However, GWT should make development easy. So instead of requiring the developer to handle every possible environment, GWT introduces a technique called deferred binding to generate this proxy object to our server-side implementation.

The primary reason Google introduced deferred binding is the wide variety of browsers available and used on the Internet. This wouldn't be a problem in itself, if browsers would follow, implement, and support the same standards. This, however, is not the case, as each browser has its own quirks and features. Deferred binding makes it possible to use each feature, while circumventing the quirks. Google used the simplest solution for this problem. It generates JavaScript specific for each of the supported browsers and serves only the required JavaScript files to the client. So if you're using Firefox, you'll get scripts with Firefox code, while Internet Explorer users will use the Internet Explorer-specific scripts.

Deferred binding is not only used to serve browser-specific JavaScript. Another strong feature it solves is internationalization. Although internationalization is covered in more detail in Chapter 8, we want to point out here that for each language, the GWT compiler generates a separate JavaScript application. As a result, you'll find a lot of files in the output directory of the compiler. For each browser and language combination, a separate file is created. Hence, a client requesting to use the application in Internet Explorer using a French locale will only receive the files needed for that purpose. It might be obvious that this will introduce heavy compile-time calculations and a relatively large number of files as a result. The result, however, is optimized for the environment of the client and requires as few bytes to transfer as possible.

Back to our example. We want to use deferred binding to get a reference to the generated proxy object. All deferred binding is done through the static method com.google.gwt.core.client.GWT.create(). This method takes a class as a parameter. This class should be the interface definition for which you need a proxy to be generated. Listing 6-9 shows how to get a reference for a server proxy. As you can see, it requires two steps. First we get the proxy, and then we tell the proxy which server it should talk to using the com.google.gwt.user.client.rpc. ServiceDefTarget class.

Example 6-9. Code Showing How to Get a Reference to a RemoteService Proxy

TaskServiceAsync service = (TaskServiceAsync) GWT.create(TaskService.class);
((ServiceDefTarget) service).setServiceEntryPoint(GWT.getModuleBaseURL() + "tasks");

A number of things need some explaining in this code snippet. First of all, you pass the interface definition to the GWT.create() method and then cast it to the asynchronous version. Also note the casting to the ServiceDefTarget class. Apparently, this interface is implemented by the proxy as well, and enables us to set the endpoint of the service. This type casting is necessary because otherwise, the compiler doesn't recognize the code as valid, because it has no knowledge of the generated proxy class. In Listing 6-9, we set the endpoint to the path where our servlet is listening; in this case it's set relative to the GWT module, extending the path with "tasks".

Now that we have our reference and have set the endpoint, we're ready to use the service and make the call to the server. Listing 6-10 shows the onModuleLoad method of our sample application.

Example 6-10. The onModuleLoad Method Loads Task Names from the Server

public void onModuleLoad() {
    TaskServiceAsync service = (TaskServiceAsync) GWT.create(TaskService.class);
    ServiceDefTarget serviceDef = (ServiceDefTarget) service;
    serviceDef.setServiceEntryPoint(GWT.getModuleBaseURL() + "tasks");
    service.getTasks(new TasksCallback());
}

As you can see, the call to the service becomes quite easy in the end. The call is made on the asynchronous service definition, so instead of a return value, there should be an argument passed to the method pointing to an implementation of GWT's AsyncCallback interface.

Before we explain the details of the AsyncCallback interface, we want to show you a good practice concerning deferred binding. If you've run the sample code in hosted mode, you might have noticed the application temporarily stalling. At a certain point, the deferred binding kicks in and the browser needs to do some processing to determine the right implementation. This is a relatively expensive procedure and you want to minimize its usage as much as you can. Therefore, you want to create a proxy only once during the application. This singleton pattern is a common practice; in GWT, however, you need to find a logical place to put the factory method. We think by putting an inner class inside the service interface, we define an easy-to-use factory. Listing 6-11 shows the inner class for our TaskService.

Example 6-11. Locator Class as a Factory for the Proxy Service

public class TaskService {
    ...
    public class Locator {
        private static TaskServiceAsync taskService;
        public static TaskServiceAsync getInstance() {
            if (taskService == null) {
                taskService = (TaskServiceAsync) GWT.create(TaskService.class);
                ServiceDefTarget serviceDef = (ServiceDefTarget) taskService;
                serviceDef.setServiceEntryPoint(GWT.getModuleBaseURL() + "task");
            }
            return taskService;
        }
    }
}

Getting a reference to our service proxy becomes as easy as calling the static getInstance method. And, because the method only calls GWT.create() once, the time to get the reference is reduced dramatically and the performance is improved. The code from Listing 6-10 would thus result in the code shown in Listing 6-12.

Example 6-12. Optimized Code for Getting a List of Tasks

public void onModuleLoad() {
    TaskServiceAsync service = TaskService.Locater.getInstance();
    service.getTasks(new TasksCallback());
}

Handling Return Values

The AsyncCallback interface defines two methods to implement. One is called when the call to the server returns successfully, not surprisingly named onSuccess. The second is named onFailure and is executed when an exception occurs somewhere along the line. Listing 6-13 gives a sample implementation of the TaskNamesCallback. It shows how we can process the tasks returned by the server by putting them in a VerticalPanel. In the last line, the VerticalPanel is put into the RootPanel to be generated into the view.

Example 6-13. The TaskNamesCallback Class Processes the Result of the Server Call

public class TasksCallback implements AsyncCallback {
    public void onSuccess(Object o) {
        List<Task> tasks = (List<Task>)o;
        VerticalPanel taskPanel = new VerticalPanel();
        for(Task task : tasks) {
            taskPanel.add(new Label(task.getTitle()));
        }
        RootPanel.get().add(taskPanel);
    }
    public void onFailure(Throwable t) {
        Window.alert(t.getMessage());
    }
}
Handling Exceptions

Handling Exceptions

The shown onFailure method only displays the received message in an alert box. Typically you want to create a default exception handler to show errors in a concise way to the user. Therefore, a good practice would be to make a default abstract implementation of the AsyncCallback class, only implementing the onFailure method. In there, you could create your own styled ErrorDialogBox to show the error message, and optionally add a hidden stacktrace as well for debugging purposes. Listings 6-14 and 6-15 respectively show the classes for the abstract callback handler and the ErrorDialogBox.

Example 6-14. An Abstract Callback Class for Centralized Error Handling

public abstract class MyAsyncCallback implements AsyncCallback {
    public void onFailure(Throwable t) {
        new ErrorDialogBox(t).show();
    }
}

Example 6-15. A Custom Dialog Box Showing a User-friendly Error Message

public class ErrorDialogBox extends DialogBox {
    public ErrorDialogBox(Throwable t) {
        super(false, true);
        setText("An error occurred.");
        VerticalPanel vp = new VerticalPanel();
        vp.add(new Label(t.getMessage()));
        DisclosurePanel panel = new DisclosurePanel("more details...");
VerticalPanel stackTracePanel = new VerticalPanel();
        for(StackTraceElement ste : t.getStackTrace()) {
            stackTracePanel.add(new Label(ste.toString()));
        }
        panel.setContent(stackTracePanel);
        vp.add(panel);
        vp.add(new Button("close", new ClickListener() {
            public void onClick(Widget sender) {
                ErrorDialogBox.this.hide();
            }
        }));
        setWidget(vp);
        center();
    }
}

The exceptions thrown from the server can't all be transferred to the client's browser. If an exception isn't a default exception supported by the JRE emulation library, it needs to comply with a small set of rules. Just like other data objects, the exception objects and all their member variables are required to implement the Serializable interface. Furthermore, they need to be defined inside the GWT client package.

Alternatives to GWT RPC

You've now seen how you can communicate with your server-side Java application using GWT RPC. Basically, this is the simplest method for communication if you're using GWT. But you don't always have a server-side application written in Java. For example, if you're creating a new front end on a legacy system, or if you have a PHP,.NET or CGI server-side application, you're restricted in the ways to implement client-server communication. Fortunately, GWT provides other ways to support communication, thus making GWT available for a much wider audience. In fact, GWT RPC really is only a small abstraction layer. The communication underneath GWT RPC is done using the basic principles of Ajax.

Basic Ajax

Ajax is a set of technologies that make it possible for web applications to give the user a highly interactive experience. Using asynchronous communication, Ajax makes it possible for the application to respond dynamically to user actions by performing background operations. Typically, Ajax calls are made from JavaScript, and using DHTML and DOM, the HTML page is updated to reflect the result. This is the basic form of Ajax, but there are other forms of Ajax that use different technologies for the implementation.

GWT also provides methods for performing simple Ajax calls. Using these methods, you can write your Ajax calls in plain Java, thus allowing GWT to handle the difficulties of the different Ajax implementations available in the different browsers. The first thing to do before you can use the Ajax functionality is enable it. You can enable it by adding an inherits statement in your module configuration. The Ajax functionality is available in a separate module called com.google.gwt.http.HTTP. Listing 6-16 shows the new module descriptor for our GWTasks sample application.

Example 6-16. GWTasks Module Descriptor with HTTP Module Enabled

<module>
    <!-- Inherit the core Web Toolkit stuff.                  -->
    <inherits name='com.google.gwt.user.User'/>
    <!--- Enable HTTP stuff support                            -->
    <inherits name="com.google.gwt.http.HTTP"/>
    <!-- Specify the app entry point class.                   -->
    <entry-point class='com.apress.beginninggwt.gwtasks.client.ui.GWTasks'/>

</module>

Now that the HTTP module is enabled, we can start programming our first basic Ajax call. You'll notice that we're going to program the communication on a much lower level than we did with the GWT RPC mechanism. As a consequence, there won't be any convenient Java interfaces to talk to. We have to create the request all by hand and specify every detail we need. There are some easy defaults, but you have to understand them to decide if they're the defaults you need.

We start by instantiating the com.google.gwt.http.client.RequestBuilder. Listing 6-17 shows what the instantiation should look like. This class helps us set up the request and configure its parameters. Its only constructor takes two arguments. The first argument specifies the type of HTTP request and can be either RequestBuilder.GET or RequestBuilder.POST. The second argument is the URL where the service is listening. This could, for example, be a URL where a basic servlet is listening for requests.

Example 6-17. Creating an Instance of the RequestBuilder

RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, requestUrl);

Now that we have the builder instantiated, we can specify the details of the request. By default, the content type specified in the request header is text/plain;charset=utf-8. You can change this value by making a call to RequestBuilder.setHeader(), or extend the values using a call to RequestBuilder.addHeader(). Both methods take a header name and value as arguments. Other methods of RequestBuilder allow you to specify username and password for authentication purposes and configure a timeout for when executing the method takes too long.

When the builder is configured, we can send our request to the server. Based on whether you're using a GET or a POST, you can pass arguments with the request in two different ways. With a GET request, the body of the message remains empty. Therefore, any argument you want to pass to the server must be encoded into the request URL as query parameters. You can recognize query parameters in a URL using a question mark. Multiple query parameters can be placed after the question mark as name-value pairs separated by ampersands. For example, the query ?firstname=bram&lastname=smeets contains two parameters as name-value pairs. Keep in mind that the URL doesn't allow all characters to be used in these name-value pairs. Special consideration should be taken when using a small set of characters. Some characters, for example the space, should be encoded for the URL to remain valid. Fortunately GWT provides a convenience class to perform this encoding called com.google.gwt.http.client.URL. This class provides an encode() and a decode() method so you don't have to bother yourself with escaping the special characters.

Going back to our GWTask example, there are some methods that could easily be implemented using the basic Ajax functionality. Listing 6-18 shows as an example how the login method of the SecurityManager could be implemented. The username and password are for sake of the example passed as query parameters.

Example 6-18. Example of an Ajax Call Using the RequestBuilder

public class BasicAjaxSecurityManager implements SecurityManager {
    ....
    public void login(String name, String password, 
Example of an Ajax Call Using the RequestBuilder
AsyncCallback<Boolean> callback) { String requestUrl = URL.encode("login?username=" + username
Example of an Ajax Call Using the RequestBuilder
+ "&password=" + password); RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, requestUrl); builder.setTimeoutMillis(5000); builder.setCallback(new RequestCallback() { public void onError(Request request, Throwable exception) { callback.onFailure(exception); } public void onResponseReceived(Request request, Response response) { if(response.getStatusCode() == 200) { callback.onSuccess(true); } else { callback.onSuccess(false); } } }); try { builder.send(); } catch(RequestException re) { callback.onFailure(re); } } }

JSON

For authenticating a user, only a little data needs to be sent between client and server. However, if we implement the rest of the interfaces, we soon realize that the data sent between client and server becomes more and more complex. We could define some XML schema for the communication and use some client-side marshaller to convert our data structures, but this becomes cumbersome and isn't easy to maintain. Our alternative is the JavaScript Object Notation (JSON). JSON is an easy-to-use data format that can be used to exchange data between systems of different origin. JSON uses a subset of the JavaScript types to define structured data. By no means does it provide any way to transport the data; its only purpose is to structure the data so that it can be easily read and interpreted both by men and machine. At the moment, there are a wide range of JSON implementations available for just as wide a set of languages. So no matter whether you're a C# developer or work with Ruby, Python or Java, there are libraries available that allow you to work with the JSON data format.

The Principles of JSON

So now you're wondering what the deal is with this data format. Well, the designers of JSON kept it real easy. Only the basic types are available. Besides the values null, true, and false, you can have string values and number values. Furthermore, you can create data structures of these values by using an array or an object. The array is like a list of elements, but it isn't type restricted. So there isn't a constriction on putting both string values and number values inside the same array. The object data structure is like a java.util.Map; it consists of key-value pairs. It's ideal for mapping a plain Java object, which Listing 6-19 shows for our Category object. Although this is the real representation, it isn't easy to read and interpret for human beings. However, if we make it pretty and give it some structure, as seen in Listing 6-20, the contents of this JSON data object become much clearer.

Example 6-19. JSON Representation of our GWTasks Category Object

{id:372,name:"work",description:"all tasks related to work",subCategories: 
JSON Representation of our GWTasks Category Object
[{id:491,name:"Calls",description:"telephone calls",subCategories:[]},{id:
JSON Representation of our GWTasks Category Object
284,name:"Meetings",description:"Upcoming meetings",subCategories:[]}]}

Example 6-20. Pretty-printed JSON Representation of our GWTasks Category

{
    id : 372, name : "work",
    description : "all tasks related to work",
    subCategories : [
        {
            id : 491,
            name : "Calls",
            description : "telephone calls",
            subCategories : []
        },
        {
            id : 284,
            name : "Meetings",
            description : "Upcoming meetings",
            subCategories : []
        }
    }
}

Now that we know what the data looks like, the next step is to actually work with the data. The basic principle behind the JSON data format is that it can be interpreted by machines. In JavaScript, this is realized using the eval() function. Note that this function is basic JavaScript; it isn't an addition to the language. The eval() function interprets a string value and executes it as if it were a piece of code. As JSON has its origin in JavaScript, the JSON format complies with the code structure of JavaScript. Therefore, the result of a call to the eval() function with a JSON string as an argument results in a variable with the interpretation of the data structure as its value. Listing 6-21 shows exactly what we could do if we parse the JSON string value of Listing 6-19.

Example 6-21. Working with JSON Objects in JavaScript

var category = eval(JSONString);
alert(category.name);

JSON and GWT

Because GWT is primarily written in Java, the guys at Google have written some specific classes to cover the JavaScript support, like the eval() function, which is needed for JSON. Introducing these classes makes compiling the classes to JavaScript easy. Again, the JSON support is a separate GWT module and needs to be inherited in the GWT module descriptor to be able to use JSON support. We can extend our descriptor as shown in Listing 6-22.

Example 6-22. GWTasks Module Descriptor with JSON Support

<module>
    <!-- Inherit the core Web Toolkit stuff.                  -->
    <inherits name='com.google.gwt.user.User'/>
    <inherits name='com.google.gwt.json.JSON'/>
    <inherits name='com.google.gwt.http.HTTP'/>
    <!-- Specify the app entry point class.                   -->
    <entry-point class='com.apress.beginninggwt.gwtasks.client.ui.GWTasks'/>
</module>

We're now able to start using JSON in our GWT client code. The wrapper class for the eval function is com.google.gwt.json.client.JSONParser. This class has one static method called parse that interprets the JSON data structure passed as a string. The result of the parse method is an instance of the abstract class JSONValue. GWT comes with a set of JSON-specific classes to represent the limited data types allowed in JSON. The JSONValue class is the abstract parent of all of these classes. The classes available are JSONBoolean, JSONString, JSONNumber, JSONNull, JSONArray, and JSONObject. The JSONValue object contains test methods for each possible type. These methods return the type-specific value if one is applicable, or otherwise null. With this knowledge, we can go back to our GWTasks application and create a new implementation for our DataManager. The first method we're going to implement is getCategories. Listing 6-23 shows a first try at implementing this method. Remember that we get back a JSON structure consisting of an array of categories with subcategories.

Example 6-23. JSON Implementation of getCategories

public void getCategories(final AsyncCallback<List<Category>> callback) {
    // make some call to the server and capture the response in a String
    String response = ...
    List<Category> categories = new ArrayList<Category>();
    JSONValue responseValue = JSONParser.parse(response);
    JSONArray responseArray = responseValue.isArray();
    if(responseArray != null) {
        for(int i=0;i<responseArray.size();i++) {
            JSONObject responseCategory = responseArray.get(i).isObject();
            Category category = parseCategory(responseCategory);
            categories.add(category);
        }
    }
    callback.onSuccess(categories);
}
private Category parseCategory(JSONObject cat) {
    Category category = new Category();
    category.setId(((Double)cat.get("id").isNumber().doubleValue()).longValue());
    category.setName(cat.get("name").isString().stringValue());
    category.setDescription(cat.get("description").isString().stringValue());
    JSONArray subCategories = cat.get("subCategories").isArray();
    if(subCategories != null) {
        for(int i=0;i<subCategories.size();i++) {
            Category subCategory = parseCategory(subCategories.get(i).isObject());
            category.addChildCategory(subCategory);
        }
    }
    return category;
}

What's left is sending the request to the server. Note in Listing 6-22 that we've also included the HTTP module, and remember that JSON only defines data structures. For the transport, we still need other technologies. Fortunately, we can easily combine JSON with the basic Ajax functionality of GWT. For convenience, we define a new class to handle the communication specifics. Listing 6-24 shows this Server class. It contains one static method and can be used to send JSON structures to the server and back.

Example 6-24. Simple Server Class for Handling Ajax Transport Between Server and Client

public class Server {
    public static void post(String url, String requestData,
            final AsyncCallback<String> callback) {
        RequestBuilder builder =
            new RequestBuilder(RequestBuilder.POST, URL.encode(url));
try {
            builder.sendRequest(requestData, new RequestCallback() {
                public void onError(Request request, Throwable throwable) {
                    callback.onFailure(throwable);
                }
                public void onResponseReceived(Request request, Response response) {
                    if (response.getStatusCode() != 200) {
                        callback.onFailure(
                            new RequestException(response.getStatusText()));
                    }
                    callback.onSuccess(response.getText());
                }
            });
        } catch (RequestException re) {
            callback.onFailure(re);
        }
    }
}

Only a small adjustment to our DataManager is necessary to make it use the new transport service. Listing 6-25 shows our final implementation for the getCategories() method.

Example 6-25. The Final Version of the DataManager.getCategories() Method

public void getCategories(final AsyncCallback<List<Category>> callback) {
    Server.post("categories", "", new AsyncCallback<String>() {
        public void onFailure(Throwable caught) {
            callback.onSuccess(new ArrayList<Category>());
        }
        public void onSuccess(String response) {
            JSONValue responseValue = JSONParser.parse(response);
            JSONArray responseArray = responseValue.isArray();
            if(responseArray != null) {
                List<Category> categories = new ArrayList<Category>();
                for(int i=0;i<responseArray.size();i++) {
                    JSONObject responseCategory = responseArray.get(i).isObject();
                    Category category = parseCategory(responseCategory);
                    categories.add(category);
                }
                callback.onSuccess(categories);
            }
        }
    });
}

Summary

In the previous chapter, you saw how we created a complex user interface. The final step was to organize the service methods in three classes and create in-memory implementations. At that point, the application was usable, but nothing was persisted. The next step was communication with a remote system, so data could be stored in a central place.

Continuing from the previous chapter, we've shown how you can interact with a remote system in GWT. We explained how the asynchronous character of GWT delivers a highly interactive experience to the user, as expected from a rich Internet application. Using GWT RPC, communication with a remote system becomes as easy as calling a method on a Java interface. We've shown you how to define such an interface, create a server-side implementation, and use it from the client-side code. Unfortunately, there are some limitations to GWT RPC. First of all, it can only be used if the server-side application is written in Java and supports servlets. Furthermore, GWT RPC only supports a limited set of the classes from Java Runtime Environment. However, knowing these limitations is as good as solving them, because you can easily extend the class support by adding your own classes.

As an alternative, you can use GWT's basic Ajax support GWT to talk to any other system that supports Ajax. Thus, even though the complexity of the code goes up due to the lower level of communication, GWT still provides clean support for communicating with the server, independent of the language in which the server-side code is written.

Even communicating complex data structures can easily be implemented using the JSON data format. This language-independent standard is by definition ideal for communicating between two systems if the implementation languages are different. This is because of the real simplicity by which the data can be parsed at the client side, in comparison with other options such as XML.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.252.126