Novell Home

Accessing exteNd Composer Services From A PowerBuilder Application

From Developer Community

by Marcel Thibault, Lead Consulting Engineer, Novell
Date Created: 2002-05-28 17:28:00.000

Contents

Introduction

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.

Connecting with MS SOAP Kit 2.0

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:

Image:marcel_1_052802.jpg

Let's assume we had the following PowerBuilder window to access SOAP services.

Image:marcel_2_052802.jpg

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.

Connecting with PowerBuilder INET Class

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.

Image:marcel_3_052802.jpg

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>

Conclusion

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.

  • It makes it easy to use industry-standard transport protocols such as HTTP and HTTPS for security.
  • It enables an industry-standard data structure in the form of XML documents.

Novell® Making IT Work As One

© 2008 Novell, Inc. All Rights Reserved.