Publish and find UDDI tModels with JAXR and WSDL

Work with WSDL, JAXR, and UDDI

Imagine you are a travel agent and frequently book cruise ship vacations. To offer your customers choice, you must maintain an up-to-date list of exotic destinations. In the past, you could have gathered this information by obtaining cruise ship company directories, inspecting each company's Website, or telephoning cruise operators to find out what cities their companies served.

Web services allow you to compile and maintain such lists automatically. That convenience comes with two requirements: First, cruise ship companies must agree to use a common Web service interface when publishing their destinations. Second, each company must implement that interface and register its implementation in Web service registries. With those requirements satisfied, you can consult Web service registries, discover all cruise destination service instances, invoke each service, and produce your cruise destinations master list.

The pattern of finding all implementations of a Web service interface and possibly invoking those service instances proves useful in other contexts as well. Portal Websites still rely on manual—or semi-manual—compilation of news articles, automobile inventories, available hotel rooms, or airline seats. Even when data exchanges electronically, that automation often comes at the expense of lengthy and pricey system integration. Among the biggest motivations for building Web services is the desire to automate those tedious information-gathering tasks. This article provides a working example of how UDDI (Universal Description, Discovery, and Integration) registries, the Java API for XML Registries (JAXR), and the Web Services Description Language (WSDL) work together to initiate that automation.

Reuse your WSDL

Currently, several industry groups are working to define Web service interface standards. Examples are the Open Travel Alliance for travel, the Star Consortium for automotive retail, and RosettaNet for supply chain management. Many of those groups employ a community-oriented process and make their specifications available to anyone for comments.

Real-world interface specifications aim to be comprehensive and are rather complex. Thus, to make this article easy to follow, I use a simple interface definition for the example cruise ship destination Web service. That interface features only a single method. When invoked, that method produces a list of destinations a cruise company serves. Here is that interface in Java:

   
public interface CruiseService {
    public String[] cruiseDestinations(); 
}

Because Web services aim to remain programming language-neutral, we must convert this interface definition to a format not tied to Java. WSDL defines a document structure suitable for describing Web service interfaces with XML data elements. My previous Web Services column demonstrated how development tools, such as open source Apache Axis, convert a Java interface to a WSDL document.

As apparent from Web service-related discussion lists, many developers debate the better practice: first define your Web service's interface with code—for instance, Java code—then convert that code to WSDL definitions, or create the WSDL documents first, then convert those documents to Java code. Finding the right method might have more to do with personal predilection than hard and fast rules. I prefer to define a service's interface in Java (or some other language) first, because I find that a programming language-based definition produces a clearer, simpler picture of a service's capabilities. WSDL-first advocates have a point too: converting Java interfaces to XML structures is not an exact science and can introduce subtleties leading to surprising results.

Regardless of how you create a WSDL document, it consists of six element types:

  1. definitions: the WSDL document's root element, also includes name space declarations and target names
  2. types: data type definitions
  3. message: message definitions
  4. portType: a set of operations provided by the services described by those operations
  5. binding: wire protocol and message formats offered by the service
  6. service: ports for using the service, each port possibly with several bindings

Here is the entire WSDL document Apache Axis created based on the CruiseService interface. To make it more readable, I divided it according to the six major WSDL elements:

  1. definitions (preceded by document header):

      <?xml version="1.0" encoding="UTF-8"?>
    <wsdl:definitions 
        targetNamespace="urn:cruise" 
        xmlns="http://schemas.xmlsoap.org/wsdl/" 
        xmlns:apachesoap="http://xml.apache.org/xml-soap" 
        xmlns:impl="urn:cruise-impl" 
        xmlns:intf="urn:cruise" 
        xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
        xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
        xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" 
        xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    
  2. types: This element first identifies the XML Schema namespace. The only type defined here is a string array for our method's return value. That value may also be null. The declaration of that string array's complex type precedes the type's definition:

      <wsdl:types>
        <schema targetNamespace="urn:cruise" 
            xmlns="http://www.w3.org/2001/XMLSchema">
            <import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
          <complexType name="ArrayOf_soapenc_string">
            <complexContent>
                <restriction base="soapenc:Array">
                    <attribute ref="soapenc:arrayType" 
                         wsdl:arrayType="soapenc:string[]"/>
                </restriction>
            </complexContent>
          </complexType>
          <element name="ArrayOf_soapenc_string" nillable="true" 
             type="intf:ArrayOf_soapenc_string"/>
        </schema>
    </wsdl:types>
    
  3. message: There are two messages: a response and a request. The response refers to the type with the name ArrayOf_soapenc_string declared in the previous element. Since our method takes no parameters, the request message is empty:

      <wsdl:message name="cruiseDestinationsResponse">
        <wsdl:part name="return" type="intf:ArrayOf_soapenc_string"/>
    </wsdl:message>
    <wsdl:message name="cruiseDestinationsRequest">
    </wsdl:message>
    
  4. portType: The only port type defined here is named CruiseService and operates on the input and output messages defined earlier:

      <wsdl:portType name="CruiseService">
        <wsdl:operation name="cruiseDestinations">
            <wsdl:input message="intf:cruiseDestinationsRequest" 
                name="cruiseDestinationsRequest"/>
            <wsdl:output message="intf:cruiseDestinationsResponse" 
                name="cruiseDestinationsResponse"/>
        </wsdl:operation>
    </wsdl:portType>
    
  5. binding: This WSDL document declares only one service binding. That binding allows access to the service via SOAP (Simple Object Access Protocol). That service binding's SOAP operation corresponds to our interface's single method, cruiseDestinations(), and that method's input and output parameters:

      <wsdl:binding name="AxisServletSoapBinding" type="intf:CruiseService">
        <wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
        <wsdl:operation name="cruiseDestinations">
            <wsdlsoap:operation soapAction=""/>
            <wsdl:input name="cruiseDestinationsRequest">
                <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
                  namespace="urn:cruise" use="encoded"/>
            </wsdl:input>
            <wsdl:output name="cruiseDestinationsResponse">
                <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
                    namespace="urn:cruise" use="encoded"/>
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    
  6. service: This section defines where the service's port bindings, defined in the previous element, are actually accessed. This example has only one such service access point, identifying a URL for the SOAP-based port:

      <wsdl:service name="CruiseService">
        <wsdl:port binding="intf:AxisServletSoapBinding" name="AxisServlet">
            <wsdlsoap:address location="http://192.168.1.10:8080/axis/servlet/AxisServlet"/>
        </wsdl:port>
    </wsdl:service>
    </wsdl:definitions>
    

While the WSDL Recommendation defines bindings for SOAP, HTTP GET and POST, and MIME (Multipurpose Internet Mail Extensions), a WSDL document allows the specification of any binding mechanism. For instance, you may choose to define a service that provides access via the RMI/IIOP (Remote Method Invocation/Internet Inter-ORB Protocol) protocols, allowing RMI-style service invocation. In addition, you may choose to provide data type and message definitions not tied to XML Schema or even to XML and rely, for example, on Java byte code. Or a service might have numerous access point URLs listed. WSDL's extensible structure offers that flexibility.

But WSDL's power comes at the price of increased document complexity. Since many WSDL elements can take multiple values, a Web service's WSDL definition can become quite extensive. To avoid the need for monster WSDL documents, the WSDL Recommendation defines an import mechanism:

The WSDL import element information item allows the separation of the different elements of a service definition into independent documents, which can be imported as needed. This technique helps in writing clearer service definitions, by separating the definitions according to their level of abstraction, and maximizes reusability.

With WSDL's import mechanism, we can split our service WSDL document into multiple logical documents. Before seeing how that works, note that WSDL's import capability is not an include mechanism; it simply associates a namespace with a WSDL document. An editor's note in the latest WSDL Recommendation draft reveals how the distinction between import and include has confounded many developers:

We have run into many, many people who appear to be confused about how import is supposed to work. The notion that it only establishes a relationship between a namespace and a location is quite hard to grasp, it appears. Specifically, the fact that nothing is said about what one may find about the namespace at that location appears to be very confusing.

WSDL's import works similarly to how one XML Schema document might refer to a namespace external to that schema. You have already seen an example of that external namespace reference in our WSDL document's types element. There, we imported the SOAP encoding namespace so that we could define our return array type with a SOAP encoding mechanism:

 ... <import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
...

WSDL's import allows us to split our CruiseService interface into a WSDL document that corresponds to the service's interface definition and another WSDL document corresponding to the service's implementation. Separating interface from implementation lets us reuse the service interface. If cruise companies agree to adhere to the reusable service interface, CruiseService, each company must only publish the implementation portion of their Web service description and declare that implementation's compliance with the CruiseService interface by importing the service interface's namespace. Figure 1 illustrates that process.

Figure 1. Separation of Web service interface and implementation in WSDL

Our service interface WSDL document will include the definitions, types, message, portType, and binding elements. The service element, referring to a specific implementation, will form another WSDL document's content. The latter document must also reference the first document's namespace via WSDL's import statement.

Apache Axis's WSDL-generation tool, Java2WSDL, offers command-line parameters for splitting a WSDL definition into a WSDL service interface and implementation-specific WSDL documents. Given the original Java interface, CruiseService, the following command creates the WSDL service interface:

  java org.apache.axis.wsdl.Java2WSDL -o cruise_interface.wsdl       -n urn:cruise -S CruiseService -w interface CruiseService

The -o parameter specifies the output file's name; -n designates the WSDL namespace; -S provides the Web service interface's name; and -w interface signifies our interest in a service interface WSDL.

To create an implementation-specific WSDL, change interface to implementation for the -w option, specify a different output filename, and provide the service implementation's location:

 java org.apache.axis.wsdl.Java2WSDL -o cruise_implementation.wsdl     -l http://192.168.1.10:8080/axis/servlet/AxisServlet       -n urn:cruise -S CruiseService -w implementation CruiseService

Register WSDL interfaces as UDDI tModels

The easier to find out about the Web service interface WSDL, the more likely companies will create compatible implementations. Web service registries, such as UDDI and ebXML, ease the discovery of well-known services and their implementations. Thus, the next step: publish the existence of that interface WSDL in Web service registries.

Figure 2. The UDDI registry information model. Click on thumbnail to view full-size image.

Figure 2 depicts the UDDI information model and the relationships between that model's elements. (To keep this article simple, I only focus on UDDI. ebXML offers analogous mechanisms.) A business entity, such as a cruise ship company, can advertise a set of business services, and each service can define access points and associated technical specifications. A service's access points and specifications together form that service's bindings. UDDI also provides a way to express business relationships—such as supplier-customer or business partner—via publisher assertions.

Apart from organizations and the services they provide, UDDI also allows the publishing of well-known specifications and taxonomies. The UDDI data structure for publishing that information is the tModel, or technical model. A tModel is often associated with many organizations and their services. tModels represent general concepts that many organizations can refer to in their businessService, bindingTemplate, or even businessEntity registrations.

Since we want others to discover and reference our service interface WSDL, we must register it as a UDDI tModel. Each UDDI entity—be it a businessEntity, a businessService, a serviceBinding, a publisherAssertion, or a tModel—is given a globally unique key by the UDDI registry. Using a tModel's unique key as a reference, a business can indicate that one or more of its services abide by the specifications that key identifies.

In addition to that unique tModelKey, a tModel data structure may contain seven other data elements. Figure 3 describes those elements.

Figure 3. Elements of the UDDI tModel data structure. Required elements are bolded. Click on thumbnail to view full-size image.

You may be surprised that no data field is assigned to hold the WSDL document. That's because UDDI registries do not hold instances of WSDL documents or other interface and taxonomy descriptions. Instead, a tModel's overviewDoc field contains references to external locations where those documents might be found. That overviewDoc structure consists of a URI and an associated human-readable description. When registering the CruiseService interface as a tModel, we must specify a URL to the service interface WSDL document in the tModel's overviewDoc field.

Perhaps the most powerful UDDI feature is the ability to associate categories, or classifications, with entities in the registry. In a previous column, "Web Services Take Float with JAXR" (May 2002), I gave an example of categorizing a business according to business classifications, such as by industry type or geographic location. Two data structure elements make it possible to classify a tModel in a similar way: identifierBag and classifierBag.

As their names suggest, both are collections holding key-value pairs. Each pair designates identifiers and classifications, respectively. The key is the identifier or classification name; the value must carry specific meaning within that key's namespace.

An identifier is any piece of data that exactly points to a tModel or business entity. In other words, it identifies that tModel or that business. In the case of a business, a unique identifier might be a business's US tax ID number or its D&B D-U-N-S Number in Dun & Bradstreet's business registry. With tModels, a unique ID might be "Theseus Cruise Lines Purchase Order No. 25," "Federal Income Tax Form 1040," or "Cruise Destination Service Version 1.0."

A category, on the other hand, identifies a business or tModel as belonging to a class of entities: that kind of business or that kind of tModel. In "Web Services Take Float with JAXR," I categorized a business according to the North American Industry Classification System (NAICS) and the ISO (International Standards Organization) 3160 international geographic categories. tModels allow a similar classification via their categoryBags. In this example, we associate the CruiseService interface tModel with the industry category for cruise lines. Based on NAICS classifications, that's category 483112, or "deep sea passenger transportation."

In addition to that industry-based classification, the UDDI technical committee designated a special classification for tModels that point to WSDL documents—wsdlSpec tModels. The corresponding category name is uddi-org:types with the value wsdlSpec.

In summary, we need to register a new tModel such that:

  • Its overviewDoc element points to a URL of the CruiseService WSDL interface
  • It's classified as belonging to the cruise industry
  • It's classified as a wsdlSpec tModel

The Java API for XML Registries (JAXR) allows you to perform that registration programmatically. JAXR models the kind of tModel that satisfies the above criteria as a javax.xml.registry.infomodel.Concept. The Concept JAXR interface represents a registry object (javax.xml.registry.infomodel.RegistryObject) that can participate in hierarchical relationships. As any RegistryObject, a Concept may be classified and also associated with a set of external identifiers and links. That sounds like the perfect data structure for our tModel: we will associate two classifications with our tModel Concept—the NAICS code and the wsdlSpec type—and specify the WSDL document's URL as an external link. Figure 4 shows the mapping between tModel data structure elements and JAXR Concept properties.

Figure 4. Mapping between UDDI tModel elements and JAXR Concept properties (Source: The Java API for XML Registries Specification, Proposed Final Draft, v. 0.9, p. 103)

The following code creates a new Concept, sets its classifications and external link, and saves it as a UDDI tModel:

  void addTModel() {
    try {
        //1. Create a new Concept.
        //The null parameter signifies that this concept has no parent.
        Concept wsdlConcept =
            businessLifeCycleManager.createConcept(
                 null,
                "Cruise ship service interface",
                 null);
        InternationalString description =
            businessLifeCycleManager.createInternationalString(
                "A service that lists the destination ports a " +
                "cruise company serves.";
        wsdlConcept.setDescription(description);
        //2. Create wsdlSpec classification. 
        //The type (scheme) for that classification is uddi-org:types.
        ClassificationScheme scheme =
            businessQueryManager.findClassificationSchemeByName(null,
                "uddi-org:types");
        Classification wsdlSpecClassification =
            businessLifeCycleManager.createClassification(
                scheme, "wsdlSpec", "wsdlSpec");
        //3. Create cruise industry classification.
        //The type for that classification is ntis-gov:naics.
        ClassificationScheme sc =
            businessQueryManager.findClassificationSchemeByName(null, 
                "ntis-gov:naics");
        Classification industryClassification =
            businessLifeCycleManager.createClassification(
                sc, "Deep sea passenger transportation", "483112");
        //4. Create an external link for the WSDL document location. 
        //NOTE: This must point to a valid URL, or JAXR will throw an exception.
        String wsdlUrl = "http://cruiseconsortium.org/cruise_interface.wsdl";
        String wsdlDesc = "WSDL service for cruises";
        ExternalLink wsdlLink =
            businessLifeCycleManager.createExternalLink(wsdlUrl, wsdlDesc);
        //5. Add the two classifications to the Concept.
        Collection classifications = new ArrayList();
        classifications.add(wsdlSpecClassification);
        classifications.add(industryClassification);
        wsdlConcept.addClassifications(classifications);
        //6. Add the external link to the Concept.
        wsdlConcept.addExternalLink(wsdlLink);
        //7. Save the new concept in UDDI.
        Collection newConcepts = new ArrayList();
        coll.add(wsdlConcept);
        BulkResponse resp = businessLifeCycleManager.saveConcepts(coll);
        //8. Check if we've succeded.
        if (resp.getExceptions() == null) {
            //Success.
        } else {
            //Something went wrong.
            for (Iterator iterator = resp.getExceptions().iterator(); iterator.hasNext();) {
                RegistryException registryException = 
                    (RegistryException) iterator.next();
                System.out.println(registryException.toString());
            }
        }
 } catch (Exception e) {
     e.printStackTrace();
}

You can associate the newly created Concept with a taxonomy by specifying that taxonomy's name as the first parameter in the createConcept() method and assign the new Concept's value within that taxonomy as the third parameter. To keep this example focused, I provided null values for those parameters. Also note that you must provide a valid URL when specifying the Concept's ExternalLink. JAXR will attempt to connect to that URL before saving the Concept; if that connection fails, JAXR will throw a ConnectionException, and the tModel's registration will abort.

Before you can execute the preceding method, you must connect to a UDDI registry. I present the following code as a refresher; for a full explanation, please read "Web Services Take Float with JAXR."

  static BusinessQueryManager businessQueryManager;
static BusinessLifeCycleManager businessLifeCycleManager;
void connectToRegistry() {
    try {
        Properties prop = new Properties();
        prop.setProperty("javax.xml.registry.queryManagerURL",
            "http://uddi.ibm.com/testregistry/inquiryapi");
        prop.setProperty("javax.xml.registry.lifeCycleManagerURL",
            "https://uddi.ibm.com/testregistry/publishapi");
        prop.setProperty("javax.xml.registry.factoryClass",
            "com.sun.xml.registry.uddi.ConnectionFactoryImpl");
        //Provide your UDDI account user name and password.
        //You must create a real UDDI account for this work.
        String userName = "magellan";
        String password = "ferdinand";
        PasswordAuthentication passwdAuth =
            new PasswordAuthentication(userName, password.toCharArray());
        Set creds = new HashSet();
        creds.add(passwdAuth);
        ConnectionFactory connFac = ConnectionFactory.newInstance();
        connFac.setProperties(prop);
        Connection connection = connFac.createConnection();
        connection.setCredentials(creds);
        RegistryService registryService = connection.getRegistryService();
        businessQueryManager = registryService.getBusinessQueryManager();
        businessLifeCycleManager = registryService.getBusinessLifeCycleManager();
   }
   catch (JAXRException e) {
       e.printStackTrace();
   }
}

If you log into your UDDI account using a Web browser, you should see the newly registered tModel. Figure 5 shows the cruise service tModel's successful registration in IBM's test UDDI registry.

Figure 5. Description of a newly registered tModel in IBM's UDDI test registry. Click on thumbnail to view full-size image.

Discover industry-specific Web services

For a Web service interface to become an industry-wide standard, you will likely need to work with many industry participants: partners, suppliers, customers, and industry experts. UDDI and ebXML registries help by making industry-specific Web services easy to find. This article shows two sides of that Web service registry benefit: first, from the vantage point of a cruise company wanting to keep current with its industry's Web service standards and proposals; second, from the viewpoint of a travel agency needing to compile that list of exotic cruise destinations. I next focus on the cruise company's aim; later I trace the travel agent's steps.

Suppose you're a developer at the imaginary Theseus Cruise Lines. You can periodically scan Web service registries for tModels related to your industry. The JAXR code for such a query starts out similarly to tModel registration:

  1. Specify the classifications or categories describing the technical specifications you're looking for. As a cruise company, you'd look for the NAICS category for cruise operators. In addition, you might want to restrict your search to WSDL-defined Web services interfaces by specifying wsdlSpec as a value in your registry query's uddi-org:types classification. Additional category restrictions might limit your search based on geography—cruise lines serving only certain countries or continents—or the organizations that registered the specifications.
  2. Next, invoke the findConcepts() method on JAXR's BusinessQueryManager instance. That method will return a BulkResponse—a collection containing Concepts corresponding to the tModels meeting your search criteria.
  3. Iterating through that collection, you can obtain each Concept's name, description, and ExternalLinks.
  4. For each ExternalLink, retrieve the appropriate WSDL document by connecting to that external link's externalURI.

The following code snippet illustrates the above steps:

  void query() {
    try {
        //1. Specify search criteria
        //(a) Search-based industry classification
        ClassificationScheme naicsScheme =
            businessQueryManager.findClassificationSchemeByName(null, 
                "ntis-gov:naics");
        Classification cruises =
            businessLifeCycleManager.createClassification(naicsScheme,
                "Deep sea passenger transportation", "483112");
        //(b) Lookup for wsdlSpec type tModels only
        ClassificationScheme uddiOrgType = 
            businessQueryManager.findClassificationSchemeByName(null, 
                "uddi-org:types");
        Classification wsdlSpecClassification =
                businessLifeCycleManager.createClassification(uddiOrgType,
                "wsdlSpec", "wsdlSpec");
        Collection classifications = new ArrayList();
        classifications.add(cruises);
        classifications.add(wsdlSpecClassification);
        //2. Invoke findConcepts
        BulkResponse results = businessQueryManager.findConcepts(
             null,
             null,
             classifications,
             null,
             null);
       //3. Iterate through results, find information about tModels
       Collection col = results.getCollection();
       if (col != null) {
           for (Iterator iterator = col.iterator(); iterator.hasNext();) {
               Concept concept = (Concept) iterator.next();
               //tModel's name and description
               System.out.println(concept.getDescription().getValue());
               System.out.println(concept.getName().getValue());
               //tModel key
               System.out.println(concept.getKey().getId());
               //4. Find external links, retrieve and save WSDL documents
               Collection links = concept.getExternalLinks();
               for (Iterator ito = links.iterator(); ito.hasNext();) {
                   ExternalLink externalLink = (ExternalLink) ito.next();
                   String externalURI = externalLink.getExternalURI();
                   //Retrieve the URL's content and save it into a file
                   //Code for this is not shown
                   retrieveWSDL(externalURI);
                }
            }
       }
    }
    catch (JAXRException e) {
        e.printStackTrace();
    }
}

Register your implementation

Once you've collected a series of WSDL specifications, you can decide which ones to implement. To make that decision, you may want to find out what organization registered a given tModel. That way you can focus on tModels published by well-known standards organizations or large travel agencies. You can retrieve organization-related information with concept.getSubmittingOrganization(). Once you've decided to implement a Web service specification, many Web service development tools allow you to generate Java source code from those downloaded WSDL documents. With the generated Java classes and interfaces in hand, you can build and deploy compatible Web service implementations. (For an example of generating Java source code from WSDL, and implementing and deploying a Web service with Apache Axis, please see my previous column.)

So that travel agencies can discover your service and identify it as being compatible with the CruiseService interface, you must publish your service implementation under your company's UDDI entry and ensure that your service's registration indicates compatibility with the tModel describing CruiseService. Since each tModel assumes a globally unique UDDI identifier, or tModelKey, you can use that key in your service registration to indicate tModel compatibility. In UDDI terms:

  1. Register your organization (if you haven't already registered)
  2. Publish a service for your organization
  3. Create a service binding for that service
  4. Associate the CruiseService tModel's key with that service binding

The following code shows the equivalent JAXR steps:

  void add() {
    try {
        //1. Create an organization
        Organization org =
            businessLifeCycleManager.createOrganization("Theseus Cruise Lines");
        InternationalString str =
            businessLifeCycleManager.createInternationalString(
                "The best Greek cruise line");
        org.setDescription(str);
         //Create primary contact, other organization info 
        User primaryContact = businessLifeCycleManager.createUser();
        PersonName pName = 
            businessLifeCycleManager.createPersonName("Theseus of Crete");
        primaryContact.setPersonName(pName);
        TelephoneNumber tNum = 
            businessLifeCycleManager.createTelephoneNumber();
        tNum.setNumber("800-1112-2222");
        Collection phoneNumbes = new ArrayList();
        phoneNumbes.add(tNum);
        primaryContact.setTelephoneNumbers(phoneNumbes);
        EmailAddress emailAddress =
            businessLifeCycleManager.createEmailAddress("theseus@labyrinth.gr");
        Collection emailAddresses = new ArrayList();
        emailAddresses.add(emailAddress);
        primaryContact.setEmailAddresses(emailAddresses);
        org.setPrimaryContact(primaryContact);
        //Classify your organization
        //Might also add geographic or other classifications
        ClassificationScheme sc =
           businessQueryManager.findClassificationSchemeByName(null, 
              "ntis-gov:naics");
        Classification classification =
        businessLifeCycleManager.createClassification(sc, 
           "Deep sea passenger transportation", "483112");
        Collection classifications = new ArrayList();
        classifications.add(classification);
        org.addClassifications(classifications);
        //2. Create a new service
        Service cruiseImpl = 
            businessLifeCycleManager.createService("Theseus destinations service");
        //3. Create a service binding, specify service access URI
        ServiceBinding binding = businessLifeCycleManager.createServiceBinding();
       //Substitute your own access URI where your service can be invoked
        binding.setAccessURI("http://192.168.1.10:8080/axis/servlet/AxisServlet");
        //4. Associate CruiseService tModel with service binding
        //(a) Retrieve tModel associated with key
        //Key for CruiseService tModel
        //You obtained that when searching for industry-specific tModels
        String tModelKey = "UUID:FF5041D0-F5D4-11D6-82AC-000629DC0A7B";
        RegistryObject rob = 
            businessQueryManager.getRegistryObject(
                tModelKey, 
                BusinessLifeCycleManager.CONCEPT);
        if (rob == null) {
             throw new NullPointerException("Registry object not found");
        }
        //(b) Create new specification link, associate it with tModel 
        SpecificationLink specLink = 
            businessLifeCycleManager.createSpecificationLink();
        specLink.setSpecificationObject(rob);
        //(c) Add specification link to service binding
        binding.addSpecificationLink(specLink);
        //Add service binding to service
        myService.addServiceBinding(bind);
         //Add service to organization
        org.addService(myService);
        //Save organization 
        Collection orgs = new ArrayList();
        orgs.add(org);
        BulkResponse response;
        try {
            response = businessLifeCycleManager.saveOrganizations(orgs);
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }
        //Check if everything went well, print new organization key
        Collection exceptions = response.getExceptions();
        if (exceptions == null) {
             System.out.println("Organization saved");
             Collection keys = response.getCollection();
             Iterator it = keys.iterator();
             while (it.hasNext()) {
                 Key o = (Key) it.next();
                 String id = o.getId();
                 System.out.println("Organization key is: " + id);
                }
            }
            else {
            //Something went wrong
                System.out.println("Couldn't save organization.");
                for (Iterator iterator = exceptions.iterator(); iterator.hasNext();) {
                    JAXRException e = (JAXRException) iterator.next();
                    System.out.println(e.getMessage());
                }
            }
    }
    catch (JAXRException e) {
        e.printStackTrace();
    }
}

Note that we've specified the service's access URI as part of the service binding template. Since a binding template represents a specific service instance, that URI points to the network address of your service's implementation. In this example, that's the address where the Axis SOAP servlet capable of servicing calls to CruiseService resides:

 ServiceBinding binding = businessLifeCycleManager.createServiceBinding();
binding.setAccessURI("http://192.168.1.10:8080/axis/servlet/AxisServlet");

When a client discovers a Web service instance through UDDI, that client typically caches some of the information it retrieves from the UDDI registries. One piece of information the client should cache is the service instance's access URI. With that caching, the client doesn't have to perform a new UDDI lookup before each service invocation. (This article's next section illustrates that caching.)

In programming real-life Web services, you might encounter a problem with that approach. The problem becomes apparent when a service provider moves its service implementation to a different server, possibly with a different URI. The client's cached data then becomes stale, and service invocation attempts fail. In those situations, your client discards the cached access URI and performs a new UDDI lookup.

That a new access URI is available in UDDI registries assumes, first, that the service administrator updated the service's UDDI entry and, second, that the UDDI registry you're querying appropriately synchronized with other members of the "UDDI operator cloud" in case the service provider used a different registry node. Those assumptions might be easy to satisfy when you interact with a handful of service instances. They become much harder to ensure in the presence of hundreds or even thousands of services instances. As a Web service client developer, you can realistically expect to interact with such large numbers of services; for instance, there might be hundreds to thousands of cruise companies exposing their destinations via a Web service. If a portion of those companies rely on dynamically assigned IP addresses—for example, when connecting to the Web via DSL—handling network failures becomes the major programming task you must tackle. And for your effort to work, those services must still update their UDDI entries each time they connect from a different IP address!

That problem resembles the classic challenge of programming peer-to-peer systems: In those systems, peers at the network's edges change their network identity (IP addresses) so often that centralized approaches that track those addresses—such as domain name servers (DNS)—prove ineffective. Instead, a peer must have some way to discover the network identity of other peers with which it wishes to interact without recourse to a centralized database.

Here is one UDDI approach that could provide a similar mechanism for Web services: Instead of specifying the URI of a service's location, specify an intermediate URL where you can dynamically obtain that service access point. Recall that we split our service's WSDL file into service interface and service instance components. As a Web service provider, you could place the service instance WSDL document on a Web server with a stable URL, such as your company's main Web server. You could then register that WSDL document's URL in UDDI. A client would first have to retrieve the service instance WSDL, and obtain from it the service's access URI. Since that WSDL would reside at a stable address, such as http://www.theseus-cruises.com/cruise_instance.wsdl, caching that URL would prove effective. When the client encounters an error invoking the service, it needs to only retrieve a new instance of the service's WSDL from that URL, and obtain from it a fresh service access URI. Neither the client nor the service provider would need to contact UDDI registries for that to work. The only requirement is for a service provider to update its instance WSDL located on his Web server. Figure 6 illustrates that more flexible approach.

Figure 6. A tModel's instance information points to a service's instance WSDL. Changes in a service's implementation no longer consult UDDI registries.

While registering a service's instance WSDL as an intermediate service access point affords greater resilience to network changes, version 2.0 of the UDDI specification doesn't offer a clear way to distinguish between the actual service access URI (where you can invoke the service) and that intermediary WSDL. Anne Thomas Manes, a member of the UDDI technical committee, offers this advice:

"UDDI Version 2 doesn't really provide you with a convenient mechanism that allows you to indicate that the user should retrieve the access point from the WSDL. This deficiency is fixed in version 3 with the useType=wsdlDeployment attribute.

"In Version 2, if you want to have your users retrieve the WSDL to obtain the access point, then the accessPoint must be empty. It's then up to your users to figure out which tModelInstanceInfo element provides the pointer to your implementation information. You could provide a pointer to your WSDL document from the overviewURL in one of the tModelInstanceDetails, or you could point to the tModel that represents your implementation, which would point to your implementation WSDL. The problem with either of these solutions is that your users don't know what convention you've used, and there's no way to tell them in UDDI Version 2.

"Since a businessService/bindingTemplate pair represents a particular Web service implementation, my recommendation is that you specify the accessPoint in the UDDI bindingTemplate, and then implement proper procedures that, first, reduce the frequency of changes to service invocation URLs and, second, ensure that if such changes occur, those changes automatically propagate to UDDI."

Regardless of what approach you take, you can verify the successful publishing of your organization and its Web service by visiting a UDDI registry's Webpage. Figure 7 shows the results from the IBM test registry, after the above code executes. For some reason, the IBM registry's Webpage does not display the service provided by the organization.

Figure 7. A newly registered organization in IBM's UDDI test registry. Click on thumbnail to view full-size image.

Put it all together

Now this JAXR and UDDI tour comes full circle as I show how you can discover all organizations implementing CruiseService. We will finally construct the master cruise destination list by invoking all the discovered Web services.

Earlier, when you searched for tModels representing industry-specific Web services, you discovered the CruiseService tModel's unique key. Using that key, the following code snippet discovers all organizations implementing compatible Web services. The code then navigates to each organization's services, then to each service's service bindings. Accessing the service binding data structures allows us to retrieve the URIs where we can invoke each service instance. To keep this example simple, we assume that a company publishes only one Web service URI for CruiseService:

  //Well-known tModelKey for the cruise reservation service.
String tModelKey =
    "UUID:FF5041D0-F5D4-11D6-82AC-000629DC0A7B";
//A mapping of companies to their CruiseService access URIs.
Hashtable companyAccessURIs = new Hashtable();
//This method populates the companyAccessURIs hashtable.
//It associates one access URI for a service that implements
//CruiseService for each organization offering such a service.
void doDiscovery() {
    try {
        RegistryObject regob =
            businessQueryManager.getRegistryObject(tModelKey,
                BusinessLifeCycleManager.CONCEPT);
        Collection coll = new ArrayList();
        coll.add(regob);
        //Find all organizations implementing that concept.
        BulkResponse results =
            businessQueryManager.findOrganizations(
                null,
                null,
                null,
                coll,
                null,
                null);
       Collection co = results.getCollection();
       //Nothing found, just return from this method.
       if (co == null) { 
           return;
       } 
       Iterator it = co.iterator();
       while (it.hasNext()) {
           Organization org = (Organization)it.next();
           String orgName = org.getName().getValue();
           System.out.println("Found organization: " + orgName);
           Collection cc = org.getServices();
           if (cc == null) continue;
           for (Iterator iterator = cc.iterator(); iterator.hasNext();) {
               Service service = (Service) iterator.next();
               if (service == null) continue;
               Collection cb = service.getServiceBindings();
               if (cb == null) continue;
               for (Iterator ito = cb.iterator(); ito.hasNext();) {
                   ServiceBinding serviceBinding = (ServiceBinding) ito.next();
                   if (serviceBinding != null || serviceBinding.getAccessURI() != null) {
                       System.out.println("Found service binding URI: " +
                           serviceBinding.getAccessURI());
                       companyAccessURIs.put(orgName, serviceBinding.getAccessURI());
                   }
                }
           }
       }
   } catch (JAXRException e) {
            e.printStackTrace();
   }
}

Once the above method populates the company-to-Web-service URI mapping, you can create the master cruise destinations list by invoking each Web service instance. The following example assumes you've already downloaded the CruiseService interface WSDL document and learned its service and operation names. The example uses the Apache Axis SOAP framework:

  //Name of the Web service.
String serviceName = "CruiseService";
//Operation name
String operationName = "cruiseDestinations";
//Store cruise destinations.
//Keys are the company names, and values are vectors of
//destination names.
Hashtable destinations = new Hashtable();
void invokeServices() {
    Set cos = companyAccessURIs.keySet();
    for (Iterator iterator = cos.iterator(); iterator.hasNext();) {
        String companyName = (String) iterator.next();
        String accessURL = (String) companyAccessURIs.get(companyName);
        try {
            //Set up a remote SOAP call.
            org.apache.axis.client.Service soapService = 
                new org.apache.axis.client.Service();
            org.apache.axis.client.Call call = 
                (org.apache.axis.client.Call) soapService.createCall();
             
            //Set the call's endpoint address to the Web service's access URI.
            call.setTargetEndpointAddress(new URL(accessURL));
            call.setOperationName(new QName(serviceName, operationName));
            //We're expecting a String array back.
            //Add elements of that array to the appropriate company's
            //destination list.
            String[] results = (String[]) call.invoke(new Object[0]);
            Vector dsts = new Vector();
            for (int i = 0; i < results.length; i++) {
                dsts.add(results[i]);
            }
            if (dsts.size() > 0) {
                destinations.put(companyName, dsts);
            }
        } catch (ServiceException e) {
           e.printStackTrace();
        } catch (MalformedURLException e) {
           e.printStackTrace();
        } catch (RemoteException e) {
           e.printStackTrace();
        }
    }
}

A word of caution

This article presented one of the most useful Web service interaction patterns: registering, finding, and invoking Web services based on service types. A key Web service registry benefit is the ability to associate human-oriented categories with Web service types. In registering the cruise reservation service, we marked it as belonging to the cruise industry. Web service client developers can query registries for industry-specific types and build compatible implementations. Registering those implementations also allows classification. We have seen how a content aggregator, such as a travel agency, can discover Web services that claim to be compatible with well-known Web service interface standards. While Web service registries provide an important infrastructure in automating hitherto time-consuming and error-prone tasks, and thus lessen the cost and time of system integration, the proliferation of Web services will unlikely replace the need for system administrators and integrators.

The biggest reason involves the GIGO (garbage in, garbage out) principle. Web service registries allow anyone to create user accounts and do not ensure the veracity of the information their users publish. A business could possibly claim compatibility with a well-known interface for a service it provides, only for that service to be, in fact, incompatible. And even if a service implementation is compatible, that implementation might not be highly available. Inaccurate registry data may be accidental—as in the case of buggy software—or the result of malicious intent.

Those causes highlight the importance of quality-of-service metrics for Web services. Quality-of-service encompasses a Web service's technical and human aspects. What good is it to discover that a cruise sails to Istanbul if that cruise company's Web service is down most of the time? Or if reservation messages are lost because of unreliable network connections? Or credit card numbers are compromised because that company's administrators play foul with the data transmitted to them? I will devote a future Web services column to measuring, publishing, and discovering service quality metrics, and cover techniques for ensuring Web service security and dependability. Meanwhile, happy sailing!

Frank Sommers is founder and CEO of Autospaces, a company focused on bringing Jini technology and Web services to the automotive software market. He is also editor in chief of the Newsletter of the IEEE Task Force on Cluster Computing.

Learn more about this topic

Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more