JBuilder in the Web Services Jungle

By: Eric Whipple

Abstract: Use of external Web service architectures is a quick way to embed functionality and resources into your applications. This article shows how to use JBuilder 9 to consume Amazon.com's Web services (AWS) for use in a custom Web application.

Part 1: Using Amazon Web Services

Introduction

Among JBuilders many virtues is its ability to create and consume Web services. It allows users to quickly consume existing services and incorporate them into Java applications. In this, the first article in a two part series, well look at how JBuilder can be used to consume AWS, the Web services architecture provided by Amazon.com (http://www.amazon.com). In the second article well move on to using JBuilder and the AWS shopping cart and payment API to enable purchasing for our application.

Web Services in e-Business

Web services is the hottest topic in internet development today. Not only does it allow companies to expose their e-business services over open technologies such as XML and SOAP, it allows consumers of those services to create service clients which integrate into their own applications with much greater speed and much lower cost. Amazon.com in this case, probably doesnt know how many applications are selling their products for them but more importantly, they probably dont care. Any developer that thinks he can present Amazons items in a more attractive way is welcome to try. And Amazon.com is laughing all the way to the bank. In addition to this, Web services has allowed Amazon.com to create an unlimited number of partners without the need for months of architectural integration meetings and without the headache of the partners trying to sway its service implementations with their own technological needs and desires.

Warning: The previous paragraph is not meant to endorse bad architectures and fly by night, functionally useless services! Extensibility and flexibility are just as important to Web services as they are to any other application. There, Ive said it. Dont blame me!

Introduction to AWS

AWS is an abbreviation for Amazon Web Services. AWS, which is currently in its third major release, is an architecture by which Amazon makes their information and purchasing services publicly available to other applications. Exposing this Internet functionality gives Amazon the ability to expose their services to a much wider range of applications and users while retaining if not increasing both the attractiveness and the profit of their business. There are a couple of steps developers need to go through to get on the AWS bandwagon. All of this information is available at Amazons site at http://www.amazon.com/webservices. According to their site, the first step is to download the Web services SDK. This is not strictly necessary for development. As we will see, the entire set of functionality can be imported and recreated by JBuilder. The most important benefit of the SDK is the documentation and examples that give you a head start on creating your own Web service. The second step in using AWS is to apply for a developers token. This is essentially an id, which you will have to use when connecting to AWS. Finally, just write your application and you have achieved integration with one of the nets most powerful e-commerce sites. Now you just have to decide how to use them. There are a few different ways in which outsiders can utilize AWS. Developers  Any application developer can create an extension of Amazons e-commerce solution. This is a great way to tailor applications more specifically for your particular users. Associates  The associates program allows Amazon partners to create applications that essentially link to Amazon. Associate companies can earn referral fees for each sale that they generate for Amazon. To enroll in the associates program, you must apply for an associate id. For more information, see http://www.amazon.com/associates. Sellers and Vendors  Finally, Amazon hosts products for a number of third parties and resellers. This section of AWS allows these parties to sell their products directly on Amazon. This is a great way for smaller companies to expose their products to a much larger audience of potential buyers.

Creating a Music Search Application

AWS applications can be written using a few different technical strategies. It can be accessed using a PHP client library, straight XML, or SOAP. For this article well be using SOAP. More specifically, we are going to be using AWS to create a music search application that allows users to search by keywords and returns details about available music CDs. Furthermore, the application allows for the purchasing of CDs remotely. This application is a cut-down version of the Barden Entertainment Music Search), located at http://www.bardenent.com/AmazonSearch/.

Importing AWS with JBuilder

The first step in creating an AWS application is to import the WSDL for Amazons Web service. A WSDL document is a public XML file that describes the functionality of the Web service as well as how to bind and invoke its methods. Fore more on WSDL, check out www.w3schools.com/wsdl/ or another WSDL tutorial. Begin by creating a new JBuilder application. Once the application is created, add a Web Application from the Web tab of the Object Gallery. Now that the basic application is created, we can import the WSDL document that describes AWS. Select File|New and from the Web Services tab of the Object Gallery, select Import a Web Service option. This will launch the Import Wizard. If your project does not already contain a Web application, JBuilder will automatically launch the Web Application wizard first. For more on creating Web applications, consult the JBuilder documentation.



The first step in the import wizard asks us to choose a WSDL document from which to import the objects of the Web service. An URL is supplied from the internet or from a local file. The WSDL document for AWS is located at http://soap.amazon.com/schemas3/AmazonWebServices.wsdl. If you are importing from a Web service which requires a user name and password, they may also be supplied here.



Step two determines whether or not you want JBuilder to generate server-side classes for the Web service itself. If so, you can select how you want the server-side classes to be created. In our case, we only want to consume the Web service so well uncheck the Generate server-side classes box.



Step three determines how client-side classes should be created in your project as well as some other code generation options. Among them, you can specify the package into which generated classes are placed and what version of the SOAP specification JBuilder should use as well as some other output options.

Overwrite any existing Bean types If JavaBeans with the same name as an imported class exist in the project, they will be overwritten.
Generate typemapping info in separate helper classes Separates type mapping info from the WSDL into separate helper classes, rather than embedding it in the class itself.
Generate JUnit test case Automatically generates a JUnit test case.
Generate wrapped style types If the WSDL document has the wrapped style specified, then JBuilder can import WSDL complex types as a flat interface definition. If the option is left unchecked, then complex types are used to create Java classes.
Generate code for all elements even if not referenced If the WSDL document contains definitions for things that are not directly required by the method calls, these elements are also imported. This would be needed for instance if a method returned a collection object. The client application would have to be able to understand and create the business objects that are contained by the collection as well as the collection itself.
Ignore imports and use only the immediate WSDL document If the WSDL document contains WSDL imports (which reference elements defined in other WSDL documents, these import statements (and the things they refer to) are ignored and JBuilder imports only elements in the specific document noted in step 1.




Finally, JBuilder gives you the opportunity to create custom namespace mappings for the service. If for instance this service were being imported into an application that consumed multiple Web services, its package mapping could be altered in order to fit in with the existing package structure. Once this is done, click Finish to import the classes. As you can see, JBuilder has been busy. Our project now contains a new package, com.amazon.soap that was created based on the namespace mappings. It contains a large number of classes, representing all of the objects necessary to create an AWS client using Java and SOAP. In addition, the project contains a collection of helper classes that enables your application to make remote calls to AWS.



Looking at the Helper Classes

AmazonSearchServicePort  The Port class represents the applications interface to the service. It is an interface declaring each of the methods publicly available through the service. Because the actual calls are invoked against a remote service, each method throws java.rmi.RemoteException. The code listing below is a small piece of the AmazonSearchPort class (which actually contains a much large number of methods). This interface is implemented by other helper classes which actually make the Web service calls. It is simply an abstraction of the service functionality. This of course allows for alternate implementations.

public interface AmazonSearchPort extends java.rmi.Remote {
    public com.amazon.soap.ProductInfo keywordSearchRequest(com.amazon.soap.KeywordRequest keywordSearchRequest) throws java.rmi.RemoteException;
    public com.amazon.soap.ProductInfo upcSearchRequest(com.amazon.soap.UpcRequest upcSearchRequest) throws java.rmi.RemoteException;
    public com.amazon.soap.ProductInfo artistSearchRequest(com.amazon.soap.ArtistRequest artistSearchRequest) throws java.rmi.RemoteException;
    // full class listing left out for brevity
}

AmazonSearchService  The Service class is an factory interface that is retrieves a stub object. This object is used to connect to and invoke methods of the remote service.

public interface AmazonSearchService extends javax.xml.rpc.Service {
    public java.lang.String getAmazonSearchPortAddress();
    public com.amazon.soap.AmazonSearchPort getAmazonSearchPort() throws javax.xml.rpc.ServiceException;
    public com.amazon.soap.AmazonSearchPort getAmazonSearchPort(java.net.URL portAddress) throws javax.xml.rpc.ServiceException;
}

AmazonSearchServiceLocator  The Locator class is an implementation of the service class. Its purpose is to retrieve a stub object that will communicate directly with the remote service. Well see how the locator is used to connect to the Web service later in this paper.

public class AmazonSearchServiceLocator extends org.apache.axis.client.Service implements com.amazon.soap.AmazonSearchService {
    // full class listing left out for brevity
}

AmazonSearchServiceBindingStub  The BindingStub handles the underlying details of communicating with the Web service. It packs up application requests as SOAP requests and serializes then invokes the call to the Web service. It also takes care of unpacking the response upon method return. The code listing below shows the implementation of the keywordSearchRequest method. This is the method from AWS that we will be invoking to search for CDs from our application. Note the creation and use of the org.apache.axis.client.Call object. This is the underlying object through which Axis transmits requests. JBuilder of course has taken care of generating the code that makes the actual call.

public class AmazonSearchBindingStub extends org.apache.axis.client.Stub implements com.amazon.soap.AmazonSearchPort {
    // full class listing left out for brevity
    public com.amazon.soap.ProductInfo keywordSearchRequest(com.amazon.soap.KeywordRequest keywordSearchRequest) throws java.rmi.RemoteException {
        if (super.cachedEndpoint == null) {
            throw new org.apache.axis.NoEndPointException();
        }
        org.apache.axis.client.Call _call = createCall();
        _call.setOperation(_operations[0]);
        _call.setUseSOAPAction(true);
        _call.setSOAPActionURI("http://soap.amazon.com");
        _call.setSOAPVersion(org.apache.axis.soap.SOAPConstants.SOAP11_CONSTANTS);
        _call.setOperationName(new javax.xml.namespace.QName("http://soap.amazon.com", "KeywordSearchRequest"));

        setRequestHeaders(_call);
        setAttachments(_call);
        java.lang.Object _resp = _call.invoke(new java.lang.Object[] {keywordSearchRequest});

        if (_resp instanceof java.rmi.RemoteException) {
            throw (java.rmi.RemoteException)_resp;
        }
        else {
            extractAttachments(_call);
            try {
                return (com.amazon.soap.ProductInfo) _resp;
            } catch (java.lang.Exception _exception) {
                return (com.amazon.soap.ProductInfo) org.apache.axis.utils.JavaUtils.convert(_resp, com.amazon.soap.ProductInfo.class);
            }
        }
    }
    // full class listing left out for brevity
}

AmazonSearchServiceTestCase - JBuilder can generate a JUnit test case for each service call. This makes incremental testing of the application a snap. Before any custom business logic is applied, you can run a test to see if the code that was imported/generated is able to connect to the Web service. For more information on JUnit, go to http://www.junit.org/.

public class AmazonSearchServiceTestCase extends junit.framework.TestCase {
    // full class listing left out for brevity
}

Understanding the details of these generated classes is not required to consume Web services with JBuilder. However, it is always a good idea to know what the code in your project is doing for you. Now that we have seen how to import Amazons WSDL data, lets get to the business of building our application.

Performing a Basic Keyword Search

Our search application has a simple architecture. The musicsearch jsp simply submits a keyword request to the musicsearch servlet. The servlet then invokes the AWS request, stores the results in an ArrayList of MusicSearchResult objects (containing attributes of the results), puts the ArrayList into the session and forwards back to the original page.



Because the jsp code is mostly formatting and display, we wont take time to get into it in detail. Instead, we want to focus on how to retrieve and use the AWS data. For that, lets take a look at KeywordSearchServlet.java. In the listing below, we see the code necessary for preparing and invoking a call to AWS.

// Retrieve a binding stub
AmazonSearchPort amazonSearch = null;           
AmazonSearchServiceLocator asl = new AmazonSearchServiceLocator();
amazonSearch = asl.getAmazonSearchPort(new java.net.URL("http://soap.amazon.com/onca/soap3"));

//Keyword Search
KeywordRequest searchString = new KeywordRequest();
searchString.setKeyword(request.getParameter("keyword"));
searchString.setDevtag("<your dev tag goes here>");
searchString.setTag("<your associate id (if applicable) goes here>");
searchString.setMode("music");
searchString.setType("lite");

The first step is to retrieve a binding stub through which we will invoke calls to AWS. To do this, create an AmazonSearchServiceLocator object and call its getAmazonSearchPort() method. This method returns an AmazonSearchBindingStub object. This may be confusing since the method is called getSearchPort. Keep in mind though that the AmazonSearchBindingStub class implements the AmazonSearchPort interface. In other words, a binding stub is a search port. Once the communication object is retrieved, we must prepare the request. To do this, we create a com.amazon.soap.KeywordSearchRequest object.

There are a few attributes of this object that must be set before it can be used. The devtag is the developers token that you were assigned when you signed up to be an AWS developer. The mode represents a category on which you want to search. You can search in a number of categories including music, books, software, video-games, and a few others. The type attribute refers to the type of request you are sending. AWS requests come in two flavors: lite and heavy. This determines what information is sent back to you in the response (that is, how much information the response contains). If your request does not require a great deal of data, the lite type will require less data transfer and therefore be faster. The following table shows the data included in each type of request (Heavy requests also include all data from the lite request type).

Lite Request DataHeavy Request Data
ASIN
Artists
Authors
Availability
Catalog
ImageUrlLarge
ImageUrlMedium
ImageUrlSmall
ListPrice
Manufacturer
OurPrice
ProductName
ReleaseDate
URL
UsedPrice
Accessories
AgeGroup
Availability
BrowseList
CollectiblePrice
Directors
Distributor
Encoding
EsrbRating
Features
ISBN
KeyPhrases
Lists
Media
MpaaRating
MPN
NumberOfOfferings
NumMedia
Platforms
Publisher
ReadingLevel
RefurbishedPrice
Reviews
SalesRank
SimilarProducts
Starring
TheatricalReleaseDate
ThirdPartyNewPrice
ThirdPartyProductInfo
Tracks
UPC

The last line in the previous listing invokes the keywordSearchRequest, passing in the com.amazon.soap.KeywordSearchRequest object just created. The results are placed into a ProductInfo object. It is very important to note that the resulting object has the same structure whether the request type is lite or heavy. Attributes that are not populated by the result type or for which AWS has no available information are null. You must remember to check for this when using the ProductInfo object to avoid NullPointerExceptions. AWS uses a best-effort model to fulfill requests. If information is currently unavailable, AWS returns that attribute as empty instead of failing the entire call. Although this puts some responsibility on the developer to check for the existence of data, it allows AWS to return the best currently available response.

ArrayList musicSearchResultList = new ArrayList();

for(int i=0;i<productInfo.getDetails().length;i++){
    Details details = productInfo.getDetails()[i];

    MusicSearchResult result = new MusicSearchResult();
    result.setProductName(details.getProductName());
    result.setAsin(details.getAsin());
    result.setImageURL(details.getImageUrlSmall());
    result.setPrice(details.getOurPrice());

    musicSearchResultList.add(result);
    }
                request.getSession(true).setAttribute("musicSearchResults",musicSearchResultList);

The second half of the code (shown above) in the KeywordSearchServlet prepares the search results to be used by the application. First an ArrayList is created to hold the collection of results. The com.amazon.soap.ProductInfo object contains a details attribute which is an array of com.amazon.soap.Details objects, each describing a particular result. For each Details object, we will instantiate a MusicSearchResult object. This is a custom Java Bean that holds just the result information that we will use in our application. Once the MusicSearchResult object is populated, it is added to the ArrayList. Once the ArrayList is complete, it is stored in the Web application session and the servlet forwards the request back to the musicsearch.jsp page (code left out for brevity). The musicsearch page looks for the existence of the ArrayList and, if found, uses the MusicSearchResult objects to display data on the page.

<%  String keyword = request.getParameter("keyword");
    if(keyword != null){
%>
<table><tr><td bgcolor="tan"><b>Search Results for: <%= keyword %></b></td></tr></table>
<%  }
    ArrayList results = (ArrayList)request.getSession().getAttribute("musicSearchResults");
    if(results != null){
%>
<table bgcolor="tan">
<%  for(int i=0;i<results.size();i++){
    MusicSearchResult msr = (MusicSearchResult)results.get(i);
%>
    <tr>
        <td rowspan="2"><img src="<%=msr.getImageURL()%>" alt="No Image"></td>
        <td><%=msr.getAsin()%></td>
        <td><%=msr.getPrice()%></td>
    </tr>
    <tr>
        <td colspan="3"><%=msr.getProductName()%></td>
    </tr>
    <tr><td colspan="3"><hr></td></tr>
<%  }
    }
%>

And there we have it! With a very simple set of code, we are ready to tap into the e-business universe of Amazon.com!

Conclusion

AWS is an outstanding example of the power of Web services. Even if you had access to the very large datastore that Amazon represents, it would take a lot of time and cost a lot of development money to create an architecture that would allow you to tap into its resources. Web services solves this problem for you by allowing Amazon to expose its services publicly through AWS. JBuilder makes it even easier by providing functionality that allows you to quickly and easily import AWS and begin invoking its methods. With Web services and JBuilder the intenet truly becomes a powerful tool for e-business success. Download the code for this article at Code Central(20596)

About the Author

Eric Whipple is the Director of Internal Development for Barden Entertainment (http://www.bardenent.com) , makers of the Digital Video Jukebox(TM) and other distributed kiosk applications. He is responsible for the design and implementation of Web services and other development architectures. He is also the author of Kylix 2 Development (Wordware Publishing) and numerous articles for Delphi Informant and the Borland Developer Network. Questions or follow-ups on this article can be sent to him at ewhipple@bardenent.com.


Server Response from: ETNASC04