by Marcel Thibault, Lead Consulting Engineer, Novell
Date Created: 2002-05-28 17:28:00.000
Contents |
This article is intended for PowerBuilder developers who want to learn how to interface their applications with SilverStream eXtend Composer Web services. In it, I show you how to connect using the Microsoft SOAP Kit 2.0, and for shops running PowerBuilder 7.0 or higher, I discuss how to interface with eXtend Composer Web services using the new PowerBuilder INET classes. To serve as an example, I have created an eXtend Composer Web service that calculates exchange rates and is deployed to a SilverStream application server.
Download the sample code for this article.
Before we can connect to an eXtend Composer Web service, we must first install the Microsoft SOAP Kit 2.0, which is located at: http://msdn.microsoft.com/downloads/default.asp?URL=/code/sample.asp?url=/msdn-files/027/001/580/msdncompositedoc.xml
Next, we will use eXtend Composer's designer tool to deploy the Web services as proper SOAP trigger services. For the purpose of this article, these SOAP services must use document binding, and return a document to the caller. For instance, the URL to access the currency exchange rate service built for this article is as follows: http://localhost:8080/pbdemo/extendComposerPBdemo/WebServices/SOAPCurrencyExchange
To access this service in PowerBuilder, we must build an ActiveX wrapper object to the MS SOAP kit calls. To do this, we could use any tool that creates ActiveX objects, but for the purpose of this article, I built an "MSSOAPConole" ActiveX object using the Microsoft Visual Studio in Visual Basic. (You could also use C++.) This object, which is a wrapper to the MS SOAP kit calls, exposes the following methods to PowerBuilder applications:
GetEndPointURL, SetEndPointURL - getter and setter methods for the HTTP URL request
GetRequestXML, SetRequestXML - getter and setter methods to set the SOAP envelope request message
GetResponseXML, SetResponseXML - getter and setter methods to retrieve the SOAP response message
ExecuteSoapService - executes the selected SOAP service (For this method to work, these services must return a document.)
Code:
Option Explicit
Private endPointURL As String
Private requestXML As String
Private responseXML As String
Public Function ToString() As String
Dim str As String
str = "[" + GetEndPointURL() + "," + GetRequestXML() + "," + GetResponseXML() + "]"
ToString = str
End Function
Public Function GetEndPointURL() As String
Println ("GetEndPointURL: started")
GetEndPointURL = endPointURL
End Function
Public Function GetRequestXML() As String
Println ("GetRequestXML: started")
GetRequestXML = requestXML
End Function
Public Function GetResponseXML() As String
Println ("GetResponseXML: started")
GetResponseXML = responseXML
End Function
Public Sub SetEndPointURL(ByVal NewValue As String)
Println ("SetEndPointURL: started. NewValue = [" + NewValue + "]")
endPointURL = NewValue
End Sub
Public Sub SetRequestXML(ByVal NewValue As String)
Println ("SetRequestXML: started. NewValue = [" + NewValue + "]")
requestXML = NewValue
End Sub
Public Sub SetResponseXML(ByVal NewValue As String)
Println ("SetResponseXML: started. NewValue = [" + NewValue + "]")
responseXML = NewValue
End Sub
Public Sub Println(ByVal Message As String)
If (Len(status.Text) > 32000) Then
status.Text = ""
End If
status.Text = status.Text + Message + Chr(13) + Chr(10)
End Sub
Public Function ExecuteSoapService() As Boolean
On Error GoTo ErrorHandler
Println ("ExecuteSoapService: started")
SetResponseXML ("<Empty></Empty>")
Dim soapConnector As soapConnector
Dim soapSerializer As soapSerializer
Dim Reader As SoapReader
Set soapConnector = New HttpConnector
Println ("ExecuteSoapService: connecting to End Point URL")
soapConnector.Property("EndPointURL") = GetEndPointURL()
soapConnector.Connect
Println ("ExecuteSoapService: creating SOAP Message")
soapConnector.BeginMessage
Set soapSerializer = New soapSerializer
Println ("ExecuteSoapService: soapSerializer.Init")
soapSerializer.Init soapConnector.InputStream
Println ("ExecuteSoapService: soapSerializer.startEnvelope")
soapSerializer.startEnvelope
Println ("ExecuteSoapService: soapSerializer.startBody")
soapSerializer.startBody
Println ("ExecuteSoapService: soapSerializer.writeXML")
soapSerializer.writeXML GetRequestXML()
Println ("ExecuteSoapService: soapSerializer.endBody")
soapSerializer.endBody
Println ("ExecuteSoapService: soapSerializer.endEnvelope")
soapSerializer.endEnvelope
Println ("ExecuteSoapService: soapConnector.endMessage")
soapConnector.EndMessage
Println ("ExecuteSoapService: retrieving Output")
Set Reader = New SoapReader
Reader.Load soapConnector.OutputStream
SetResponseXML (Reader.DOM.xml)
Println ("ExecuteSoapService: done. XML = [" + GetResponseXML() + "]")
If Not Reader.Fault Is Nothing Then
Println ("ExecuteSoapService: Fault Found = [" + Reader.Fault.nodeTypedValue + "]")
ExecuteSoapService = False
Else
ExecuteSoapService = True
End If
Exit Function
ErrorHandler:
Println ("ExecuteSoapService: Error Handler. Description = [" + Err.Description + "] Number = [" + CStr(
Err.Number) + "] DLL error = [" + CStr(Err.LastDllError) + "]")
ExecuteSoapService = False
End Function
The "ExecuteSoapService" method will perform the wrapper calls to the Microsoft SOAP kit.
From PowerBuilder, we can now instantiate an OLE object of MSSOAPConsole type, and we will now have the ability to access any SOAP service that returns a document. We can achieve this by simply creating a PowerBuilder window object and dropping an OLE object of type MSSOAPConsole onto it. For this example, I also created a global variable called "soapWebService", which points to the OLE object and is used throughout the application.
Example:
Let's assume we had the following PowerBuilder window to access SOAP services.
Once the SOAP Web service object is instantiated we can call SOAP services by first calling the "SetEndPointURL" method to set the URL where the SOAP service resides. We can then call the "SetRequestXML" method to set the input XML required by the SOAP service, then call the ExecuteSoapService method to activate the service. In our currency exchange example, the XML would be as follows:
<ROOT> <FromCountry>us</FromCountry> <ToCountry>canada</ToCountry> <Amount>1</Amount> </ROOT>
The output would then be retrieved by the PowerBuilder application as a string via the "GetResponseXML" call.
If the above input XML were requested, the string would be a SOAP response message that would read as follows:
SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="
http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="
http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Body>
<XC:OUTPUT xmlns:XC="urn:eXtendComposer">
<XCSERVICE>
<NAME>CurrencyExchange</NAME>
<TYPE>XMETHODS</TYPE>
<MONITOR>
<STARTOPERATION>24Apr2002 09:10:03 866</STARTOPERATION>
<ENDOPERATION>24Apr2002 09:10:04 828</ENDOPERATION>
</MONITOR>
</XCSERVICE>
<QUERY_PARAMETERS>
<ARGUMENT parameterName="FromCountry">us</ARGUMENT>
<ARGUMENT parameterName="ToCountry">canada</ARGUMENT>
<ARGUMENT parameterName="Amount">1</ARGUMENT>
</QUERY_PARAMETERS>
<CALL_RESULTS>
<ERROR_CODE>0</ERROR_CODE>
<ERROR_MESSAGE/>
</CALL_RESULTS>
<METADATA columncount="1">
<COL>Converted Amount</COL>
</METADATA>
<ROWCOUNT>1</ROWCOUNT>
<ROW>
<COL>1.5700</COL>
</ROW>
</XC:OUTPUT>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The code for the "execute" button would be as follows:
soapWebService.Object.SetEndPointURL(sle_endpointurl.text)
soapWebService.Object.SetRequestXML(mle_requestxml.text)
if (not soapWebService.Object.ExecuteSoapService()) then
messagebox("Error","Failed to execute WEB Service. Plese check Console for more information.")
end if
mle_responsexml.text = soapWebService.Object.GetResponseXML()
For richer UI controls, the PowerBuilder application would then need to parse this data and populate the appropriate UI controls.
Developers using PowerBuilder 7.0 or higher can use the new INET class to access eXtend Composer Web services. The INET class has a "GetURL" (urlname, data) function that activates eXtend Composer Web services, provided the services were deployed as proper servlet-based service triggers of type "Params(URL/Form)".
To use the GetURL function, we will need to create a non-visual PowerBuilder object that descends from the "InternetResult" object. (See PowerBuilder help for more detail on how to do this.) Next, we will override the "InternetData" function for the capture of the eXtend Composer service output. For our currency exchange example, I populated a global variable called "responseXML" with the data received from the call.
Now, assume we have the following PowerBuilder window to access eXtend Composer Web services and we built a non-visual object "n_internetresult" to receive the data.
Given the above, the following execute button code would activate the selected eXtend Composer Web service:
String httprequest inet internet internetresult result internet = CREATE inet result = CREATE n_internetresult httprequest = sle_url.text + "?" + sle_params.text internet.GetUrl(httprequest, result) mle_response.text = responseXML
If we ran the above example, we would receive the following response:
<?xml version="1.0" encoding="UTF-8"?>
<XC:OUTPUT xmlns:XC="urn:eXtendComposer">
<XCSERVICE>
<NAME>CurrencyExchange</NAME>
<TYPE>XMETHODS</TYPE>
<MONITOR>
<STARTOPERATION>25Apr2002 16:27:20 431</STARTOPERATION>
<ENDOPERATION>25Apr2002 16:27:24 297</ENDOPERATION>
</MONITOR>
</XCSERVICE>
<QUERY_PARAMETERS>
<ARGUMENT parameterName="ToCountry">canada</ARGUMENT>
<ARGUMENT parameterName="Amount">1</ARGUMENT>
<ARGUMENT parameterName="FromCountry">us</ARGUMENT>
</QUERY_PARAMETERS>
<CALL_RESULTS>
<ERROR_CODE>0</ERROR_CODE>
<ERROR_MESSAGE></ERROR_MESSAGE>
</CALL_RESULTS>
<METADATA columncount="1">
<COL>Converted Amount</COL>
</METADATA>
<ROWCOUNT>1</ROWCOUNT>
<ROW>
<COL>1.5600</COL>
</ROW>
</XC:OUTPUT>
We now have two ways of consuming eXtend Composer Web services from PowerBuilder applications - the Microsoft SOAP Kit 2.0 and straight URL calls.
The benefits of this approach are:
Doing so creates a true distributed environment, separating the display layer from the business and/or data access layer. This means all business logic can be written as EJB components, then served up by eXtend Composer as Web services or exposed directly as SOAP services.
Categories: Developer Library | Java | Extend | SOAP | Web Services | General
© 2008 Novell, Inc. All Rights Reserved.