Google Web Toolkit and Web Services: The XML Way

By on Friday, November 4th, 2011 in Technical | Related Software Packages: , | Keywords: ,

Modern web applications that make heavy use of Ajax benefit from mashing up data from different services. Applications built with the Google Web Toolkit (GWT) can work with both XML and JSON data. In this two-part series, we’ll build a project to show how to get and process XML and JSON data, and deal with sundry matters such as security restrictions and server-side proxies. What you’ll learn here should help you deal with all kinds of services and enhance your GWT applications.

In order to profit from this article, you need to have a working knowledge of GWT, the basics of XML, and some JavaScript practice.

To show how to process XML and JSON services, we first need a simple API that can deliver data in both these formats. The Google Geocoding API fits the bill. Geocoding is a process that can derive geographic coordinates (usually latitude and longitude) from an address such as “10901 W 120th Ave, Broomfield, CO.” (There’s also a reverse geocoding service to turn coordinates into an address, but we won’t be using it.) You can then use the returned data to show a marker on a map or for navigation purposes. We’ll use the current version 3 of this API here, but we’ll also use V2 for our JSONP examples in part 2 of this series.

To show what you can do with GWT, we’ll write a simple application, based on a form where a user can enter an address to look up, a couple of output fields for the geographic coordinates of the place, and a text area for some extra information about the found site. (See figure below.) Check the source code for details, but building the form is straightforward. It’s based on a HTML simple design to which widgets are added through GWT.

Our basic form, based on straightforward GWT

We’ll use three ways of getting geocoding data from the remote server; each of the three buttons will invoke a different one. If the API call happens to fail, the application will give a warning. On success, it will show the latitude and longitude of the address and some extra information.

Getting Data from Any Server

Getting data from the server where your GWT application is loaded is easy to do and well-documented. You can use both Remote Procedure Calls (RPC) for connecting with your own Java-based GWT servlets, and Ajax for other services.

However, you cannot use these methods to connect to other remote servers, because of the Same Origin Policy (SOP), a security restriction built into browsers that prevents any page from loading data from any URL other than its origin. The origin of a page is considered to be the protocol plus host plus port, so there’s no way to access data from a URL that differs in any of those three parts. If your page was loaded from http://your.site/some/place/within/it, SOP won’t let you get data from addresses such as https://your.site (different protocol), http://another.site (different host), or http://your.site:8080 (different port; the standard is 80).

Why is SOP a good idea? Because it ensures that whenever you view a site, you actually execute code downloaded from that site, and only that site. If phishers could disable the SOP, they could set up pages that use actual code from legitimate servers, plus some extra rogue code to monitor your interactions or lift your passwords, for example.

If you want to access web services from your client code, SOP becomes an obstacle. Connecting to remote servers is out of the question, and you might even have problems connecting to services of your own, if they require a different port or protocol.

There are some ways out. One method is Cross-Origin Resource Sharing (CORS), the W3C Access Control Specification, but that isn’t a complete solution, at least for the time being, because not all browsers implement it. A better solution is to implement a server-side proxy: the client code requests a service from the proxy, the proxy server connects to the requested remote server and get whatever data it provides, then the proxy sends the data back to the application for processing. For our example, a simple proxy capable of dealing with GET and POST requests is enough (in fact we can do with just GET requests) but you could extend it for full REST compliance, or for working with SOAP.

We will define a ServerProxy service with two calls, getFromRemoteServer(…) and postToRemoteServer(…). (For extra security, we could have used GWT’s RPC Cross-Site Request Forgery (XSRF) safe servlets, but given that our application cannot produce any server-side changes, going with the usual servlets is safe enough.) We’ll define a ServerProxy interface (see listing below) for client-side use. Only one parameter is required: the URL of the remote service, complete even with all required parameters. Finally, in case an error happens, we’ll throw an exception.

package com.google.gwt.kereki.xmlJsonTest.client;

import com.google.gwt.kereki.xmlJsonTest.shared.ServerProxyException;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

@RemoteServiceRelativePath("serverproxy")
public interface ServerProxy extends RemoteService {

    public String getFromRemoteServer(final String serviceUrl)
        throws ServerProxyException;

    public String postToRemoteServer(final String serviceUrl)
        throws ServerProxyException;
}

According to GWT RPC rules, we must also define the asynchronous equivalent, ServerProxyAsync (see listing below.) We must also add a callback that’s invoked when results come back. Since both XML and JSON are plain text, the callback will be of AsyncCallback<String> class.

package com.google.gwt.kereki.xmlJsonTest.client;

import com.google.gwt.user.client.rpc.AsyncCallback;

public interface ServerProxyAsync {

    void getFromRemoteServer(
        final String serviceUrl,
        AsyncCallback<String> callback);

    void postToRemoteServer(
        final String serviceUrl,
        AsyncCallback<String> callback);
}

On the server side, we have to provide the actual implementation of the code that provides the service, named ServerProxyImpl; see listing below. Since we’re using only GET calls, we can omit the POST part of the logic, but you can find it in the source code files provided with this article. As for error handling, note that we pared it to a minimum; should there be any kind of problem, a ServerProxyException, with no extra text or explanations, will be thrown; obviously you could (and should!) do a bit better for actual production code. The exception just extends Exception and has no code whatsoever, so we don’t need to show its code here.

package com.google.gwt.kereki.xmlJsonTest.server;

...several import lines...

@SuppressWarnings("serial")
public class ServerProxyImpl extends RemoteServiceServlet implements
    com.google.gwt.kereki.xmlJsonTest.client.ServerProxy {

    @Override
    public String getFromRemoteServer(final String serviceUrl)
        throws ServerProxyException {

        String result= "";

        try {
            final URL url= new URL(serviceUrl);

            final BufferedReader in= new BufferedReader(new InputStreamReader(
                url.openStream()));

            String inputLine;
            while ((inputLine= in.readLine()) != null) {
                result+= inputLine;
            }

            in.close();
            return result;

        } catch (final Exception e) {
            throw new ServerProxyException();
        }
    }

    @Override
    public String postToRemoteServer(final String serviceUrl)
        throws ServerProxyException {
        //
        // we won't be using this code
        // see its implementation in the provided source files
        //
    }
}

We’ll see how this code is used in a moment. With this proxy code installed, we can connect our GWT client to any remote server; let’s move on to our example.

Finding an Address Through the Google API

Let’s turn back to the Google Geocoding API. A call to this API can have several parameters, but at its simplest it would look like http://maps.googleapis.com/maps/api/geocode/xml?address=address.to.be.geocoded&sensor=false. You could substitute json for xml in order to change the output format. You can add extra parameters to limit the search to a region, or to produce results in a given language, but these are not relevant to our example, so we won’t be using them.

The answer to a query, in XML format, comprises one or more result elements, each with data that provides further descriptions of the found location. (See sample XML below.) We will (gratuitously!) assume that the first match is the best one, and we’ll show that result in our application.

<?xml version="1.0" encoding="UTF-8"?>
<GeocodeResponse>
 <status>OK</status>
 <result>
  <type>street_address</type>
  <formatted_address>10901 W 120th Ave, Broomfield, CO 80021, USA</formatted_address>
  <address_component>
   <long_name>10901</long_name>
   <short_name>10901</short_name>
   <type>street_number</type>
  </address_component>
  <address_component>
   <long_name>W 120th Ave</long_name>
   <short_name>W 120th Ave</short_name>
   <type>route</type>
  </address_component>

  ...several lines snipped...

  <address_component>
   <long_name>80021</long_name>
   <short_name>80021</short_name>
   <type>postal_code</type>
  </address_component>
  <geometry>
   <location>
    <lat>39.9159880</lat>
    <lng>-105.1178850</lng>
   </location>
   <location_type>ROOFTOP</location_type>
   <viewport><em>...several lines snipped...</em></viewport>
  </geometry>
 </result>
</GeocodeResponse>

Notice there are no attributes in the XML code; it’s just nodes and values. Since attributes are processed a bit differently, it would be better if the XML result included a few somewhere, so we can see the required code. We can cheat a little and modify the server proxy so it will add a couple of invented, random (useless and unneeded!) attributes to the <location> element by adding the code below to the while loop of the getFromRemoteServer(…) method. We will display these attributes in our XML processing code.

if (inputLine.trim().equals("<location>")) {
    inputLine= "<location useless='"
        + (new BigInteger(48, new Random()).toString(32))
        + "' unneeded='" + (new BigInteger(48, new Random()))
        + "'>";
}

We now have a service (with some fakery thrown in) that we can use, and a proxy with which to access it. Let’s turn to our simple GWT application and mesh everything together.

Getting and Processing XML

Let’s go back to our GWT form and work on getting and processing XML code. The code for the first command button is as follows:

private final ServerProxyAsync serverProxy= GWT.create(ServerProxy.class);

getXmlFromServerButton.addClickHandler(new ClickHandler() {
    @Override
    public void onClick(ClickEvent event) {
        serverProxy.getFromRemoteServer(
            "http://maps.googleapis.com/maps/api/geocode/xml?address="
                + URL.encode(addressField.getText()) + "&sensor=false",
            new AsyncCallback<String>() {

                @Override
                public void onFailure(Throwable caught) {
                    Window.alert("Failure getting XML through proxy");
                }

                @Override
                public void onSuccess(String result) {
                    processXml(result);
                }
            });
        }
    }
);

On detecting a click event, we build up the address of the geocoding service, and then call our server proxy through the serverProxy variable, using an anonymous inner class for the required AsyncCallback. If the call fails, we show an appropriate message (adequate error processing isn’t our goal here); on success, we call the processXml(…) method, below. Note the use of URL.encode(…) when setting up the service call; all values must be “percent encoded” in order to have a valid URL. Also note that in order to process XML with GWT, you have to add <inherits name="com.google.gwt.xml.XML"/> to your application gwt.xml file.

public void processXml(final String xml) {
    final Document xmlDoc= XMLParser.parse(xml);
    final Element root= xmlDoc.getDocumentElement();
    XMLParser.removeWhitespace(xmlDoc);

    final NodeList results= root.getElementsByTagName("result");

    final Element firstResult= (Element) results.item(0);

    final NodeList addressParts= firstResult
        .getElementsByTagName("address_component");

    final Element geometry= (Element) firstResult.getElementsByTagName(
        "geometry").item(0);

    final Element location= (Element) geometry.getElementsByTagName(
        "location").item(0);

    final String latitude= location.getElementsByTagName("lat").item(0)
        .getFirstChild().getNodeValue();

    final String longitude= location.getElementsByTagName("lng").item(0)
        .getFirstChild().getNodeValue();

    latitudeField.setText(latitude);
    longitudeField.setText(longitude);

    String detailedAddress= "";

    /*
     * The following is just to show how to get the values of the invented
     * "useless" and "unneeded" attributes of the <location> element
     */
    detailedAddress= "USELESS=" + location.getAttribute("useless") + "\n"
        + "UNNEEDED=" + location.getAttribute("unneeded") + "\n\n";

    /*
     * Let's build up the address description by joining all the (possibly
     * more than one) <type> and (only one) <long_name> values
     */
    for (int i= 0; i < addressParts.getLength(); i++) {
        Element components= (Element) addressParts.item(i);

        /*
         * Loop over all the <type> nodes
         */
        String separator= "";

        NodeList types= components.getElementsByTagName("type");
        for (int j= 0; j < types.getLength(); j++) {
            detailedAddress= detailedAddress + separator
                + types.item(j).getFirstChild().getNodeValue();
            separator= ", ";
        }

        /*
         * Add the <long_name> node value
         */
        detailedAddress= detailedAddress
            + ": "
            + components.getElementsByTagName("long_name").item(0)
                .getFirstChild().getNodeValue() + "\n";
    }
    fullAddressField.setText(detailedAddress);
}

Processing XML starts by parsing the original text to build a Document. You can get the document root with the getDocumentElement(…) method; in this case, the <GeoResponse> element. You must always remember to removeWhiteSpace(…) from the document; GWT’s XML parsing is based on the browser’s own JavaScript DOM routines, and browsers usually create empty text nodes for all sorts of whitespace (tabs, line breaks, and so on) which, if not removed, will complicate and possibly even break your code. Also note that your code will usually require plenty of casts; it’s up to you to decide what kind of nodes you will be processing, and let the compiler know.

Since there may be several <result> elements, we’ll use the getElementsByTagName(…) method to get a list of all such nodes. We can then use item(0) to get at the first element in the list. To get a specific node within an element, we use the getElementsByTagName(…).item(0) pattern; see geometry and location, for example. Then, in order to access the value of a node, we need the getFirstChild().getNodeValue() pattern; see how we get the latitude and longitude values. If you need to process all results, use getLength(…) to get the node count; for an example of this, see how we process all the type values of the different addressParts. Finally, we get the values of the (invented) attributes of the <location> node by means of the getAttribute(…) method. You can see the results of this code in the following figure.

The result of using the XML service, including two invented attributes

There are a few more XML methods you might want to use, such as hasAttributes(…) and getAttributes(…) to determine if a node has attributes, and to get a map with all of them; getFirstChild(…), getLastChild(…), and getChildNodes(…) to access the first, the last, and the complete list of children of a node; and getNextSibling(…) and getPreviousSibling(…) to move between children, as documented), though you can usually make do with just the ones shown above.

In Conclusion

This is all the code you need to use GWT to contact a remote server through a proxy, get an XML response, and then extract data from it. In our second part, we’ll change from XML to JSON, and also consider JSONP, which allows us to do without any proxy.

Download the Open Source Support Evaluation Kit

Related posts:

  1. Google Web Toolkit and Web Services: The JSON/JSONP Way
  2. Generating Web Charts with Gnuplot and Web Scripting
  3. Throw a cURL at Your Web Work
  4. Choosing the Right Java Web Development Framework
  5. Simplify Administration with Directory Services

Related Open-Source Packages

GWT: See all GWT Articles » Get GWT Support at OLEX »
JSON: See all JSON Articles » Get JSON Support at OLEX »

Federico Kereki

Federico Kereki is a Uruguayan systems engineer with more than 20 years of experience developing systems, doing consulting work, and teaching at universities. He currently works with a good jumble of acronyms: GWT, PHP, SOA, REST, AJAX, XML, JSON, and of course FLOSS! He recently wrote Essential GWT for intermediate to advanced programmers.

10 Responses to “Google Web Toolkit and Web Services: The XML Way”

  1. Awesome article! I enjoyed it!

  2. Federico Kereki says:

    Glad you liked it! Be sure to check the second part, for a JSON point of view; hope it works for you!

  3. Tom Watson says:

    Thanks Federico,

    Very informative. I am looking forward to the next article on JSON.

  4. [...] the first part of this series, we studied how to implement and use a server-side proxy to contact a remote service, and how to [...]

  5. Very well written article. It will be useful to everyone who usess it, as well as myself. Keep doing what you are doing for sure i will check out more posts.

  6. Federico Kereki says:

    Thanks a lot! Will be doing more on GWT; hope they’ll be of use!

  7. [...] Google Web Toolkit and Web Services: The XML Way | Wazi – In this two-part series, we’ll build a project to show how to get and process XML and JSON data, and deal with sundry matters such as security restrictions and server-side proxies. What you’ll learn here should help you deal with all kinds of services and enhance your GWT applications. [...]

  8. Newbie says:

    I tried to run this example but when i click on “Get XML from Server” or “Get JSON from Server” i get the error “Failure getting XML through proxy”/”Failure getting JSON through proxy”
    It works fine for “GET JSON using JSONP”
    I am new to GWT, any help is appreciated!
    Thanks

  9. Newbie says:

    In the console it says :

    Mar 5, 2012 3:38:17 PM com.google.appengine.tools.development.LocalResourceFileServlet doGet
    WARNING: No file found for: /xmljsontest/serverproxy

Leave a Reply

© 2012 OpenLogic, Inc. | Licensing | Privacy Policy | Terms of Use

Bad Behavior has blocked 2283 access attempts in the last 7 days.