PowerBuilder Authors: Chris Pollach, Yeshim Deniz, Jayaram Krishnaswamy, Kevin Benedict, Avi Rosenthal

Related Topics: PowerBuilder

PowerBuilder: Article

Implementing PowerBuilder-Based Web Services from PowerDynamo

Implementing PowerBuilder-Based Web Services from PowerDynamo

PowerBuilder 9 will make the creation and consumption of Web services easier; however, we don't have to wait for its release to take advantage of these features.

Berndt Hamboeck wrote an article, "Web Services with PowerJ 4.0 and PowerBuilder 8.0" (PBDJ, Vol. 9, issue 2) - a further development of a Sybase white paper ( www.sybase.com/detail/1,6904,1015795,00.html) - in which he demonstrated how to implement a Web service using the current version of PowerBuilder. (Incidentally, PowerJ is not required. Any Java editor will do; I used my favorite: WordPad.)

However, the Web service used in that article was a typical "Hello World" type. I wanted to do something a little more practical. In particular, I'm a big fan of the newsgroup search capability of http://groups.google.com, except they don't archive all the Sybase newsgroups. I wanted to do my own version that covered all the Sybase newsgroups and could be invoked as a Web service. I then wanted to use PowerDynamo to create a Web page that interacted with that Web service. In this article I discuss how I did that and some of the lessons I learned along the way. First look at Berndt's article for the specifics on how to set up a PowerBuilder nonvisual object as a Web service using the Apache SOAP classes; I'll continue from that point.

I started with a nonvisual object called "ngs" (newsgroup search) with a single overloaded method called "search." The first implementation of "search" took two arguments: "product" and "keywords." If you want to do a search in groups.google.com against the Sybase newsgroups, the best approach is to do an advanced search and to enter "powersoft.public.powerbuilder.*" to search for PowerBuilder information or "sybase.public.easerver.*" to search for EAServer information. I wanted to spare my end users from having to know that. Instead, they can simply select the product they want to search for, and the PowerBuilder nonvisual knows which newsgroups it needs to search.

Defining and Retrieving the Data as XML
When "search" is invoked, it returns the results (or the first 10 records of the results if there are more than 10) in XML format. For those who understand XSD files (the data description of the XML), the XSD for the results is given in Listing 1. (Listings 1-12 can be downloaded from the PBDJ Web site, www.sys-con.com/pbdj/sourcec.cfm.) Basically, it means that the results were returned in a set of records called "matches," with each record called "match" and composed of:

  • Link: A URL that could be used to display that record
  • Keywords: The keywords that were originally used in the search
  • Product: The product that was originally used in the search
  • Recordnum: The number of the record in the total result set
  • MessageID: The messageID of the message on the newsgroup
  • Subject: The subject of the message on the newsgroup
  • Preview: A couple of lines of text from the message body
If there were more than 10 records in the result, the second implementation of the search method could be used to obtain the additional results. The second implementation takes the original two arguments (product and keywords) plus an additional argument, recordnum. This second implementation of the search method would return the next 10 records in the result set beginning with the recordnum passed in, which allows the end user to page forward and backward through the result set.

Finally, when users find a message they want to view, the HTML page invokes the third implementation of the search method, which takes the three arguments from the last implementation (product, keywords, and recordnum) and adds an additional argument: messageID.

When I first tested this, I was using a PB application that invoked the nonvisual object locally and displayed the resulting XML in Internet Explorer. Things seemed to be working well, and then I deployed the object into EAServer, created the SOAP wrapper for it, and tried to call it. The result was a bunch of error messages, bringing us to the first lesson learned: SOAP, or at least the Apache implementation of it, doesn't understand overloaded methods. Once I realized this, I renamed the second and third implementations of the original search method to "searchcont" and "display." Things worked much better after that.

Transforming XML to HTML via XSLT
Once I had the service working, I needed to get the Web page set up in PowerDynamo. Since the result from the Web service was XML, the most direct thing to do was create an XSLT file and use it and an XSLT processor to convert the XML into formatted HTML. An XSLT file is basically a template that tells the XSLT processor how to display the data from the XML file. For those who understand XSLT files, the one I used is provided in Listing 2. It's not quite as complicated as it appears. The file basically lays out a nested table. The inner table repeats once for each record in the returned result set:

<xsl:for-each select="matches/match">

The subject and the preview from the result set are displayed in the inner table:

<xsl:value-of select="subject"/> and <xsl:value-of select="preview"/>

An argument passed into the template ("base") was combined with the link returned in the result set to create a hyperlink that will request the actual message from the server:

<a href="{$base}{$url}">

The value of the link had to be assigned to a local variable in the XSLT template before it could be used in the concatenation operation:

<xsl:variable name="url" select="link"/>

XSLT processors are Java-based, so I created a small Java class to do the transformation, and then used that class from within PowerDynamo (see Listing 3). This class has one method called "ngs" that takes the product and keyword arguments that will get passed to the Web service. The template argument tells the class which XSLT file to use to transform the XML result to HTML. Because I wanted the HTML to contain links for further action by the user, I also passed in a base URL argument to be used in the creation of those links. The class then took the string arguments product and keywords, turned them into the kind of argument that the SOAP libraries can use to invoke the Web service, and then called the Web service. The response was then processed using the XSLT file that referenced in the template argument. Note that the base URL was passed in as an argument to the transformation process. The result was returned as a string to whatever called the class.

Invoking from PowerDynamo
The next thing I needed was the PowerDynamo script called ngs.stm to call this from the Web server (see Listing 4). A couple of those META tags are included because this page will be called not only from a standard Web browser, but from a Palm Query Application (PQA) as well. I created a local variable that captured the base URL from where the page was called. Then I created an instance of the Java class and called its method, passing in the arguments that came from the Web form that called the script, as well as the template name and the local variable with the base URL. Since the results from the Java class were HTML, it's written directly into the page. The last piece of the puzzle was the HTML page that calls the PowerDynamo script (see Listing 5), which was pretty straightforward.

All this worked great while I was testing it in my development environment. Things took a turn for the worse when I went to deploy it to the production site. It's colocated with a number of other sites, some of which are also using a JVM, specifically the Sun 1.1 JVM. The libraries I'm relying on to do the SOAP calls and the XSLT transformations all require the Sun 1.2 JVM at a minimum. My efforts to get PowerDynamo to support different JVM versions for different sites did not meet with great success. The second lesson learned: make sure the server hosting your site can support the Java VM version that your application requires before coding it!

Another Approach to Retrieving the Data as XML
That didn't stop me, however; I just had to come up with something to replace those libraries that were going to do all the work. In particular I needed some method to invoke to the Web service and some means of converting the resulting XML to HTML. Fortunately, Bob DuCharme's article, "A Simple SOAP Client for XML Geeks" ( www-106.ibm.com/developerworks/xml/library/x-soapcl), was right up my alley for the first requirement. With a few modifications - make it a class, use named arguments rather than a string array, and use the length function of the string class rather than the copy routine of the original - I got our SOAPClient.java (see Listing 6). The only thing this class actually did was open an HTTP connection, then send the XML-formatted request that was passed to it and return the XML response from the service.

Another Approach to Transforming XML to HTML
Converting the XML to HTML can be handled within PowerDynamo. The latest version incorporated a number of new functions for handling XML and DOM data, so I just used those. The end result is the new version of the ngs.stm file (see Listing 7), but there's a bit of work going on in the background as well. First I imported a number of other script files so I could invoke functions in them as if they were part of this page. That helped keep the clutter down. The "useContext" flag behind some of the imports meant that the functions in that script could access variables defined in the calling script.

Then I created the SOAPClient class, generated the SOAP request, and called the Web service by passing the SOAP request to the SOAPClient class. If an error occurred during the creation of the SOAPClient class, the SOAPClientCreateError function from the errors.scc file (see Listing 8) was called. Similarly, if I got something other than an XML document (i.e., an error message) back from the Web service, the SOAPResponseError function from the errors.scc file was called. The GenerateSOAPRequest function being called here was the sole function of the SOAPRequest.ssc script file (see Listing 9). That function took the arguments that were passed into the ngs.stm page and prepared the XML request based on the number and value of the arguments. The number of arguments determined whether this was an initial search, a search continuation, or a request to display a certain record.

The toDOMDocument I called here was one of those new XML/DOM functions in PowerDynamo. The first time I called it to validate that the response from the Web service was a real XML document. However, what I got back from a Web service was an entire SOAP message composed of the SOAP body (the data I wanted) and the SOAP envelope used to transport it. All I wanted at this point was the body, so I called the ExtractSOAPBody function that was defined in the functions.scc script file (see Listing 10).

Since the body was XML, it had to be escaped before it could be included in the SOAP response, which meant I needed to unescape it when I extracted it. Unfortunately, although PowerDynamo has an XMLescape function, for some reason it doesn't have an XMLunescape function. I had to create one in the functions.scc script, which was used in turn by the ExtractSOAPBody function. In addition, since the SOAP response was XML within XML, there's only one XML header - the one for the entire response. The PowerDynamo XML functions that I used expect to find a header on the data, so the other thing the ExtractSOAPBody function did was add that header back onto the body once it was extracted from the envelope.

One nice feature of the Apache XSLT transformation libraries I used was an argument that could be passed to them to tell them to ignore "white space" in the XML. As a result, an XML document that's formatted with tabs and carriage returns between tags to make it more "human readable" could still be processed. The Web service I created to return the newsgroup search data did format it with such characters. However, since the PowerDynamo XML functions didn't handle such characters, one of the other things the ExtractSOAPBody function did was remove such "white space" characters from the data as well.

Now that I had the data, I called the toDOMDocument function again to verify that it was also properly formatted XML, and called the XMLParseError function from the error.scc file when an error occurred. The ngs.stm script now looked at the number of arguments that were passed to it to determine which transformation will be used on the data: one for a set of matches and the other for a single message. If it's a set of matches, the GenerateMatchList function from the MatchList.ssc script file (see Listing 11) is called. Otherwise it's a single message, and the DisplayMessage function from our DisplayMessage.scc script file (see Listing 12) is called instead. Both of those functions parse the XML document and prepare an HTML document from them, which is then written back into the page.

In this article, I took a practical look at implementing a PowerBuilder-based Web service, with a particular focus on "consuming" it using PowerDynamo. I showed a couple of ways of doing that, one involving the use of some popular open-source Java classes from Apache, and the other using some simple Java and native PowerDyanmo functions.

Note: With one exception, the code provided here should work as written. The lone exception is that the URL I have for the Web service is not the actual URL for the service. I need to do a hardware upgrade on the server the service is running on before it's ready for public use. Once that's complete, I'll make the service available for use and publicize the actual URL.

More Stories By Bruce Armstrong

Bruce Armstrong is a development lead with Integrated Data Services (www.get-integrated.com). A charter member of TeamSybase, he has been using PowerBuilder since version 1.0.B. He was a contributing author to SYS-CON's PowerBuilder 4.0 Secrets of the Masters and the editor of SAMs' PowerBuilder 9: Advanced Client/Server Development.

Comments (0)

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.