WSDL creation with Domino R6
Finally GOT IT!! As is the case with most catastrophes there was more than one cause. In a nutshell, I had some conflicting namespaces (which I now know are ultra sensitive to things like conflicts), and I finally learned how to return arrays of complex data types.
First, Thank goodness I had access to Google’s Web Services API, which had a great WSDL file and a sample request with the sample response. It was only through their example was I able to get this done. I never realized how much crap there is on the Internet until I tried doing this WSDL project. Everyone has advice about simple data types and the occasional array of scalar variables, but nothing on arrays of complex data types. So here it goes, manual WSDL creation for Dummies with Domino.
To get started, a good web service testing tool is mandatory. The easiest, most portable one I found was the .NET WebService Studio (win32 only). This tool allows you to test the functionality of your web services, see the raw requests and responses, and generates VB.NET or C# code for you.
Now, the WSDL. I find that reading the WSDL from the bottom up makes sense because it reads from the generalities to the specifics and that seems easiest to me. So from the bottom up, here is the breakdown of what you need:
<service name="addressMessageService"> <port name="dominoPort" binding="typens:dominoBinding"> <soap:address location="http://<Computed Value>/ws"/> </soap></port> </service>
The service name is the generic wrapper that groups various methods together in the same way that a script library groups common methods together. Your WSDL file can have multiple service names for different service groupings. With the service group is the port setting. The port name is the equivalent of a sub-category to your service groupings. Finally, the soap:address node specifys the endpoint for your service. In the database that I created for this app I have a configuration document which specifys the host name, which I combine with an @WebDBName to dynamically create a URL. The agent name is called “ws”. Next we will see where we bind the method names to the different groupings.
<binding name="dominoBinding" type="typens:dominoPortType"> <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="getAddressBookNames"> <soap:operation soapAction="getAddressBookNames" /> <input /> <soap:body use="encoded" namespace="urn:addressMessage" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> <output> <soap:body use="encoded" namespace="urn:addressMessage" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> </output> </operation> </binding>
Within the binding operation we define the style of the service which is ‘rpc’ (remote procedure call). The other type is ‘document’ but you can read up on that elsewhere. We define the operation here and we call it “getAddressBookNames” (this is what others may call the method name). I will add two things here; first is the XML namespace of “typens” which I will show later is a pointer to the existing WSDL file. Anytime I refer to something I define here, I will use that namespace. Second is the “urn:addressMessage” namespace. I have to be honest and say that I pulled this method from the Google API, and I believe this is a reference to the service itself. I defiitely appears that a web service client sees this syntax different from the typens.
The next section is the portType definition which is where we connect the operation/method name with the inbound and outbound messages.
<portType name="dominoPortType"> <operation name="getAddressBookNames"> <input message="typens:getAddressBookNamesRequest"/> <output message="typens:getAddressBookNamesResponse"/> </operation> </portType>
The typical naming convention for messages is “operationName+Request” and operationName+Response” but you can use your own. Note the use of the “typens” namespace which tells the soap client to look to this WSDL to find the definition. This is pretty simple so next we will look at each message definition.
<message name="getAddressBookNamesRequest"> <part name="starts_with" type="xsd:string" /> </message> <message name="getAddressBookNamesResponse"> <part name="return" type="typens:ArrayOfemployee" /> </message>
In this section we define the message contents and data types. The request message is easy, just a single string. But the response contains the typens namespace which means that we have defined a special data type called “ArrayOfemployee”. So let’s take a peak at our local type definitions.
<types> <xsd:schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:addressMessage" > <xsd:complexType name="ArrayOfemployee"> <xsd:complexContent> <xsd:restriction base="soapenc:Array"> <xsd:sequence> <xsd:element maxOccurs="unbounded" minOccurs="0" name="item" type="typens:employee"/> </xsd:sequence> <xsd:attribute ref="soapenc:arrayType" wsdl:arrayType="typens:employee[]"/> </xsd:restriction> </xsd:complexContent> </xsd:complexType> <xsd:complexType name="employee"> <xsd:all> <xsd:element name="lastName" type="xsd:string" /> <xsd:element name="firstName" type="xsd:string" /> <xsd:element name="notesName" type="xsd:string" /> <xsd:element name="emailAddress" type="xsd:string" /> </xsd:all> </xsd:complexType> </xsd:schema> </types>
So here we create our complex data type of “employee” which is nothing more than a a collection of four string. Then we set the “ArrayOfemployee” type to a soap encoded array of “employees”. Lastly is our xml namespace declarations. These are done first and are attributes of the “definitions” wrapper. The “definitions” is the wrapper for the entire WSDL file and of course needs to have an end node at the very bottom of the page.
<definitions name="addressMessage" targetNamespace="urn:addressMessage" xmlns:typens="urn:addressMessage" 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/" > .... all other nodes </definitions>
Below is a sample response for this request:
<?xml version="1.0" encoding="utf-16"?> <soap:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <ns1:getAddressBookNamesResponse xmlns:ns1="urn:addressMessage" soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <return xsi:type="soapenc:Array" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" soapenc:arrayType="ns1:employee[2]"> <item xsi:type="ns1:employee"> <lastName xsi:type="xsd:string">Crossett</lastName> <firstName xsi:type="xsd:string">Other</firstName> <notesName xsi:type="xsd:string">CN=Other Crossett/O=MyCompany</notesName> <emailAddress xsi:type="xsd:string">[email protected]</emailAddress> </item> <item xsi:type="ns1:employee"> <lastName xsi:type="xsd:string">Crossett</lastName> <firstName xsi:type="xsd:string">Jeff</firstName> <notesName xsi:type="xsd:string">CN=Jeff Crossett/O=MyCompany</notesName> <emailAddress xsi:type="xsd:string">[email protected]</emailAddress> </item> </return> </ns1:getAddressBookNamesResponse> </soap:Body> </soap:Envelope>
So hopefully this will help some folks because this was one hell of an exercise for me.
December 5th, 2005 at 2:38 pm
I am not doing such a project, but thank you very much for sharing your findings.
December 5th, 2005 at 6:29 pm
You never know when you may need it
Thanks for the input
December 6th, 2005 at 10:05 pm
Nice work, Jeff. Added it to my del.icio.us for future reference. Thanks for sending the link!