Forum OpenACS Development: Re: Tcl Web Services Toolkit: TWiST

Posted by Claudio Pasolini on
I'd like to rectify my previous post regarding the basic tWSDL API.
  • I found how to show the WSDL definitions using xml::document::print ::wsdb::definitions::${serverName}]
  • I ingenuously assumed that once defined a complex type I could use it as a component of a higher level complex type, but it is not so: when I try
    eval [::wsdl::elements::modelGroup::sequence::new $usersns Users {
    {oneUser userdecoder::oneUser}}]
    I get the error
    can't read "::wsdb::types::twist2ns::oneUser::validate": no such variable
    and so my question is: how do I define higher level complex types?
Posted by Tom Jackson on
The tWSDL API modelGroup::sequence::new is currently restricted to the case where all child elements are simpleTypes. This is a common use case. It is also restricted to a each child occurring zero or one time.

I know the API call seems simple enough, but it hides a lot of code generation, including the type validation routines and the invoke routine. These routines would look different than the simple case. This seems to suck pretty bad until you consider the number of Tcl API which take or return multi-dimensional data.

It turns out that type validation is the easiest to extend to multi-dimensional data, but overall, the problem is not too difficult and was planned for in advance.

This is the meaning of the {ElementA Value ElementB Value} configuration (called a conversionList). The modelGroup::sequence API assumes actual values for each child element. This configuration is used in the invoke API (which is auto-generated) by the API ::xml::childElementsAsListWithConversions

Currently this simple API only handles the Value type, although if you look at it, other types are commented about.

Let me just say that developing the type system and the API is very delicate, it has to reflect the method of type development used by XML-Schema, otherwise we cannot publish the interface in a way that a generic client can use. Beyond the simple interface, the developer must become more aware of how things work throughout the entire system and in XML-Schema, WSDL, etc.

I'll post another note about the invoke procedure interface, which is a key abstraction. Any developer using tWSDL for complex services must understand this interface.

Posted by Tom Jackson on

Here is an example auto-generated invoke procedure:

proc ::wsdb::operations::openacs::CheckEmailOperation::Invoke {
} {
variable conversionList
::xml::childElementsAsListWithConversions $inputXMLNS $conversionList
return [::wsdb::elements::openacs::CheckEmailResponse::new $outputXMLNS [::openacs::CheckEmail $Email]]

This tiny proc is the interface between the XML world and the Tcl world. This procedure could be completely hand written as long as it takes the same input (where to find the Tcl rep of the XML document and where to put the output document) and returns the same output (reference to the return document, a child of outputXMLNS).

In addition, the operation's namespace variable 'invoke' must be set to the fully qualified proc name.

From the point of view of the server, the size and complexity of the document isn't important. But obviously the underlying Tcl API which can handle complex data is beyond the scope of the interface: it is hidden inside the invoke procedure. This is the purpose of this interface, otherwise it would take a few seconds of thought to discover something you couldn't handle, and hacking would begin. The invoke procedure is independent of other parts of the tWSDL API. You can completely test this procedure without the need to run the SOAP server, therefore it is also independent of the binding or protocol. If someone adds a new binding, the invoke procedures remain valid.

One use case which I am working on is a complexType which contains a sequence of children all of the same type, such as a sequence of database rows. It works as expected, but I don't yet have a higher level API to write the code.

If you run the modelGroup::sequence::new API with 'puts' or 'log Notice' instead of 'eval', you can see what code is actually generated.