Friday, May 20, 2011

Converting a WSDL to Javascript with Web 2.0 Feature Pack

In a previous post I discussed how easy it is to invoke web services from a browser using Websphere web 2.0 feature pack. One of the things I noticed is that it seems to parse the WSDL every time a page is loaded, which isn't necessary if we take a few additional steps.

WSDL2JS
Most developers who work with web services are familiar with a utility which converts a WSDL into generated code for a given language. Something like wsdl2java will generate java, wsdl2perl will generate perl and so on. What about wsdl2js?

As it turns out, the documentation for ibm_soap mentions that the parser holds JSON markup in a variable - all we have to do is grab the contents of that variable (smdString) and save it off to a file. Below is code that will parse a given WSDL and then copy the resulting JSON into a textbox for easy access.

<html>
<head>
<title>DsixE WSDL2JS</title>

<script type="text/javascript" src="dojo/dojo.js" djConfig="isDebug: false, parseOnLoad: true"></script>
<script type="text/javascript" src="ibm_soap/util/WsdlParser.js"></script>
<script type="text/javascript">
  
dojo.require("ibm_soap.widget.SoapService");
dojo.require("dojox.data.dom");
  
function wsdl2js() {
    
    // This is our homebrew wsdl2js. Json code is dumped to a textbox where
    // we can copy/paste it into a file.
    var wsdlParser = new ibm_soap.util.WsdlParser();
    var wsdl = dojo.byId("wsdl").value;
    wsdlParser.parse(wsdl);
       
    dojo.byId("json").value = "var myService=" + wsdlParser.smdString;
}
</script>
</head>

<body>
    <H3>Generate JSON from WSDL (wsdl2js)</H3>
    
    WSDL must not contain externalized schema, all schema must be defined in the WSDL.
    <br>
    
    WSDL URL: <input type="text" id="wsdl" size="80" value="http://"></input>
    <p>
    JSON: <br><textarea id="json" rows="20" cols="80">var myService={}</textarea>
    
    <p>
    <input type="button" id="callServiceButton" value="Create JS from WSDL" onclick="wsdl2js()"/>
</body>
</html>

I wish I could provide a working version on this blog, but I don't have access to a publicly available WAS server. Instead, here's a screenshot of what it looks like:


Now we can include our SOAP service using a script tag instead of parsing a WSDL every time a page loads.

Friday, May 13, 2011

Using Web Services from a Browser

For a long time I wondered why nobody has created a javascript library for web services. There are all kinds of libraries for REST services, but they're not very developer friendly. I much prefer the structure provided by a WSDL, and with tools like wsdl2java creating stubs for my code is a snap.


IBM's Dojo Soap Library

IBMS's Websphere application server offers a set of dojo extensions in their web 2.0 feature pack (FEP) which includes a SOAP library. I decided to take a look at it and found that it is quite easy to use although there aren't many examples to be found on the web. There is a sample provided with the feature pack but it uses the declarative dojo notation which doesn't work for everyone depending on what the goal is.


I opted to take a scripting approach since I already know how to invoke web services on the server side, and I wanted to see how it measures up in ease of use. Below is a simple example which retrieves data from a contacts web service I blogged about a while back.


Setting up the Infrastructure




My setup involved two different boxes - one running the webservice (axisbox) and another which hosted the HTML and the dojo SOAP library (dsixedev1). Due to the cross domain security restriction enforced by the browser, I also had to set up a reverse proxy (lenovobox). Your setup may vary, if everything runs on one box then this part doesn't really matter. Here's what I have in my apache httpd.conf:

ProxyPass /contacts http://lenovobox:8080/axis2
ProxyPassReverse /contacts http://lenovobox:8080/axis2

ProxyPass /was http://dsixedev1:9080
ProxyPassReverse /was http://dsixedev1:9080

The WSDL is loaded using this code:
var wsdlParser = new ibm_soap.util.WsdlParser();
wsdlParser.parse("contacts.wsdl");

Note that I don't load the WSDL directly from the service because it contains a reference to an external schema:
<xsd:schema>
   <xsd:import namespace="http://dsixe.com/blog" schemaLocation="ContactService.ContactInfoPort?xsd=ContactService_schema1.xsd"/>
</xsd:schema>

This reference causes the parser to fail, so I have to manually copy/paste the schema into the WSDL and then save it on the WAS server.

Putting it all Together
Here's a complete listing of the HTML document:
<html>
<head>
<title>DsixE SOAP Sample</title>

<script type="text/javascript" src="dojo/dojo.js" djConfig="isDebug: false, parseOnLoad: true"></script>
<script type="text/javascript">
  
    dojo.require("ibm_soap.widget.SoapService");
    dojo.require("dojox.wire.ml.util");
 
    function init() {
  
        var wsdlParser = new ibm_soap.util.WsdlParser();
        wsdlParser.parse("contacts.wsdl");

        // Create reference to service and set the URL
        var myService = new ibm_soap.rpc.SoapService(wsdlParser.smdObj);
        myService.serviceUrl = "http://lenovobox/contacts/services/ContactService.ContactInfoPort/";

        var params = new dojox.wire.ml.XmlElement("getContact");
        params.setPropertyValue("arg0", "ABC123");
        deferred = myService.getContact(params);
  
        deferred.addCallback(function(xmlData){
            // do something useful with the data here
            console.log(xmlData);
      
            var phone = dojo.query("phone",xmlData)[0].textContent;
            var name = dojo.query("name",xmlData)[0].textContent;
      
            dojo.byId("contact").innerHTML = name + " " + phone;
      
        });
    }

dojo.addOnLoad(init);

</script>
</head>
<body>


<H3> DsixE SOAP Sample - Calling a custom Contacts web service </H3>

This is a pure javascript example of how to invoke a SOAP operation without using declarative dojo markup.
<br><br>

<div id="contact"/>

</body>
</html> 

the SOAP response:
<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Body>
        <dlwmin:getContactResponse xmlns:dlwmin="http://dsixe.com/blog">
            <return>
                <name>Carl</name>
                <phone>678 555-1212</phone>
            </return>
        </dlwmin:getContactResponse>
    </soapenv:Body>
</soapenv:Envelope>

and the output it produces:
DsixE SOAP Sample - Calling a custom Contacts web service
This is a pure javascript example of how to invoke a SOAP operation without using declarative dojo markup.

Carl 678 555-1212

Overall I'm really pleased with the way IBM has done this, I've always been leery of REST services because they lack a structured type definition available in a WSDL, and the SOAP approach makes the code much easier to read.