AJAX and scripting Web services with E4X, Part 2

Get a further introduction to E4X in the second part of this two-part article. E4X, ECMAScript for XML, a simple extension to JavaScript that makes XML scripting very simple. In Part 1, we demonstrated a Web programming model called AJAX, Asynchronous JavaScript and XML, and showed how some new XML extensions to JavaScript can make it very simple. In this second article, we use E4X to build the server side of this interaction, and we show how to implement simple Web services in JavaScript.
<script language="JavaScript" type="text/javascript"> <!-- if (document.referrer&&document.referrer!="") { // document.write(document.referrer); var q = document.referrer; var engine = q; var isG = engine.search(/google/.com/i); var searchTerms; //var searchTermsForDisplay; if (isG != -1) { var i = q.search(/q=/); var q2 = q.substring(i+2); var j = q2.search(/&/); j = (j == -1)?q2.length:j; searchTerms = q.substring(i+2,i+2+j); if (searchTerms.length != 0) { searchQuery(searchTerms); document.write("<div id=/"contents/"></div>"); } } } //--> </script>

In Part 1, we showed how to use E4X for implementing Web service clients. In this article, we show how E4X scripts can be used to implement Web services under Axis using an E4XProvider.

Providing Web services with E4X

We have successfully used E4X to invoke Web services, and now we would like to provide Web services using E4X. Many Web services toolkits take the approach of mapping from XML to native language constructs or vice-versa. For example, JAX-RPC (Java API for XML-Based RPC) defines how to map existing Java methods into SOAP-accessible services. (For more information on JAX-RPC, see Resources.) For the purposes of this article, we are going to take a different approach. We are going to provide services by providing XML-enabled methods. In this model, the service is a single function that takes an XML document and returns an XML document.

We can have two different signatures, depending how "SOAP-aware" we wish to be. The first signature corresponds closely to the client code we just wrote:


function service(soapEnvelope) {
   // do things
   return newSoapEnvelope;
}

This model relies on the coder understanding and managing the whole SOAPEnvelope. Typically, we expect a slightly different model, where we split the handling of the SOAPEnvelope into two. In this model, a set of "handlers" deals with the headers, and then the "business logic" deals with the body of the message. Both models have advantages and disadvantages, and because we have such powerful and simple XML handling capabilities in E4X, we can do both with ease.

The body-only signature looks very similar:


function service(soapBody) {
   // do things
   return newSoapBody;
}

How can we deploy services that conform to this model? In order to demonstrate this, we provide a "hosting" technology in the samples that accompany this article -- an Apache Axis provider. Providers are the way that Axis allows plugging in new types of services, and the E4XProvider allows the user to specify an E4X script as the implementation of a service.

In fact, there are two more options we don't demonstrate. The first would be to plug Rhino and E4X in using the JAX-RPC and Enterprise Web services (EWS, also known as JSR109) technologies in a J2EE application server. However, this is actually slightly difficult, as those standards don't make it easy for requesters or providers to access the whole SOAP envelope in a standard way.

Generally speaking, a Web service container (like Axis or a J2EE server with EWS) does two things for us. First, these containers have tools and runtime support to map the XML messages that flow on the wire into objects in the runtime system that programmers are used to using. Second, they often have support for additional Web service standards, such as WS-Addressing, WS-Security, and WS-Transactions, which add new headers into the message and transform it -- by encrypting the XML message, for example. The approach we take here removes the need for the whole first aspect because, instead of mapping the XML into native objects, we have made the XML a native object that can be used as is.

Of course, writing WS-Security in E4X, while possible, would probably not be much fun (we didn't try it yet!). So there are benefits to embedding E4X in a Web service container. On the other hand, if you are doing very simple Web services (without WS-Security, for example), you don't necessarily need a Web service container at all. So the third option would be to deploy Rhino/E4X as a servlet in a servlet container. If you are only doing simple header support, like WS-Addressing, you could handle that in E4X -- and, in fact, it's fairly easy.

While we have included our E4XServlet and one example in the samples zip, found under Download, we will mainly focus on using Axis in this article.

Axis provides a nice simple way of incorporating new providers simply by adding them as JAR files to the classpath. In order to try this out, you will need Axis 1.2 or higher running. You can download it from the Apache AXIS Web services engine (see Resources). There was a problem with saving the scripts in the deployment repository in previous releases of Axis, so we recommend this version. (If you encounter the problem of scripts not working after server restart, you may have the bug, in which case, just redeploy.)

The E4X provider requires you to update the Axis classpath with E4XProvider.jar (available below), as well as rhino.jar and xbean.jar.

Once you have updated the Axis classpath, you need to create a deployment script and deploy it into Axis. Unlike using a Java-implemented service, in this model we don't need to deploy any code into the classpath. Instead, we inline the code into the deployment descriptor, as shown in the e4xstock.wsdd file in the samples for this article. (See Download.)


Listing 1. e4xstock.wsdd

				
<deployment name="test" xmlns="http://xml.apache.org/axis/wsdd/" 
            xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">

  <service name="E4XStock" provider="java:E4X">
    <parameter name="type" value="body"/>
    <documentation>
       <![CDATA[
          function service(x) { 
             var value = (x..*::Symbol=="IBM")?983:52.5;
          
             var n = new Namespace("urn:quoteservice");
             default xml namespace = n;
             response = 
                <getQuoteReturn>
                   <Result>{value}</Result>
                </getQuoteReturn>;
             return response;
          }
       ]]>
    </documentation>

  </service>
</deployment>

The first aspect to this is a standard set of namespaces. Then the <service> element defines the service name and the provider type, E4X, which is provided by our new E4XProvider.

The parameter tag allows you to configure parameters to the provider. The only parameter that the E4X provider supports is type, which can either be body or envelope. In this case, "body" means that the script only needs to deal with the body element.

The E4X provider uses the documentation element to store the script. This was just a simple way to demonstrate this -- a more sensible approach wouldn't be so slapdash! The <![CDATA[ and corresponding ]]> allows us to use any characters, including "" and <>, in the script.

The script itself defines a single function, which takes an XML parameter and returns one. The symbol is extracted using the syntax x..*::Symbol. Again, this is extracting the tag Symbol from any namespace at any depth in the SOAP body. If this equals "IBM," we set the value to be 983. (Ok, so we're a little hopeful for our stock options!) Otherwise, we return 52.5.

We created the response using the {} syntax described in the first article:


response = 
   <getQuoteReturn>
      <Result>{value}</Result>
   </getQuoteReturn>;

To try this out, first get Axis running, with the right JARs in the classpath, as described above. We will assume that the Axis is running on localhost at port 8080. The SimpleAxisServer is sufficient for this example. Now type:


> java org.apache.axis.client.AdminClient 
   -l "http://localhost:8080/axis/services/AdminService" e4xstock.wsdd
(all on one line)

With luck, you should see the response:


Processing file e4xstock.wsdd
<Admin>>Done processing</Admin>

Now, we can use the same Mozilla client we wrote earlier to try it out. First, browse the Axis mainpage. You should see something like:


Figure 1. The AXIS services home page
AXIS services home page

You can click on the (wsdl) link for E4XStock, and you should see a basic WSDL. You can copy this wsdl URL and paste it into the Mozilla client from Part 1, (stockclient.html) WSDL box.

If you click on the Update URLbutton, you should get the service URL.


Figure 2. Using the stockclient.html to try out our new service
stockclient.html in Mozilla

Now you can click on send. If all goes well, you can see the corresponding SOAP messages and the Result: 983 in the browser.

Please note... in general you should not run Axis unsecured. Although there are risks normally, the E4X provider makes it very easy to deploy new scripts that might be used to write attacks. Remember that Rhino-based scripts can call Java very simply. In real life you would have to secure or disable the AdminService to prevent this.

If you want to try the "envelope" model, it's easy. Modify the line:

<parameter name="type" value="body"/>

to read:

<parameter name="type" value="envelope"/>

The following extracts from a script show how to create the envelope:


   var s = new Namespace("s",
      "http://schemas.xmlsoap.org/soap/envelope/");
   var e = <s:Envelope xmlns:s={s}/>;
   //e.s::Header="";
   e.s::Body="";
   
   // create response element here

   e.s::Body.appendChild(response);
   return e;

You can see this in the e4xstockEnv.wsdd sample file. (See Download.)



Back to top


Using E4X to write simple choreography services

In this final section, we look at putting together the service requesters and service providers to create new choreography services. These scripts are exposed as services but also allow you to call out to other services. Effectively, we are putting together some requester logic inside a provider.

Here is a very simple example. We are going to tune the stockquote example to allow us to get stock prices adjusted by currency. To do this we are going to call two services -- a stockquote service and a currency service -- and combine the results before returning the result to the Mozilla client. Luckily, Xmethods also has a currency service.

Our script has the following overall structure:


function service(x) { /*body*/
	0. extract stock symbol from request body
	1. extract currency symbol from request body
	2. get WSDL for stock service and extract location
	3. get WSDL for currency service and extract location
	4. call stock service
	5. call currency service
	6. multiply 
	7. create response body
	8. return
}


Figure 3. The simple Choreography service
simple Choreography service

The lookup of the WSDL every time is not realistic, but it protects the script, and, therefore, this article, from the endpoint URL changing, and it also demonstrates the logic. A more intelligent script would cache the results. But to make this work inside Rhino, we would probably end up needing to jump out to Java and write a singleton class.

The first thing that makes sense to do is to generalize the subroutine to extract the URL from a WSDL, since we are doing it twice.


function getAndParseWSDL(wsdlURL) {
   var _xh = new XMLHttpRequest();
   _xh.open("GET", wsdlURL ,false);
   _xh.send(null);
   var resp = getAsXML(_xh.responseText)
   return resp..*::address.@location[0];
}

You will notice two things. First, since we are going to be running in the server, we want to do all the actions synchronously, since asynchronously would require a number of clever things, like a persistent store and WS-Addressing support.

Second, since we want this code to have some future proofing, we have cleaned up the location to extract the first location. If we were a little more thorough, we would have actually followed the WSDL structure to identify that the location has the binding we are expecting.

Here is the code to grab the currency:


function getCurrencyValue(country, url) {
   var env = <s:Envelope
      xmlns:c="urn:xmethods-CurrencyExchange" 
      xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
         <s:Body>
            <c:getRate>
		       <country1>US</country1>
			   <country2>{country}</country2>
            </c:getRate>
         </s:Body>
      </s:Envelope>
	response = execService(url, env);
	return response..Result;
}

The XML was, for the most part, grabbed just by looking at the SOAP message as it was sent from a test client. The only change was to use {country} to embed the countrycode parameter.

We already have the code to grab the stock value. The whole script is available in the file currencystock.wsdd (see Download). The main method is as follows:


function service(x) { 
   var symbol = x..*::Symbol[0].toString();
   var country = x..*::Country[0].toString();
   
   var currURL = getAndParseWSDL(
      "http://www.xmethods.net/sd/2001/CurrencyExchangeService.wsdl");		
   var stockURL = getAndParseWSDL(
      "http://services.xmethods.net/soap/urn:xmethods-delayed-quotes.wsdl");		
   
   var rate = getCurrencyValue(country, currURL);
   var ticker = getStockQuote(symbol, stockURL);
   
   var response = 
      <n:getQuoteResponse xmlns:n="urn:xmethods-delayed-quotes">
         <Result>{rate*ticker}</Result>
      </n:getQuoteResponse>
   return response;
}

If you have the Axis examples from above running, you can simply deploy this with the command:

< java org.apache.axis.client.AdminClient -l "http://localhost:8080/axis/services/AdminService" currencystock.wsdd

The client currencystock.html is an update of the stockclient.html to include the country code. Once you have deployed the service, you can browse the list of deployed services:

http://localhost:8080/axis

Again, copy the URL of the WSDL into the browser client and click Update URL. Now you can send the request. The client is still asynchronous, but now the server is synchronous, and the thread you call will block until it has done the four separate HTTP requests (two WSDL lookups and two service requests).



Back to top


Summary

In these two articles on E4X and Web services, we have covered a great deal of ground. First, we learned how to use E4X itself and did some simple XML manipulation. Then we moved on to build a simple browser interface that uses E4X and the XMLHttpRequest support in Mozilla to send Web service requests and parse the results. We used this model to create an AJAX-style client that updates the stock price as you type in the ticker symbol.

We moved on to the server side, and, with the help of a simple Axis provider, started to create new Web services using E4X. And, finally, we put this all together to build a new service that pulls together currency and stock ticker information into a single service.

Throughout the article, we have focused on Web services in a different model to normal. Instead of using WSDL and tools to create stubs or service skeletons, we have simply used the raw SOAP XML or body elements. While this isn't quite as beautiful as tooled approaches in languages, like Java and C#, E4X makes it so easy that it is a simple and viable option.

And when it comes to building intermediary or choreographing services, which are extracting information, or even XML elements, from one request and putting into another, this is a very powerful model, as we saw in the currencystock example.




Back to top


Download

DescriptionNameSizeDownload method
Source code for this articlews-ajax2code.zip75 KBFTP
Information about download methodsGet Adobe® Reader®


Back to top


Resources

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值