Shared knowledge leads to accumulated knowledge

Printer Friendly Page

VDF SOAP Client Class v0.1

Date: 13th May 2002
by Mike Peat, Unicorn Financial Solutions

Note: an article I have written on the business case for Web Services is now available at Unicorn InterGlobal's Web Site

The Soap Client
The Class
The Examples
Reading WSDML
Development Roadmap
License
Download


The SOAP Client


This is a "first cut" at developing initially a SOAP (it used to stand for Simple Object Access Protocol, but now it is just a name!) client, and eventually full Web Services client, capability for Visual DataFlex.

Capabilities


This version has very limited capabilities at present. I can get it to work with a number of SOAP server interfaces I have developed myself and also with several external services available on the Internet (three of which I am providing sample code for).

There are also many Internet web services I cannot currently get it to work with.

To start with there are two styles of SOAP service: RPC style and Document style. Services built with Microsoft's .NET framework tend to be in Document style, while those built with Java (many platforms and frameworks), Delphi, Perl or whatever tend to be in RPC style. Currently this implementation only works with RPC style services - a failing I hope to address as soon as possible.

There are also other services I can't get it to work with for reasons I am as yet unclear about (i.e. if I knew what was wrong I would have fixed it already!).

Usage Notes


I have only tested this with VDF 8.1. It may well work with other versions - feel free to try - but I know it works with 8.1 for me (to the extent that it works at all - see Capabilities above [ed: You need at least VDF8.0 as the classes use the cHTTPtransfer class]).

Fairly obviously most of the files supplied should be placed in the AppSrc directory of your VDF workspace. The exception is "NASDAQ-Ticker-Symbols.csv" (for the US Stock Quotations sample) which should go in the Data directory (unless you want to explicitly change the property psStockFile in "US-Quotes.vw").

Note: For reasons I am unclear about, it is necessary to have Internet Explorer 6 installed for the client to work. I have v6.0.26 and everything works for me. With IE 5.x have I found that some of the VDF XML classes did not work as expected, causing the whole mechanism to fail. Another case of Microsoft having stuck its browser to far up its own operating system (or something similar) I guess.

To use the samples, first register them in the VDF IDE (Component -> Register External Component - they are all views), then build a program and "Add" the views to is. Compile and run the program. The "Captain Haddock's Curser" is probably the simplest view to start with.

Top of the page

The Class


The SOAP client itself is in a package called "cSoapClient.pkg" and is a class called "cSoapClient". This is suitable for instantiation ("Object oSoap is a cSoapClient") or sub-classing ("Class cMySoap is a cSoapClient") and both of these approaches are demonstrated in the examples, with some variations.

There are several other classes in the same package file, but if you are instantiating or sub-classing you should start from cSoapClient. I will try and keep this as stable as possible (these are very early days yet however), but I will feel totally free to change the other classes as required (this is the reason everything is not in a single class).

The Interface


There are only a few properties to set and methods to call to invoke the simple Web Services that are used in the examples, but some other, rather more raw, interfaces are available for trying to deal with more complex things.

Property psServer String Set this to the IP address (123.456.789.012) or host ID (www.soap-serv.org) at which the server resides.
Property piPort Integer Set this to the TCP/IP port number on which the service is provided.
Property psAction String Set this to the SOAPAction HTTP header required by the service.
Property psURN String Set this to the URN (/services/soap/TheService) within the host that delivers the service.
Property psMethod String Set this to the operation name (getInfo) of the service you are calling
Property psMethodNsUri String Set this to the namespace URI or URN of the operation you are calling.
Property psMethodNsPre String The prefix that will be used to represent the method namespace URI in QNames in the method call by appearing with a colon before their descriptive name. You can set this to anything you like. My usual choice is "n", but "NS1" is also common.
Property psHttpContentType String This is set to "text/xml; charset=UTF-8" by default and you should probably leave it that way unless you know why it should be set differently.
Procedure Add //String// name //String// value; Types: //Integer, String, Float, Double, Boolean//These are easy-to-use mechanisms for adding the arguments required for the method you are calling (i.e.: //Send AddStringArg "symbol" "MSFT"//). If you need a type not offered you should use the raw AddArgument procedure.
Procedure AddArgument String type String name String value This allows arbitrary argument types to be added to the method call. The type should be one of those defined by the XML Schema definition schema and will be specified in the services WSDL.
Procedure Reset This will initialise the SOAP client ready for use - important if you will be making repeated calls.
Function CallSingleElement Returns String This invokes the call to the service you have set up through the properties and procedures above so long as it only returns a single item of data. (Many do.)
Function CallFullXml Returns Handle This invokes the call to the service you have set up, but returns a handle to an XML document containing the returned SOAP response XML. If you need to use a service that returns other than a single item of data (or need to debug your call, or just want to see exactly what comes back) you should use this. It is your responsibility to deal with destroying the XML object afterwards.
Procedure DestroyXmlDocument This destroys the current XML document (if any) held by the SOAP client.

Top of the page

The Examples


There are four sample views. I have used different class/object styles in them to illustrate various usages of the SOAP client class.

In the "SOAP Client Test" view the package is used and a simple object created from the class, with the view code then doing all the work from the outside.

In the "Captain Haddock" view the custom calling code is placed inside the object with only UI-specific code being in the view.

In the US Stock Quotes view a SOAP client sub-class ("cUsStockQuote") is created in a separate package ("cUsStockQuote.pkg") and the custom calling code placed in that, then that is used in the view and an object of the sub-class created, with only minimal code being in the view.

The BabelFish view follows the same approach as the US Stock Quotes ("cBabelSoap" in "cBabelSoap.pkg"), but there is a fair amount of code in the view for dealing with correctly extracting text from the Edit object for passing to the service and also for formatting the returned string for display in the Edit (I can never seem to get the hang of doing that an easy way!).

In my opinion the latter two represent the "best practice" way to do it, preserving as they do the custom calling code for a given service for easy reuse in other applications.

SOAP Client Test View


(SoapTest.vw)
This view allows you to test out connecting to a service before you actually have to cut any code. It allows you to see the request XML before you send it, the returned XML when you get it back or, if the service returns a single element of data (and many do), the returned value.

To determine what information to place in the various inputs refer to the Interface section above and the "Reading WSDL" section below.

Note: the "RCP Style" vs. "Doc Style" radio is optimism in action: the current implementation does not work with Document style SOAP - leave it set to RPC!

Captain Haddock's Curser


(Haddock.vw)
If you remember "Hergé's Adventures of Tin-Tin" from your youth, you may recall the black-bearded Captain Haddock who had a fine line in blistering invective. Now, thanks to the magic of SOAP, you can enjoy these colourful curses to your heart's content in four languages: English (US), French (FR), Dutch (NL) and Swedish (SE).

Sadly I have not managed to work out how to properly display the special language characters that sometimes occur in the French version and almost always in the Swedish - nor have I tried too hard.

I have built a performance test into this view, but don't use too large a number of calls until you get a feel for how long they are taking as there is no interrupt mechanism and you will have to kill off the program to stop it.

US Stock Quotations


(US-Quotes.vw)
This provides access to 20-minutes-delayed stock quotes from the NASDAQ stock exchange, keyed by ticker symbol. Simply selecting one of the four thousand odd items from the combo pull-down will get the quote. (Note: I have not checked these for accuracy, but when the NASDAQ is open you can often see some of them moving on a minute to minute basis.)

I have also built a performance test into this view - see above under Captain Haddock.

SOAP BabelFish Translator


(BabelFish.vw)
This accesses the X-Methods interface to AltaVista's BabelFish translation service.

Personally I wouldn't trust this service to do any real translation until it gets much better at idiom. A favourite game is to translate some English text into another language then immediately translate it back and see how badly mangled it is. The original English:
Hello Brian!  How are you today?
I have to say that I am feeling a touch under the weather today Harry.
You English idiot Brian!  Of course you are under the weather.  
The weather is up in the sky.  You would have to be in a spaceship to not be under it.

becomes:
Hello Brian!  How are you today?  
I must say that I smell today a contact under time Harry.  
You idiotic English Brian!  Naturally you are under time.  
Time is in rise in the sky.  You should be in a spaceship not to be under him.
via French and:
hello Brian!  How do you go today?  
I must say that I believe under the weather today Harry to a note.  
It English idiot Brian!  You are of course under the weather.  
The weather is above in the sky.  
They would have to be in a spaceship not to be to under it.
via German.

Now you can see where all those interestingly worded instruction manuals for Taiwanese and Chinese gadgets are composed!

Sadly I have not managed to get the Edit object involved to display the alternative characters in the translated-to language in question properly. (Well... I have not really tried! A little exercise for the reader perhaps?)

Top of the page

Reading WSDL


Each public web service will generally come with a WSDL (Web Service Description Language) definition of the services it offers. Interpreting these is the key to accessing the service successfully, so here is a very brief tutorial in extracting what you need from what can be quite a confusing format.

Note: You can use the "SOAP Client Test" view to check that you have successfully got to grips with your particular piece of WSDL before starting to code access to it into an application.

The following is a sample of WSDL formatted for readability (Microsoft Internet Explorer does this quite well by the way):
<?xml version="1.0"?>

<definitions name="BabelFishService"
         xmlns:tns="http://www.xmethods.net/sd/BabelFishService.wsdl"  
         targetNamespace="http://www.xmethods.net/sd/BabelFishService.wsdl" 
         xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
         xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
         xmlns="http://schemas.xmlsoap.org/wsdl/">

<message name="BabelFishRequest">
<part name="translationmode" type="xsd:string"/>
<part name="sourcedata" type="xsd:string"/>
</message>

<message name="BabelFishResponse">
<part name="return" type="xsd:string"/>
</message>

<portType name="BabelFishPortType">
<operation name="BabelFish">
<input message="tns:BabelFishRequest" />
<output message="tns:BabelFishResponse" />
</operation>
</portType>

<binding name="BabelFishBinding" type="tns:BabelFishPortType">
<soap:binding style="rpc"
                    transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="BabelFish">
<soap:operation soapAction="urn:xmethodsBabelFish#BabelFish"/>
<input>
<soap:body use="encoded" 
                 namespace="urn:xmethodsBabelFish"
                 encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</input>
<output>
<soap:body use="encoded"
                 namespace="urn:xmethodsBabelFish" 
                 encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</output>
</operation>
</binding>

<service name="BabelFishService">
<documentation>
            Translates text of up to 5k in length, between a variety of
            languages.
</documentation>
<port name="BabelFishPort" 
            binding="tns:BabelFishBinding">
<soap:address
              location="http://services.xmethods.net:80/perl/soaplite.cgi"/>
</port>
</service>

</definitions>

This is the WSDL for the BabelFish service (the source was: "http://www.xmethods.net/sd/2001/BabelFishService.wsdl ") used in one of the sample views. It was the simplest I could find, but don't be put off! It gets a lot easier as you get more familiar with the format.

Logically the document is structured from the finest granularity units to the coarsest. This is because the more complex structures are built up of the simpler ones that precede them. (There is a level of granularity even below the first - "messages" - that we see here, which is "types": custom data types or structures beyond the basics defined in the XML Schema definition. BabelFish only uses standard types so this is not present here.)

The first things to be defined are the two "messages": "BabelFishRequest" and "BabelFishResponse". From these we can see that BabelFishRequest (the name is a bit of a giveaway as to what it is used for, although we cannot make that assumption) is composed of two parts: "translationmode" and "sourcedata" and both are of type "string", while BabelFishResponse (again a bit of a clue) has a single element called "return", also of type string.

Next comes the "portType" definition: "BabelFishPortType, with an "operation" element named "BabelFish" (this is going to be our method name) which takes an input message of "BabelFishRequest" and yields an output message of "BabelFishResponse".

Is it starting to make sense yet?

Next comes the "binding" definition which has a "style" attribute of "rcp", which is just as well as we can't cope with any other style yet. In the "soap:operation" sub-element of the "operation" element (named again, "BabelFish" - the method we will call) is defined a "soapAction" attribute of "urn:xmethodsBabelFish#BabalFish" - unsurprisingly this will be our "SOAPAction" HTTP Header setting. In the operation's "input" element is a "namespace" attribute of "urn:xmethodsBabelFish" which will be our method namespace URI.

Finally comes the "service" definition which has a "port" element that has a "binding" of "tns:BabelFishBinding" ("tns" is shorthand for "this namespace" and is explicitly defined as the referring to the URL in question at the start of the document "xmlns:tns=...") which ties it to the binding defined above it. This "port" has a "soap:address" element with a location attribute of "http://services.xmethods.net:80/perl/soaplite.cgi ". This tells us that the host we want is "services.xmethods.net" the TCP/IP port number we want is 80 (the default if it is not present in any case) and the URN on the host to call is "/perl/soaplite.cgi". (You might guess from this that "soapLite" was written in Perl. and you would be right!)

On the X-Methods web site (http://www.xmethods.com ) where I discovered this service, the BabelFish page explains (in ordinary text, not XML), what should go in the "translationmode" element of the Input message to translate which language to which other language.

From this you should have everything you need to set up your SOAP call.

What I do is open the WSDL in Internet Explorer while running the "SOAP Client Test" view. I then cut and paste from the WSDL in IE to the inputs in the view, then press "See XML Result" to see what I get back.

Top of the page

Development Roadmap


There are some fairly obvious shortcomings in the current implementation which need to be addressed, in particular the inability to work with Document style SOAP services.

The ability to work with services which return complex data is also an area for development, but to address this correctly (and indeed to even deal correctly with simple services - the current arrangement is really a quick kludge) a whole new area of capability needs to be added. This is the ability to parse and understand WSDL within the software. This is really where the roadmap begins.

1. Build a WSDL parser/interpreter.
2. Use 1. to create a "Wizard" program that will allow the user to browse to the WSDL description of a service and write an appropriate SOAP client sub-class to access it.
3. Also use 1. to create a program which will allow the user to browse to the WSDL and then dynamically invoke it and see the results, much like the current "SOAP Client Test" view, but with the software doing the WSDL interpretation.
4. Investigate building UDDI capabilities.

These are very early days, so this roadmap may be departed from in fairly short order as I learn more about what I am doing, but it is how I see going forward with the project right now.

I intend this to be an Open Source project, which means that you are free to use it pretty much however you like without reference to me so long as you abide by the terms of the GNU Public Licence. I hope however that people will let me know whether it works or does not work, what it does not work with (I already know all too much of that, but I'm working on it!) and report any bugs they find. Better yet would be if they fixed the bugs and told me how! If other people would like to contribute in a larger way that just fixing the bugs, that would be great - there is a lot of potential work to be done - but for best results it may require some coordination. Feel free to e-mail me on mailto:mike@unicorn-fs.com on any or all of these points.

Top of the page

License


This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation (a copy is included in "license.htm" with this distribution).

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Comments, bug reports (or better yet, bug fixes), etc. to:

mailto:mike@unicorn-fs.com