Designing a CRUD Webservice for a Domino Form
Domino 7 allows you to create web services easily. You can start with a WSDL file and have Domino generate the Stub Classes for you or you start with the Class File and get a ready made WSDL file. Unfortunately both approaches require that you code quite a bit. Domino doesn't provide a ready baked set of SOAP web services out of the box. You do have a kind of REST web service using ?ReadViewEntries, but that is read ony.
A very likely candidate for a web service is the creation, update and deletion of documents with a given form (commonly refered as CRUD). Domino allows you to store any field in a document, even if it hasn't been defined anywhere making it very flexible and so difficult to understand if you are used to an RDBMS.
It would be fairly easy to design a web service (after Julian has enlightened you here, here and here), that allows an arbitrary number of field value pairs to be stored in a document. However this would defeat the principle of clear cut interfaces. More appropriate is to limit the possible input to the input fields defined in a form and giving back all fields including the computed ones. How to get there?
Exporting the form's DXL and transforming it is done with a few lines of LotusScript. The "meat" is in the stylesheet.
This LotusScript was converted to HTML using the ls2html routine,
provided by Julian Robichaux at nsftools.com.
To plan the XSLT you need to get an idea what is inside the WSDL file. The official definition is rather difficult to digest, so you might want to go for some alternate descriptions or a tutorial first.
A WSDL file consist of definitions for datatypes, messages, portTypes, bindings and service. To implement the CRUD Webservice we need to focus on the datatypes. The messages, portTypes and the service will follow the data type structure.
To simplify matters I will use the form alias and the field names to build a custom XML datastructure. Field names in Domino are suitable to be used as XML Tags, form names might or might not be suitable. I presume you will use some sensible alias (no spaces, no funny characters) otherwise that example won't work. First we setup the basic structure of the WSDL file to hold our results in the root tag of the XSLT file:
Next stop: generate the LotusScript.
A very likely candidate for a web service is the creation, update and deletion of documents with a given form (commonly refered as CRUD). Domino allows you to store any field in a document, even if it hasn't been defined anywhere making it very flexible and so difficult to understand if you are used to an RDBMS.
It would be fairly easy to design a web service (after Julian has enlightened you here, here and here), that allows an arbitrary number of field value pairs to be stored in a document. However this would defeat the principle of clear cut interfaces. More appropriate is to limit the possible input to the input fields defined in a form and giving back all fields including the computed ones. How to get there?
- Export a domino form as DXL
- Transform the DXL into WSDL using XSLT
- Import the WSDL into a Domino Web Service
Exporting the form's DXL and transforming it is done with a few lines of LotusScript. The "meat" is in the stylesheet.
Sub Initialize Dim db As NotesDatabase Dim rt As NotesRichTextItem Dim exporter As NotesDXLExporter Dim transformer As NotesXSLTransformer Dim result As String Dim stream As NotesStream Dim filename As String Dim formname As String Dim f As NotesForm Dim ncol As NotesNoteCollection 'Setup envionment On Error Resume Next Dim s As New NotesSession Set db = s .CurrentDatabase 'Get the source and destination formname = Inputbox ( "Please select the form name to export" , "DXL export" , "Memo" ) filename = Inputbox ( "Please select your destination file" , "DXL export" , "C:\temp\" +formname + "wsdl" ) 'Retrieve the form -- will crash if the form name isn't valid Set f = db .GetForm (formname ) Set ncol = db .CreateNoteCollection ( False ) Call ncol .Add (f ) Call ncol .BuildCollection REM create the file Set stream = s .CreateStream Call stream . Open (filename ) Call stream .Truncate REM Export current database design as DXL into a new Notes document Set exporter = s .CreateDXLExporter Set transformer = s .CreateXSLTransformer Call exporter .SetInput (ncol ) Call exporter .SetOutput (transformer ) Call transformer .setOutput (stream ) Call transformer .SetStylesheet ( "C:\temp\Form2wsdl.xslt" ) exporter .ExitOnFirstFatalError = False Print "Conversion runs" Call exporter .Process Print "Conversion completed" result = exporter . log Call stream . Close Beep Msgbox (result ) End Sub
provided by Julian Robichaux at nsftools.com.
A WSDL file consist of definitions for datatypes, messages, portTypes, bindings and service. To implement the CRUD Webservice we need to focus on the datatypes. The messages, portTypes and the service will follow the data type structure.
To simplify matters I will use the form alias and the field names to build a custom XML datastructure. Field names in Domino are suitable to be used as XML Tags, form names might or might not be suitable. I presume you will use some sensible alias (no spaces, no funny characters) otherwise that example won't work. First we setup the basic structure of the WSDL file to hold our results in the root tag of the XSLT file:
<?xml version="1.0" encoding="UTF-8"?> <wsdl:definitions targetNamespace="urn:DefaultNamespace" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:apachesoap="http://xml.apache.org/xml-soap" xmlns:impl="urn:DefaultNamespace" xmlns:intf="urn:DefaultNamespace" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <wsdl:types> <!-- Here are all the datatypes for the webservice --> </wsdl:types> <wsdl:message name="SampleRequest"> <wsdl:part name="SampleID" type="xsd:string"/> </wsdl:message> <wsdl:portType name="SamplePort"> <wsdl:operation name="SampleOperation"> <wsdl:input message="impl:SampleRequest" name="SampleRequest"/> <wsdl:output message="impl:SampleResponse" name="SampleResponse"/> </wsdl:operation> </wsdl:portType> <wsdl:binding name="DominoSoapBinding" type="impl:DocumentDemo"> <wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="SampleOperation"> <wsdlsoap:operation soapAction=""/> <wsdl:input name="SampleRequest"> <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:DefaultNamespace" use="encoded"/> </wsdl:input> <wsdl:output name="SampleResponse"> <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:DefaultNamespace" use="encoded"/> </wsdl:output> </wsdl:operation> </wsdl:binding> <wsdl:service name="SampleService"> <wsdl:port binding="impl:DominoSoapBinding" name="Domino"> <wsdlsoap:address location="http://localhost:80/sample.nsf/Sample?OpenWebService"/> </wsdl:port> </wsdl:service> </wsdl:definitions>Now you need to gain access to the fields in a form. Looking at the DXL you see, that fields can be at any level of the DXL document. To get access to the fields you need a suitable XPath expression. Luckily that is easy: <xsl:apply-templates select="//a:field" />. There are a number of decisions you have to make when designing the WSDL files. I'm not sure if my set (in brackets below) makes sense, so I leave that open to discussion:
- Name the document "document" or use the form name (form name)
- Use primitive data types, one set of custom data types or individual data types for all fields (individual data types)
- Name the SOAP actions same for all documents like getDocument, saveDocument etc. or use the form name like get[formName], save[FormName] etc (use form name)
- Return only a status code or return the document including computed fields (return document with computed fields)
Next stop: generate the LotusScript.
Posted by Stephan H Wissel on 13 December 2006 | Comments (1) | categories: Show-N-Tell Thursday XML