The Source for Java Technology Collaboration
User: Password:



Richard Bair

Richard Bair's Blog

XMLHttpRequest and Swing

Posted by rbair on August 31, 2006 at 05:01 PM | Comments (11)

For a while now (since well before we started the SwingX-WS project) I've been interested in making Swing accessible to the hordes of web authors out there. This demographic is being actively persued by Adobe(Flex) and Microsoft. Web folks are doing more and more to enhance their web applications via AJAX in order to get closer to a rich client experience. At some point it makes sense to use a rich client toolkit -- and each of us (Flex/Flash, Avalon/XAML/WPF, and Swing) want to be the toolkit used. This space (called Rich Internet Application or Smart Clients) is, I believe, a real growth area.

Our job, on the Swing team, is to ensure that when a development team hits that inflection point and wants to write a rich client app, they can do so in Java with Swing with the minimum amount of fuss. It is in this vein that XMLHttpRequest -- the Java implementation -- came to be.

In SwingLabs and on the Swing team we've long been aware of the difficulties people face when trying to write responsive GUI apps due to the inherent difficulties in writing threaded apps. In the past I've blogged about SwingWorker and BackgroundWorker -- two different approaches to this problem. Both of these are reasonable ways to simplify threading issues. The XMLHttpRequest code I'm introducing today also deals with threading issues -- but isn't as general purpose as SwingWorker or BackgroundWorker.

In this installment (I'm getting to the point, I promise!) I introduce the XMLHttpRequest and JSONHttpRequest beans, both part of the SwingX-WS project. I based the design and implementation on the W3C Working Draft Specification for XMLHttpRequest. So the good news is, for those of you familiar with XHR, you have essentially the same API available now for your Swing apps.

NOTE: The classes mentioned in this blog have been refactored into the org.jdesktop.http.async package of SwingX-WS. Also, the class HttpRequest has been renamed AsyncHttpRequest.

Before I get into the mechanics of making the call (if you care, just scroll down to the examples below), I want to take a few bytes of bandwidth and describe the kind of applications that would care about XMLHttpRequest. If you have an application that makes calls to REST web services (either structured or ad hoc), you'd probably care. If you are writing a fairly lightweight application -- heavy on graphics and light on data objects -- that uses, say, online photos, you'd care. If you were an AJAX saavy developer who wanted to try your hand at a little Swing -- you'd care.

At the heart of the API is a class called HttpRequest. This little fella is really where all the fun is. It implements almost all of the API (all, in fact, except for getResponseXML) defined in the aforementioned W3C specification. HttpRequest is a concrete implementation, which can be used directly.

The general concepts are pretty simple to grasp. Here's a basic code sample:


        final HttpRequest req = new HttpRequest();
        req.addOnReadyStateChangedListener(new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent evt) {
                if (evt.getNewValue() == ReadyState.LOADED) {
                    String response = req.getResponseText();
                    //do what you like with the response here
                    //for example, if the XML was XMLDecoder
                    //compatible:
                    java.beans.XMLDecoder decoder = new java.beans.XMLDecoder(
                        new ByteArrayInputStream(response.getBytes()));
                    Customer c = (Customer)decoder.readObject();
                    decoder.close();
                }
            }
        });
        try {
            req.open(HttpMethod.GET, new URL("http://MyCompany.com/someService?customer=\"A2342DAD\"/"));
            req.send();
        } catch (Exception e) {
            e.printStackTrace();
        }

Those familiar with XMLHttpRequest will find this code to be really straitforward. In fact, the course grained API calls are all exactly the same:

  1. Create the HttpRequest object
  2. Attach an onreadystatechanged event listener
  3. Call "open", passing in the http method and URL
  4. Call "send"
  5. In the onReadyStateChanged event listener, listen for the ready state of LOADED.
  6. Do the stuff

This basic mechanism could be used rather cleanly for all kinds of scenarios. In this example I use it as a mechanism for deserializing some Customer java bean. I could also have passed the responseText to a SAX/STAX/DOM parser. Or maybe the text is HTML and I wanted to display it in an XHTML renderer. Maybe it was JAXX XML document, and represented some Swing widgets that should be created. Maybe it is some JavaScript code which, when used with JDK 6 and the scripting APIs could modify the Swing UI in some way. Perhaps it is JSON. Perhaps it is Java2D drawing code. Perhaps a serialized set of Painters. You get the drift.

In addition to getting data, HttpRequest can also be used to send data. Here is some simple code to do so:


        req.open(HttpMethod.POST, new URL("http://MyCompany.com/submit?Customer=\"A2342DAD\""));
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        java.beans.XMLEncoder e = new java.beans.XMLEncoder(out);
        e.writeObject(customer);
        e.flush();
        req.send(out.toString());
        out.close();

This example reverses the last -- it constructs XML representing this customer bean and, using HTTP POST, sends this data to the web server.

For convenience, I extended HttpRequest with XMLHttpRequest and JSONHttpRequest. XMLHttpRequest simply adds the getResponseXML method to finish the XMLHttpRequest spec. This method returns a DOM object. Not always the most useful API to work with from within Java, but sometimes quite powerful when combined with XPath. A silly example:


     Document dom = req.getResponseXML();
     XPath xpath = XPathFactory.newInstance().newXPath();
     try {
         XPathExpression exp = xpath.compile("//p");
         NodeList nodes = (NodeList)exp.evaluate(dom, XPathConstants.NODESET);
         for (int i=0; i<nodes.getLength(); i++) { 
             System.out.println(nodes.item(i).getTextContent());
         }
     } catch (Exception e) {
         e.printStackTrace();
     }

JSON is another alternative. Not as expressive or structured as XML but much faster to parse and easy to work with loosey-goosey. JSONHttpRequest supplies two new methods: getResponseJSON which returns a JSONObject, and getResponseMap returning a Map -- essentially what JSON boils down too. Ok, one more example and I'm calling it quites for today:


    public static void main(String[] args) {
        final JsonHttpRequest req = new JsonHttpRequest();
        req.addOnReadyStateChangedListener(new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent evt) {
                if (evt.getNewValue() == ReadyState.LOADED) {
                    //extracting the data using JSON Java library
                    JSONObject json = req.getResponseJSON();
                    json = json.optJSONObject("ResultSet");
                    System.out.println("Total Results Avail.: " + json.optString("totalResultsAvailable"));
                    System.out.println("Results: ");
                    JSONArray results = json.optJSONArray("Result");
                    for (int i=0; i<results.length(); i++) {
                        System.out.println("\tTitle: " + results.optJSONObject(i).optString("Title"));
                    }
                    
                    //extracting the data just using maps & arrays
                    Map map = (Map)req.getResponseMap().get("ResultSet");
                    System.out.println("Total Results Avail.: " + map.get("totalResultsAvailable"));
                    System.out.println("Results: ");
                    Object[] results2 = (Object[])map.get("Result");
                    for (int i=0; i<results2.length; i++) {
                        Map m = (Map)results2[i];
                        System.out.println("\tTitle: " + m.get("Title"));
                    }
                }
            }
        });
        try {
            req.open(HttpMethod.GET, new URL("http://api.search.yahoo.com/ImageSearchService/V1/imageSearch?appid=YahooDemo&query=JavaOne&output=json"));
            req.send();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

I hope you found this interesting. Hopefully in a future installment I'll get to showing a real live Swing demo doing something useful, besides printing junk out to System.out. Romain? Come on buddy, give a brother a hand!


Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • Cool stuff man! I'll have to work on my factories for SwingX that will let you create nice UIs on top of that easily :)

    Posted by: gfx on August 31, 2006 at 05:11 PM

  • Hi Rich,

    Have you considered using JAX-WS ( https://jax-ws.dev.java.net/ )?

    Posted by: idk on August 31, 2006 at 06:48 PM

  • Romain: gimme those factories! :-). We especially need to make the progress notification job a no-brainer.

    idk: I've used JAX-WS in some previous demos, but not extensively. It felt like a heavier solution than XMLHttpRequest . Is there a link to some code examples of using JAX-WS with REST? I'd really be interested in comparing the first code sample above. What would that look like in JAX-WS?

    Posted by: rbair on September 01, 2006 at 05:21 AM

  • Richard Bair: Can't wait for some framework to handle multiple jobs. Ideally, there would be an API to queue multiple server requests with an ability to track progress and cancel each job.

    You guys already have some work done on status and progress in Swingx. Just need to add some type of Cancellable interface and a class that can combine progress notifications from multiple jobs and emit summarized progress events. Say if at least one job has indeterminable work load, then the combined job will be indeterminable. Otherwise, you can calculate the sum of workloads of all the jobs and emit determinable progress events.

    Posted by: nsushkin on September 01, 2006 at 11:22 AM

  • Richard Bair: there is a blog you might look at http://weblogs.java.net/blog/mhadley/archive/2006/03/restful_web_ser_1.html

    Posted by: idk on September 01, 2006 at 01:36 PM

  • Sorry, I don't really get this one. Where are the threads?

    What thread executes HttpRequest.open() and HttpRequest.send()? What happens if the server is unreachable and HttpRequest.open() is not issued from EDT? What thread will execute the listener code?

    Is this only a new API for interacting with REST WSs ( and if it is why it is so different from apache httpclient libs ) ?

    Regards,
    Horia

    Posted by: sevenm on September 01, 2006 at 01:53 PM

  • Richard, regarding the JSONHttpRequest, you can use Json-lib (shameless plug) if you need to convert the JSONObject/JSONArray into a java bean or DynaBean, that may work better when binding to an specific UI =)

    Posted by: aalmiray on September 01, 2006 at 02:25 PM

  • Using a Spring HttpInvoker is a much more elegant solution to exchanging Java objects between a Swing client and multiple web applications. Swing clients can also use JMS/Lingo to subscribe to remote events.

    Posted by: javawerks on September 02, 2006 at 04:23 AM

  • very nice! I am glad that people discover rich client apps tied to the internet as descibed here finally. Keep it up!

    PS: Not that this is something really new. One canachieve this with XML-RPC already for a long time withut someone noticing (including the asynchronous threading) but nonetheless....

    Posted by: uhilger on September 03, 2006 at 12:45 AM

  • If you want to do RIA with Swing, Spring HttpInvoker provides a very neat solution. One of the big advantage is that all of the development can be done without any server stop/restart cycle using a plain Spring Context without any remoting.
    When I am ready for integration testing, I just build a server-war that exposes the beans with Spring remoting and a client-war with the Webstart launcher. Its really very easy and elegant and it is all JAVA.

    Posted by: tsawan on September 03, 2006 at 03:22 AM

  • Dear Richard
    I need help. I want be a Java Developer like you. but I don't Know what I do. Please help and guide me. my address email is
    reza.nojavan@gmail.com. waiting for your mail. at the end I happy for your reply.

    Posted by: rezanojavan on September 25, 2006 at 12:57 AM



Only logged in users may post comments. Login Here.


Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds