Chapter 46. SOAP

Table of Contents

1. <soap>
1.1. <soap> attributes
1.2. Read-only properties
1.3. Events
1.4. Methods
2. Document style operation
3. RPC style operation
3.1. Passing complex type parameters
3.2. Type Mapping
4. SOAP headers
4.1. Request headers
4.2. Response headers
5. Handling of overloaded methods on the service
6. Limitations on OpenLaszlo SOAP implementation
7. References
[Warning] Warning

This feature works in proxied applications only. It does not work in SOLO applications.

SOAP (Simple Object Access Prototcol) is used to exchange information in a distributed environment. A typical scenario involves a SOAP client invoking a client-side function stub to invoke a SOAP web service operation. The SOAP web service then returns data —such as stock information or the result to a math function— to the client. The SOAP protocol is a work in progress being drafted by the W3C.

This chapter discusses how to use and invoke SOAP web services in an LZX application. OpenLaszlo SOAP is part of OpenLaszlo RPC and shares many of the same APIs and concepts. This chapter assumes that you have read the OpenLaszlo RPC chapter and have a basic knowledge of SOAP, WSDL, XML, namespaces, and XML Schema. WSDL (Web Service Definition Language) is an XML format to describe a web service. A WSDL may also describe the types used by a web service using an XML Schema. XML Schema defines the structure and constraints of XML documents. For more information, see the references section at end of this chapter.

1. <soap>

The <soap> element creates a client-side representation of a SOAP service based on a WSDL. The name and wsdl attributes are required.

Example 46.1. A simple SOAP tag

<soap wsdl="..."
      service="..."
      port="..."
      autoload="[true|false]"
      secure="[true|false]"
      secureport="..." >

1.1. <soap> attributes

The <soap> element accepts the following attributes, some of which are required:

wsdl: (String) the WSDL to use for the SOAP object. File or http URLs may be used, such as file:mylocal.wsdl or http://api.google.com/GoogleSearch.wsdl. This is a required attribute.

service: (String) set this value to the desired SOAP service if more than one SOAP service is available. Default is the first SOAP service. After the soap object loads, the service property is set with the name of the SOAP service being used.

port: (String) set this value if more than one SOAP port is available. Default is the first SOAP port. Once the soap object loads, the service property is set with the name of the SOAP port being used.

requestheaders: (String) set this value to set SOAP request headers. The string must contain XML and multiple nodes are allowed, i.e., a root node is not required. See the "SOAP headers" section for more details. Default is null.

responseheaders: (LzDataset) if this is set, the responseheaders dataset contains the response header information from the last call that returned headers. See the "SOAP headers" section below for more details. Default is null.

autoload: (Boolean) if true, calls to load client proxy during init stage. If false, the proxy must be loaded using the load() method. See the proxy section in the RPC chapter for details. Default is true.

secure: (Boolean) if true, creates a secure HTTPS connection between the client and OpenLaszlo Server. Also see secureport below. Default is false.

secureport: (Number) valid only when secure attribute is set to true. The secure port to use. There is no client-side default. Most servers use port 443 as the default HTTPS port.

1.2. Read-only properties

proto: (Object) contains prototypes that can be used to create objects described in the types section of a WSDL. These prototypes can be used to instantiate parameter objects to pass SOAP rpc-style functions. Returned objects from a SOAP call will also be prototyped. The prototypes are mapped to <complexType> elements found in a WSDL's schema. See the "Passing complex type parameters" section for more details.

proxy: (Object) this is the object containing function stubs. It is created by calling load() (which happens during init if autoload is true). The proxy functions are stubs to SOAP operations defined by the WSDL.

Note that proxy is not defined until the onload event is sent, thus function stubs cannot be invoked until onload. Each function stub requires two arguments: an array of parameters and delegate. You can unload it (i.e., set to null) by calling the unload()() method. Go to the proxy section in the RPC chapter for details.

1.3. Events

Note: event handler methods must be declared in the body of <soap>. Attribute event handlers will not work.

onload: this event is triggered when the proxy is returned to the client.

onunload: this event is triggered when the proxy is unloaded from the client.

ondata: this event is triggered when a declared <remotecall> doesn't handle its ondata events. See the <remotecall> section for details.

onerror: this event is triggered if there was a problem loading or unloading the stub, or if a declared <remotecall> didn't handle its onerror event. See the <remotecall> section for details.

1.4. Methods

1.4.1. load()

Load() is responsible for setting up the proxy property. This method is automatically invoked if autoload is true. When the call returns, an onload event is sent and the proxy will contain function stubs that mirror the SOAP operations defined in the WSDL.

Here's an example that shows a Laszlo application interacting with the SOAP service provided by Amazon.com. This example doesn't do much: it merely shows that the two ends are communicating.

Example 46.2. Loading Amazon SOAP service

<canvas debug="true" height="530">

    <debug x="15" y="15" width="415" height="500" />

    <soap name="amazon" wsdl="http://soap.amazon.com/schemas3/AmazonWebServices.wsdl">

        <handler name="onload">
            Debug.write('Amazon soap service loaded');
            Debug.write('Compare proxy stubs with WSDL SOAP operations.');
            Debug.write('Amazon WSDL at ' + this.wsdl);            
            Debug.write('proxy:');
            Debug.inspect(this.proxy);
        </handler>

        <handler name="onerror" args="error">
            Debug.write('error:', error);
        </handler>

    </soap>

</canvas>

1.4.2. unload()

This method unloads the proxy from the RPC object and sets it to null. When the call returns, an onunload event is sent.

In the example below, the, SOAP service is unloaded.

Example 46.3. Unloading Amazon SOAP service

<canvas debug="true" height="170">

    <debug x="10" y="10" width="415" height="150" />

    <soap name="amazon" wsdl="http://soap.amazon.com/schemas3/AmazonWebServices.wsdl">

        <handler name="onload">
            Debug.write('Amazon soap service loaded');
            Debug.write('proxy is:', this.proxy);
            this.unload();
        </handler>

        <handler name="onunload">
            Debug.write('Amazon soap service unloaded');
            Debug.write('proxy is:', this.proxy)
        </handler>

        <handler name="onerror" args="error">
            Debug.write('error:', error);
        </handler>

    </soap>

</canvas>

2. Document style operation

Document style operations use XML data(that is, documents) as paramaters. The SOAP specification is moving towards document style operations as being the preferred way of invoking web services. The alternative RPC style posed interoperability challenges that were difficult to resolve. The WS-I's Basic Profile 1.0 Draft describes a preference to document style, which is also the preferred style in SOAP 1.2.

Example 46.4. WS-I Basic Profile 1.0 Draft: Section 4.1, R1005-R1007

R1005 MESSAGEs MUST NOT contain soap:encodingStyle attributes on any of the
elements whose [namespace name] is "http://schemas.xmlsoap.org/soap/envelope/".

R1006 MESSAGEs MUST NOT contain soap:encodingStyle attributes on any element
which is a child of soap:Body.

R1007 MESSAGEs MUST NOT contain soap:encodingStyle attributes on any elements
which are grandchildren of soap:Body.

For interoperability, literal XML is preferred.

In the application, a document style operation returns an array of <LzDataElement> s, though often only a single <LzDataElement> will exist in the array.

The parameter passed into the operation must match the XML schema as defined in the WSDL. The parameter passed in can be an XML string or a dataset. The following example demonstrates how to invoke a document style operation through LZX.

Example 46.5. Document style SOAP operation

<canvas debug="true">

    <debug y="30" x="145" width="350" height="300" />

    <!-- This SOAP service uses document/literal messages for its
         operations. Each operation is passed a document as a parameter. -->
    <soap name="maths" 
          wsdl="http://www.dotnetjunkies.com/quickstart/aspplus/samples/services/MathService/VB/MathService.asmx?WSDL">

        <!-- Method to make a document for SOAP message requests -->
        <method name="makedoc" args="func, av, bv">
        <![CDATA[
            if (func == null) return;
            var s =  '<' + func + ' xmlns="' + 'http://tempuri.org/' + '" >' + 
                               '<A>' + av + '</A>' + 
                               '<B>' + bv + '</B>' + 
                   '</' + func + '>';
            Debug.write(s);
            return s;
        ]]>
        </method>
       
        <handler name="onload">
            // make buttons visible once SOAP object is loaded
            canvas.buttons.setAttribute('visible', true);            
        </handler>

        <handler name="onerror" args="error">
            Debug.write('error:', error);
        </handler>

        <handler name="ontimeout" args="error">
            Debug.write('timeout:', error);
        </handler>

        <handler name="ondata" args="value">
            Debug.write(value);
            result.setText(value);
        </handler>

        <remotecall funcname="Add">
            <param value="${ canvas.maths.makedoc(parent.name, a.text, b.text) }" />
        </remotecall>
        <remotecall funcname="Subtract">
            <param value="${ canvas.maths.makedoc(parent.name, a.text, b.text) }" />
        </remotecall>
        <remotecall funcname="Multiply">
            <param value="${ canvas.maths.makedoc(parent.name, a.text, b.text) }" />
        </remotecall>
        <remotecall funcname="Divide">
            <param value="${ canvas.maths.makedoc(parent.name, a.text, b.text) }" />
        </remotecall>
    </soap>

    <view name="buttons" x="10" y="10" visible="false" layout="spacing: 10" >
        <text><b>.NET MathService</b></text>

        <view layout="axis: x" ><text y="3">a:</text><edittext id="a" text="10"/></view>
        <view layout="axis: x" ><text y="3">b:</text><edittext id="b" text="2" /></view>
        <view layout="axis: x" ><text>result:</text><text id="result"/></view>

        <button text="add"      onclick="canvas.maths.Add.invoke()" />
        <button text="subtract" onclick="canvas.maths.Subtract.invoke()" />
        <button text="multiply" onclick="canvas.maths.Multiply.invoke()" />
        <button text="divide"   onclick="canvas.maths.Divide.invoke()" />

    </view>

</canvas>

The XML Schema in the WSDL describes how the XML should be structured for each of the operations. The WSDL below describes how what the schema should look like for the Add operation.

Example 46.6. XML Schema for .NET Math

<definitions xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
             xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
             xmlns:s="http://www.w3.org/2001/XMLSchema"
             xmlns:s0="http://tempuri.org/"
             xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
             xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
             xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
             targetNamespace="http://tempuri.org/"
             xmlns="http://schemas.xmlsoap.org/wsdl/">

  <types>
    <s:schema elementFormDefault="qualified" targetNamespace="http://tempuri.org/">
      <s:element name="Add">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="1" maxOccurs="1" name="A" type="s:float" />
            <s:element minOccurs="1" maxOccurs="1" name="B" type="s:float" />
          </s:sequence>
        </s:complexType>
      </s:element>

      <s:element name="AddResponse">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="1" maxOccurs="1" name="AddResult" type="s:float" />
          </s:sequence>
        </s:complexType>
      </s:element>

      <!-- MORE SCHEMA DECLARATION (for Subtract, Multiply, Divide) HERE -->

    </s:schema>
  </types>

  <message name="AddSoapIn">
    <part name="parameters" element="s0:Add" />
  </message>
  <message name="AddSoapOut">
    <part name="parameters" element="s0:AddResponse" />
  </message>

  <!-- OTHER MESSAGES (for Subtract, Multiply, Divide) HERE -->

  <portType name="MathServiceSoap">
    <operation name="Add">
      <input message="s0:AddSoapIn" />
      <output message="s0:AddSoapOut" />
    </operation>
    <!-- OTHER PORT TYPE OPERATIONS (for Subtract, Multiply, Divide) HERE -->
  </portType>

  <binding name="MathServiceSoap" type="s0:MathServiceSoap">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" />
    <operation name="Add">
      <soap:operation soapAction="http://tempuri.org/Add" style="document" />
      <input>
        <soap:body use="literal" />
      </input>
      <output>
        <soap:body use="literal" />
      </output>
    </operation>
    <!-- OTHER SOAP BINDING OPERATIONS (for Subtract, Multiply, Divide) HERE -->
  </binding>

  <service name="MathService">
    <port name="MathServiceSoap" binding="s0:MathServiceSoap">
      <soap:address location="http://www.dotnetjunkies.com/quickstart/aspplus/samples/services/MathService/VB/MathService.asmx" />
    </port>
  </service>

</definitions>

The WSDL describes that Add is a document style operation and that a single element (XML message) is passed in. According to the XML schema, the XML message for the Add operation has to look something like:

<Add xmlns="http://tempuri.org/">
  <A>NUMBER</A>
  <B>NUMBER</B>
</Add>

Notice that the XML Schema describes elements that are in the "http://tempuri.org/". When creating the XML, make sure namespaces and elements match exactly as described in the schema. For example, "http://tempuri.org/" (trailing slash) and "http://tempuri.org" (no trailing slash) are not the same namespace. The schema describes that the Add element contains elements <A>NUMBER</A> and <B>NUMBER</B>. Since the case of each element name matters, it would be wrong to declare <a>number</a> and <b>number</b> in the Add element.

3. RPC style operation

RPC style operations behave just like functions in that, instead of documents, values are passed in as parameters. Parameters can be of simple data type (number, boolean), array, or object. The parameter type for the operation is described in the WSDL's XML schema.

Example 46.7. Passing simple paramaters in RPC style operation

<canvas debug="true" height="400" width="530">

    <debug x="10" y="190" width="510" height="200" />

    <dataset name="googleDset" />

    <soap name="google" wsdl="http://api.google.com/GoogleSearch.wsdl">
        <handler name="onload">
            Debug.write('google soap service loaded');
        </handler>

        <handler name="onerror" args="error">
            Debug.write('error:', error);
        </handler>

        <!-- See RPC chapter for details on remotecall and how dataobject is
             used to data bind to RPC operation results. --> 
        <remotecall name="search" funcname="doGoogleSearch" 
                    dataobject="googleDset">

            <param value="'2TKUw4ZQFHJ84ByemZK0EXV0Lj+7xGOx'" />
            <param value="${ s.text }" />
            <param value="1" />
            <param value="10" />
            <param value="true" />
            <param value="''" />
            <param value="true" />
            <param value="''" />
            <param value="''" />
            <param value="''" />

            <handler name="ondata" args="value">
                Debug.write('search result:\n', value);
            </handler>

        </remotecall>
    </soap>

    <view x="10" y="10" layout="spacing: 5" >
        <view layout="axis: x; spacing: 5">
            <edittext id="s" text="SOAP" />
            <button text="search" onclick="Debug.write('Invoking search...'); google.search.invoke()" />
        </view>

        <view width="505" height="140" bgcolor="silver" clip="true" layout="axis: y" >
            <view>
                <datapath xpath="googleDset:/resultElements/item" pooling="true" />
                <text width="200" datapath="title/text()" clip="true" />
                <text x="205" width="300" datapath="URL/text()" clip="true" />
            </view>
        </view>

    </view>

</canvas>

The example demonstrates how a result value, which is actually a JavaScript object, can be data bound through the dataobject attribute in remotecall. For more details, see the remotecall section in the RPC chapter.

3.1. Passing complex type parameters

There are RPC style operations that require complex type parameters which are described in the WSDL's XML schema. For instance, the Amazon WSDL has a SOAP operation called KeywordSearchRequest that expects a KeywordRequest type parameter.

Example 46.8. Amazon WSDL: KeywordSearchRequest SOAP operation

<!-- For complete WSDL go to
        http://soap.amazon.com/schemas3/AmazonWebServices.wsdl -->

<wsdl:definitions name="AmazonSearch"
                  xmlns:typens="http://soap.amazon.com"
                  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
                  xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
                  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
                  xmlns="http://schemas.xmlsoap.org/wsdl/"
                  targetNamespace="http://soap.amazon.com">

  <wsdl:types>

    <xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
                targetNamespace="http://soap.amazon.com">

      <xsd:complexType name="KeywordRequest">
        <xsd:all>
          <xsd:element name="keyword" type="xsd:string"/>
          <xsd:element name="page" type="xsd:string"/>
          <xsd:element name="mode" type="xsd:string"/>
          <xsd:element name="tag" type="xsd:string"/>
          <xsd:element name="type" type="xsd:string"/>
          <xsd:element name="devtag" type="xsd:string"/>
          <xsd:element name="sort" type="xsd:string" minOccurs="0"/>
          <xsd:element name="locale" type="xsd:string" minOccurs="0"/>
          <xsd:element name="price" type="xsd:string" minOccurs="0"/>
        </xsd:all>
      </xsd:complexType>

      <xsd:complexType name="ProductInfo">

        <xsd:all>
          <xsd:element name="TotalResults" type="xsd:string" minOccurs="0"/>
          <!-- Total number of Search Results -->
          <xsd:element name="TotalPages" type="xsd:string" minOccurs="0"/>
          <!-- Total number of Pages of Search Results -->
          <xsd:element name="ListName" type="xsd:string" minOccurs="0"/>
          <!-- Listmania list name -->
          <xsd:element name="Details" type="typens:DetailsArray" minOccurs="0"/>
        </xsd:all>

      </xsd:complexType>

      <!-- OTHER SCHEMA TYPES HERE -->

    </xsd:schema>

  </wsdl:types>

  <message name="KeywordSearchRequest">
    <!-- Messages for Amazon Web APIs -->
    <!-- KeywordSearchRequest message contains only one part and is of type
         typens:KeywordRequest -->
    <part name="KeywordSearchRequest" type="typens:KeywordRequest"/>
  </message>

  <message name="KeywordSearchResponse">
    <part name="return" type="typens:ProductInfo"/>
  </message>

  <portType name="AmazonSearchPort">
    <!-- Port for Amazon Web APIs -->
    <operation name="KeywordSearchRequest">
      <!-- The KeywordSearchRequest message element (above) describes input. -->
      <input message="typens:KeywordSearchRequest"/>
      <!-- The KeywordSearchResponse message element (above) describes
           input. -->
      <output message="typens:KeywordSearchResponse"/>
    </operation>

    <!-- OTHER OPERATIONS HERE -->

  </portType>

  <binding name="AmazonSearchBinding" type="typens:AmazonSearchPort">

    <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>

    <!-- Binding for Amazon Web APIs - RPC, SOAP over HTTP -->
    <!-- This binds to the AmazonSearchPort portType operation above. --> 
    <operation name="KeywordSearchRequest">

      <soap:operation soapAction="http://soap.amazon.com"/>

      <input>
        <soap:body use="encoded" 
                   encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
                   namespace="http://soap.amazon.com"/>
      </input>
      <output>
        <soap:body use="encoded" 
                   encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
                   namespace="http://soap.amazon.com"/>
      </output>

    </operation>

    <!-- OTHER OPERATIONS HERE -->

  </binding>

  <service name="AmazonSearchService">

    <port name="AmazonSearchPort" binding="typens:AmazonSearchBinding">

      <soap:address location="http://soap.amazon.com/onca/soap3"/>

    </port>

  </service>

</wsdl:definitions>

The proto property in a soap object can be used to pass complex types as parameters. When the soap object loads, its proto property is set with JavaScript function prototypes representing complex structures described in the WSDL. In Amazon WSDL, the KeywordSearchRequest operation expects a KeywordRequest parameter. To invoke KeywordSearchRequest, the proto property should be used to instantiate a KeywordRequest object as shown in this example.

Example 46.9. Invoking a SOAP operation using a complex type value

<canvas debug="true" height="530">

    <debug x="10" y="40" width="470" height="450" />

    <soap name="amazon" wsdl="http://soap.amazon.com/schemas3/AmazonWebServices.wsdl">

        <attribute name="handlerDel" value="null" type="expression" />

        <handler name="onload">
            Debug.write('Amazon soap service loaded.');
            Debug.write('Click keyword search button to get started.');
            Debug.write('--');
            search.setAttribute('visible', true);            
        </handler>

        <handler name="onerror" args="error">
            Debug.write('error:', error);
        </handler>

        <method name="invokeKeywordSearchRequest" args="keyword">

            //------------------------------------------------------------
            // Instantiate proto.KeywordRequest prototype to pass into
            // KeywordSearchRequest.
            //------------------------------------------------------------
            var keyReqParam = new this.proto.KeywordRequest();

            keyReqParam.keyword = keyword;
            keyReqParam.page = 1;
            keyReqParam.mode = 'book';
            keyReqParam.tag = 'webservices-20';
            keyReqParam.type = 'lite';
            keyReqParam.devtag = '12FRM39DPGD6QZRMTEG2';
            keyReqParam.format = 'xml';
            keyReqParam.version = '1.0';

            if (this.handlerDel == null) {
                this.handlerDel = new LzDelegate(this, 'handler');
            }

            //------------------------------------------------------------
            // Array of parameters and a delegate for callback handler must
            // be passed in when invoking raw stub function.  For more
            // information on proxy, see proxy section in RPC chapter
            //------------------------------------------------------------
            Debug.write('Invoking Amazon KeywordSearchRequest...');

            this.proxy.KeywordSearchRequest( [ keyReqParam ], this.handlerDel);
        </method>

        <method name="handler" args="response">
            Debug.write('Got response:', response);
        </method>

    </soap>

    <view id="search" x="10" y="10" layout="spacing: 5" visible="false">
        <view layout="axis: x; spacing: 5">
            <edittext id="s" text="services" />
            <button text="keyword search" onclick="amazon.invokeKeywordSearchRequest(s.text)" />
        </view>
    </view>

</canvas>

There are two kinds of prototypes: objects and arrays. An object prototype contains three properties:

  • name: (String) type name.

  • ns: (LzNamespace) prototype container which is associated to a particular namespace. This object is the same object pointed by LzNamespace.ns[MYNAMESPACE]. Inspect LzNamespace.ns in the debugger for details. The source for LzNamespace can be found in lps/components/rpc/library/namespace.js.

  • members: (Object) members this prototype contains as described in the WSDL.

The members property gives the developer an idea of what values should be set in the object. In the Amazon example, each member in the instantiated KeywordRequest object can be seen in the members property of the KeywordRequest prototype. Objects are represented in the WSDL's XML schema like:

<!-- Assume xsd is "http://www.w3.org/2001/XMLSchema" and
     soapenc is "http://schemas.xmlsoap.org/soap/encoding/". -->
<xsd:complexType name="COMPLEX_TYPE_NAME">
  <xsd:all>
    <xsd:element name="NAME1" type="TYPE1" />
    <xsd:element name="NAME2" type="TYPE2" />
    <!-- other elements... -->
  </xsd:all>
</xsd:complexType>

An array prototype contains three properties:

  • name: (String) type name.

  • ns: (LzNamespace) prototype container which is associated to a particular namespace. This object is the same object pointed by LzNamespace.ns[MYNAMESPACE]. Inspect LzNamespace.ns in the debugger for details. The source for LzNamespace can be found in lps/components/rpc/library/namespace.js.

  • arraytype: (QName) type of array this prototype represents, as described in the WSDL. A Qname contains a namespace and a localname, both of type string. The source for QName can be found in lps/components/rpc/library/qname.js.

Basic <complexType> and arrays are prototyped in the client based on a WSDL's schema. The prototypes are stored in LzNamespace.ns[NAMESPACE], where NAMESPACE is the namespace of the schema.

The OpenLaszlo SOAP WSDL parser expects the XML schema array pattern to look like:

Example 46.10. XML schema array pattern

<xsd:complexType name="ARRAY_NAME">
    <xsd:complexContent>
          <xsd:restriction base="soapenc:Array">
             <xsd:attribute ref="soapenc:arrayType" wsdl:arrayType="ARRAY_TYPE[]"/>
          </xsd:restriction>
    </xsd:complexContent>
</xsd:complexType>

The following example demonstrates what the client-side prototype of the Google XML schema type looks like:

Example 46.11. Google array complex type and object complex type

<canvas debug="true" height="580" width="530">

    <debug x="10" y="10" width="510" height="560" />

    <dataset name="googleDset" />

    <soap name="google" wsdl="http://api.google.com/GoogleSearch.wsdl">
        <handler name="onload">
            Debug.write('google soap service loaded');
            Debug.write('----------------');
            Debug.write('google proto:');
            Debug.inspect(google.proto)
            Debug.write('----------------');
            Debug.write('ResultElement proto:');
            Debug.inspect(google.proto.ResultElement)
            Debug.write('ResultElement members are:');
            Debug.inspect(google.proto.ResultElement.members)
            Debug.write('----------------');
            Debug.write('ResultElementArray proto:');
            Debug.inspect(google.proto.ResultElementArray)
        </handler>

        <handler name="onerror" args="error">
            Debug.write('error:', error);
        </handler>
    </soap>

</canvas>

The XML schema in the WSDL for Google looks like:

<definitions name="GoogleSearch"
             targetNamespace="urn:GoogleSearch"
             xmlns:typens="urn:GoogleSearch"
             xmlns:xsd="http://www.w3.org/2001/XMLSchema"
             xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
             xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
             xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
             xmlns="http://schemas.xmlsoap.org/wsdl/">

  <types>
    <xsd:schema xmlns="http://www.w3.org/2001/XMLSchema" 
                targetNamespace="urn:GoogleSearch">
            
      <!-- This is represented as an object prototype with 11 members in the
              client. -->
      <xsd:complexType name="GoogleSearchResult">
        <xsd:all>
          <xsd:element name="documentFiltering" type="xsd:boolean"/>
          <xsd:element name="searchComments" type="xsd:string"/>
          <xsd:element name="estimatedTotalResultsCount"  type="xsd:int"/>
          <xsd:element name="estimateIsExact" type="xsd:boolean"/>
          <xsd:element name="resultElements" type="typens:ResultElementArray"/>
          <xsd:element name="searchQuery" type="xsd:string"/>
          <xsd:element name="startIndex" type="xsd:int"/>
          <xsd:element name="endIndex" type="xsd:int"/>
          <xsd:element name="searchTips" type="xsd:string"/>
          <xsd:element name="directoryCategories" type="typens:DirectoryCategoryArray"/>
          <xsd:element name="searchTime" type="xsd:double"/>
        </xsd:all>
      </xsd:complexType>

      <!-- This will be represented as an object prototype with 9 members in
              the client. -->
      <xsd:complexType name="ResultElement">
        <xsd:all>
          <xsd:element name="summary" type="xsd:string"/>
          <xsd:element name="URL" type="xsd:string"/>
          <xsd:element name="snippet" type="xsd:string"/>
          <xsd:element name="title" type="xsd:string"/>
          <xsd:element name="cachedSize" type="xsd:string"/>
          <xsd:element name="relatedInformationPresent" type="xsd:boolean"/>
          <xsd:element name="hostName" type="xsd:string"/>
          <xsd:element name="directoryCategory" type="typens:DirectoryCategory"/>
          <xsd:element name="directoryTitle" type="xsd:string"/>
        </xsd:all>
      </xsd:complexType>

      <!-- This is represented as an array prototype in the client. -->
      <xsd:complexType name="ResultElementArray">
        <xsd:complexContent>
          <xsd:restriction base="soapenc:Array">
             <xsd:attribute ref="soapenc:arrayType" 
                             wsdl:arrayType="typens:ResultElement[]"/>
          </xsd:restriction>
        </xsd:complexContent>
      </xsd:complexType>

      <!-- This will be reprsented as an array prototype. -->
      <xsd:complexType name="DirectoryCategoryArray">
        <xsd:complexContent>
          <xsd:restriction base="soapenc:Array">
             <xsd:attribute ref="soapenc:arrayType"
                             wsdl:arrayType="typens:DirectoryCategory[]"/>
          </xsd:restriction>
        </xsd:complexContent>
      </xsd:complexType>

      <!-- This will be reprsented as an object prototype with 2 members. -->
      <xsd:complexType name="DirectoryCategory">
        <xsd:all>
          <xsd:element name="fullViewableName" type="xsd:string"/>
          <xsd:element name="specialEncoding" type="xsd:string"/>
        </xsd:all>
      </xsd:complexType>

    </xsd:schema>    
  </types>

  <!-- rest of WSDL -->

</definitions>

3.2. Type Mapping

3.2.1. Type mapping of parameters from JavaScript to SOAP encoding

Parameters passed into a SOAP function from a Laszlo application will automatically be typed according to the XML schema description in the WSDL. For example, if a function foo() expects an integer parameter, the function can be invoked either like foo(1) or foo('1'). When foo is invoked(), the SOAP message sent is serialized appropriately with the correct encoding type.

3.2.2. Type mapping of SOAP to JavaScript return types

Javascript is a loosely-typed language and may not support all the types used by the originating service. Therefore the OpenLaszlo soap service must perform a mapping. Here are the values used:

SOAP types JavaScript function stub return types
XSD types  
xsd:anySimpleType String
xsd:string String
xsd:boolean Boolean
xsd:double Number+
xsd:float Number+
xsd:int Number+
xsd:integer Number+
xsd:long Number+
xsd:short Number+
xsd:byte Number+
SOAP-ENC types  
soapenc:string String
soapenc:boolean Boolean
soapenc:double Number+
soapenc:float Number+
soapenc:int Number+
soapenc:long Number+
soapenc:short Number+
soapenc:byte Number+
soapenc:Array Array
Other types  
complex types Object (based on SOAP object proto prototype)

3.2.3. Numbers

In JavaScript all numbers are floating-point numbers. JavaScript uses the standard 8 byte IEEE floating-point numeric format, which means the range is from:


+/- 1.7976931348623157x10^308 - very large, and +/- 5x10^-324 - very small.

As JavaScript uses floating-point numbers the accuracy is only assured for integers between: -9,007,199,254,740,992 (-2^53) and 9,007,199,254,740,992 (2^53)

3.2.4. Unsupported return types

OpenLaszlo SOAP doesn't currently support the following return types:

  • xsd:QName

  • xsd:anyType

  • xsd:date

  • xsd:time

  • xsd:gYearMonth

  • xsd:gYear

  • xsd:gMonth

  • xsd:gDay

  • xsd:gMonthDay

  • xsd:token

  • xsd:normalizedString

  • xsd:unsignedLong

  • xsd:unsignedInt

  • xsd:unsignedShort

  • xsd:unsignedByte

  • xsd:nonNegativeInteger

  • xsd:negativeInteger

  • xsd:positiveInteger

  • xsd:nonPositiveInteger

  • xsd:Name

  • xsd:NCName

  • xsd:ID

  • xsd:language

  • xsd:NMTOKEN

  • xsd:NMTOKENS

  • xsd:NOTATION

  • xsd:ENTITY

  • xsd:ENTITIES

  • xsd:IDREF

  • xsd:IDREFS

  • xsd:duration

  • xsd:anyURI

  • xsd:schema

  • soapenc:Map

  • soapenc:Element

  • soapenc:Document

  • soapenc:Vector

4. SOAP headers

SOAP headers are the metadata passed with SOAP messages. During transmission, intermerdiaries may process headers while leaving the SOAP body unchanged. For example, a security service could provide authentication this way. Though SOAP headers are largely unused today, this may change as more secured SOAP services are implemented.

4.1. Request headers

To set SOAP request headers with each outgoing message, set the requestheaders attribute with an XML string (a root node is not required).

Example 46.12. SOAP request headers

<soap name="calendar" wsdl="http://www.mycompany.com/Calendar.wsdl">  
    <attribute name="requestheaders" value='<usr>name</usr><pwd>secret</pwd>' />  
<!-- ... --> 
</soap>  

You can use the onload() event to trigger the setting of a header value.

Example 46.13. Setting SOAP request headers in a method

<soap name="calendar" wsdl="http://www.mycompany.com/Calendar.wsdl"> 
    <handler name="onload">  
    <![CDATA[
       this.requestheaders = '<usr>name</usr>' +  '<pwd>secret</pwd>'
    ]]> 
    </handler>  
    <!-- ... --> 
</soap> 

To remove request headers, simply clear the requestheaders attribute by resetting its value, for example, requestheaders=''.

4.2. Response headers

To access response headers from a SOAP response, set the responseheaders attribute with a dataset.

Example 46.14. SOAP response headers

<canvas>

    <dataset name="resds" />

    <soap name="lq" wsdl="file:ReservationService.wsdl" requestheaders="..." 
          responseheaders="resds">
          ...
        <handler name="ondata">
            // print out response headers whenever we get data.
            Debug.write(resds.serialize());
        </handler>

    </soap>

    <!-- ... -->
</canvas>

5. Handling of overloaded methods on the service

The SOAP specification allows overloaded operations, that is, methods that contain the same name but have different parameters. This creates a mismatch with Javascript, which does not allow overloaded methods. Here is how we handle this.

If there are overloaded SOAP operations the OpenLaslo Server returns "mangled" names for those operations based on the request and response parameters. For example, if there are SOAP operations that are defined like the following in the WSDL:


  getFoo(request1, response1)
  getFoo(request2, response2)
  getFoo(request3, response3)

the SOAP object in the LZX application will contain the following methods:

  getFoo()
  getFoo_request2_response2()
  getFoo_request3_response3()

The reason why these methods need to be mangeld in LZX is because there's no way to have more than instance of getFoo() in Javascript so we do this on the client to differentiate the three method calls.

6. Limitations on OpenLaszlo SOAP implementation

The OpenLaszlo implementation of the SOAP specification has the following deviations:

  • Only WSDL 1.1 documents are suppported. WSDL 2.0 documents are not supported.

  • The WSDL <import> tag is not supported.

  • SOAP 1.1 partially transmitted arrays and sparse arays are not supported.

  • Multidimensional arrays are not supported.

  • SOAP attachments are not supported.

  • Multireferences are not supported. Items that are referenced more than once are created as different instances.

7. References