Friday, February 13, 2009

Consuming .Net web services in Notes 8.5

A new challenge this week: A client wants to consume a web service from an external party.

So they are using Notes & Domino 7 at the moment, but have an upgrade to 8.5 planned (although timing has not been confirmed yet).

I have not used Julian Robichaux's tool Stubby, but have only ever read good things about it. My Java is pretty basic but I have written a few small things and used LS2J so I figured this would be achievable in Notes 7.

However I was excited about the idea of doing it in 8.5 as I had read about the new Web Service Consumer feature.

So after a meeting with the client, a phone conference with the web service provider and a bit of a discussion with my team, I cracked open my barely touched DDE 8.5.

After a bit of reading on the net I found this example. It worked first time and couldn't be easier.

So I created a new web service consumer, named it and pointed it to the URL of the WSDL of the service I wanted to test. The WSDL failed to load, and presented the following error:
The request operation failed: Element
{http://www.w3org/2001/XMLSchema}schema is referenced but not defined
Not a terribly informative message, and nothing I searched for in the error message shed any light on the problem.

I downloaded the WSDL and opened it in an editor.

First problem - it was defined using the SOAP 1.2 protocol. Now this is fine for Notes 8 or 8.5, but it pretty much rules out using Notes 7.

Now learning to reading a WSDL file for me had previously rated somewhere up there with root canals - every time I looked at one it made my eyes glaze over and I found myself switching to more pressing tasks such as cleaning out my desk draws, filing emails, anything. But it could be avoided no more. I looked through the WSDL file and compared it with the one that loaded. It seemed to contain all the same basic elements, the namespace declaration for schema seemed identical. Message and operations all seemed correctly defined.

I found a web page that allows you to query web services. This site reported that the web service was valid - it returned all the available methods, queried the web service and returned valid results.

So I reverted to the most basic of debugging techniques - chop out 90% of the WSDL until I could get it to load, and started putting things back a small chunk at a time until I find the offending code.

The WSDL contained a schema at the top, defining the parameters for the input and output when calling the web service methods. A typical example is:
<s:element name="GetDealers">
<s:complextype />
</s:element>
<s:element name="GetDealersResponse">
<s:complextype>
<s:sequence>
<s:element minoccurs="0" maxoccurs="1" name="GetDealersResult">
<s:complextype>
<s:sequence>
<s:element ref="s:schema">
<s:any />
</s:sequence>
</s:complextype>
</s:element>
</s:sequence>
</s:complextype>
</s:element>
The problem is the line with ref="s:schema".

Now that I had found the problem line, a web search turned up some useful information.

The problem is that .Net allows developers to define the return value of a web service to be a DataSet - which basically means the return values will be defined at run time, and a schema will be included in the web service return value. This schema will be called 'schema' and the elements will be prefixed with 's:' . Unfortunately this is the same naming scheme used within the WSDL itself, but it turns out that this is just a co-incidence (or lazy programming?). The java libraries used by Notes complain because this schema is undefined when it is attempting to create the stub functions in the web service consumer, so it cannot define the objects returned by the methods.

From what I managed to find on the web, this is becoming an increasingly popular way for .Net developers to define their web services. I can see some instances where the returned results may not be known in advance, but I suspect this is a default setting in the IDE, and if the developers never work in the Java world they see no need to explicitly define their data as .Net consumers don't complain.

The solution is to remove the offending line, and change the following line to read:
<s:any minoccurs="2" maxoccurs="2">
This will return an object of type XSD_ANYTYPE, with a public array called 'any' which contains two NotesDOMElementNode members.

So after changing the local copy of WSDL, I was able to import it and create my web consumer. After that, I just needed to create a test button with the following code:
Option Declare
Use "[web service consumer name]"

Sub Click(Source As Button)

Dim myWebService As New [class name of porttypebase class]
Dim wsReturnObj As [class name of method return object]
Dim xsdParam1 As New XSD_STRING
Dim xsdParam2 As New XSD_STRING
Dim session As New NotesSession
Dim ws As New NotesUIWorkspace
Dim doc As NotesDocument
Dim dataNode As NotesDOMElementNode
Dim response As String
Dim nodeList As NotesDOMNodeList
Dim targetNode As NotesDOMNode

Set doc = ws.CurrentDocument.Document

' set the input parameters
xsdParam1.setValueFromString( doc.Field1(0) )
xsdParam2.setValueFromString( doc.Field2(0) )

' call the web service
Set wsReturnObj = myWebService.MethodName( xsdParam1, xsdParam2 )
Set dataNode = wsReturnObj.any(1)
' any(0) contains the schema if you need it

' now we have a familiar object we can process any way we want
Set nodeList = dataNode.GetElementsByTagName("elementName")
Set targetNode = nodeList.GetItem(1)
If Not targetNode.IsNull Then
If Not targetNode.FirstChild.IsNull Then
response = targetNode.FirstChild.NodeValue + Chr(10)
End If
End If

doc.response = response

End Sub
Done!

10 comments:

Anonymous said...

Great job... I have some additional info on http://www.recondite2.com ...

Julian Robichaux said...

This is a fantastic tip! DataSet data types are the scourge of non-.NET web services. Thanks so much for posting it.

Oh, and regarding Stubby: that's definitely R7 only. You really do want to be using the built-in R8.x stuff. Good luck!

Jaime Bisgrove said...

Spent hours on this until I found your post. Brilliant ! Now we just need to fix the underlying "issue". Thanks for allowing me to keep my head up around the .Net crew

Anonymous said...

When will notes support SOAP 1.2? I have to consume a SOAP 1.2 app and notes doesn't like it. I'm rather dismayed at the lack of mocement on IBM's part to integrate SOAP 1.2 into domino.

Michelle said...

Notes has supported SOAP 1.2 since version 8. The WSDL I imported in this example was version 1.2, and aside from the dataset issue, SOAP 1.2 works fine.

The dataset problem is a .Net vs Java issue - nothing specifically to do with Notes

Anonymous said...

Quote from Notes 8.5 Help File:
"Domino supports Web services as defined in the W3C documents Simple Object Access Protocol (SOAP) 1.1 (http://www.w3.org/TR/2000/NOTE-SOAP-20000508) and Web Services Description Language (WSDL) 1.1 (http://www.w3.org/TR/2001/NOTE-wsdl-20010315). See also Web Services Architecture (http://www.w3.org/TR/ws-arch) and Web Services Activity (http://www.w3.org/2002/ws/)."My guess is your WSDL has SOAP 1.1 and SOAP 1.2 support in it allowing you to interface with it. The WSDL I am attempting to use ONLY supports a SOAP 1.2 binding, there are no SOAP 1.1 bindings defined and notes balks at it as shown here in the error upon trying to import the WSDL:

http://img352.imageshack.us/img352/9589/errorc.png

to further show IBM's total disregard for SOAP 1.2 in domino:

http://www-01.ibm.com/support/docview.wss?rs=463&context=SSKTMJ&dc=DB550&uid=swg1LO39408&loc=en_US&cs=UTF-8&lang=en&rss=ct463lotus

grrr...

Anonymous said...

I also spent hours on this until I found your post. I'm trying to consume a MS Commerce Server web service in Lotus Notes 8.5 and was getting the error you describe. I've modified the wsdl as per your suggestions and now I'm getting this error:
The requested operation failed: Arrays of or optional XML Schema "choice" elements are not supported.

Any suggestions on how to resolve this?

Thanks in advance for your help.

Palle Juul said...

Great article ! I am trying to use a webservice consumer up agains a webservice that requires a login. I have tried sending crendentials by using the function "Service.setCredentials" but keep geting error "(401) Unauthorized". Do you have any suggest...

Anonymous said...

Thank you for the tips and thanks to the others who commented. Putting it all together explained why I could not get my web service consumer to work. I too get the error: "The requested operation failed: Arrays of or optional XML Schema "choice" elements are not supported." This is due to the web service using SOAP 1.2. There appears to be no workaround for this delima.

Rob said...

Thanks for posting this, it has saved me hours of pain!

One minor problem I found was that
<s:any minoccurs="2" maxoccurs="2">
should read
<s:any minoccurs="2" maxoccurs="2" />