Ajax programming with the Java Scripting API

An excerpt from 'Beginning Java SE 6 Platform: From novice to professional'

1 2 3 4 5 Page 5
Page 5 of 5

The weather.js JavaScript source code is presented in Listing 7.

Listing 7. weather.js

// weather.js

// The event-dispatching thread is currently running.

// Create the XMLHttpRequest object.

var xmlHttp = new XMLHttpRequest ();

// Assign a function to the object's onreadystatechange property. The function 
// extracts content from a loaded RSS file and assigns the content to a Java
// program's weather output label. As an exercise, include the file's forecast
// data, which is stored in a pair of <yweather:forecast> elements.

xmlHttp.onreadystatechange = function ()
   // The event-dispatching thread is not running here.

   // If document is in the LOADED state...

   if (xmlHttp.readyState == 4)
       // Start building the result. A JLabel requires its text to be begin
       // with the <html> tag when HTML content is to be rendered.

       var result = "<html><center>";

       // Reference the responseXML object.

       var XMLObj = xmlHttp.responseXML;

       // Extract the document's first description element.

       var desc = XMLObj.getElementsByTagName ("description").item (0)
                        .getChildNodes ().getFirstChild ().getNodeValue ();

       result = result + desc + "<br>";

       // If the description reveals no error (an invalid zip code indicates
       // an error)...

       if (desc.indexOf ("Error") == -1)
           // Extract the units indicator (F, Fahrenheit - C, Celsius).

           var units = XMLObj.getElementsByTagName ("yweather:units").item (0);
           var attr = units.getAttributes ();

           var temperature = "";

           for (var i = 0; i < attr.getLength (); i++)
                if (attr.item (i).getNodeName () == "temperature")
                    temperature = attr.item (i).getNodeValue ();

           // Extract the weather conditions.

           var cond = XMLObj.getElementsByTagName ("yweather:condition")
                            .item (0);
           attr = cond.getAttributes ();

           var text = ""; // Cloudy, Fair, and so on.
           var code = ""; // Code for GIF file containing conditions image.
           var temp = ""; // Temperature
           var date = ""; // Date conditions are based on.

           for (var i = 0; i < attr.getLength (); i++)
                if (attr.item (i).getNodeName () == "text")
                    text = attr.item (i).getNodeValue ();
                if (attr.item (i).getNodeName () == "code")
                    code = attr.item (i).getNodeValue ();
                if (attr.item (i).getNodeName () == "temp")
                    temp = attr.item (i).getNodeValue ();
                if (attr.item (i).getNodeName () == "date")
                    date = attr.item (i).getNodeValue ();

           result = result + "As of: " + date + "<br><br>";

           // To prevent the event-dispatching thread (which is not running at
           // this point) from being delayed due to downloading the conditions
           // image (via the <img> tag in the JLabel component's text), the
           // image file is cached to the hard drive. file.exists() returns
           // false if the file is not cached.

           // NOTE: For brevity, exception handling has been omitted. However,
           // you might consider adding exception handling in the if statement
           // below (as an exercise).

           var file = new java.io.File (code + ".gif");
           if (!file.exists ())
               var url;
               url = new java.net.URL ("http://l.yimg.com/us.yimg.com/i/us/we/52/"
                     + code + ".gif");
               var is = url.openStream ();
               var fos = new java.io.FileOutputStream (code + ".gif");
               var ch;
               while ((ch = is.read ()) != -1)
                  fos.write (ch);
               fos.close ();

           var path = new java.io.File ("").getAbsolutePath ();
           result = result + "<table border=1 bgcolor=#ffffff><tr><td>" +
                    "<img src=\"file:///" + path + "/" + code + ".gif\">";
           result = result + "</td></tr></table><br>";
           result = result + "Current Conditions: ";
           result = result + text + ", " + temp + " " + temperature;

       result = result + "</center></html>";

       // The output label component must be accessed on the event-dispatching
       // thread.

       var r = new java.lang.Runnable ()
                   run: function ()
                       // The event-dispatching thread is currently running
                       // Assign the weather result to this label.

                      lblWeather.setText (result);
       java.awt.EventQueue.invokeLater (r);

// Get an RSS document containing weather information for the specified zip
// code. Asynchronous mode is used to prevent the event-dispatching thread
// from being delayed.

xmlHttp.open ("GET", "http://weather.yahooapis.com/forecastrss?p="+zipcode,
xmlHttp.send (null);

This script creates an XMLHttpRequest object and then assigns a function to this object's onreadystatechange property. This function extracts weather data from an XML-based RSS document after the document loads. The object's open() and send() functions are then invoked to get the document identified by the zipcode script variable's value from Yahoo! Weather.

Weather data is extracted by invoking various Java DOM methods via XMLHttpRequest's responseXML property. The extracted data consists of the content of the document's first description element and various attributes of its yweather:units and yweather:condition elements. Listing 8 illustrates these elements.

Listing 8. Snippet of an XML-based RSS document from Yahoo! Weather

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<rss version="2.0" xmlns:yweather="http://xml.weather.yahoo.com/ns/rss/1.0" 
 <description>Yahoo! Weather for Miami, FL</description>

 <yweather:units temperature="F" distance="mi" pressure="in" speed="mph" />
 <yweather:condition text="Mostly Cloudy" code="28" temp="82" date="Wed, 31 Oct 2007 12:15 pm EDT" />
</rss><!-- p2.weather.re3.yahoo.com compressed/chunked Wed Oct 31 09:38:59 PDT 2007 -->
Trouble in Yahoo! Weather
I've noticed a small problem with the yweather:condition element's text attribute. Every now and then, this attribute is empty. In other words, it consists of an empty string. I'm pretty sure that my code is not responsible; I believe that the problem originates with Yahoo! Weather.

The extracted weather data is assembled into a result string. (The data includes an image that is framed in a table on a white background; not all images look nice when placed on a gradient background.) This string is then assigned, on the event-dispatching thread, to the JLabel component identified by the lblWeather script variable. This script variable was created in the application's constructor.

Playing with the Weather app

If you like, you can build and run the Weather application. Unzip the article's code archive and set the current directory to the unzipped Weather directory (which contains Weather.java, ajax.js, and weather.js). Compile the Java source code via javac Weather.java. To run the application, specify java Weather. You should see the GUI previously revealed in Figure 1.

You can easily distribute this application as a JAR file. Create a manifest.mf file that contains Main-Class: Weather, and invoke jar cfm Weather.jar manifest.mf *.class *.js to build the JAR. (The JavaScript files can be included in the JAR because Weather.java accesses them via getResourceAsStream().) Invoke java -jar Weather.jar to run the jarred application.

All-Java version of XMLHttpRequest
Although you can work with XMLHttpRequest in the context of ajax.js and the Java Scripting API, you can alternatively take advantage of an all-Java version of this object. You will find the Java version in the org.jdesktop.http.async package, which is part of the SwingLabs SwingX-WS project.

In conclusion

In this article I've introduced some of the fundamentals of the Java Scripting API and shown how you can use it in an Ajax programming example involving a Swing-based mashup. My book, Beginning Java SE 6 Platform: From Novice to Professional contains much more coverage of the Java Scripting API, including a project that introduces JavaScript to the JEditorPane component. It also explores Java SE 6 features related to annotations, JDBC, Web services, Swing programming, internationalization, security, application monitoring and management, networking, and more.

If you like what you've learned in this article, feel free to check out the book. In fact, I'll send a free copy of my book to the first three people who correctly answer the following question:

In Weather.java, why did I specify engine = manager.getEngineByName ("rhino"); instead of engine = manager.getEngineByExtension ("js"); to obtain a script engine?

Email your answer to jeff@javajeff.mb.ca before December 15. This offer is good in the United States and Canada only!

Jeff Friesen is a freelance software developer and educator who specializes in Java technology. Check out http://www.javajeff.mb.ca to discover all of his published Java articles and more.

Learn more about this topic

1 2 3 4 5 Page 5
Page 5 of 5