SOAP WSDL
Inhaltsverzeichnis
SOAP WSDL Package[Bearbeiten]
Basic Usage[Bearbeiten]
These chapter describes the basic underlying implementation. The code generated by the WSDL class generator calls those functions.
To make a SOAP request as a client, given a WSDL (either as file, string or URL),
you have to:
- create a service from the WSDL
- instantiate a client
- create a call object with call arguments
- perform the call
- extract the values from the returned result object
Creating a Service Instance[Bearbeiten]
The service instance keeps the information about the available entry points, the protocol and encoding and the data types. You need a WSDL to create a service. This can come from multiple sources:
From URL (preferred)[Bearbeiten]
this is the preferred method, especially as it will automatically deal with imported schema definitions (i.e. if the WSDL refers to other documents via an import)
service := SprayWSDLService onUrl: 'anUrlString'
From a String[Bearbeiten]
this only works, if the WSDL is self contained (eg. contains all required definitions and does not import other documents). If it does, missing documents will be fetched automatically via HTTP (see description on transports below).
service := SprayWSDLService onXmlString: 'wsdlString'
From a Set of Files/Strings[Bearbeiten]
when a WSDL URL is parsed, an transport object instance is used to fetch imported documents. By default, an instance of SptHTTPClient is used to fetch required documents.
A mock transport class named SptHTTPLocalTransport can be used. This keeps a list of string documents in a dictionary mapped by URL and delivers that contents, when asked for (i.e. it simulates an SptHTTPClient-Transport).
Thus, if you have a WSDL document, which imports other documents, AND you want to prevent WSDL fetches via http of your program at runtime, you should setup an instance of SptHTTPLocalTransport, give it all the documents (incl. any imported docs), and provide that as a document retriever.
For example, if you have a WSDL in (urlA), which imports urlB and urlC, and urlC imports urlD, use the following setup:
localTransport := SptHTTPLocalTransport new.
localTransport localDocuments
at: urlA "eg something like: 'http://foo.services.de:30050/partner/PartnerBusinessService?SCHEMA'"
put:
'<?xml version="1.0"?>
... the whole urlA document as string...
'.
localTransport localDocuments
at: urlB "eg something like: 'http://foo.services.de..."
put:
'<?xml version="1.0"?>
... the whole urlB document as string...
'.
localTransport localDocuments
at: urlC "eg something like: 'http://foo.services.de..."
put:
'<?xml version="1.0"?>
... the whole urlC document as string...
'.
and then create the service with:
definitions := WSDLDefinitions
onUrl: urlA
transport: localTransport.
service := SprayWSDLService onDefinitions: definitions.
In such a setup, all required import urls will be fetched from there (eg. no HTTP requests required).
You can provide the url contents from class variables, class getter methods, etc. Of course, you can also read the documents from a local file, and setup the localTransport instance from their contents.
From Binary Storage[Bearbeiten]
All of the above methods require some initial processing time (in the order of a few 100ms), because they read the textual WSDL with the XML parser, and construct a ratherr complicated object structure containing those definitions. (most of the space is taken up by XML-schema definitions, which are kept as XML-node trees).
You can experiment with a scheme used in the original Dolphin implementation, where definition objects are converted to a binary store string at development time, and restored from it in the deployed system at execution time.
Be aware, that such binary storage data is both system dependent and harder to analyze in case of errors. Our experience is that it is worth to have the WSDL around in plain text, as fixes are much easier to make there, in case of problems.
Instantiating a Client[Bearbeiten]
Once you have the service object, you have to create a client to a concrete partner service. This client prepares the outgoing and incoming envelopes, to save some processing time in the actual message send.
To create a client, use either:
client := service createClient
which creates a client to the service's default host. This is the one as specified in the URL.
Often, this is not the one you want to connect to (for example, if you have a local test service running, and/or the host address of the service is different for in-house connections). Also, some WSDLs do not include a valid service port URL.
Then use:
client := service createClientTo:'http://foo.bar.com:4933/bla/...'.
Create a Call Object with Call Arguments[Bearbeiten]
Finally, an actual service call is to be performed.
This is a two-step operation: first, a call instance is created, which takes the name of the operation and its arguments, and generates an out-envelope (which includes the XML representation of the arguments, as specified in the WSDL's encoding information). The second step is the actual call, which is described below.
Call argument objects fall into two categories:
- simple objects (xsd:simpleTypes, which can be directy mapped to Smalltalk objects)
- complex objects (in the XML schema, these are xsd:complexType elements)
Simple types are mapped to corresponding Smalltalk objects:
- xsd:string - String
- xsd:int - Integer
- xsd:integer - Integer
- xsd:boolean - Boolean
- xsd:date - Date
- xsd:time - Time
- etc.
There are a few xsd:simpleTypes, for which no standard Smalltalk object exists on all machines (xsd:datetime). Depending on the dialect, these are either mapped to existing classes or to classes from the SOAP framework. For example, xsd:datetime is mapped to the Timestamp class in Smalltalk/X but to SOAP__XeXSDDateTime in VSE.
Complex types must be represented as instances of one of:
- an XeQstruct - this is a special kind of Dictionary, which uses qualified names (eg. XML names with namespace and prefix) as keys. This is the default representation used by the SOAP framework.
- a Dictionary - if values are given as dictionary instance, it must contain key-value mappings for the sub-elements. The keys are the localName of the schema's element names (i.e. if the element is named foo:someType, then the key should be 'foo' only).
- a special data holder object - this must understand getter- and setter messages, corresponding to the local names of the elements.
A concrete example is found in the BLZ-testcase:
call := client send: 'getBank' withArguments:getBankArg.
Performing the Service Call[Bearbeiten]
Finally, execute the call by giving it a #value message (think of the call object as a block, which does the call for you):
result := call value.
this harmless looking message performs the transfer. It sets up the communication (usually HTTP, where the SOAP message is wrapped into an HTTP-request), sends the message in an asynchronous send process, awaits the response, decodes the returned XML and instantiates the result object(s).
Notice that SOAP allows for multiple return values to be specified in the WSDL. If this is the case, the result returned by #value is the first return value, and a sequenceable collection of all return values is retrieved via #outParameters.