Tutorial: Get cross-site JSON via HTTP with GWT and the GWT Designer by Instantiations

Post to Twitter

This is the third tutorial that takes the GWT Team’s Getting Started Tutorial and modifies it to use Instantiations GWT Designer. If you haven’t already done the first and second tutorials in this series, now would be a good time. Also, make sure to read the “Get JSON via HTTP” section of the GWT tutorial.

A few things to disclose first off:
1. I don’t work for Instantiations. I am a happy customer as I did purchase a license of their GWT Designer tool (a trial version is available for download)
2. I’m not on the GWT team
3. I in no way represent these teams/companies and my comments are my own

If you want the project file to start with for this tutorial you will want to download the project file from the second tutorial. You will also need to have the all the software mentioned in the first tutorial installed and ready to go as well as Python (I used version 2.5.2).

If your a web developer then chances are pretty good you’ve already heard of and possibly used JSON. In a nutshell, JSON is a lightweight way to exchange data. In the second tutorial we did our communications to and from the server via RPC (Remote Procedure Calls). This time we’ll modify the code to use JSON instead and call a third-party server (we will simulate this with a Python web server on the same computer).

Assuming you’ve got everything ready go ahead and open the project now. One of the first things the GWT JSON tutorial shows is how simple the JSON format is:

[
  {
    "symbol": "BA",
    "price": 87.86,
    "change": -0.41
  },
  {
    "symbol": "KO",
    "price": 62.79,
    "change": 0.49
  },
  {
    "symbol": "JNJ",
    "price": 67.64,
    "change": 0.05
  }
]

You can see JSON is just name/value pairs and human readable. Its simpler than XML and less verbose (less eyeball noise). Keep in mind though if you do need XML support that GWT offers that as well. Today though we are focusing purely on JSON.

So how do we actually get the JSON from the server to the client? Well, fortunately GWT provides everything we need in the form of the HTTP client classes. There are three items in particular we’ll use from there:
1. RequestBuilder and calling the “sendRequest” method
2. RequestCallback which (remember the callback we had in the second tutorial?) will call “onResponseReceived” on a successful callback or “onError” if something goes wrong.
- Note: Toward the end of this tutorial we will replace the RequestBuilder code.

Now, in the GWT JSON Tutorial they use PHP outputting JSON on the server side to talk to the client. I’m going to skip ahead of the GWT Tutorial and move to the part where they setup the Python web server to simulate a third party web server.

Here is the code they use and which we will use as well for building the Python web server:

#!/usr/bin/env python2.4
#
# Copyright 2007 Google Inc. All Rights Reserved.

import BaseHTTPServer
import SimpleHTTPServer
import urllib
import random

MAX_PRICE = 100.0
MAX_PRICE_CHANGE = 0.02

class MyHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):

  def do_GET(self):
    form = {}
    if self.path.find('?') > -1:
      queryStr = self.path.split('?')[1]
      form = dict([queryParam.split('=') for queryParam in queryStr.split('&')])

    body = '['

    if 'q' in form:
      quotes = []

      for symbol in urllib.unquote_plus(form['q']).split(' '):
        price = random.random() * MAX_PRICE
        change = price * MAX_PRICE_CHANGE * (random.random() * 2.0 - 1.0)
        quotes.append(('{"symbol":"%s","price":%f,"change":%f}'
                       % (symbol, price, change)))

      body += ','.join(quotes)

    body += ']'

    if 'callback' in form:
      body = ('%s(%s);' % (form['callback'], body))

    self.send_response(200)
    self.send_header('Content-Type', 'text/javascript')
    self.send_header('Content-Length', len(body))
    self.send_header('Expires', '-1')
    self.send_header('Cache-Control', 'no-cache')
    self.send_header('Pragma', 'no-cache')
    self.end_headers()

    self.wfile.write(body)
    self.wfile.flush()
    self.connection.shutdown(1)

bhs = BaseHTTPServer.HTTPServer(('', 8000), MyHandler)
bhs.serve_forever()

This is a bare-bones server running on port 8000. Go ahead and copy in that code and save it as quoteServer.py and then run the script. On Windows it would be something like this:

C:\Temp\python quoteServer.py

Open a web browser now and navigate to: http://localhost:8000/?q=GOOG+AAPL+JAVA

Ok that looks like its working. We need to modify the “refreshWatchList” function now to call the Python server. However we first need to modify the StockWatcher.gwt.xml file and add these lines:

<inherits name="com.google.gwt.json.JSON" />
<inherits name="com.google.gwt.http.HTTP" />

It should look similar to this now:

<module>
	<inherits name="com.google.gwt.user.User"/>
	<inherits name="com.google.gwt.json.JSON" />
	<inherits name="com.google.gwt.http.HTTP" />
	<entry-point class="com.google.gwt.sample.stockwatcher.client.StockWatcher.client.StockWatcher"/>
	<servlet path="/StockPriceService" class="com.google.gwt.sample.stockwatcher.client.StockWatcher.server.StockPriceServiceImpl"/>
</module>

Move over the to StockWatcher.java file and add these imports:

import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONException;
import com.google.gwt.json.client.JSONNumber;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.json.client.JSONValue;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.http.client.URL;

import java.util.Iterator;

Add this code just under the class declaration:

private static final String JSON_URL = "http://localhost:8000/?q=";

Now lets add a couple new functions that we’ll use to notify us of any errors that might occur:

private void displayError(String error)
{
  //errorMsgLabel.setText(error);
  //errorMsgLabel.setVisible(true);
}

For now ignore the fact that the code is commented out. We want to add the errorMsgLabel to the GUI. Click on the “Design” tab to take us into the GWT Designer. Click on the Label widget and then click just below the “New Label” to place the label widget. Set it’s variable name to “errorMsgLabel” and make sure to press the “Convert field to local” button after setting the variable name. You can avoid doing this in the future if you go into the GWT Designer’s preferences settings and change this so that your variables are always declared as class level variables.

Go back into the StockWatcher.java code by clicking on the “Source” tab from the designer and un-comment those two lines of code now.

private void displayError(String error)
{
  errorMsgLabel.setText(error);
  errorMsgLabel.setVisible(true);
}

Lets style that error label. Switch back to design mode and click on the error label. Click on the “styleName” in the properties and then click on the “…” button. Once the “CSS Style Selection” dialog opens press the “add” button and then change the name to “.gwt-Label-Error” and press “Ok”.

Click on the “Edit” button. Set the color for the font to red. Your “CSS Rule” will look like this:

.gwt-Label-Error
{
	font-color: Red;
}

Assuming that your Python server is still running you can compile and then run the project to test it out so far.

I believe the expression is KABOOM! We’ve got a little problem, however, this is a problem we can fix. As the GWT JSON Tutorial mentions we just ran into the same origin policy (SOP). Their tutorial explains it very well:

SOP is a browser security measure that restricts client-side JavaScript code from interacting with resources originating from “other” websites; that is, resources loaded from any other domain and/or port. So, for example, JavaScript running in a web page on http://abc.com:80 cannot interact with data loaded from http://xyz.com or even http://abc.com:81

Read that whole section in the tutorial for possible solutions and more information that I won’t repeat here, I’ll just move onto the solution now.

Basically we need to mix some hand written JavaScript with the Java source code using JSNI.

Lets get started with JSNI. Add the following import to the StockWatcher.java file.

import com.google.gwt.core.client.JavaScriptObject;

Here is the JSNI code:

public native static void getJson(int requestId, String url, StockWatcher handler) /*-{
    var callback = "callback" + requestId;

    var script = document.createElement("script");
    script.setAttribute("src", url+callback);
    script.setAttribute("type", "text/javascript");

    window[callback] = function(jsonObj) {
      handler.@com.google.gwt.sample.stockwatcher.client.StockWatcher.client.StockWatcher::handleJsonResponse(Lcom/google/gwt/core/client/JavaScriptObject;)(jsonObj);
      window[callback + "done"] = true;
    }

    // JSON download has 1-second timeout
    setTimeout(function() {
      if (!window[callback + "done"]) {
        handler.@com.google.gwt.sample.stockwatcher.client.StockWatcher.client.StockWatcher::handleJsonResponse(Lcom/google/gwt/core/client/JavaScriptObject;)(null);
      }

      // cleanup
      document.body.removeChild(script);
      delete window[callback];
      delete window[callback + "done"];
    }, 1000);

    document.body.appendChild(script);
}-*/;

If you’ve written much JavaScript then your probably familiar with the code. There is a timer that is checking a flag to see if the JSON callback was ever called. You will also see that we need to add a “handleJsonResponse” method.

Add this variable declaration to the top of the class:

private int jsonRequestId = 0;

Now add this code to the StockWatcher class:

public void handleJsonResponse(JavaScriptObject jso) {
  if (jso == null) {
    displayError("Couldn't retrieve JSON");
    return;
  }

  updateTable(new JSONArray(jso));
}

Finally the last step. We need to modify the “refreshWatchList”:

private void refreshWatchList()
{
  if (stocks.size() == 0) {
    return;
  }

  // add watch list stock symbols to URL
  String url = JSON_URL;

  Iterator<string> iter = stocks.iterator();
  while (iter.hasNext()) {
      url += iter.next();
      if (iter.hasNext()) {
          url += "+";
      }
  }

  url = URL.encode(url) + "&callback=";
  getJson(jsonRequestId++, url, this);
}

Yeah. I know what your thinking: “Didn’t we just modify refreshWatchList a little while ago, run the app, and it broke, and now we are tossing out that code with this?” Yes, we are. Keep in mind that previous code was used to talk to a PHP page that served stock quotes via JSON according to the GWT tutorial. We didn’t do that step but I wanted to show the SOP issue, so that is why we wrote that code and now tossed it. Kinda the long way around but if you wanted to actually test the PHP part you’d be half way there!

Compile, then run the project.

Much better results this time. You can download the finished project here.

GWT Resources:
1. onGWT – is a treasure chest of news and information on everything GWT
2. gwtsite - is another great GWT news and information site
3. The GWT Team’s Blog as well as the GWT Newsgroups
4. If your using GWT Designer then you’ll want to be on their forums for the latest information and product release announcements

Update: Here are the first and second tutorials in this series.

Post to Twitter

This entry was posted in AJAX, GWT, Java. Bookmark the permalink.

10 Responses to Tutorial: Get cross-site JSON via HTTP with GWT and the GWT Designer by Instantiations

  1. Pingback: Weekly GWT Links For 9/13/08 | GWT Site

  2. Pingback: Get cross-site JSON via HTTP with GWT and the GWT Designer by Instantiations | Ajaxonomy

  3. Iwein Fuld says:

    Great post!

    This is what I planned to do with a Spring back end, if we have JSON REST support in Spring 3.0 this is going to be awesome.

  4. James Heggs says:

    Hi there

    Thank you for a very useful tutorial!

    I’m wondering whether you can help me? Using your example I have written a Java Servlet server side test to see if I can implement your tutorial.

    I can successfully call into my servlet but when the callback has finished using your example my JavascriptObject is always null?

    Am I doing something wrong on the server side? My server side code is very basic and shown below:

    String output =”[{color: \"red\",value: \"#f00\"}]“;
    resp.setContentType(“text/javascript”);
    resp.addHeader(“Pragma”, “no-cache”);
    resp.setStatus(200);

    PrintWriter out = resp.getWriter();
    out.println(output);

    Thanks in advance and thanks again for the tutorial

    Eggsy

  5. Chad Lung says:

    @Eggsy

    First, are you doing this via JSNI? My following information is not using JSNI – I’m using RequestBuilder so it might not be applicable to you but it was faster for me to code and test this way.

    I tried your code out in a servlet. First I built a web project in Netbeans 6.1 using Tomcat 6. I added a servlet to the project and used your code. I ran that on port 8084.

    I went into Eclipse then with GWT Designer and created a new project. Added a textbox (called “output_txt”) and button to the designer and had the button call the other other server.

    JSONValue jsonValue = JSONParser.parse(response.getText());
    JSONArray json_arr = jsonValue.isArray();
    JSONObject json_obj = json_arr.get(0).isObject();
    output_txt.setText(json_obj.get(“color”).toString());

    This returns “red”.

    Chad

  6. Pingback: Tutorial: RPC with GWT and the GWT Designer by Instantiations « Giant Flying Saucer

  7. Pingback: Tutorial: Getting started with GWT and the GWT Designer by Instantiations « Giant Flying Saucer

  8. Pingback: StÃ¥le’s weblog » A GWT request que

  9. Pingback: Tutorial: Creating a Stock Watcher with GWT Designer (UPDATED) « Giant Flying Saucer

  10. Pingback: Tutorial: Creating a Stock Watcher with GWT Designer (UPDATED) | Giant Flying Saucer

Comments are closed.