A SOAP WEB Server client and server written in TCL
Both Client and Server side require tdom and either 8.6 (Version 3.x) or 8.4 + dict extension for Version 2.x.
On August 8, 2006, Gerald Lester announced on comp.lang.tcl his web services library. This library, Web Services for Tcl [L1 ], provides commands to both easily create web services and to access web services from a client.
This article gives a briefto the client side of Gerald's library by presenting an example that accesses the Google SOAP Search API [L2 ] with just a couple dozen lines of code.
Show hole articleBefore you can run the examples in this article you will need to download and install some software and obtain a license key for the Google web service.
Unfortunately, the Tcl community does not yet have a definitive way to download and install packages so you'll have to perform a couple of manual steps. It is easy though; the matter boils down to downloading the software and copying it to a directory where your particular installation of Tcl will find it.
To access any web service using thepresented in this article you will need Gerald Lester's library. You can download it here: [L3 ]
Once downloaded, copy the folder to a location where tcl can find it. Start up a tcl shell and print theof the variable ::auto_path. Put the WebServices folder in one of the directories that is printed out.
You must also make sure you have all of the other dependencies installed on yourThese dependencies are listed on the same page where you find the web services library. If you are running a recent version of ActiveTcl [L4 ] you've likely already got all the dependencies so you'll only need to download and install the web services library.
One final note: the web services library and the example in this article make use of a new feature in tcl 8.5 named dict [L5 ]. Even though dict is a new feature in 8.5, a backport for 8.4 is available as a loadable extension here [L6 ] if you don't yet have an early release of tcl 8.5 installed on your machine.
To gain access to the Google search API you must register with Google and get a license key. If you already have a gmail account it's just a matter of asking for the key. If you don't have an account you'll have to set one up, awhich takes just a couple of minutes. Start theby going to [L7 ]. Note that you do not have to download their developers kit, you simply need a license key to perform the search.
We're going to create a script named "googleapi.tcl". Since we are needing to use the web services library we must add a statement to load the library at startup. This first version of the script will look like this:
package require WS::Client package require dict proc main {} { puts "The library is properly installed" } main
(The main procedure isn't strictly necessary but I find it makes the code easier to write and maintain. For more on this, read the article Your Second Tcl Program [L8 ].)
If you save the above code in a file named googleapi.tcl and run it with the command tclsh googleapi.tcl you should see the output "The library is properly installed".
Now that we know we can access the web services library, the next step is to make sure we can access and parse the WSDL. The WSDL for the Google SOAP search API is at [L9 ]. If you click the link you'll get back an XML document that describes the service in detail.
Before we can call a service at this address we must first fetch and parse this document with ::WS::GetAndParseWSDL. Modify the main procedure to look like this:
proc main {} { ::WS::Client::GetAndParseWsdl "http://api.google.com/GoogleSearch.wsdl" puts "WSDL successfully fetched and parsed" }
If you again run your script, this time you should see "WSDL successfully fetched and parsed".
This step is necessary as it initializes the web services library to know about the operations and arguments supported by the given web service. This step is notto the google API — no matter what web service you want to access, it all begins with fetching and parsing the WSDL.
The web services library can be used to make both synchronous and asynchronous calls to the web service. The simplest case is to make a synchronous call that returns a dict. We do this by calling the function WS::Client::DoCall.
Assuming the call succeeds, we will get back a dict object that contains the data returned by the web service. A dict can also be treated as a set of nested lists, so you aren't yet up to speed on dicts you can use ordinary tcl list commands to pull the data apart.
In the case of the Google API, we must pass in a number of arguments as defined by the WSDL (and on the human readable description at 2). The argument list itself is a dict, but can also be thought of as a list of keys and To print out the raw data, our main procedure now looks like this:
proc main {} { ::WS::Client::GetAndParseWsdl "http://api.google.com/GoogleSearch.wsdl" dict set args key "<your google license key here>" dict set args q {site:tclscripting.com font} dict set args start 0 dict set args maxResults 10 dict set args filter true dict set args restrict {} dict set args safeSearch false dict set args lr {} dict set args ie latin1 dict set args oe latin1 set result [::WS::Client::DoCall GoogleSearchService doGoogleSearch $args] puts $result }
You should see output that looks something like this:
return {searchComments {} resultElements {item {{relatedInformationPresent true summary {} title { to named <b>fonts</b>} URL http://www.tclscripting.com/articles/jun06/article1.html snippet {This article briefly describes how and why you should use named <b>fonts</b>. <b>...</b> <br> The advantage to using named <b>fonts</b> is that when the <b>font</b> is<b>...</b>} cachedSize 15k hostName {} directoryCategory {specialEncoding {} fullViewableName {}} directoryTitle {}} {relatedInformationPresent true summary {} title {Your second Tcl/Tk application} URL http://www.tclscripting.com/articles/sep06/article1.html snippet {package man page, https://www.tcl-lang.org/man/tcl8.4/TkCmd/<b>font</b>.htm ; sqlite <br> http://www.sqlite.org ; tcllib http://tcllib.sourceforge.net <b>...</b>} cachedSize 14k hostName {} directoryCategory {specialEncoding {} fullViewableName {}} directoryTitle {}}}} searchQuery {site:tclscripting.com font} estimateIsExact true searchTime 0.014507 startIndex 1 searchTips {} documentFiltering false estimatedTotalResultsCount 2 endIndex 2 directoryCategories {}}
Unlike dealing with block of raw XML, dealing with the output returned by ::WS::Client::DoCall is quite simple. The result is a dict, which is to say it is a nested list of keys anddicts are a new feature of Tcl 8.5, with a backport available as a loadable extension for tcl 8.4. While the data can be manipulated with traditional list commands, using dict commands makes parsing the output quite simple.
As a simple example, the following code prints out the title and URL of each page returned by the query:
foreach item [dict get $result return resultElements item] { puts [dict get $item title] puts [dict get $item URL] puts "" }
As you can see, using the Web Services for Tcl library along with the new dict feature of tcl 8.5, accessing web services is remarkably simple. In our example we are able to formulate a request, make the request, and parse the results, all with just a couple dozen lines of code.
Of course, this just scratches the surface of what we can do. The web services library provides even more features such as the ability to make asynchronous calls, saving and loading pre-parsed WSDL documents, and being able to send and receive additional headers in the SOAP envelope.
The complete script looks like the following:
package require WS::Client package require dict proc main {} { ::WS::Client::GetAndParseWsdl http://api.google.com/GoogleSearch.wsdl dict set args key "<your google license key here>" dict set args q {site:tclscripting.com font} dict set args start 0 dict set args maxResults 10 dict set args filter true dict set args restrict {} dict set args safeSearch false dict set args lr {} dict set args ie latin1 dict set args oe latin1 set result [::WS::Client::DoCall GoogleSearchService doGoogleSearch $args] foreach item [dict get $result return resultElements item] { puts [dict get $item title] puts [dict get $item URL] puts "" } } main
I'm not sure it can be any easier to access a web service no matter what language you use. Gerald Lester has given the Tcl community a real gem.
© 2006 Bryan Oakley
TCLWS 3.0.0 tries to improve the support of HTTPS / TLS. TCLTLS and twapi is supported on client and server.
If you want to use the embedded server and a self-signed certificate, you may look to the remarks on this page: Embedded TCL Web Server.
There are several open points:
Has anyone worked on UDDI support for software written using WebServices?