Table of Contents
Warning | |
---|---|
This feature works in proxied applications only. It does not work in SOLO applications. |
SOAP (Simple Object Access Protocol) 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.
The <soap>
element creates a client-side representation of a SOAP
service based on a WSDL. The name and wsdl attributes are required.
Example 47.1. A simple SOAP tag
<soap wsdl="..." service="..." port="..." autoload="[true|false]" secure="[true|false]" secureport="..." >
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: (lz.dataset) 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.
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.
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.
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 47.2. Loading Amazon SOAP service
<canvas
debug
="true
" height
="530
" width
="100%
">
<soap
name
="amazon
" wsdl
="http://soap.amazon.com/schemas3/AmazonWebServices.wsdl
">
<handler
name
="onload
">
Debug.debug('Amazon soap service loaded');
Debug.debug('Compare proxy stubs with WSDL SOAP operations.');
Debug.debug('Amazon WSDL at %w', this.wsdl);
Debug.debug('proxy: %w', this.proxy);
</handler
>
<handler
name
="onerror
" args
="error
">
Debug.error(error);
</handler
>
</soap
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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 47.3. Unloading Amazon SOAP service
<canvas
debug
="true
" height
="200
" width
="100%
">
<debug
height
="150
" y
="10
"/>
<soap
name
="amazon
" wsdl
="http://soap.amazon.com/schemas3/AmazonWebServices.wsdl
">
<handler
name
="onload
">
Debug.debug('Amazon soap service loaded');
Debug.debug('proxy is: %w', this.proxy);
this.unload();
</handler
>
<handler
name
="onunload
">
Debug.debug('Amazon soap service unloaded');
Debug.debug('proxy is: %w', this.proxy)
</handler
>
<handler
name
="onerror
" args
="error
">
Debug.error(error);
</handler
>
</soap
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
Document style operations use XML data(that is, documents) as parameters. 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 47.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
<lz.DataElement>
s, though often only a single
<lz.DataElement>
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 47.5. Document style SOAP operation
<canvas
debug
="true
" width
="100%
">
<!-- 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.debug("%w", 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.error(error);
</handler
>
<handler
name
="ontimeout
" args
="error
">
Debug.error("RPC timeout: %w", error);
</handler
>
<handler
name
="ondata
" args
="value
">
Debug.debug("%w", value);
result.setAttribute("text", 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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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 47.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.
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 47.7. Passing simple parameters in RPC style operation
<canvas
debug
="true
" height
="400
" width
="100%
">
<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.debug('google soap service loaded');
</handler
>
<handler
name
="onerror
" args
="error
">
Debug.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.debug('search result: %w', 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.debug('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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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.
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 47.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 47.9. Inovoking a SOAP operation using a complex type value
<canvas
debug
="true
" height
="530
" width
="100%
">
<soap
name
="amazon
" wsdl
="http://soap.amazon.com/schemas3/AmazonWebServices.wsdl
">
<attribute
name
="handlerDel
" value
="null
" type
="expression
"/>
<handler
name
="onload
">
Debug.debug('Amazon soap service loaded.');
Debug.debug('Click keyword search button to get started.');
Debug.debug('--');
search.setAttribute('visible', true);
</handler
>
<handler
name
="onerror
" args
="error
">
Debug.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.debug('Invoking Amazon KeywordSearchRequest...');
this.proxy.KeywordSearchRequest( [ keyReqParam ], this.handlerDel);
</method
>
<method
name
="handler
" args
="response
">
Debug.debug('Got response: %w', 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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
There are two kinds of prototypes: objects and arrays. An object prototype contains three properties:
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:
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 47.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 47.11. Google array complex type and object complex type
<canvas
debug
="true
" height
="580
" width
="100%
">
<dataset
name
="googleDset
"/>
<soap
name
="google
" wsdl
="http://api.google.com/GoogleSearch.wsdl
">
<handler
name
="onload
">
Debug.debug('google soap service loaded');
Debug.debug('----------------');
Debug.debug('google proto:');
Debug.inspect(google.proto)
Debug.debug('----------------');
Debug.debug('ResultElement proto:');
Debug.inspect("%w", google.proto.ResultElement)
Debug.debug('ResultElement members are:');
Debug.inspect(google.proto.ResultElement.members)
Debug.debug('----------------');
Debug.debug('ResultElementArray proto:');
Debug.inspect("%w", google.proto.ResultElementArray)
</handler
>
<handler
name
="onerror
" args
="error
">
Debug.error(error);
</handler
>
</soap
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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>
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.
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) |
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)
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
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.
To set SOAP request headers with each outgoing message, set the requestheaders attribute with an XML string (a root node is not required).
Example 47.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 47.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=''.
To access response headers from a SOAP response, set the responseheaders attribute with a dataset.
Example 47.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.debug("%w", resds.serialize()); </handler> </soap> <!-- ... --> </canvas>
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.
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.
Copyright © 2002-2010 Laszlo Systems, Inc. All Rights Reserved. Unauthorized use, duplication or distribution is strictly prohibited. This is the proprietary information of Laszlo Systems, Inc. Use is subject to license terms.