Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Web Development

UDDI & Dynamic Web Service Discovery


Feb04: UDDI & Dynamic Web Service Discovery

Peter is an engineer at Systinet. He can be contacted at [email protected].


The triad of web service Standards consists of: the Simple Object Access Protocol (SOAP), an XML syntax for exchanging messages; the Web Service Description Language (WSDL), an XML format for describing network services; and Universal Description, Discovery and Integration (UDDI), a platform-independent framework for describing services and businesses and integrating them via the Internet. Regrettably, UDDI is often given short shrift. This may be because of the seeming complexity of the Standard, its early positioning as a universal business registry, or simply because it is entirely optional. For whatever reasons, you're doing yourself a disservice in delaying UDDI deployment.

UDDI offers a number of capabilities once deployed in the enterprise—and still more when opened up to business partners and the general public. This value can be recognized at both development and runtime. During development, you can search a UDDI repository for information about available services. At runtime, client applications can use UDDI to discover all instances of a web service that conform to a given interface. And, since services registered in UDDI can be tagged with a wealth of information, client applications can narrow this discovery to just those services adhering to a particular technical fingerprint. For instance, clients may ask UDDI to return information on all services that implement, say, the StockOption interface and are accessible over SOAP and HTTP. In this article, I show how runtime discovery can be used by web service clients.

UDDI repositories can warehouse information about businesses, services, and more. How this data is physically stored is immaterial, as UDDI repositories expose it solely as XML. The UDDI specification also describes two distinct SOAP-based APIs—an Inquiry API and Publishing API. Currently, the UDDI specification is at Version 2, though Version 3 is underway (and offers many changes and enhancements). In this article, I limit my discussion to the more common UDDI Version 2.

Mapping WSDL-Based Web Services to UDDI

There are currently two Best Practice recommendations for representing WSDL-based web services in UDDI—Version 1.08 and Version 2.0. Version 2.0 is superior to 1.08, but also more complex. For simplicity, I follow the 1.08 best practice, though I encourage the use of 2.0, which is downwards compatible with the information I present here.

One of the more intriguing entities modeled in UDDI is the Technical Model or tModel, which can be created in UDDI for any piece of relevant technical information. For instance, you might create a tModel that signifies the use of HTTP Digest Authentication, and another to represent the SMTP transport. Among the more meaningful elements of a tModel are its key and its overviewURL field. The tModelKey is usually autogenerated by the UDDI implementation and is a universally unique identifier. More importantly, once a tModel key is generated, it can be considered "well known"—that is, static and publicly visible. The overviewURL field should be set to point at the actual specification that a tModel represents; see Figure 1.

A UDDI design goal (and of the Service Oriented Architecture in general) is to allow for Design-by-Contract development when using web services. In this light, organizations should first work to craft the interfaces of expected web services and make these public. Later, these services can be implemented by developers. The interfaces should be described in a WSDL document. Since this WSDL precedes any implementation, it must omit the <service> element, keeping only those elements that describe the abstract definition of a service. If the organization is UDDI oriented, this "technical model" of a web service can then be registered in UDDI as a tModel. The name of the tModel should be set to the target namespace given in the WSDL and the overviewURL should point to the WSDL itself.

When a tModel is published, it becomes possible for services that reference this tModel to be registered. Any service that does so is then known to implement that interface, and can be bound to dynamically via client code derived from the same interface. To register a service, you must first create a UDDI businessService, which is little more than a noncanonical name (meaning clients should not rely on it) and one or more UDDI bindingTemplates. It is the bindingTemplate that represents an actual runtime implementation of an interface. A bindingTemplate contains an accessPoint field, which should point to the service's URL (endpoint) and a collection of tModel keys. At least one of these tModel keys should be the key of the service's WSDL tModel. The bindingTemplate should be considered the definitive source for address information of a web service.

Because a businessService can contain multiple bindingTemplates, it is possible to instantiate the same web service multiple times on a network. This allows client applications to continue functioning even when one or more service instances fail. Furthermore, a bindingTemplate can reference more than one tModel, each tModel representing another technical aspect of the instantiated service. For instance, beyond the initial WSDL tModel, a service may reference a tModel representing the HTTP transport, while another service referencing the same WSDL tModel may reference a tModel signifying the use of SMTP as a transport. The complete collection of tModels that a bindingTemplate references are known as the "technical fingerprint" of that web service instance.

Mapping an Actual Service

To map actual web services, you need an instance of UDDI. Many vendors make UDDI implementations freely available. Windows 2003, for instance, has a UDDI server preinstalled with the OS, while many J2EE vendors build UDDI instances into their application servers. For the purpose of illustration, I use the web service client from Systinet (the company I work for). You can download the Systinet WASP UDDI server from http://www.systinet.com/products/wasp_uddi/download. Additionally, I reference services described by the SOAPBuilders group (http://www.soapbuilders.org/), an informal collection of web service vendors. Each participating vendor makes certain formally described services externally available so that toolkit interoperability can be tested. More specifically, I reference the service known as SOAPBuilders, Round 3, Group D, InteropTestDocLitParameters, which is described by a WSDL at http://www.whitemesa.com/r3/InteropTestDocLitParameters.wsdl. There are other copies of this WSDL floating around the Web, but this is the definitive instance and the one I register with UDDI.

According to the Version 1.08 best practices, after being registered, a tModel should exist in your instance of UDDI that looks something like Figure 2. Listing One is the actual XML. The tModelKey in Figure 2 was generated by my instance of UDDI; you will have a different key. Regardless of its value, what's important is that this key can and should be known outside of UDDI, and therefore must remain static from this point forward. Client applications that wish to discover services that implement this WSDL will query UDDI for services that reference this tModel. Also note that the overviewURL field points at the definitive WSDL.

With this tModel in place, you need services that implement it. Thanks to SOAPBuilders, there are many implementations publicly available.

Here, I reference an implementation from Systinet (http://soap.systinet.net/ws/InteropTestDocLitParametersService/) and one from Sun (http://soapinterop.java.sun.com:80/round3/groupd/doclitparams/). The service itself is simple, consisting of several "echo" operations.

Finally, I pretend that both implementations are instantiated by a single business—Dynavoke. Therefore, once registered with UDDI, these two implementations of a single service look something like Figure 3 (Listing Two is the complete XML representation).

Dynamic Discovery of Web Services

If there were no UDDI servers in place, you might implement a Java client (like Listing Three) to this service. (For clarity, I've removed all error handling from the sample code.) Listing Three was developed using Systinet's Dynamic Proxy API, while the WSDL2Java utility (also Systinet's) was run against the SOAPBuilder WSDL to automatically generate the service interface and classes that make up the operation's parameters and response. The local web service proxy is created at runtime based on the contents of the WSDL. By default, Systinet binds to the service address specified in the WSDL. The input/output messages are defined as strings inside structures.

Although it works, this service is naive, relying on continued uninterrupted access to the WSDL and that the WSDL's service entry always points to a running implementation. Furthermore, the code is unable to avail itself of multiple implementations of this interface—everything is static and chosen at development time.

A more useful approach would be to delay the decision of which service to bind to until runtime by querying UDDI for the access points of services that are known to implement this WSDL. Again, UDDI defines an Inquiry API—a SOAP-based interface for querying a UDDI server. All UDDI servers must implement this API. Most vendors wrap this API in simpler language-specific APIs. The standard API put forth by the JCP is JAXR, an API that's registry independent and currently implemented for both UDDI and ebXML (an abstraction that makes it somewhat unwieldy). There are also dedicated APIs that map directly to the UDDI Inquiry API. IBM and Systinet provide one for Java, and Microsoft makes a .NET version. (Systinet also provides a C++ version.) The code I present here uses the Systinet Java API.

Listing Four introduces a new (overly simplistic) class, UddiRegistry. When instantiated, this class creates a reference to a UDDI server. It contains one method, lookup(), that queries UDDI for the binding templates of services that implement the WSDL associated with a particular tModel key, and returns a local proxy of that service.

The Systinet UDDI API programming model I use here takes advantage of the UDDI Proxy interface, letting you instantiate a local reference to a UDDI server and associate it with various user profiles. It also maps the Standard UDDI SOAP Inquiry API to a set of convenient methods. Though the proxy model does not map directly to the SOAP Inquiry API, it does generate standard SOAP messages.

In the simplistic constructor of the UddiRegistry class, the UDDI proxy is bound to the passed in inquiry port of a UDDI server. Of course, this design simply replaces one single point of failure—the WSDL URL—with another. A superior design would be to store two or more UDDI URLs in, say, a properties file, and attempt to bind to each until successful, in much the same way as client-side DNS is commonly configured.

The lookup() method takes two arguments—the tModel key of the WSDL interface in question, and a Class object that is the service's interface. This last argument is an artifact of the dynamic proxy model used in Systinet's SOAP API. There are other Java SOAP client implementations that don't work this way, such as JAX-RPC and JAXM, and a proper design would be to overload the lookup() method accordingly. The return value is given as an Object; at runtime, this is the service proxy object of the type specified in the method's second parameter.

UDDI best practice recommends that you persistently cache the results of some previous query. You should first try to bind to this cached access point and save the expense of a UDDI query. Only if this attempt fails should you query UDDI for another access point, which should then replace the previous one in cache. (None of this is presented in the sample code.)

Ultimately, the code needs to do two things—retrieve a WSDL from UDDI to create the service proxy (this is unique to Systinet's API), and retrieve the access points of all services that have registered support for the given tModel. So the first thing the lookup() method does is retrieve the contents of the overviewURL field of the tModel passed in. According to best practices, the overviewURL field contains the URL of the WSDL.

A quirk of the UDDI Version 2 Inquiry API is that to retrieve the binding templates associated with particular tModels, you must pass in both a tModel key and a service key; the service key being the unique identifier of the service containing the binding templates. This is unfortunate because the technical fingerprint (collection of tModels) and categorization data is the only information that a runtime client should depend on. The good news is that UDDI Version 2 supports a version of find_service that can take one or more tModels as an argument and return the keys of all services that have binding templates associated with these tModels. This limitation has been removed in the Version 3 API and certain vendor implementations.

Therefore, the next thing that the code does is call find_service(), passing in the WSDL's tModel. All of the parameters that are given as null represent other means of searching for services, for instance by name or by business. They also represent find qualifiers, such as the maximum number of rows to return. For this example, they are unimportant.

The call to find_service returns an array of matching services. Again, a simplification of the code is to tacitly ignore all but the first element. The service key contained in this element and the tModel passed in to lookup() are then given to the find_binding() method. This method returns the binding templates associated with the specified service that reference the specified tModel.

For each of the binding templates returned, the code retrieves the access point (that is, the URL of the service) and attempts to bind to it. The binding shown here is somewhat different than the one in the original web service client. As I now want to specify the service's address rather than use the one that may be (but shouldn't be) specified in the WSDL, I need to create a ServiceClient object. In the original version, this was created for me in the Registry.lookup() call; here I create it manually. This object lets me set not just the WSDL address, but the service address, connection timeout setting, and more. After creating a ServiceClient with an associated WSDL, the code loops through each of the binding templates retrieved. For each one, it sets the service's access point, then attempts to create a proxy. If the attempt fails (for instance, createProxy() throws a LookupException), the loop tries the next one. If they all fail, a LookupException is thrown; otherwise a proxy object is returned.

The main code has been slightly modified (see Listing Five) and instantiates the new UddiRegistry class, calls its lookup() method, and passes in the tModelKey (and not the URL) of the WSDL interface. After that, it is exactly the same. When this client is run, the output should be similar to Listing Six.

The UddiRegistry package can be improved in many ways, such as adding the caching component and the overloaded lookup() methods. Furthermore, it should allow for more than one tModel to be passed in so that a query can be done on the complete technical fingerprint and not just the WSDL interface. However, this sample code should be enough for you to see the usefulness of the UDDI Standard and begin to investigate how it fits into your environment.

DDJ

Listing One

<tModel authorizedName="..." operator="..." 
 tModelKey="uuid:c01dd3c0-f83e-11d7-bbaa-b8a03c50a862">
    <name>http://soapinterop.org/WSDLInteropTestDocLit</name>
    <description xml:lang="en">
        SOAPBuilders, Round 3, Group D, InteropTestDocLitParameters
    </description>
    <overviewDoc>
        <overviewURL>
            http://www.whitemesa.com/r3/InteropTestDocLitParameters.wsdl
        </overviewURL>
    </overviewDoc>
    <categoryBag>
        <keyedReference keyName="Specification for a Web Service 
                                                          described in WSDL"
         keyValue="wsdlSpec" 
                    tModelKey= "uuid:c1acf26d-9672-4404-9d70-39b756e62ab4"/>
    </categoryBag>
</tModel>

Back to Article

Listing Two

<businessEntity authorizedName="..." 
  businessKey="fe3f5b90-f831-11d7-bba8-b8a03c50a862" operator="...">
    <discoveryURLs>
        <discoveryURL useType="businessEntity">
            http://uddiservername/uddi/web?businessKey=
                                         fe3f5b90-f831-11d7-bba8-b8a03c50a862
        </discoveryURL>
    </discoveryURLs>
    <name xml:lang="en">Dynavoke</name>
    <description xml:lang="en">Doctor Dobbs Demo Company</description>
        <businessServices>
            <businessService authorizedName="..." 
              businessKey="fe3f5b90-f831-11d7-bba8-b8a03c50a862" 
              serviceKey="3d21f530-f840-11d7-bbaa-b8a03c50a862">
            <name xml:lang="en">InteropTestDocLitParameters</name>
            <bindingTemplates>
                <bindingTemplate authorizedName="..." 
                  bindingKey="05e3b210-f841-11d7-bbaa-b8a03c50a862" 
                  serviceKey="3d21f530-f840-11d7-bbaa-b8a03c50a862">
                    <description xml:lang="en">Sun access point</description>
                    <accessPoint URLType="http">
                      http://soapinterop.java.sun.com:
                                             80/round3/groupd/doclitparams
                    </accessPoint>
                    <tModelInstanceDetails>
                        <tModelInstanceInfo 
                          tModelKey=
                              "uuid:c01dd3c0-f83e-11d7-bbaa-b8a03c50a862">
                        <instanceDetails>
                            <overviewDoc>
                                <overviewURL/>
                            </overviewDoc>
                        </instanceDetails>
                        </tModelInstanceInfo>
                    </tModelInstanceDetails>
                </bindingTemplate>
                <bindingTemplate authorizedName="..." 
                  bindingKey="b6c83b10-f840-11d7-bbaa-b8a03c50a862" 
                  serviceKey="3d21f530-f840-11d7-bbaa-b8a03c50a862">
                    <description xml:lang=
                                    "en">Systinet access point</description>
       <accessPoint URLType="http">
                      http://soap.systinet.net/ws/
                                         InteropTestDocLitParametersService/
                    </accessPoint>
                    <tModelInstanceDetails>
                        <tModelInstanceInfo 
                          tModelKey="uuid:c01dd3c0-f83e-11d7-
                                                         bbaa-b8a03c50a862">
                            <instanceDetails>
                                <overviewDoc>
                                    <overviewURL/>
                                </overviewDoc>
                            </instanceDetails>
                        </tModelInstanceInfo>
                    </tModelInstanceDetails>
                </bindingTemplate>
            </bindingTemplates>
        </businessService>
    </businessServices>
</businessEntity>

Back to Article

Listing Three

package com.systinet.uddi.bindingDemo;
import org.systinet.wasp.webservice.Registry;
import com.systinet.uddi.bindingDemo.iface.WSDLInteropTestDocLitPortType;
import com.systinet.uddi.bindingDemo.iface.struct.EchoString;
import com.systinet.uddi.bindingDemo.iface.struct.EchoStringResponse;

public class SBInteropDocLit extends Object {
    public static void main (String args[]) throws Exception {
        // Hard coding address of WSDL.  Relying on WSDL <service> 
        // element for service address
    String wsdlURI = "http://soap.systinet.net/ws/
                                   InteropTestDocLitParametersService/wsdl";
        // Create local proxy of the remote service        
        WSDLInteropTestDocLitPortType WSDLInteropService = 
          (WSDLInteropTestDocLitPortType) Registry.lookup(wsdlURI, 
          WSDLInteropTestDocLitPortType.class);
        // Create class to send to service
        EchoString stringParam = new EchoString();
        stringParam.param0 = "UDDI";
        // Call service
        System.out.println("Sending string \"" + stringParam.param0 + "\"");
        EchoStringResponse stringResponse = 
                                  WSDLInteropService.echoString(stringParam);
        System.out.println("Received \"" + 
                                   stringResponse._return_ + "\" in return");
    }
}

Back to Article

Listing Four

package com.systinet.uddi.bindingDemo;
import org.idoox.uddi.UDDIException;
import org.idoox.uddi.client.api.v2.UDDIProxy;
import org.idoox.uddi.client.api.v2.response.ServiceInfo;
import org.idoox.uddi.client.structure.v2.binding.BindingTemplate;
import org.idoox.uddi.client.structure.v2.service.ServiceKey;
import org.idoox.uddi.client.structure.v2.tmodel.TModelKey;

import org.systinet.wasp.webservice.ServiceClient;
import org.systinet.wasp.webservice.Registry;
import org.systinet.wasp.webservice.LookupException;

public class UddiRegistry {
    UDDIProxy uddiProxy;
    public UddiRegistry(String inquiryPort) {
       // Instantiate a local proxy of the UDDI server
       uddiProxy = new UDDIProxy(inquiryPort);
    }
    public Object lookup (TModelKey tModelKey, Class proxyInterface) 
      throws UDDIException, LookupException {
        // Ideally, the access point retrieved in some previous search should 
        // be cached and indexed by the tModelKey. You should try to bind to 
        // this cached instance first to avoid the expense of a UDDI lookup. 
        // This step is skipped here
        // Get WSDL from tModel
        String wsdlURI = 
          uddiProxy.get_tModelOverviewURL(tModelKey.getValue()).getValue();   
        System.out.println("Found Service 
                                    Definition (WSDL) URI at: " + wsdlURI);
        // Get all services that have binding templates associated with tModel
        ServiceInfo[] serviceInfoArray = 
         uddiProxy.find_service(null, null, null, tModelKey.getValue(), null);
        String serviceKey = serviceInfoArray[0].getServiceKey().getValue();
        System.out.println("Found Service Key: " + serviceKey);
        // Get all binding templates associated with service and tModel
        BindingTemplate[] bindingTemplateArray = 
          uddiProxy.find_binding(serviceKey, tModelKey.getValue(), null);
        System.out.println("Found " + bindingTemplateArray.length + 
                                                        " implementations");
        ServiceClient serviceClient = ServiceClient.create(wsdlURI);
        // for each binding template, get the access point and try to bind
        for (int i=0; i < bindingTemplateArray.length; i++) {
            String accessPoint = 
                         bindingTemplateArray[i].getAccessPoint().getValue();
            serviceClient.setServiceURL(accessPoint);
            System.out.println("Attempting to bind to access 
                                                   point at " + accessPoint);
            try {
                Object ServiceProxy = 
                                   serviceClient.createProxy(proxyInterface);
                System.out.println("Success");
                return ServiceProxy;
            } catch (LookupException le) {
                System.out.println("Unable to bind to 
                                                 service at " + accessPoint);
            }   
        }
        throw new LookupException();
    }
}

Back to Article

Listing Five

package com.systinet.uddi.bindingDemo;
import com.systinet.uddi.bindingDemo.iface.WSDLInteropTestDocLitPortType;
import com.systinet.uddi.bindingDemo.iface.struct.EchoString;
import com.systinet.uddi.bindingDemo.iface.struct.EchoStringResponse;

import org.idoox.uddi.client.structure.v2.tmodel.TModelKey;
public class DynamicSBInteropDocLit {
    public static void main(String args[]) throws Exception {
        // Well known tModel Key of the InteropTestDocLitParameters WSDL
        TModelKey tModelKey = 
                new TModelKey("uuid:c01dd3c0-f83e-11d7-bbaa-b8a03c50a862");
        // Instantiate a new UddiRegistry bound to the inquiry portof a local 
        // WASP UDDI server
        UddiRegistry uddiRegistry = 
                      new UddiRegistry("http://localhost:8080/uddi/inquiry");
        // Retrieve a service proxy for one of the possibly many 
        // services that implement the InteropTestDocLitParameters interface
        WSDLInteropTestDocLitPortType WSDLInteropService = 
          (WSDLInteropTestDocLitPortType) uddiRegistry.lookup(tModelKey, 
          WSDLInteropTestDocLitPortType.class);
        // Create class to send to service
        EchoString stringParam = new EchoString();
        stringParam.param0 = "UDDI";
        // Call service
        System.out.println("\nSending string \"" + stringParam.param0 +"\"");
        EchoStringResponse stringResponse = 
                                  WSDLInteropService.echoString(stringParam);
        System.out.println("Received \"" + 
                                   stringResponse._return_ + "\" in return");
    }
}

Back to Article

Listing Six

Found Service Definition (WSDL) URI at: 
http://www.whitemesa.com/r3/InteropTestD
ocLitParameters.wsdl
Found Service Key: 3d21f530-f840-11d7-bbaa-b8a03c50a862
Found 2 implementations
Attempting to bind to access point at 
http://soap.systinet.net/ws/InteropTestDoc
LitParametersService/
Success

Sending string "UDDI"
Received "UDDI" in return


Back to Article


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.