Web Services between .NET, Java and MS SOAP Toolkit – Part II


This second article in this series dedicated to Web services comes as asequel to the first one in which I started to tell you how you couldbuild different kind of clients and services using MS SOAP Toolkit,Apache SOAP for Java and .NET Framework.

In the last article I mentioned something about the incompatibilitybetween a MS SOAP client and an Apache SOAP server (the infamousxsi:type). As far as I know the version 2.2 didn't solve this problem,which is Apache SOAP Server is still expecting all parameters to have atype specified. But, the good news is that there is a work around tothis problem. Many of you sent me e-mails asking how to do this. Wellit is fairly simply.

Apache SOAP Server and clients

Do you remember the Apache SOAP server we wrote last time? Wellanother good news is that you don't have to change anything inside theserver. You only need to change the deployment descriptor.

Let me tell you more about the workaround before writinganything. The major difference between MS implementation of SOAP andApache for Java's is that the former relies on WSDL files to fullydescribe the service when the latter doesn't. This is why in theinitial implementation of the Apache SOAP for Java the type is requiredto be specified for each parameter. The server has no other mean toknow the type of the parameters unless is specified in the call. In theMS implementation the WSDL file has enough information and the servercan figure out even if the call tells nothing about this.

Now, the solution to our problem is: when you deploy yourservice you can specify the type mapping of each parameter in yourmethods. The Java service uses this whenever there isn't enoughinformation in the method call.

So you must add a mappings section to your deployment script. I modified the deployment descriptor for our service.

<isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment"
id="urn:MyService ">

<isd:mappings>
<isd:map encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:x="" qname="x:num1"
xml2JavaClassName="org.apache.soap.encoding.soapenc.DoubleDeserializer"/>
<isd:map encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:x="" qname="x:num2" xml2JavaClassName="org.apache.soap.encoding.soapenc.DoubleDeserializer"/>
</isd:mappings>

</isd:service>

That's all.Believe it or not that's all you have to change. The MS STK clientdoesn't need any change either. The same mention for the .NET client,don't change anything.

This will complete our incursion to building clients and services(simple ones) using the three major frameworks available. I mustmention here that there are other frameworks for building SOAP serversand clients so don't be shy and publish source code and information sowe can all learn about them. I remember seeing among people on SOAPBuilders discussion group another name: Glue. They were at beta versionlast time a checked their web site but I'm sure they will quickly bereleasing their product.

Generic client for Web Services

Ok so now we know hot to build a web service when we have enoughinformation about the service itself: we are either the developers ofthe service or good friends with the developer, so we can get the�signature� of the service at the time we write the client.

Now, suppose you want to build a client that is able to call a serviceknowing only the name of the methods or something similar. My examplewill try to add two numbers searching a Math service and calling themethods. A more practical example could be a stock checker or somethingsimilar.

The steps I follow are:
- search the UDDI and findbusinesses which can help me solve my problem. The way we find thebusiness is not standardized since there isn't a set of categories withbusinesses. For example we'll make the convention that Math businesseswill be stored under Math category. Next we'll query for the URL wherethe web service stores the WSDL file.
This is the first step to finalize our solution. I'll skip the code forthis section since UDDI and related APIs are the subject of a futurearticle.
- Once we have the WSDL file we'll parse this file and find outinformation about the web service. Based on this information we'llbuild a SOAP request. To build a soap request I used the low level APIin STK since we don't know from the beginning how many parameters theservice will call.

And here is the code:

Option Explicit

Function BuildOperation( _
ByVal WSDLFileName As String, _
ByVal WSMLFileName As String, _
sOperation As String) As CWSDLOperation

Dim Reader As WSDLReader
Dim EnumService As EnumWSDLService
Dim Service As WSDLService
Dim EnumPort As EnumWSDLPorts
Dim Port As WSDLPort
Dim EnumOperation As EnumWSDLOperations
Dim Operation As WSDLOperation
Dim EnumMapper As EnumSoapMappers
Dim Mapper As SoapMapper
Dim Fetched As Long

Dim objWSDLOperation As CWSDLOperation
Dim objOperationPart As COperationPart
Dim bAddParts As Boolean

Set objWSDLOperation = New CWSDLOperation

Set Reader = New WSDLReader
Reader.Load WSDLFileName, WSMLFileName

Reader.GetSoapServices EnumService

EnumService.Next 1, Service, Fetched
Do While Fetched = 1

Service.GetSoapPorts EnumPort

EnumPort.Next 1, Port, Fetched
Do While Fetched = 1

Port.GetSoapOperations EnumOperation

EnumOperation.Next 1, Operation, Fetched
Do While Fetched = 1

//check to see if the operation is here
bAddParts = False
If InStr(1, Operation.soapAction, "." + sOperation) > 0 Then
With objWSDLOperation
Set .m_Parts = New Collection
.m_PortAddress = Port.address
.m_SoapAction = Operation.soapAction
End With
bAddParts = True

Operation.GetOperationParts EnumMapper

EnumMapper.Next 1, Mapper, Fetched
Do While Fetched = 1

If bAddParts Then
Set objOperationPart = New COperationPart
objOperationPart.m_Name = Mapper.partName
Call objWSDLOperation.m_Parts.Add(objOperationPart)
End If

EnumMapper.Next 1, Mapper, Fetched
Loop

End If

EnumOperation.Next 1, Operation, Fetched
Loop

EnumPort.Next 1, Port, Fetched
Loop

EnumService.Next 1, Service, Fetched
Loop
Set BuildOperation = objWSDLOperation

End Function

Function buildBuildOperation() parses the WSDL file and gathers information about theservice: name, parameters, namespaces used, etc and finally returns aCWSDLOperation object.

With this information we can move next to build the SOAP requestand invoke the web service. The service is invoked by calling Executemethod on CWSDLOperation object. In my example the Execute method willsimply print the result of the add operation.

In my example the rule I applied for finding the right methodand passing the parameter is pretty simple but in a real applicationyou can implement more sophisticated mechanisms or assume there are fewpatterns for each method naming.

Here is the code for the CWSDLOperation class.

 

Option Explicit

Private Const WRAPPE
R_ELEMENT_NAMESPACE = ""

Public m_PortAddress As String
Public m_SoapAction As String
Public m_Parts As Collection

Public Sub Execute()
If m_SoapAction <> vbNullString Then
On Error GoTo ErrorHandler

Dim Serializer As SoapSerializer
Dim Reader As SoapReader
Dim Connector As SoapConnector

Dim part As COperationPart
Dim sMethod As String, sNamespace As String

Set Connector = New HttpConnector
Connector.Property("EndPointURL") = m_PortAddress
Connector.Property("SoapAction") = m_SoapAction
Connector.BeginMessage

Set Serializer = New SoapSerializer
Serializer.Init Connector.InputStream

sMethod = Mid$(m_SoapAction, InStrRev(m_SoapAction, ".") + 1)
sNamespace = Left$(m_SoapAction, InStr(m_SoapAction, "/action") – 1)

Serializer.startEnvelope
Serializer.startBody
Serializer.startElement sMethod, sNamespace + "/message/", , "m"

For Each part In m_Parts
Serializer.startElement part.m_Name
Serializer.writeString "20"
Serializer.endElement
Next

Serializer.endElement
Serializer.endBody
Serializer.endEnvelope

Connector.EndMessage

Set Reader = New SoapReader
Reader.Load Connector.OutputStream

If Not Reader.Fault Is Nothing Then
MsgBox Reader.faultstring.Text, vbExclamation
Else
Debug.Print Reader.RPCResult.Text
End If
End If

Exit Sub

ErrorHandler:
MsgBox "ERROR: " & Err.Description, vbExclamation
Err.Clear
Exit Sub
End Sub

COperationPart class is very simply:

Option Explicit

Public m_Name As String

If you want tosimplify the code you can substitute the COperationPart class with aString variable. There are more fields that can be added to theCOperationPart class but I removed them for simplicity of the code. Theother information available in the WSDL file for a OperationPart are:

- elementName
- callIndex
- elementType
- messageName
- isInput
- partName
- xmlNameSpace
- comValue
- parameterOrder

To test the sample application you need to build 2 simple Web serviceswith STK MathLib1 and MathLib2. MathLib1 implements add method andMathLib2 implements addNumbers method. Both methods accept 2 parametersas double values.

After you create these two web services change the namespace inone of them so something else than default, so you replicate a realsituation.

Create a VB project, add the two classes, than add a form, onthe form add a button and insert the following code in his click event.

Private Sub cmdCallServices_Click()
Call BuildOperation("C:\work\xarea\soap\MathLib\MathLib1\MathLib1.WSDL", _
"C:\work\soap\MathLib\MathLib1\MathLib1.WSML", "add").Execute
Call BuildOperation("C:\work\xarea\soap\MathLib\MathLib2\MathLib2.WSDL", _
"C:\work\soap\MathLib\MathLib2\MathLib2.WSML", "add").Execute
End Sub

Compile and you are ready to go!

I hope this article will help better understand web services and SOAP.

Related Articles :

Twitter Digg Delicious Stumbleupon Technorati Facebook Email

No comments yet... Be the first to leave a reply!