| By Bruce Armstrong | Article Rating: |
|
| September 11, 2003 04:00 PM EDT | Reads: |
12,686 |
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:
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:
product, string searchterm, integer
maxresults, integer startingrow, integer
rowstoreturn)
(string msgid)
where the arguments are:
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:
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
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
Published September 11, 2003 Reads 12,686
Copyright © 2003 SYS-CON Media, Inc. — All Rights Reserved.
Syndicated stories and blog feeds, all rights reserved by the author.
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.
- Why SOA Needs Cloud Computing - Part 1
- Cloud Expo and The End of Tech Recession
- The Transition to Cloud Computing: What Does It Mean For You?
- A Rules Engine Built in PowerBuilder
- Sybase Named “Silver Sponsor” of iPhone Developer Summit
- How PowerBuilder Got Its Groove Back
- The Cloud Has Cross-Border Ambitions
- Ulitzer Names The World's 30 Most Influential Virtualization Bloggers
- Ulitzer Named "New Media" Partner of Greatly Anticipated iStrategy Event in Berlin
- Risks and Enterprise Mobility?
- Steps for Success in Enterprise Mobility?
- Are Mobile Luddites Resisting Mobility?
- The Difference Between Web Hosting and Cloud Computing
- Sybase CTO to Speak at 4th International Cloud Computing Expo
- Why SOA Needs Cloud Computing - Part 1
- Cloud Expo and The End of Tech Recession
- The Transition to Cloud Computing: What Does It Mean For You?
- Five Reasons to Choose a Private Cloud
- Seeding The Cloud: The Future of Data Management
- The Threat Behind the Firewall
- Economy Drives Adoption of Virtual Lab Technology
- Tips for Efficient PaaS Application Design
- A Rules Engine Built in PowerBuilder
- Sybase Named “Silver Sponsor” of iPhone Developer Summit
- Where Are RIA Technologies Headed in 2008?
- PowerBuilder History - How Did It Evolve?
- The Top 250 Players in the Cloud Computing Ecosystem
- Custom Common Dialogs Using SetWindowsHookEx
- DDDW Tips and Tricks
- OLE - Extending the Capabilities of PowerBuilder
- DataWindow.NET How To: Data Entry Form
- Book Excerpt: Sybase Adaptive Server Anywhere
- Sybase ASE 12.5 Performance and Tuning
- Working with SOA & Web Services in PowerBuilder
- Office 2003 Toolbar: A New Look For Your Old PowerBuilder App
- Dynamically Creating DataWindow Objects

































