Welcome!

PowerBuilder Authors: Chris Pollach, Avi Rosenthal, Yakov Fain, RealWire News Distribution, Al Soucy

Related Topics: PowerBuilder

PowerBuilder: Article

Creating a Web Service with PowerBuilder

Creating a Web Service with PowerBuilder

In an article in PBDJ last year ("Implementing PowerBuilder-Based Web Services from Dynamo") [Vol. 9, issue 10]), I showed how to create Web pages in PowerDynamo by calling a Web service created using PowerBuilder and EAServer (along with Apache SOAP) that allowed you to search the Sybase newsgroups. Technology has improved significantly since then, and this article examines how I've rewritten that application to take advantage of those improvements.

Specifically, since the original article, technology has improved in the following areas:

  • PowerBuilder 9 now offers native XML generation from the DataWindow. In the previous version of the application, the XML was generated through PowerScript.
  • PowerBuilder 9 now supports the creation of JavaServer Pages (JSP) as an alternative to PowerDynamo. JSP technology has also improved significantly in the interim, particularly with the introduction of tag libraries.
  • Apache introduced a "second-generation" implementation of SOAP known as Axis. The primary advantage for the developer is that Web services are much easier to publish through Axis. Axis also includes a number of classes for calling Web services, which we'll also use.

    Before we launch into the details, look at Figure 1, as it shows the various components involved and how they interact with each other.

    The top portion of the figure indicates how the Web service works. The Web service call is received by Apache Axis, which invokes a Java class called NewsgroupSearch. That class makes a call to the PowerBuilder component deployed in EAServer through its EJB stubs. The PowerBuilder component retrieves the matching newsgroup message data from the ASE database and then generates XML from it, which is then returned back to the NewsgroupSearch class that returns it as the Web service response.

    The lower section of Figure 1 shows how the JSP invokes the Web service and processes the results. The JSP invokes another Java class called Newsgroups, which then uses the Web service client classes of Apache Axis to call the Web service. The JSP takes the XML data returned from the Newsgroups class and then uses the Jakarta Xtags tag libraries to parse it to create the returned HTML page.

    It's actually not quite as complicated as it looks. With the exception of the PB component, all I had to write were two rather small and simple Java classes and the JSP. We'll now look at all of the components.

    PowerBuilder Component
    The PowerBuilder component has only two methods: search and displaymsg:

  • public function string search (string
    product, string searchterm, integer
    maxresults, integer startingrow, integer
    rowstoreturn)

  • public function string displaymsg
    (string msgid)

    where the arguments are:

  • Product/Searchterm: The product we want to search for messages about (e.g., "powerbuilder," "easerver," etc.) and the text that we want to search for. Within the database, I associate certain newsgroup sections with each product and restrict the search to those sections.
  • Maxresults: For performance reasons, I don't want to deal with passing huge result sets between the database and the PowerBuilder component, so I restrict the search at the outset to a fairly small number of matching messages. Like most other search tools, users are encouraged to add more restrictive search criteria if they get too many results. This argument is passed to the database during the retrieve to force the database to limit the number of rows returned.
  • StartingRow/RowstoReturn: I want the PowerBuilder component to support pagination through the result set, particularly when it comes time to call it from the JSP. The JSP shouldn't have to retrieve the full result set and then parse out the page it wants. This option, in conjunction with the Rowstoreturn argument, allows consumers of the Web service to set up the pagination however they want, rather than having the Web service force a particular number of rows per page on them. Also, the consumer of the Web service can simply pass 1 for the startingrow and the same value used for maxresults for the Rowstoreturn argument if he or she doesn't want the result set paginated.
  • MsgID: Every newsgroup message has a msgID that uniquely identifies it to the NNTP server. When it comes time to display the full text of one of the messages in the search results, the msgID is used to identify and retrieve the message.

    Both functions return an XML string. The schema for the return from the search function is provided in Listing 1 and the schema for the displaymsg function is provided in Listing 2. (Listings 2-10 can be downloaded from http://www.sys-con.com/pbdj/sourcec.cfm.) In the search function, return subject is the message subject, groups is the newsgroup section(s) the message appears in, date is the date the message was posted, sender is the name and e-mail address (if provided) for the poster, and msgID is the unique message ID for the message. Summary is the first three lines of the message body, so the end user can have a preview of the message. The recordnum is the record number of the message within the result set returned. The JSP uses it when establishing links to other pages to determine which page of the result set it's currently dealing with. In the displaymsg function, return sender, subject, date, and groups have the same meaning as above, and text is the actual text of the message body.

    Within the search function, the product, searchterm, and maxresults arguments are passed to a DataWindow that returns the full set of matching messages (up to maxresults number of messages). The search function then parses out the particular page of results by copying the desired rows into another DataWindow before generating the XML (see Listing 3). In the displaymsg function, the XML for the message is returned directly (see Listing 4).

    EJB Stubs

    Once the PowerBuilder component has been deployed into EAServer, the EJB stubs are created by right-clicking on the package containing the component in Jaguar Manager and selecting Generate Stub/Skeleton from the popup menu (see Figure 2).

    And, from the dialog that appears, it generates and compiles Java stubs and skeletons for the component (see Figure 3).

    NewsgroupSearch Java Class
    Once we have EJB stubs for our PowerBuilder component, we need to write a small Java class that makes our component available as a Web service (see Listing 5). In this particular case, TeamSybase is the name of the package the PowerBuilder component was deployed into, so the:

    import TeamSybase.* ;

    statement means that this class is importing our EJB stubs, and the newsgroups class being referenced is the name of the component. Also note the "public static void main" method. This isn't used by our Web service; I added it so I could test out the class via the command line before deploying it to Apache Axis. The "public String search" and "public String display" methods simply expose the methods of our PowerBuilder component. The GetComponent method is used internally by the class to create an instance of the PowerBuilder component to be called.

    All we need to do to make our PowerBuilder component available as a Web service now is to deploy the uncompiled NewsgroupSearch Java file into the Apache Axis directory with a .jws rather than a .java filename extension. Axis compiles the file the first time the service is called and will automatically generate a WSDL document for the class based on the exposed methods. However, we can gain a bit more control over the Web service, in particular restricting the methods exposed by using a Web Service Deployment Descriptor (WSDD) file to tell Axis more specifically how to handle the new service. The WSDD file is an XML file that simply describes the service and the methods we actually want exposed (see Listing 6).

    Axis has an AdminClient service that we pass the WSDD file to in order to do the custom deployment of the service.

    Newsgroups Java Class
    What we have now is our PowerBuilder component exposed as a Web service. What we need to do still is write the JSP that calls it. The first thing we're going to need is a small Java class that provides methods that call the two functions of the Web service (see Listing 7). The class also contains a utility function that we will also be calling from the JSP. When the end user selects one of the search results that he or she wants to see, we'll be passing the message ID for the message to the display function. To do that, we'll be including the message ID in a hyperlink on the search results page. The problem with that is some characters that are valid for a message ID have special meaning within a hyperlink, thus changing the results. One particular example is the "#" character, which designates a target location within a document. The replaceAll function in the Newsgroups class will be used by the JSP to convert such characters to an encoded form during the hyperlink creation.

    JSP
    Before we start coding our JSP though, we need to get the following installed in our JSP target:

  • The Newsgroups class
  • The Apache Axis libraries used by the Newsgroups class
  • The Xerces libraries used by Axis
  • The Jakarta Xtags taglib that the JSP will use
  • The DOM4J libraries that Xtags uses

    We do this by opening up the WEB-INF directory in the PB9 System Tree, selecting the classes folder, right-clicking on it, selecting Import Files... from the popup menu, and then selecting our Newsgroups class file. We do the same for the root of WEB-INF for the XTags TLD file, and the remaining files. JAR files from Xtags, Axis, Xerces, and DOM4J are imported into the lib directory (see Figure 4). The CVS directories that appear in the system tree are there because this project is under CVS source control.

    searchform.jsp

    The JSP file (see Listing 8) used to display the search form (see Figure 5) doesn't even contain any Java. It's straight HTML used to generate the form and then pass the query off to the search.jsp file. It has a .jsp file extension just in case I decide to add some Java scripting to it later and don't want to change any of the links that reference it. Note that the values for the maxrows, startrow, and returnrows parameters are established as hidden fields in the form.

    search.jsp
    Here's where things finally get a bit more interesting (see Listing 9). The first part of the file is the JSP imports for the Newsgroups class and the Axis and Xtags libraries, followed by some fairly typical HTML to set up the head and the beginning of the body of the page we'll return. The next Java section retrieves the values that were passed from the search form (or from a hyperlink that called the page) and assigns them to JSP variables.

    The next section is where the search function in our Web service is called and the result parsed by the Xtags library. It's as simple as:


    <%=ngs.search( product, searchterm, maxrows, startrow, returnrows )%>

    Xtags parses everything between the and tags as an XML document. In this case, it's simply the return from our search query. Now all we need to do is use the Xtags functions to render the page. Pretty simple!

    The next statement uses an XSLT function to count the number of rows that were returned and assign the result to an Xtags variable.

    The count is examined to determine if we need to display an error page to the user rather than try to process the results any further.





    Your search did not match any
    newsgroup messages.


    Your search did not match any
    additional
    newsgroup messages.


    If the count was good, we create a couple of additional Xtag variables representing the first and last record number on the result page. We'll use that later for the generation of Prior and Next hyperlinks at the bottom of the page.


    Now we just start looping through the rows that were returned and transform the XML into HTML. Note that the following tag:

    is all we need for an iterator. We don't need to know how many rows there are or track them through an index. Also note the generation of the hyperlink to the message:

    <%
    String msgurl = new String ( msgid.substring ( 2,
    msgid.length() - 1 ) ) ;
    msgurl = ngs.replaceAll ( msgurl, "#", "%23" ) ;
    msgurl = ngs.replaceAll ( msgurl, " ", "" ) ;
    %>
    >

    We use Java to strip the "<" and ">" characters from the message ID (also reserved characters, which the PowerBuilder component adds back on before it searches for the message). We also call the replaceAll function of our component to transform other special characters. (Note that the Java 1.4 String class has a replaceAll function that should be used in place of our component's function. The component function was written because the JSP server is running an earlier version of Java.)

    The final portion of the page is where we generate hyperlinks that allow us to paginate through the result set. Note that the hyperlinks allow us to step into any page even if we haven't visited pages prior to it. First, we check to see if we're at the first page and, if not, generate a Prior link:

    <% if ( Integer.parseInt ( first ) != 1 ) { %>
    href=search.jsp?product=<
    %=product%>&searchterm=<%=searchterm%>&maxrows=<%=maxrows.toString()%>
    &startrow=<%=Integer.parseInt(first)
    - returnrows.intValue()%>&returnrows=<%=returnrows.toString()%> >
    Prior

    Then we create links for up to (maxrows/returnrows) pages of results (10 pages with the values of 100 and 10 that our searchform.jsp is passing):

    <% for ( int i = 1, j = 1; i < maxrows.intValue() ; i = i +
    returnrows.intValue(), j++ ) { %>
    <% if ( Integer.parseInt ( first ) == i ) { %>
    <%=j %>
    <% } else { %>
    href=search.jsp?product=<%=product%>&searchterm=<
    %=searchterm%>&maxrows=<%=maxrows.toString()%>
    &startrow=<%=i%>&returnrows=<%=returnrows.toString()%> >
    <%=j %>

    Note that we're also checking to see what page we're currently on, and creating a nonhyperlinked reference in a different font style for that page.

    The last thing we do for this page is check if we're expecting more results than those shown on this page and, if so, generate a Next link:

    <% if ( Integer.parseInt ( first ) < ( maxrows.intValue() -
    returnrows.intValue() ) ) { %>
    href=search.jsp?product=<%=product%>&searchterm=<
    %=searchterm%>&maxrows=<%=maxrows.toString()%>
    &startrow=<%=Integer.parseInt(first)
    + returnrows.intValue()%>&returnrows=<%=returnrows.toString()%> >
    Next

    display.jsp
    The final page is somewhat anticlimactic. As with the search.jsp page, we have the imports for our Newsgroups class and the Axis and Xtags libraries, followed by some standard HTML (see Listing 10). Again we get the parameter passed to the page (the message ID) and then call our component and parse the returned XML. The remainder of the page just transforms the XML into HTML, with a little formatting for the message header. The primary item of interest in this page is the user of the

     HTML tag around the message text, 
    which tells the browser that the text is preformatted and it 
    shouldn't attempt to apply any additional formatting. This ensures 
    that any line breaks, etc., in the original message text are 
    preserved when displayed in the browser.
    

    Conclusion
    I hope you have come to appreciate from this article how much simpler it is to create and consume Web services using PowerBuilder, particularly if you read my previous article and remember how much work was involved there. The PowerBuilder component has less than 100 lines of code, including the code used to connect and disconnect from the database. The Java class in the application server comes in at around 55 lines and the one used by the JSP engine comes in at around 75 lines. The search form is less than 45 lines of generic HTML code, and the two JSP files come in at 110 and 55 lines each. That's a total of just over 400 lines of code to implement one of the major features of Google Groups on the Sybase newsgroups!

    Resources

  • Apache Axis: http://ws.apache.org/axis/index.html
  • Apache Axis: "The Third Generation SOAP Implementation" (Sybase white paper): www.sybase.com/detail?id=1020318
  • Apache Jakarta Xtag Taglibs: http://jakarta.apache.org/taglibs/doc/xtags-doc/intro.html
  • Apache Xerces: http://xml.apache.org/xerces2-j/index.html
  • DOM4J: http://dom4j.org/
  • 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.