Thursday, February 26, 2009

Giffly - An on-line Drawing tool

I had a small project to modify an existing Notes database, adding a new process to an existing workflow application.

The initial request was worded along the lines of 'like form XXX but with the following changes: (etc)'

The database is a mature application, and is beginning to suffer from code rot. What appears to have originally been a reasonably clearly structured function has degenerated into over 300 lines of nested if and case statements. I was struggling to work through the logic and decided I needed to map the existing code before I could change it.

Although I do have it installed on my laptop, I have an irrational dislike of the industry leading software for creating flow-charts so I went looking for an alternative and found Gliffy

It has a 30 day free trial and I am impressed. For a quick chart or diagram, this is very easy to use and has all the features I was looking for.

As an example, this is the flow-chart I created for two functions I examined:


(Full size image)

You can export the images as jpg or png. The limited free version adds the Gliffy logo to images. The Premium service has single user access from $5 per month but for my purposes the free features are probably enough - when you just occasionally want to put together something for yourself, to clarify your thought processes, etc.

Now on to some serious refactoring ...

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!