AJAX and scripting web services with E4X, Part 1

Introducing E4X

Get an introduction to ECMAScript for XML (E4X), a simple extension to JavaScript that makes XML scripting very simple. In this paper, the authors demonstrate a Web programming model called Asynchronous JavaScript and XML (AJAX) and show you how some new XML extensions to JavaScript can make it very simple.

Share:

Paul Fremantle, Senior Technical Staff Member, IBM Application and Integration Middleware

Paul Fremantle is an architect working in the IBM Hursley development laboratory on web services and XML intermediaries. He is the co-author of Building Web Services in Java 2nd Edition, as well as a number of articles and publications on XML and web services.



Anthony Elder (ant.elder@uk.ibm.com), Consulting IT Specialist, IBM

Anthony Elder is a developer in the IBM Hursley development laboratory working in the area of web services, messaging, and Enterprise Service Bus.



08 April 2005

ECMAScript for XML

You might not have come across the term ECMAScript before. It is actually the official name for JavaScript. The European Computer Manufacturers Association (ECMA) is the standards body where JavaScript is standardized (it is also the place where C# and the CLR are standardized). The ECMAScript standard is freely available from the ECMA Web site.

E4X is an extension of JavaScript that adds direct support for XML to the language. It's also an ECMA standard (see Resources -- ECMA-357). So what is direct support and why is it valuable? If you are a JavaScript programmer, you might already use technology such as Netscape LiveConnect or Rhino (a freely available JavaScript library that runs under Java™) to use Java libraries with your JavaScript. That means that you can already create, manipulate and use XML with the help of an XML library. Similarly, if you use Microsoft® Internet Explorer, you have XML support through the Microsoft MSXML2 library. Well, if you have used those libraries, prepare for a big change -- E4X is much simpler and easier.

Before we look at examples, a word on trying this out: E4X is available in two implementations at the moment. Both are available from Mozilla. One is the C JavaScript engine that the browser uses (it's code-named SpiderMonkey), and it's available in the latest Mozilla builds -- we used Mozilla 1.8a6. E4X is also available in Rhino. Rhino is a JavaScript interpreter and compiler built in Java, which we will demonstrate as both stand-alone and running inside Axis. Both are available from Mozilla.

In these examples we start off with using E4X on a command-line with Rhino, and then we move to using it inside a browser with Mozilla, demonstrating the AJAX model. In the second paper, we will show you how you can use E4X inside a server by embedding Rhino inside the Apache Axis web services engine. But before we move on to web services, we will show you the basics of XML programming in E4X.


A simple example

Let's begin with a simple example. We parse and manipulate an XML that represents some information about the authors. The XML we want looks like this:

Listing 1. The authors
<people>

   <person gender="male">

      <name>Ant</name>

      <hair>Shaggy</hair>

      <eyes>Blue</eyes>

      <height measure="metric">176</height>

   </person>

   <person gender="male">

      <name>Paul</name>

      <hair>Spiky</hair>

      <eyes>Grey</eyes>

      <height measure="metric">178</height>

   </person>

</people>

If we had this in a string, we could "parse" the string by simply doing the following:

var x = new XML(peopleXMLString);

Alternatively, we can simply "in-line" the XML in the code:

var x = 

<people>

   <person gender="male">

      <name>Ant</name>

      <hair>Shaggy</hair>

      <eyes>Blue</eyes>

      <height measure="metric">176</height>

   </person>

   <person gender="male">

      <name>Paul</name>

      <hair>Spiky</hair>

      <eyes>Grey</eyes>

      <height measure="metric">178</height>

   </person>

</people>;

Yes -- that's right -- the XML becomes a direct part of the language.


Using E4X with Rhino

If you are anything like we are, you'll want to try this by now. E4X is supported by the latest version of Rhino 1.6R1, in conjunction with the XMLBeans library from Apache. Simply grab the packages, extract them and add js.jar and xbean.jar to your classpath, and then start up the JavaScript shell.

java -cp js.jar;xbean.jar org.mozilla.javascript.tools.shell.Main

You can now try out the examples that follow. You can either cut-and-paste them from here, or they are included in a file, examples1.js, which is in the zip file (ws-ajax1code.zip) which you can download by clicking on the code icon at the top or bottom of this paper.

You can refer to any part of the XML simply using JavaScript properties. For example:

print(x.person[0].name);

Ant

print(x.person[1].hair);

Spiky

Have you noticed yet that we aren't using XML APIs like DOM or SAX? XML has simply become one of the native types that JavaScript understands.

Use the following to print out the heights for both people:

for each (var h in x..height) { print(h) };

176

178

The .. syntax is very useful. It returns all of the child elements, at any depth, that match the following tagname. So x..height returns the values of the height tags.

Another useful syntax is the following:

print(x.person.(name=="Ant").hair);

Shaggy

This makes it very simple to do look-ups inside the tree.


A more complex example

Suppose you want to change the heights from metric to imperial measurements (that's what we British folks call feet and inches).

First here's the conversion from centimeters to inches...

function metricToImperial(cms) {

	var totalinches = Math.round(cms/2.54); 

	var inch = totalinches%12;

	var ft = (totalinches-inch)/12; 

	var response = <height/>;

	response.feet = ft;

	response.inches = inch + 2; // we sounded a bit short

	response.@measure = "imperial";

	return response;

}

The first interesting line is this:

var response = <height/>;

This line allows you to "in-line" XML in JavaScript. There are two benefits to this syntax. Firstly, it makes it really easy to use XML. Secondly, the meaning of the code is very clear.

Now you can add further children to this element:

response.feet = ft;

This creates a child element of <height> with the tag name set to be "feet" and the value set to the value of the variable ft.

You can also manipulate attributes this way:

response.@measure = "imperial";

Now let's use this function to update the XML:

for each (var p in x.person) { 

	if (p.height.@measure=="metric") 

		p.height=metricToImperial(p.height);  

}



print (x);

And here is the output:

<people>

  <person gender="male">

    <name>Ant</name>

    <hair>Shaggy</hair>

    <eyes>Blue</eyes>

    <height measure="imperial">

      <feet>5</feet>

      <inches>11</inches>

    </height>

  </person>

  <person gender="male">

    <name>Paul</name>

    <hair>Spiky</hair>

    <eyes>Grey</eyes>

    <height measure="imperial">

      <feet>5</feet>

      <inches>12</inches>

    </height>

  </person>

</people>

XML Namespaces in E4X

If you are an XML guru, at this point you are probably wondering how you can manage XML Namespaces with this syntax. There are three ways of doing this:

Firstly, you can use the in-line XML syntax:

var soapMsg = <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"/>;

print(soapMsg.namespace());

http://www.w3.org/2003/05/soap-envelope

The next way is to set the default XML Namespace before creating elements:

default xml namespace = new Namespace("http://www.w3.org/2003/05/soap-envelope");

You can reset the default namespace by setting it to an empty string:

default xml namespace = ""

The final way is to use the :: operator

var f = new Namespace("http://fremantle.org");

soapMsg.Body.f::GetStockQuote="IBM";

print(soapMsg);

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">

  <s:Body>

     <frem:GetStockQuote xmlns:frem="http://fremantle.org">

        IBM

     </frem:GetStockQuote>

  </s:Body>

</s:Envelope>

XML element ordering

One significant benefit that E4X has is that it fully supports XML, including ordering. Many mappings from XML directly into language objects end-up only supporting a subset of XML semantics that matches normal object semantics -- thereby missing the ability to represent not only objects but also documents. While it isn't appropriate for this paper, a quick look at the specification will show that the built-in functions that E4X XML objects have allow the order of the XML to be carefully crafted.

Using Javascript expressions within XML

There is one last thing we would like to tell you about before moving on to web services -- the curly braces. Above we covered "in-lining" XML. E4X also allows you to re-enter the world of JavaScript and include evaluated expressions. So for example:

var num = 36;

var p = <paul><age>{num}</age></paul>

print(p);

<paul>

  <age>36</age>

</paul>

We've now covered the basics of E4X, so let's do some work with it.


Using E4X to call web services

In this section we describe how to use E4X in the following two environments:

  1. Mozilla 1.8 with XMLHttpRequest
  2. Java/Rhino

You can use E4X very easily to call web services in a browser. There is an issue though! The only browser that supports E4X so far is the developer release of Mozilla 1.8.

While we aren't recommending this yet as a portable cross-browser solution, the following example demonstrates how E4X can be used to call web services in a simple fashion. In the next section we will look at another approach that works in the Rhino Javascript engine.

AJAX

The simple example shows the browser sending and receiving SOAP messages to a SOAP server. In order to do this, we use E4X with the XMLHttpRequest object. This useful object (which is supported in Mozilla and Internet Explorer) allows scripts running in the browser to make HTTP requests in the background. In fact, this is how Google's GMail does almost everything. This architecture has recently been named Asynchronous JavaScript + XML (AJAX). You can read more about AJAX in the article "Ajax: A New Approach to Web Applications" (see Resources).

Fundamentally, the idea of AJAX is to improve the responsiveness and usability of Web pages by interacting with the server in a more flexible manner than the "page" model of standard HTML and HTTP. A great example of this is the Google Maps beta, which is considerably more interactive than previous mapping Web sites.

The great news is that AJAX combined with E4X is even better! We will show two versions of our browser apps. The first demonstrates the workings of the interactions, while the second version hides the buttons and inner workings of the Web page to show the interactivity and asynchrony.

Browser security

In order to demonstrate this, we use a standard service available over the Web from xmethods.net. However, there is a catch! Browser security rules do not in general allow scripts or Java applets to create network connections except to the server where the page came from. Otherwise you could have a spyware page that copied your keystrokes to another server.

You can, however, get around this. To do so, you need to do two steps. First, you have to enable enhanced privileges for scripts in the config for Mozilla. (Assuming you've downloaded and installed the Mozilla 1.8 beta).

Type about:config into the browser address bar, and then update the value of signed.applets.codebase_principal_support from false to true. (For security reasons please remember to set this back after you have finished.)

Then, in the script, you can ask for enhanced privileges. When the script runs, the user will be prompted to allow these. The code line is:

netscape.security.PrivilegeManager.enablePrivilege( "UniversalXPConnect UniversalBrowserAccess");

The other alternative is to deploy a service and the Web page into a Web application server like IBM® WebSphere® Application Server or Tomcat. For example, this works with Apache Axis and the default stock ticker sample that comes with Axis (see Resources).

The stock quote client sample

The script is part of stockclient.html. If you download the ws-ajax1code.zip file from this paper, extract the zip contents, and then open stockclient.html with Mozilla you should see the following:

Figure 1. stockclient.html in Mozilla
stockclient.html in Mozilla

To try it out, first click Update URL. This uses the XMLHttpRequest to fetch the WSDL file from http://services.xmethods.net/soap/urn:xmethods-delayed-quotes.wsdl (or whatever URL you type in the WSDL box) and then uses E4X to grab the port address URL from there. Now click Send, and you should see the SOAP request fill in. A second or two later, the SOAP response should update as well as the result field. Let's look at the code.

The stock quote client script

The script calls the URL with a request for the IBM stock price. If you are using the Axis server, we recommend using the ticker symbol XXX, which is a special symbol -- the deployed service will always return a fixed response for this ticker rather than making a Web request to find the real price - therefore it's better for testing with.

The first thing you need to do is define that you wish to use E4X:

<script type="text/javascript;e4x=1">

When you push the send button, the script does the following:

    var s = new Namespace(

       "s",

       "http://schemas.xmlsoap.org/soap/envelope/");

    var envelope = <s:Envelope xmlns:s={s}/>;

    envelope.s::Body="";

    var body = envelope.s::Body;

This is generic to any SOAP request. It simply creates a SOAP envelope with an empty body.

An equivalent way of doing this would be the following:

var envelope = 

      <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">

         <s:Body/>

      </s:Envelope>

However, the previous code is simpler and also gives you a pointer to the body element.

The next step is to create the Body of the message:

   var x = new Namespace("x","urn:xmltoday-delayed-quotes");

   body.x::getQuote = <x:getQuote xmlns:x={x}/>;

Finally you need to add the correct symbol:

   var symbol = document.getElementById("symbol").value;	

   var getQuote = body.x::getQuote;

   getQuote.symbol=symbol;

You now have a fully-formed SOAP request. If you were to evaluate the envelope, you would get the following:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">

  <s:Body>

    <x:getQuote xmlns:x="urn:xmethods-delayed-quotes">

      <symbol>XXX</symbol>

    </x:getQuote>

  </s:Body>

</s:Envelope>

To send this, you need to use the XMLHTTPRequest object. We created a simple helper function to support using the XMLHttpRequest object to invoke services using E4X. The function execService supports both asynchronous and synchronous usage.

function execService(url, xml, callback) {

   var xmlhttp = new XMLHttpRequest();

   var async=false;

   if (arguments.length==3) async=true;

      xmlhttp.open("POST", url, async);

      xmlhttp.setRequestHeader("SOAPAction", "\"\"")

      xmlhttp.setRequestHeader("Content-Type", "text/xml")

      if (async) { 

         var f = function() {

         if (xmlhttp.readyState==4) {

            callback(new XML(xmlhttp.responseText));

         }

      }



      xmlhttp.onreadystatechange = f;

   }

   xmlhttp.send(xml.toString());

   if (!async) return new XML(xmlhttp.responseText);

}

Let?s look at the code in more detail. Firstly, the code supports two ways of calling. You can call either:

XML execService(String url, XML envelope);

or

void execService(String url, XML envelope, function callback);

in which case the callback function should be void callback(XML x).

So either you can use this to invoke the XML service directly and wait for the response, or you can pass in a function which will be invoked with the response XML.

The function decides if it is async or sync based on the number of parameters (3 is async), and then simply uses the XMLHttpRequest object to POST the XML to the URL.

We set a couple of HTTP headers -- SOAPAction and Content-Type -- and then send the envelope parameter using xmlhttp.send(xml.toString()).

If the invocation is async, then it waits until the readyState is 4 (complete) before invoking the callback with the XML created from the response.

So the code to use this is as follows:

   var url = document.getElementById("url").value;



   var callback = function(resp) {

      alert(resp..*::getQuoteReturn);

   }



   execService(url, envelope, callback);

In this situation, we use the asynchronous model. Web browsers typically do not block while talking to servers, and we don?t want to either. For example, if we did, the browser window might end up "Not Responding" to Windows prompting the user to kill it.

resp..*::getQuoteReturn

This syntax takes a second to digest if you are new to E4X. The .. means search down the tree for the named element. The *:: means any Namespace, and so the value will be the value of any element called getQuoteReturn in any Namespace in the response SOAP envelope.

The actual sample code stockclient.html also displays the request and response SOAP envelopes. Try it out -- you should see the following:

Figure 2. stockclient.html displaying the request and response SOAP envelopes
stockclient.html displaying the request and response SOAP envelopes

The stockclient.html looks like a traditional Web page with a submit button, although it isn?t really (the page that the browser is viewing never changes). We built it that way to let you understand the interaction. However, the real AJAX version of the page is much nicer. The stockclientAjax.html has no buttons. As you type, it automatically updates the stock price. Try it out.

Figure 3. AJAX version of the Web page -- stockclientAjax.html
AJAX version of the Web page -- stockclientAjax.html

Now the page has no buttons, but instead, it automatically makes requests as soon as you stop typing (it waits 0.6 seconds before making the request in order to "sense" when you've stopped).


Making web services requests from Rhino

Rhino doesn?t support the XMLHTTPRequest object, but don't worry. Since Rhino runs in a Java environment, you can use the Java capabilities to make the web service request. To demonstrate this, we wrote a simple Java implementation of the XMLHttpRequest object. Rhino allows Java programmers to extend their JavaScript environment issuing Java language. To use the XMLHttpRequest object in the Rhino shell, you simply make sure that the e4xutils.jar is in your classpath, and then you use a shell command, defineClass, to add it to your environment:

>set classpath=.\js.jar;.\xbean.jar;.\e4xutils.jar;.

>java org.mozilla.javascript.tools.shell.Main

Rhino 1.6 release 1 2004 11 30H

js> defineClass('xmlhttp.XMLHttpRequest');

Here is a simple script to test this:

>test.js

defineClass("xmlhttp.XMLHttpRequest");

var xh = new XMLHttpRequest();

xh.open("GET",

"http://services.xmethods.net/soap/urn:xmethods-delayed-quotes.wsdl",

 false);

xh.send(null);

var wsdl = new XML(xh.responseText);

print(wsdl..*::address.@location);



>java org.mozilla.javascript.tools.shell.Main test.js

http://64.124.140.30:9090/soap

The result is that you can now use the same sort of scripts that you have written so far (E4X + XMLHttpRequest) both in Mozilla and in Rhino. In the second part of this paper, we will use this in some interesting scenarios.


Conclusion

So far you have seen how to use E4X and Javascript to initiate web services requests. In the next paper, we will show you how to provide web services using E4X.


Download

DescriptionNameSize
Source code for this articlews-ajax1code.zip75 KB

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into SOA and web services on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=SOA and web services, XML
ArticleID=67531
ArticleTitle=AJAX and scripting web services with E4X, Part 1
publish-date=04082005