Java & UDDI Registries

Dr. Dobb's Journal September 2002

Yes, you can access web services with Java

By Paul Tremblett

Paul is a software developer with AudioAudit and author of Instant Wireless Java With J2ME (McGraw-Hill, 2002). Paul can be reached at ptremblett@ earthlink.net.

Applications that require web services send a request to a service at an advertised URL. The service processes the request and returns the result. Applications obtain information about how to contact a service (along with other useful data) from business registries such as the Universal Description, Discovery, and Integration (UDDI) project (http://www .uddi.org/), a platform-independent, open framework for describing services and businesses and integrating them via the Internet. Currently, more than 200 companies support UDDI. In this article, I'll show how Java applications contact business registries such as UDDI and retrieve information from them. A WAR file containing the components necessary to run the examples presented here is available electronically; see "Resource Center," page 5.

The Contents of a UDDI Registry

The UDDI registry, which is publicly accessible to any business or individual, contains the data you need to answer "who, what, where, and how" questions before engaging in business transactions. This includes:

Information from a UDDI registry is contained in the four data structure types in Figure 1. As you can see, at the top of the hierarchy is the businessEntity structure. This structure is identified by a Universally Unique ID (UUID), a 128-bit identifier generated by an algorithm designed to prevent duplication. A typical UUID might be "51A5B3B0-32BD-11D6-83CD-000C0E00ACDD." In addition to holding information such as the name of the business and optional elements like a description of the business and contact information, the businessEntity structure serves as a container for the other three structures. A businessEntity structure can contain zero or more businessServices structures. Each of these structures is identified by a UUID and contains a human-readable name for a family of services, zero or more optional language-qualified text descriptions of the service family, and a bindingTemplate structure. From a bindingTemplate, you can get a technical description of a service, a technical entry point, and technical characteristics of a given implementation. A bindingTemplate also contains a number of tModels. A tModel can be used to represent a technical specification such as a wireless protocol, an interchange format, or interchange sequencing rules.

The UDDI API

A UDDI registry is intended to be accessed programatically via an API based on XML (the complete API is available at http:// www.uddi.org/). The XML messages that comprise the API are transmitted in Simple Object Access Protocol (SOAP) envelopes using HTTP.

Figure 2 is a typical message that represents the find_business API and is the message you would send to a UDDI registry to get a list of all business that start with the string "Section VIII." Responses are also SOAP messages. Figure 3 is the list returned by the registry server in response to the find_business request.

As Table 1 shows, the APIs are divided into three categories: general browsing, drill down, and publishing. Here, I focus only on the find_business API.

Querying a Registry Using JAXM

To illustrate how to send a query to a registry server, I'll use Java to send a query that originates from a browser.

The JavaServer page QueryRegistryV1.jsp (available electronically) generates HTML code that produces the display in Figure 4. When users click on one of the two buttons, a request containing values of the parameters queryString, disposition, and depending on which button is clicked, either submitPredefined/predefinedURL or submitUserSpecified/userSpecifiedURL are sent to RegistryServerV1. You can see from web.xml (available electronically) that this URL pattern maps to com.ddj.uddi.RegistryServerV1 (available electronically). After extracting the value of queryString and assigning a value to the local variable url, the servlet invokes the findBusiness() method passing these two variables as arguments. Listing One is the code for findBusiness.

This method introduces the Java API for XML Messaging (JAXM). I start by invoking the static method newInstance() of class MessageFactory to create a SOAPFactory object. This is the simplest way to create a MessageFactory, which is usually used by a standalone application and is adequate for the example I present here. A more usual approach is to obtain a connection to a messaging provider and invoke the ProviderConnection object's createMessageFactory() method. Next, I create a SOAPMessage by invoking the MessageFactory object's createMessage() method. The SOAPBody element in which the payload will be stored is contained in the SOAPEnvelope. I get the envelope by invoking the getEnvelope() method of the SOAPPart object returned from the SOAPMessage object's getSOAPPart() method. After saving the envelope for later use, I use the getSOAPBody() method to get the contents of the SOAPBody and construct a query within this body by:

After the message has been constructed, I invoke the saveChanges() method to update the message with all the changes that have been made. Even though this message is called automatically when the message is sent, it's a good idea to call it whenever you make changes to a message. A little redundancy is better than a lot of debugging. The message I just created looks like that in Figure 2, except for the contents of the query string.

The next step is to send the message. There are two ways to do this. The first uses the ProviderConnection object and the second method uses a SOAPConnection, which is a point-to-point connection a client uses for sending messages directly to a remote party without using a message provider. Since I already said I would not be using a message provider, I'll use the second method. I start by invoking the static method newInstance() of the SOAPConnection class. This method returns a SOAPConnection object. Next, I create the endpoint to which the SOAPConnection object connects by passing the value in the variable url to the constructor of class URLEndpoint. Finally, I transmit the message by passing the SOAPMessage and URLEndpoint to the SOAPConnection object's call() method. Messages sent using the call() method follow the request/response paradigm — the method blocks until it receives a reply. Normally, you'd make sure that a blocking call is in a separate thread, but I wanted to keep this example simple. The findBusiness() method returns the SOAPMessage returned by the call() method.

The way in which the SOAPMessage returned from the findBusiness() method is sent back to the browser depends on the value of the disposition parameter. This value depends on which radio button users click. If users click the radio button labeled "XML," the servlet sets the content type of the response being sent to the client to "text/xml" and calls the SOAPMessage object's writeTo() method to write the contents of the SOAPMessage to the servlet output stream. The results are similar to Figure 3, but since I searched for businesses starting with "ibm" and specified the IBM Test Registry, the result contains a number of businessEntity structures.

If users click the radio button labeled "HTML," the servlet passes the message returned from findBusiness(), an .xsl file (such as List1.xsl, available electronically), and the servlet response object to the method transformMessage(); see Listing Two.

The actual transformation is done by an instance of a Transformer object, which can be found in the package javax.xml.transform. Since I will be using a PipedOutputStream to deliver the XML to the Transformer object, and since having both ends of a pipe in the same thread can result in deadlock, I create a separate thread using an instance of TransformerThread, which implements Runnable. Listing Three presents the run() method.

I do the transformation this way:

  1. Create a PipedOutputStream to deliver the XML to the thread.
  2. Create a PipedInputStream to receive HTML back from the thread.
  3. Create the thread and start it running.
  4. Write the XML to the PipedOutputStream.
  5. Close the PipedOutputStream.
  6. Read data from the PipedInputStream a byte at a time and write it to the servlet's output stream.

The data read from the PipedInputStream is written in the thread, in which I do the following actions:

  1. Get a DocumentBuilder using the static method newInstance() of class DocumentBuilderFactory.
  2. Create an instance of org.w3c.dom.Document by parsing the data received via the pipe.
  3. Create a DOMSource from the Document object so that the transformation source can be in the form of a DOM tree.
  4. Create a Transformer object by passing the .xsl file to an instance of TransformerFactory.
  5. Pass the DOMSource and an output stream to the transform() method of the Transformer object (the output stream is a pipe with the other end in the findBusiness() method).

Figure 5 depicts what the browser looks like when the HTML resulting from the transformation is displayed.

Making Things Easier Using JAXR

Even though the first example represented an interesting way to learn about SOAP messages and provided a peek at JAXM and XSLT, it does have problems. For instance, the preparation of the SOAP message for each of the UDDI APIs requires a thorough knowledge of the structure being passed to the API. Second, the program is UDDI specific and UDDI is not the only registry specification. There are others, such as the ebXML Registry and Repository standard, which is being developed by the Organization for the Advancement of Structured Information Standards (OASIS) and the United Nations Centre for the Facilitation of Procedures and Practices in Administration, Commerce, and Transport (U.N./CEFACT). The program presented here could not be used to retrieve data from such registries.

Both of these problems are addressed by the Java API for XML Registries (JAXR), a single, easy-to-use abstraction API that lets you develop portable code that can access a variety of registries. The servlet RegistyServerV2 (available electronically) uses JAXR to query a registry. It is identical to RegistryServerV1 except for the findBusiness() method, which looks like Listing Four.

In the findBusiness() method, I create a ConnectionFactory object and use its createConnection() method to create a Connection object. Next, I invoke the Connection object's getRegistryService() method to get the RegistryService interface associated with the Connection. I then call the RegistryService interface's getBusinessQueryManager() method, which returns the BusinessQueryManager interface implemented by the JAXR provider. BusinessQueryManager is the interface exposed by the RegistryService that implements the business-style query interface. BusinessQueryManager is referred to as a focused query interface. The actual query is performed by passing the Collection objects in Table 2 to the findOrganizations() method, which returns a BulkResponse object.

The BulkResponse object returned by findOrganizations() is not XML but rather a Collection of objects, so it can't be simply sent to the browser or transformed. To address this issue, I use the convertToSOAPMessage() method. As its name implies, it converts the BulkResponse to a SOAP message. It does this by obtaining the Collection of objects from the BulkResponse, iterating across the Collection, and using the JAXM API to create a SOAP message whose body contains elements representing the objects in the Collection. The convertToSOAPMessage() method looks like Listing Five.

I dispose of the SOAPMessage returned by convertToSOAPMessage in the same manner as in the first example. If I simply send it back to the browser, it looks like Figure 6. If I transform it using Business2.xsl (available electronically), it looks like Figure 7.

Conclusion

The tools needed to query a UDDI registry are available and easy to use, and the JAXM API is suitable for preparing SOAP messages. Since the SOAP messages are XML, the JAXP API can be used to parse them. JXSLT provides a way to transform XML to HTML, making Java a good choice when you are using web services.

DDJ

(Listings begin on page 40)

Listing One

private SOAPMessage findBusiness(String url, String queryString)
      throws Exception {
    MessageFactory msgFactory = MessageFactory.newInstance();
    SOAPMessage msg = msgFactory.createMessage();
    SOAPEnvelope envelope = msg.getSOAPPart().getEnvelope();
    SOAPBody body = envelope.getBody();
    SOAPElement findBusiness = body.addBodyElement(
        envelope.createName("find_business",
        "", "urn:uddi-org:api"));
    findBusiness.addAttribute(
        envelope.createName("generic"), "1.0");
    findBusiness.addAttribute(
        envelope.createName("maxRows"), "100");
    SOAPElement businessName =
        findBusiness.addChildElement(
        envelope.createName("name"));
    businessName.addTextNode(queryString);
    msg.saveChanges();
            SOAPConnectionFactory scf = 
        SOAPConnectionFactory.newInstance();
    SOAPConnection connection = scf.createConnection();
    URLEndpoint endpoint = new URLEndpoint(url);
    SOAPMessage reply = connection.call(msg, endpoint);
    return reply;
}

Back to Article

Listing Two

private void transformMessage(SOAPMessage msg, String xslSource,
            HttpServletResponse response)
        throws Exception {
    response.setContentType("text/html");
    ServletOutputStream out = response.getOutputStream();
    ServletContext context = getServletConfig().
        getServletContext();
    String filePath = context.getRealPath(xslSource);
    PipedOutputStream pipeToThread = new PipedOutputStream();
    PipedInputStream pipeFromThread = new PipedInputStream();
    TransformerThread transformer =
    new TransformerThread(filePath, pipeToThread, pipeFromThread);
    new Thread(transformer).start();
    msg.writeTo(pipeToThread);
    pipeToThread.flush();
    pipeToThread.close();
    int b;
    while ((b = pipeFromThread.read()) >= 0) {
        out.print((char)b);
    }
    pipeFromThread.close();
    out.flush();
    out.close();
}

Back to Article

Listing Three

public void run() {
    try {
        factory = DocumentBuilderFactory.newInstance();
        builder = factory.newDocumentBuilder();
        document = builder.parse(pipeFromMain);
        DOMSource source = new DOMSource(document);
        TransformerFactory tFactory = 
            TransformerFactory.newInstance();
        Transformer transformer = 
            tFactory.newTransformer(xslSource);
        StreamResult result = new StreamResult(pipeToMain);
        transformer.transform(source, result);
        pipeToMain.flush();
        pipeToMain.close();
    }
    catch (Exception e) {
        e.printStackTrace();
    }
}

Back to Article

Listing Four

private SOAPMessage findBusiness(String url, String queryString)
        throws Exception {
    BulkResponse response = null;
    Connection connection = null;
    SOAPMessage msg = null;
    Properties props = new Properties();
    props.setProperty("javax.xml.registry.queryManagerURL",
        url);
    props.setProperty("javax.xml.registry.factoryClass",
        "com.sun.xml.registry.uddi.ConnectionFactoryImpl");
    try {
        ConnectionFactory factory =
        ConnectionFactory.newInstance();
        factory.setProperties(props);
        connection = factory.createConnection();
        RegistryService rs = connection.getRegistryService();
        BusinessQueryManager bqm =
        rs.getBusinessQueryManager();
        Collection findQualifiers = new ArrayList();
        findQualifiers.add(FindQualifier.SORT_BY_NAME_DESC);
        Collection namePatterns = new ArrayList();
        namePatterns.add(queryString + "%");
        
        response = bqm.findOrganizations(findQualifiers,
        namePatterns, null, null, null, null);
        msg = convertToSOAPMessage(response);
    }
    catch (Exception e) {
        e.printStackTrace();
    } finally  {
        if (connection != null) {
            try {
                connection.close();
            } catch (JAXRException je) {}
        }
        return msg;
    }
}

Back to Article

Listing Five

private SOAPMessage convertToSOAPMessage(BulkResponse response) 
    throws Exception {
  MessageFactory factory = MessageFactory.newInstance();
  SOAPMessage msg = factory.createMessage();
  SOAPEnvelope envelope = msg.getSOAPPart().getEnvelope();
  SOAPBody body = envelope.getBody();
  Collection orgs = response.getCollection();
  Iterator orgIter = orgs.iterator();
  while (orgIter.hasNext()) {
      Organization org = (Organization) orgIter.next();
      SOAPBodyElement orgElement = body.addBodyElement(
          envelope.createName("Organization"));
      SOAPElement orgName = orgElement.addChildElement(
          envelope.createName("name"));
      orgName.addTextNode(getName(org));
      SOAPElement orgKey = orgElement.addChildElement(
          envelope.createName("key"));
      orgKey.addTextNode(getKey(org));
      SOAPElement orgDesc = orgElement.addChildElement(
          envelope.createName("description"));
      orgDesc.addTextNode(getDescription(org));
  }
  msg.saveChanges();
  return msg;
}

Back to Article