Two APIs are described by the UDDI specification: the inquiry API and the Publishing API. They are accessed using the same techniques but use different XML documents, data structures, and access points. The inquiry API locates information about a business, the services a business offers, the specifications of those services, and information about what to do in a failure situation. Any read operation from a UDDI registry uses one of the inquiry API’s messages. The inquiry API does not require authenticated access and is subsequently accessed using HTTP.
The Publishing API is used to create, store, or update information located in a UDDI registry. All functions in this API require authenticated access to a UDDI registry; the UDDI registry must have a logon identity, and the security credentials for this identity must be passed as a parameter of the XML document for each UDDI invocation. Because publishing requires authenticated access, it is accessed over HTTPS, with a different URL than the one used with the inquiry access point. Table 6-1 lists the inquiry and publishing access point URLs for some major operator nodes.
Table 6-1. Access point URLs for some operator nodes
Operator node |
Inquiry URL |
Publishing URL |
---|---|---|
HP |
http://uddi.hp.com/inquire |
https://uddi.hp.com/publish |
IBM Production |
http://www-3.ibm.com/services/uddi/inquiryapi |
https://www-3.ibm.com/services/uddi/protect/publishapi |
IBM Test |
http://www-3.ibm.com/services/uddi/testregistry/inquiryapi |
https://www-3.ibm.com/services/uddi/testregistry/protect/publishapi |
Microsoft Production |
http://uddi.microsoft.com/inquire |
https://uddi.microsoft.com/publish |
MicrosoftTest |
http://test.uddi.microsoft.com/inquire |
https://test.uddi.microsoft.com/publish |
SAP Test |
http://udditest.sap.com/UDDI/api/inquiry/ |
https://udditest.sap.com/UDDI/api/publish/ |
Systinet |
http://www.systinet.com/wasp/uddi/inquiry/ |
https://www.systinet.com/wasp/uddi/publishing/ |
Several primary information types construct the XML documents used as input and output to UDDI invocations. This section shows these data structures along with the major APIs as defined by the UDDI specifications.
UDDI APIs are designed to be simple. All operations that a UDDI registry performs are synchronous, meaning that the requesting client blocks and waits until it receives a response message. Additionally, all operations have a simple request/response mechanism that gives them a stateless behavior. Therefore, using the UDDI APIs doesn’t require a lot of complex ordering.
To understand the structure of the messages that are part of the API, you need a basic appreciation for the different data structures and XML formats that are used. This section discusses the major data structures that are passed as input and output parameters for major API messages. Figure 6-3 shows the relationships between the primary UDDI data structures.
A <businessEntity>
structure represents a
business’s basic information. This information
includes contact information, categorization, identifiers,
descriptions, and relationships to other businesses. UDDI allows
companies to establish relationships with one another. Many different
types of relationships are possible. For example, a conglomerate can
reference a subsidiary, or two companies can declare a partnership.
In either case, each company must establish a unique
<businessEntity>
and separately establish
its relationships to other companies that have their own
<businessEntity>
structures.
The <publisherAssertion>
structure is used to establish public
relationships between two <businessEntity>
structures. A relationship between two
<businessEntity>
structures is visible only
to the “public” when both companies
have created the same assertion with two separate
<publisherAssertion>
documents
independently. Thus, a company can claim a business relationship only
if its partner asserts the same relationship. One
company’s assertion about a business relationship
isn’t visible to the public until its partner
creates a similar, but separate,
<publisherAssertion>
document for its own
<businessEntity>
structure. Thus, if Company
A asserts a relationship with Company B (fromKey=A, toKey=B), then
the relationship will become public when Company B asserts a
relationship with Company A (fromKey=B, toKey=A).
A <businessEntity>
contains one or more
<businessService>
structures. A
<businessService>
represents a single,
logical service classification. A
<businessService>
element is used to
describe a set of services provided by the business. These services
can be web services or manual services such as a nonelectronic
service. A <businessService>
document is
reusable (i.e., a <businessService>
element
can be used by several <businessEntity>
elements). For example, GE might create an HR web service and publish
that service as part of an “HR web
service” <businessService>
structure. Additionally, GE might choose to list each of its
subsidiaries as a separate <businessEntity>
,
since each subsidiary has its own IT infrastructure. Doing so would
allow the <businessEntity>
structure for the
Plastics division to reference the same “HR web
service” <businessService>
as the Chemicals division.
A <businessService>
contains one or more
<bindingTemplate>
structures. A
<bindingTemplate>
contains pointers to
technical descriptions and the access point URL, but does not contain
the details of the service’s specifications. A
<bindingTemplate>
contains an optional text
description of the web service, the URL of its access point, and a
reference to one or more
<tModel>
structures. A <tModel>
is an abstract
description of a particular specification or behavior to which the
web service adheres. A <tModel>
is a type of
digital “fingerprint” for
determining the specifics of how to interact with a particular web
service. The <tModel>
structure does not
provide the web service’s specification directly.
Instead, it contains pointers to the locations of the actual
specifications. Companies can use the information pointed to by a
<tModel>
to determine whether a web service
is compatible with their business requirements.
A series of messages allow a program to retrieve basic information
about a business, a web service, or metadata about a specification
that a web service supports. These messages all have
SOAP messages whose
XML body element begins with
find
. Table 6-2 lists the
messages that can be used to retrieve basic information for searching
purposes. The “Message name” column
lists the name of the XML root element used as the body of the SOAP
envelope on the call’s request portion. The
“Response document” column shows
the name of the XML root element that is the body of the SOAP
envelope for the response.
Table 6-2. XML documents used in browsing inquiry messages
Message name |
Response document |
Brief description |
---|---|---|
|
Given a UUID to a | |
|
Given a regular expression, business category, business identifier,
or | |
|
Given the UUID of a | |
|
Given the UUID of a | |
|
Given the a name, a category, or identifier, this message returns all
matching |
The UDDI Programmer’s API and UDDI Schema documents identify dozens of different structures used to make up the request and response messages. The Programmer’s API identifies the structure of the request and response messages, paying particular attention to the input parameters for every request message. The UDDI Schema represents the same data structures, but provides datatyping and constraint information that can’t be conveyed in the Programmer’s API. When doing any development with UDDI, you should keep a copy of these two documents.
Traversing UDDI data structures can be complicated. To demonstrate
this complexity, let’s delve into the inner workings
of the <find_business>
message. The
<find_business>
message returns a
<businessList>
structure. Here’s
the definition of <businessList>
from the
UDDI Schema:
<element name="businessList" type="uddi:businessList" /> <complexType name="businessList"> <sequence> <element ref="uddi:businessInfos" /> </sequence> <attribute name="generic" use="required" type="string" /> <attribute name="operator" use="required" type="string" /> <attribute name="truncated" use="optional" type="uddi:truncated" /> </complexType>
This definition says that a <businessList>
contains a single
<businessInfos>
subelement (defined in the same
schema, as indicated by the preceding uddi:
) and
three attributes named
generic
,
operator
, and
truncated
. Doesn’t tell us
much, does it? So, let’s delve further. The schema
for the <businessInfos>
structure is:
<element name="businessInfos" type="uddi:businessInfos" /> <complexType name="businessInfos"> <sequence> <element ref="uddi:businessInfo" minOccurs="0" maxOccurs="unbounded" /> </sequence> </complexType>
This definition tells us that a
<businessInfos>
structure contains zero or
more <businessInfo>
subelements, which are
also defined in the same schema document.
minOccurs="0"
and
maxOccurs="unbounded"
tell us that the included
<businessInfo>
elements can be repeated zero
or more times. We now need to seek out the schema definition of the
<businessInfo>
structure, which is:
<element name="businessInfo" type="uddi:businessInfo" /> <complexType name="businessInfo"> <sequence> <element ref="uddi:name" maxOccurs="unbounded" /> <element ref="uddi:description" minOccurs="0" maxOccurs="unbounded" /> <element ref="uddi:serviceInfos" /> </sequence> <attribute name="businessKey" use="required" type="uddi:businessKey" /> </complexType>
This structure contains three subelements and an attribute. The
attribute, businessKey
, is the UUID for this
business. The first subelement, <name>
,
gives the name of the business. The second subelement,
<description>
, is zero or more text elements
that describe what the business does. The third subelement,
<serviceInfos>
, is a grouping of
<businessService>
documents. To figure out
what a <businessService>
document is, we
must search the schema for the
<serviceInfos>
element.
Searching for this schema is left as the proverbial “exercise for the reader.” At this stage, you should have an idea of the complexity of UDDI data structures and their navigation. An entire book could be dedicated to exploring every facet of the UDDI Programmers API. The rest of this chapter focuses on how to interact with UDDI and presents Java clients that demystify some of the complexity in the UDDI API and its data structures.
Now it’s finally time to pull everything that we have talked about together into a program. The examples in this chapter use Systinet WASP UDDI Standard. We selected this software because it is robust and free for development purposes. It includes:
A “local” UDDI registry (server) that runs as a servlet under Apache Tomcat 3.2.3, WebLogic Server 6.1, or IBM WebSphere 4.0
Database scripts for using Oracle, PostgreSQL, Cloudscape, Microsoft SQL Server, IBM DB2, and Sybase as the persistent store for the local UDDI registry
A Java-based UDDI client API
Sample code that illustrates how to use its custom client API in Java
We use Systinet WASP UDDI Standard primarily for its local registry, which allows you to run a registry locally on your computer for testing and development. We won’t focus on the client API. Since a UDDI server accepts standard SOAP messages, we can use any Java-based SOAP client API to create the appropriate messages and direct them to a valid UDDI registry.
Our first UDDI client retrieves basic business information for a
fictitious company called Demi Credit. The Systinet WASP UDDI
registry comes with a preconfigured entry for Demi Credit. This
example uses the Apache SOAP client library to create an appropriate
SOAP message that has a <find_business>
document as its body. We won’t create this document
programmatically, which would be an exercise in the use of the DOM or
JDOM APIs; instead, we’ll take the body for our SOAP
request message from the file
Ch6_FindBusiness.xml:
<uddi:find_business generic="2.0" maxRows="10"> <uddi:name> Demi Credit </uddi:name> </uddi:find_business>
The <uddi:find_business>
tag indicates that this element is
named find_business
and defined in the
uddi
namespace. The contents of the tag must
adhere to the schema for find_business
, which
defines a couple of different attributes. The
generic
attribute indicates the UDDI API version
that is used (Version 2.0, in this case). maxRows
indicates how many matching <businessInfo>
structures should be returned if the query matches more than one
company.
This <find_business>
element has a single
subelement, <name>
, which is the meat of our
request. The value of the <name>
element is
a simple regular expression used to search the names of different
businesses. The percentage sign
(%
) can be used for wildcard matching. In this
example, we know the name of the company we are searching for:
Demi Credit
.
Before looking at the code for the client, let’s run it and observe its behavior. The client reads an XML file, wraps it in a SOAP envelope, and sends it to a URL destination, adding various UDDI and Systinet namespace declarations that are required to make the SOAP message comply to the UDDI specification. The destination is the URL of an endpoint configured to accept UDDI inquiry messages. Run the command:
java UDDISoapClient -df ./Ch6_FindBusiness.xml
You should see the following output:
_________________________________________________ Starting UDDISoapClient: host url = http://localhost:8080/wasp/uddi/inquiry/ data file = Ch6_FindBusiness.xml _________________________________________________ Sent SOAP Message with Apache HTTP SOAP Client. Waiting for response.... <?xml version="1.0" encoding="UTF-8"?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Body> <businessList xmlns="urn:uddi-org:api_v2" generic="2.0" operator="SYSTINET"> <businessInfos> <businessInfo businessKey="892ac280-c16b-11d5-85ad-801eef208714"> <name xml:lang="en"> Demi Credit </name> <description xml:lang="en"> A smaller demo credit agency used for illustrating UDDI inquiry. </description> <serviceInfos> <serviceInfo serviceKey="860eca90-c16d-11d5-85ad-801eef208714" businessKey="9a26b6e0-c15f-11d5-85a3-801eef208714"> <name xml:lang="en"> DCAmail </name> </serviceInfo> </serviceInfos> </businessInfo> </businessInfos> </businessList> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
This output is saved as
Ch6_FindBusiness_OUTPUT.xml
and is included with
the examples provided for this chapter. Other examples that use this
program have their response documents saved in the same format.
Let’s pick apart the response to see what it
contains. The UDDI Server returned a single
<businessList>
structure which, in turn, has a
single <businessInfos>
structure. The
<businessInfos>
element can have zero or
more <businessInfo>
elements, based upon the
number of businesses that were matched as part of the query. In this
case, the server found only one business matching the name Demi
Credit in the UDDI registry.
The <businessInfo>
element contains several other
important pieces of information. First, the
businessKey
attribute contains the UUID of Demi
Credit. The UUID value is needed to do a more detailed information
search or an update using the Publisher’s API.
Second, the <businessInfo>
structure has a
<description>
that contains a textual
description of what the company does. Next, the
<businessInfo>
structure contains a
<serviceInfos>
structure that contains a
collection of all web services registered by this business. Each web
service is described by a single
<serviceInfo>
structure, which contains the
web service’s UUID as an attribute.
Now that we’ve seen what the client does and
examined the documents it sends and receives, it is time to look at
UDDISoapClient.java
in its entirety:
import java.io.*; import java.util.*; public class UDDISoapClient { // Default values used if no command line parameters are set private static final String DEFAULT_HOST_URL = "http://localhost:8080/wasp/uddi/inquiry/"; private static final String DEFAULT_DATA_FILENAME = "./Default.xml"; // In the SOAP chapter, we used "urn:oreilly:jaws:samples", // but Systinet UDDI requires this to be blank. private static final String URI = ""; private String m_hostURL; private String m_dataFileName; public UDDISoapClient(String hostURL, String dataFileName) throws Exception { m_hostURL = hostURL; m_dataFileName = dataFileName; System.out.println( ); System.out.println("______________________________________"); System.out.println("Starting UDDISoapClient:"); System.out.println(" host url = " + m_hostURL); System.out.println(" data file = " + m_dataFileName); System.out.println("______________________________________"); System.out.println( ); } public void sendSOAPMessage( ) { try { // Get soap body to include in the SOAP envelope from FILE FileReader fr = new FileReader (m_dataFileName); javax.xml.parsers.DocumentBuilder xdb = org.apache.soap.util.xml.XMLParserUtils.getXMLDocBuilder( ); org.w3c.dom.Document doc = xdb.parse (new org.xml.sax.InputSource (fr)); if (doc == null) { throw new org.apache.soap.SOAPException (org.apache.soap.Constants.FAULT_CODE_CLIENT, "parsing error"); } // Create a vector for collecting the body elements Vector bodyElements = new Vector( ); // Parse XML element as soap body element bodyElements.add(doc.getDocumentElement ( )); // Create the SOAP envelope org.apache.soap.Envelope envelope = new org.apache.soap.Envelope( ); envelope.declareNamespace("idoox", "http://idoox.com/uddiface"); envelope.declareNamespace("ua", "http://idoox.com/uddiface/account"); envelope.declareNamespace("config", "http://idoox.com/uddiface/config"); envelope.declareNamespace("attr", "http://idoox.com/uddiface/attr"); envelope.declareNamespace("fxml", "http://idoox.com/uddiface/formxml"); envelope.declareNamespace("inner", "http://idoox.com/uddiface/inner"); envelope.declareNamespace("", "http://idoox.com/uddiface/inner"); envelope.declareNamespace("uddi", "urn:uddi-org:api_v2"); // // NO SOAP HEADER ELEMENT AS SYSTINET WASP DOES NOT REQUIRE IT // // Create the SOAP body element org.apache.soap.Body body = new org.apache.soap.Body( ); body.setBodyEntries(bodyElements); envelope.setBody(body); // Build and send the Message. org.apache.soap.messaging.Message msg = new org.apache.soap.messaging.Message( ); msg.send (new java.net.URL(m_hostURL), URI, envelope); System.out.println("Sent SOAP Message with Apache HTTP SOAP Client."); // Receive response from the transport and dump it to the screen System.out.println("Waiting for response...."); org.apache.soap.transport.SOAPTransport st = msg.getSOAPTransport ( ); BufferedReader br = st.receive ( ); if(line == null) { System.out.println("HTTP POST was unsuccessful. "); } else { while (line != null) { System.out.println (line); line = br.readLine( ); } } ///// // Version in examples has XML pretty printing logic here. //// } catch(Exception e) { e.printStackTrace( ); } } // // NOTE: the remainder of this deals with reading arguments // /** Main program entry point. */ public static void main(String args[]) { // Not Relevant } }
This code is similar to the code presented in the SOAP chapters, with
a couple of exceptions. First, Systinet
WASP UDDI uses different servlets to implement the inquiry and
publisher ports. When Systinet WASP UDDI is first installed, the URL
of the inquiry port is
http://localhost:8080/wasp/uddi/inquiry
. In the
program, this URL is assigned to the constant
DEFAULT_HOST_URL
:
private static final String DEFAULT_HOST_URL = "http://localhost:8080/wasp/uddi/inquiry/";
Second, UDDI SOAP messages don’t require the use of
a SOAP header. Thus, all of the code used to create a SOAP header and
fill it with values, such as mustUnderstand
, is
not needed. Next, UDDI and Systinet WASP UDDI SOAP envelopes require
the addition of several different namespaces that they have defined.
These namespaces are required at the envelope level of the message,
not the SOAP header or body. In the Apache SOAP API, the
Envelope
interface has a method called
declareNamespace( )
that adds these additional namespaces:
// Create the SOAP envelope org.apache.soap.Envelope envelope = new org.apache.soap.Envelope( ); // Add the Systinet namespaces. envelope.declareNamespace("idoox", "http://idoox.com/uddiface"); envelope.declareNamespace("ua", "http://idoox.com/uddiface/account"); envelope.declareNamespace("config", "http://idoox.com/uddiface/config"); envelope.declareNamespace("attr", "http://idoox.com/uddiface/attr"); envelope.declareNamespace("fxml", "http://idoox.com/uddiface/formxml"); envelope.declareNamespace("inner", "http://idoox.com/uddiface/inner"); // Add the default namespace envelope.declareNamespace("", "http://idoox.com/uddiface/inner"); // Include the standard UDDI namespace. // This URN contains all of the UDDI XML data structures and messages. envelope.declareNamespace("uddi", "urn:uddi-org:api_v2");
The simple SOAP client we’ve just examined is sufficient to demonstrate a variety of UDDI APIs and data structures. It has some obvious limitations, however:
SOAP envelope complexities such as namespaces have to be coded manually.
SOAP Fault messages that are received have to be handled manually.
The input and output XML documents are weakly typed as XML. A more sophisticated package would have Java interfaces that represent each UDDI data structure, allowing a program to check datatypes at compile time instead of discovering faults at runtime.
To get a feeling for what these limitations mean, we’ll implement the same example using Systinet’s UDDI Java API. To run this program, compile the file SystinetFindBusiness.java and execute the following command:
java -Dwasp.restrictor.packages=- SystinetFindBusiness
Because of a limitation in the way the Systinet UDDI client library operates,
you get a series of array typing error messages if you omit the
-Dwasp.restrictor.packages=-
environment variable
definition. Don’t forget to include the hyphen (-)
after the equal sign!
Here’s a listing of the Systenet-based client in its entirety:
import org.idoox.uddi.client.api.v2.request.inquiry.*; import org.idoox.uddi.client.structure.v2.tmodel.*; import org.idoox.uddi.client.api.v2.response.*; import org.idoox.uddi.client.structure.v2.base.*; import org.idoox.uddi.client.structure.v2.business.*; import org.idoox.uddi.client.api.v2.*; import org.idoox.uddi.client.*; /** * This is simple example of Systinet's UDDI Java API for accessing * a UDDI registry. * This program does a find_business call by name. */ public class SystinetFindBusiness { // Program Entry Point public static void main(String args[]) throws Exception { String company = "Demi Credit"; findBusinessByName(company); } public static void findBusinessByName(String name) throws Exception { System.out.println("Searching for businesses named '" + name + "'..."); // Create a FindBusiness instance. // This creates a SOAP message. FindBusiness findBusiness = new FindBusiness( ); // Set the name to use in the query. findBusiness.addName(new Name(name)); // This will limit the number of returned matches. // maxRows is an optional attribute. findBusiness.setMaxRows(new MaxRows("10")); // This will retrieve a stub to the UDDI inquiry port. UDDIApiInquiry inquiry = UDDILookup.getInquiry("http://localhost:8080/wasp/uddi/inquiry/"); // Send the message and retrieve the response. BusinessList businessList=inquiry.find_business(findBusiness); // Show the results if (businessList==null) { System.err.println("ERROR: Business list is null!"); } else { // Business list is holder for results - business infos. BusinessInfos businessInfos = businessList.getBusinessInfos( ); System.out.println(" Found: " + businessInfos.size( ) + " businesses. "); // Iterate through each company found in the query. BusinessInfo businessInfo = businessInfos.getFirst( ); BusinessKey result; if (businessInfo != null) { result=businessInfo.getBusinessKey( ); while (businessInfo!=null) { System.out.println("BusinessEntity name = " + businessInfo.getNames().getFirst().getValue( )); System.out.println("BusinessEntity UUID = " + businessInfo.getBusinessKey( )); System.out.println("***"); businessInfo = businessInfos.getNext( ); } } } } }
The Systinet UDDI client library is spread throughout several
different packages. Since the UDDI API and data structures changed
from Version 1.0 to Version 2.0 of the specification, Systinet opted
to create separate Java packages for each version. This can be
problematic for developers, but it is workable. Here are the
import
statements needed for the current crop of packages:
import org.idoox.uddi.client.api.v2.request.inquiry.*; import org.idoox.uddi.client.structure.v2.tmodel.*; import org.idoox.uddi.client.api.v2.response.*; import org.idoox.uddi.client.structure.v2.base.*; import org.idoox.uddi.client.structure.v2.business.*; import org.idoox.uddi.client.api.v2.*; import org.idoox.uddi.client.*;
The main( )
method is responsible for declaring
the search string for the company and
calling
findBusinessByName( )
,
where the bulk of the work is performed:
// Program Entry Point public static void main(String args[]) throws Exception { String company = "Demi Credit"; findBusinessByName(company); }
Within findBusinessByName( )
, the program needs to
create a <find_business>
message and
populate it with the search criteria we established. The Systinet
UDDI library has a separate class abstraction representing each UDDI
XML message. Therefore, to create a
<find_business>
structure, you merely need
to create an instance of their FindBusiness
class.
The FindBusiness
class has several methods that
add elements and attributes to the underlying
<find_business>
structure. In this example,
we’ll use addName( )
,
which adds the company name to search for, and setMaxRows( )
,
which limits the number of matches returned:
// Create a FindBusiness instance. // This creates a SOAP message. FindBusiness findBusiness = new FindBusiness( ); // Set the name to use in the query. findBusiness.addName(new Name(name)); // This will limit the number of returned matches. // maxRows is an optional attribute. findBusiness.setMaxRows(new MaxRows("10"));
Next, the program creates a connection to a UDDI
server’s inquiry port. The
UDDILookup
class has static methods that create a
dynamic Java proxy object that communicates using SOAP. From the
developer’s point of view, it looks and feels like
an RMI stub, except it doesn’t communicate over RMI.
The UDDILookup.getInquiry( )
method creates an inquiry connection. This program uses the same
inquiry port as the UDDISoapClient
program,
http://localhost:8080/wasp/uddi/inquiry/. An
UDDIApiInquiry
object is returned and encapsulates
an active stub that communicates with the UDDI server:
// This will retrieve a stub to the UDDI inquiry port. UDDIApiInquiry inquiry = UDDILookup.getInquiry("http://localhost:8080/wasp/uddi/inquiry/");
Finally, the program needs to send the
<find_business>
document as part of a SOAP
message. The UDDIApiInquiry
object has a number of
methods that create the SOAP envelope and populate it with an XML
structure. A separate method exists for each UDDI XML message in the
Programmer’s API. For our example, the program calls
the find_business( )
method on the
UDDIApiInquiry
object, passing in the
FindBusiness
object containing the XML structure.
The Systinet API also has matching classes for the response XML
structures. Thus, since a <find_business>
request yields a <businessList>
response
structure, the Systinet API has a
BusinessList
class. We
can traverse this class to get to each subelement and attribute that
was returned:
BusinessList businessList=inquiry.find_business(findBusiness); // Show the results if (businessList==null) { System.err.println("ERROR: Business list is null!"); } else { // Business list is holder for results - business infos. BusinessInfos businessInfos = businessList.getBusinessInfos( ); System.out.println(" Found: " + businessInfos.size( ) + " businesses. "); // Iterate through each company found in the query. BusinessInfo businessInfo = businessInfos.getFirst( ); BusinessKey result; if (businessInfo != null) { result=businessInfo.getBusinessKey( ); while (businessInfo!=null) { System.out.println("BusinessEntity name = " + businessInfo.getNames().getFirst().getValue( )); System.out.println("BusinessEntity UUID = " + businessInfo.getBusinessKey( )); System.out.println("***"); businessInfo = businessInfos.getNext( ); } } }
Now that we’ve looked at a simple SOAP client to build a request by hand, and a client that uses a UDDI API to build a request with slightly higher-level tools, let’s proceed to the next level of abstraction: the Java API for XML Registries (JAXR). JAXR is a uniform approach to accessing a registry that advertises business information and services in XML. JAXR attempts to provide a single API that can access many different kinds of registries, including ISO 11179, OASIS, eCo Framework, ebXML, and UDDI (although the reference implementation can access only a UDDI registry).
The
JAXR reference implementation is unique
because it requires Tomcat for the client implementation! This
requirement is somewhat odd, but fortunately, it is only a
characteristic of the reference implementation. The provider
implementations created by vendors will probably be simple libraries
that don’t require an external server such as
Tomcat. You can get JAXR and Systinet
WASP UDDI Standard to use the same Tomcat installation; details on
how to accomplish this installation are in this
chapter’s README.txt file. When
installing and configuring JAXR on your machine, make sure that the
.jaxr.properties
file
included with this chapter’s examples is placed in
your home directory. On a Unix system, this directory is the
~/ directory; on NT or Windows 2000, the home
directory is given by the value of the
%USERPROFILE%
environment
variable. To run the program to search for Demi Credit using JAXR,
use this command:
java JAXRFindBusiness "Demi Credit"
The following output should be seen on the console:
Query string is Demi Credit JAXR Reference Implementation—logging started Org name: Demi Credit Org description: A smaller demo credit agency used for illustrating UDDI inquiry . Org key id: 892ac280-c16b-11d5-85ad-801eef208714 Contact name: David Tarnov ---
Since you should now have a better understanding of how these UDDI queries work, this example provides a more thorough parsing of the response message. This program provides a formatted output, rather than simply dumping an XML document to the screen. The code for this client is in the file JAXRFindBusiness.java. Here is the source code in its entirety:
import javax.xml.registry.*; import javax.xml.registry.infomodel.*; import java.net.*; import java.util.*; /* * This is the FindBusiness UDDI example implemented using * the JAXR libraries and the reference implementation * JAXR provider for accessing a UDDI registry. */ public class JAXRFindBusiness { public JAXRFindBusiness( ) {} public static void main(String[] args) { if (args.length != 1) { System.out.println("Usage: java " + "JAXRFindBusiness <query-string>"); System.exit(1); } String queryString = new String(args[0]); System.out.println("Query string is " + queryString); doQuery(queryString); } public static void doQuery(String qString) { Connection conn = null; // Define connection configuration properties // To query, you need only the query URL Properties props = new Properties( ); props.setProperty("javax.xml.registry.queryManagerURL", "http://localhost:8080/wasp/uddi/inquiry/"); props.setProperty("javax.xml.registry.factoryClass", "com.sun.xml.registry.uddi.ConnectionFactoryImpl"); try { // Create the connection, passing it the // configuration properties ConnectionFactory factory = ConnectionFactory.newInstance( ); factory.setProperties(props); conn = factory.createConnection( ); // Get registry service and query manager RegistryService rs = conn.getRegistryService( ); BusinessQueryManager bqm = rs.getBusinessQueryManager( ); // Define find qualifiers and name patterns Collection qualifiers = new ArrayList( ); qualifiers.add(FindQualifier.SORT_BY_NAME_DESC); Collection namePatterns = new ArrayList( ); namePatterns.add(qString); // Find using the name BulkResponse response = bqm.findOrganizations(qualifiers, namePatterns, null, null, null, null); Collection orgs = response.getCollection( ); // Display information about the organizations found Iterator orgIter = orgs.iterator( ); while (orgIter.hasNext( )) { Organization org = (Organization) orgIter.next( ); System.out.println("Org name: " + getName(org)); System.out.println("Org description: " + getDescription(org)); System.out.println("Org key id: " + getKey(org)); // Display primary contact information User pc = org.getPrimaryContact( ); if (pc != null) { PersonName pcName = pc.getPersonName( ); System.out.println(" Contact name: " + pcName.getFullName( )); Collection phNums = pc.getTelephoneNumbers(pc.getType( )); Iterator phIter = phNums.iterator( ); while (phIter.hasNext( )) { TelephoneNumber num = (TelephoneNumber) phIter.next( ); System.out.println(" Phone number: " + num.getNumber( )); } Collection eAddrs = pc.getEmailAddresses( ); Iterator eaIter = eAddrs.iterator( ); while (phIter.hasNext( )) { System.out.println(" Email Address: " + (EmailAddress) eaIter.next( )); } } // Display service and binding information Collection services = org.getServices( ); Iterator svcIter = services.iterator( ); while (svcIter.hasNext( )) { Service svc = (Service) svcIter.next( ); System.out.println(" Service name: " + getName(svc)); System.out.println(" Service description: " + getDescription(svc)); Collection serviceBindings = svc.getServiceBindings( ); Iterator sbIter = serviceBindings.iterator( ); while (sbIter.hasNext( )) { ServiceBinding sb = (ServiceBinding) sbIter.next( ); System.out.println(" Binding " + "Description: " + getDescription(sb)); System.out.println(" Access URI: " + sb.getAccessURI( )); } } // Print spacer between organizations System.out.println(" --- "); } } catch (Exception e) { e.printStackTrace( ); } finally { // At end, close connection to registry if (conn != null) { try { conn.close( ); } catch (JAXRException je) {} } } } private static String getName(RegistryObject ro) throws JAXRException { try { return ro.getName().getValue( ); } catch (NullPointerException npe) { return ""; } } private static String getDescription(RegistryObject ro) throws JAXRException { try { return ro.getDescription().getValue( ); } catch (NullPointerException npe) { return ""; } } private static String getKey(RegistryObject ro) throws JAXRException { try { return ro.getKey().getId( ); } catch (NullPointerException npe) { return ""; } } }
JAXR uses
javax.xml.registry
for the base package name for
all of its classes. The main( )
method
for this program parses a single parameter, which is the query string
to use as the business name in the request:
import javax.xml.registry.*; import javax.xml.registry.infomodel.*; import java.net.*; import java.util.*; /* * This is the FindBusiness UDDI example implemented using * the JAXR libraries and the reference implementation * JAXR provider for accessing a UDDI registry. */ public class JAXRFindBusiness { public JAXRFindBusiness( ) {} public static void main(String[] args) { // Parameter parsing, not entirely relevant doQuery(queryString); }
Most work for this program takes place in the doQuery( )
method. A client program first needs to create a connection to the
service provider. In our case, the service provider is our local UDDI
registry running at
http://localhost:8080/wasp/uddi/inquiry/. To
create the connection, we create a Properties
object and fill it with relevant information: the
javax.xml.registry.queryManagerURL
value should be
the URL of the UDDI registry that you are accessing, while the
javax.xml.registry.factoryClass
is the class that
implements a ConnectionFactory
object. Different
JAXR providers provide different values for this property; the JAXR
reference implementation uses
com.sun.xml.registry.uddi.ConnectionFactoryImpl
.
Finally, the client code creates an instance of the
ConnectionFactory
class, associates the properties
with this class, and then creates a Connection
object using the createConnection( )
method:
public static void doQuery(String qString) { Connection conn = null; // Define connection configuration properties // To query, you need only the query URL Properties props = new Properties( ); props.setProperty("javax.xml.registry.queryManagerURL", "http://localhost:8080/wasp/uddi/inquiry/"); props.setProperty("javax.xml.registry.factoryClass", "com.sun.xml.registry.uddi.ConnectionFactoryImpl"); try { // Create the connection, passing it the // configuration properties ConnectionFactory factory = ConnectionFactory.newInstance( ); factory.setProperties(props); conn = factory.createConnection( );
Once we have a connection to a service provider, we need to connect
to a RegistryService
object. Since different
registries support different types of services, a
RegistryService
object tells your program exactly
which services the registry supports. For example, some registries
allow declarative SQL queries (UDDI does not). The
RegistryService
interface has methods for telling
a program the registry’s capabilities and returning
manager objects that support a particular type of capability. For
business requests, such as the requests that UDDI supports, the
BusinessQueryManager
interface must be used. To retrieve a
reference to a BusinessQueryManager
object, call
the getBusinessQueryManager( )
method on a
RegistryService
object:
// Get registry service and query manager RegistryService rs = conn.getRegistryService( ); BusinessQueryManager bqm = rs.getBusinessQueryManager( );
The BusinessQueryManager
interface has a series of
findXXX( )
methods that perform different types of
queries. Different methods query for different items; for example,
the findOrganizations( )
method queries a registry
for business information, while the findServices( )
method asks for different services that may or may not be
available. Most methods take one or more
Collection
objects as input; these objects refine
the query using qualifiers. The first parameter of the
findOrganizations( )
method takes a
Collection
of find qualifiers that refines how the
query should be performed. Find qualifiers can apply a sort or
restrict the number of entries that are returned; in this case, we
ask that the responses be sorted by name. The second parameter of the
findOrganizations( )
takes a
Collection
of name patterns to apply to the
search. To populate this Collection
, we add the
business name that we read from the command line. The other
parameters (all set to null
in this example) take
qualifiers that search for businesses based upon classifications,
specifications supported, external identifiers, and external URLs,
respectively. The query returns a BulkResponse
object that can be checked for exceptions from the server or
converted to a Collection
:
// Define find qualifiers and name patterns Collection qualifiers = new ArrayList( ); qualifiers.add(FindQualifier.SORT_BY_NAME_DESC); Collection namePatterns = new ArrayList( ); namePatterns.add(qString); // Find using the name BulkResponse response = bqm.findOrganizations(qualifiers, namePatterns, null, null, null, null); Collection orgs = response.getCollection( );
The rest of the program is responsible for iterating through the output and formatting it for display on the screen. It’s a bit wordy, so it’s not included again here. A client application would use the information retrieved from the query to perform other queries or to leverage a particular service.
As you undoubtedly noticed, the JAXR API is more complicated than the Systinet API. JAXR does not have class representations for each UDDI XML structure; instead, we have to work with query managers and lists of various qualifiers. Working with the Systinet API is convenient because every class has an XML counterpart with the same name. You pay a price for abstraction, though: the Systenet client is tied to UDDI, while the JAXR client could conceivably make a similar request from a different kind of registry with little or no modification.
find_
messages are designed to return basic
information about the structures that a UDDI registry manages. Given
the UUID to one of the major data structures, you can drill down into
the registry to get a full listing of the details stored in that
structure. The UDDI inquiry API provides a series of messages that
begin with get_
for retrieving information from the
registry. Table 6-3 lists these messages.
Table 6-3. XML documents used to get detailed information
Message name |
Response document |
Brief description |
---|---|---|
|
|
Given one or more UUIDs of different
|
|
|
Given one or more UUIDs of different
|
|
|
Given one or more UUIDs of different
|
|
|
Given one or more UUIDs of different
|
All of these messages are fairly straightforward. As long as you can
get a valid UUID for the data structure you are interested in, you
can get its details. In the <find_business>
example for Demi Credit, the response document indicated that Demi
Credit had published a web service named DCAmail
with the UUID
860eca90-c16d-11d5-85ad-801eef208714
.
Let’s send a
<get_serviceDetail>
message to get all of the information
about this web service. To get this information,
we’ll use the UDDISoapClient
program from our previous examples to send a handwritten XML
document. This document contains a
<get_serviceDetail>
message using the UUID
for the DCAmail
web service.
Here’s a listing of
Ch6_GetServiceDetail.xml:
<uddi:get_serviceDetail generic="2.0"> <uddi:serviceKey>860eca90-c16d-11d5-85ad-801eef208714</uddi:serviceKey> </uddi:get_serviceDetail>
The <get_serviceDetail>
message
doesn’t have any optional attributes; it has only
one subelement, <serviceKey>
, which is the
UUID of the web service for which you want more detail. The
<get_serviceDetail>
message can accept one
or more <serviceKey>
subelements on which to
query. Here is the response document returned by the UDDI server:
<serviceDetail generic="2.0" operator="SYSTINET" xmlns="urn:uddi-org:api_v2"> <businessService businessKey="9a26b6e0-c15f-11d5-85a3-801eef208714" serviceKey="860eca90-c16d-11d5-85ad-801eef208714"> <name xml:lang="en">DCAmail</name> <description xml:lang="en">Get credit assessment by email</description> <bindingTemplates> <bindingTemplate bindingKey="f9274a50-c16f-11d5-85ad-801eef208714" serviceKey="860eca90-c16d-11d5-85ad-801eef208714"> <description xml:lang="en">The address to which you should send the name and address of your credit report target</description> <accessPoint URLType="mailto">mailto:[email protected]</accessPoint> <tModelInstanceDetails> <tModelInstanceInfo tModelKey="uuid:93335d49-3efb-48a0-acea-ea102b60ddc6"> <description xml:lang="en">The smtp protocol is used when sending information</description> <instanceDetails> <overviewDoc> <description xml:lang="en">Describes how to use this service</description> <overviewURL>http://www.creditdemo.bar/DCAmail/howto</overviewURL> </overviewDoc> </instanceDetails> </tModelInstanceInfo> <tModelInstanceInfo tModelKey="uuid:25ddf051-c164-11d5-85a6-801eef208714"> <description xml:lang="en">The namespace in which our credit numbers are used.</description> </tModelInstanceInfo> </tModelInstanceDetails> </bindingTemplate> </bindingTemplates> <categoryBag> <keyedReference keyName="Personal credit agencies" keyValue="841416" tModelKey="uuid:db77450d-9fa8-45d4-a7bc-04411d14e384"/> <keyedReference keyName="Credit agencies" keyValue="8414" tModelKey="uuid:db77450d-9fa8-45d4-a7bc-04411d14e384"/> <keyedReference keyName="Netherlands" keyValue="NL" tModelKey="uuid:4e49a8d6-d5a2-4fc2-93a0-0411d8d19e88"/> <keyedReference keyName="France" keyValue="FR" tModelKey="uuid:4e49a8d6-d5a2-4fc2-93a0-0411d8d19e88"/> <keyedReference keyName="Belgium" keyValue="BE" tModelKey="uuid:4e49a8d6-d5a2-4fc2-93a0-0411d8d19e88"/> <keyedReference keyName="Business credit agencies" keyValue="841417" tModelKey="uuid:db77450d-9fa8-45d4-a7bc-04411d14e384"/> <keyedReference keyName="Luxembourg" keyValue="LU" tModelKey="uuid:4e49a8d6-d5a2-4fc2-93a0-0411d8d19e88"/> <keyedReference keyName="Germany, Federal Republic of" keyValue="DE" tModelKey="uuid:4e49a8d6-d5a2-4fc2-93a0-0411d8d19e88"/> </categoryBag> </businessService> </serviceDetail>
This document contains a
<businessService>
structure, which is a logical
grouping of web services by a business. In the case of Demi Credit,
this grouping lists a number of web services that allow you to do a
credit check via email. The returned
<businessService>
has a single
<bindingTemplate>
that provides technical details of
how to access the web service. The
<accessPoint>
is
the web service endpoint URL. In this case, it is a simple email
address: mailto:[email protected].
More importantly, the <bindingTemplate>
has
two <tModelInstanceInfo>
documents that show where to find more information about how this web
service runs and the specifications it supports. Each
<tModelInstanceInfo>
document contains a
tModelKey
attribute, which is the UUID of a
<tModel>
structure
that contains a particular specification’s metadata.
The <tModelInstanceInfo>
document also
contains an <instanceDetails>
subelement
that contains a description of how to use the web service.
Our
<businessService>
document also contains a
<categoryBag>
structure.
<categoryBag>
documents can appear with
<businessEntity>
,
<businessService>
, and
<tModel>
documents.
Categorization of data was an important requirement during the development of UDDI. Categorization allows data in a UDDI registry to be associated with an industry, product, or geographic code set. Some obvious problems come with the use of categories; they should be familiar to anyone who’s ever searched for something on the Web. Broad categories, such as manufacturing, can return thousands of matching services and businesses—certainly too many to sift through manually. On the other hand, specific categories, such as “manufacturing in Buffalo,” might be too specific to return any results.
It’s probably not realistic to expect software to dynamically discover and use new businesses on the fly in the near future. Realistically, human analysts need to browse a UDDI portal that allows customized searches and queries to discover the businesses they are interested in working with. It’s more likely that software will contain the logic necessary to locate and integrate with web services for companies that have been predetermined. It’s also likely that businesses will set up private UDDI registries that they can share with their approved partners to facilitate B2B integration.
Many categorization systems can be used on data within UDDI. These
systems are summarized in Table 6-4. Each taxonomy
categorization is registered as a <tModel>
structure within UDDI. This registration means that each
categorization has a tModel
name and UUID that can
be used to reference it. The tModel
name is the
same in all UDDI registries, but the UUID for the
tModel
may change between operator nodes.
Table 6-4. Supported categorization taxonomies
Taxonomy name |
tModel name |
Description |
---|---|---|
|
The North American Industry Classification system. Hundreds of classifications are in this system, including “Pet supply stores,” “Hazardous waste collection,” and “Diet and weight reducing centers.” More information can be found at http://www.census.gov/epcd/www/naics.html. | |
|
The Universal Standard Products and Services Classification. It is the first system to classify products and services for worldwide use. More information can be found at http://www.unspsc.org. | |
|
International standard geographical regions. This taxonomy includes codes for countries and their administrative support staffs. More information can be found at http://www.din.de/gremien/nas/nabd/iso3166ma. | |
Other |
|
General-purpose associations that a business might want to make. This taxonomy allows operator nodes to promote invalid entries or entries that would otherwise be rejected by another classification system. There is no specification on how this works; it is operator-node specific. |
A <categoryBag>
structure contains zero or
more <keyedReference>
structures. Each <keyedReference>
structure
contains the name and value of a category to which the data element
belongs. In the previous <businessService>
example, the <categoryBag>
had eight
<keyedReference>
subelements. Three
<keyedReference>
subelements were for NAICS
categorizations; the other five were for ISO 3166 country
categorizations.
Determining which categorization a
<keyedReference>
belongs to can be
difficult, but more details can be discovered by looking up the
<tModel>
document, using the
tModelKey
attribute that is also part of a
<keyedReference>
. If you look at the
<categoryBag>
, you will notice that three of
the <keyedReference>
elements have the same
tModelKey
value and the other five attributes have
a different tModelKey
value. For example, here is
one of the ISO 3166 country categorization
<keyedReference>
elements returned as part
of the <categoryBag>
:
<keyedReference keyName="Netherlands" keyValue="NL" tModelKey="uuid:4e49a8d6-d5a2-4fc2-93a0-0411d8d19e88"/>
The keyName
value identifies the categorization.
It is also a textual name given to the categorization. The
keyValue
is the categorization code, as identified
by the specification. The categorization code is guaranteed to be
unique. The tModelKey
value is the UUID of a
<tModel>
document that provides metadata of
the specification that this categorization supports.
An identifier is
a type of property or keyword used to uniquely identify a business or
specification. Identifiers can be applied to
<businessEntity>
and
<tModel>
structures. Identifiers, like
categorizations, can be used as part of a search when doing a
<find_business>
or
<find_tModel>
request message.
Identifiers and categorizations are implemented similarly.
Identifiers are attached to <businessEntity>
and <tModel>
documents through an
<identifierBag>
structure. The
<identifierBag>
structure can have one or
more <keyedReference>
structures that
provide the name, value, and <tModel>
UUID
reference for locating more information.
At this time, only two general-purpose identifier schemes have been incorporated into all operator nodes, but other schemes can be used as well. Table 6-5 lists the identifier types that are a core part of an operator node.
Table 6-5. Supported identifier types
Identifier name |
tModel name |
Description |
---|---|---|
D-U-N-S |
|
The Dun & Bradstreet D-U-N-S number is a unique nine-digit identification sequence. This sequence provides unique identifiers for single business entities, while linking corporate family structures. More information can be found at http://www.d-u-n-s.com. |
Thomas Register |
|
This scheme provides identifiers for over 150,000 manufacturing and e-commerce companies worldwide. More information can be found at http://www.thomasregister.com. |
<tModel>
documents
provide metadata information about a web service specification,
categorization specification, or identifier specification.
<tModel>
documents are a core data structure
in the UDDI specification and represent the most detailed information
that a UDDI registry can provide about any specification.
Looking at Demi Credit, we can see that the DCAmail
<businessService>
has a
<bindingTemplate>
with two
<tModelInstanceInfo>
documents. Each
<tModelInstanceInfo>
document contains a
tModelKey
attribute that is the UUID of a
<tModel>
document representing information
about the supporting specification. There are also
tModelKey
attributes for each
<keyedReference>
structure that was part of
the <categoryBag>
. We can use the
UDDISoapClient
to retrieve the
<tModel>
document for any of these UUIDs.
Let’s get the <tModel>
document for
uuid:93335d49-3efb-48a0-acea-ea102b60ddc6
, which
is a specification implemented by the DCAmail
web
service. Here is the listing of
Ch6_GetTModelDetail.xml, which is used as the
body of the SOAP request:
<uddi:get_tModelDetail generic="2.0"> <uddi:tModelKey>uuid:93335d49-3efb-48a0-acea-ea102b60ddc6</uddi:tModelKey> </uddi:get_tModelDetail>
The resulting response is saved as Ch6_GetTModelDetail_OUTPUT.xml:
<tModelDetail generic="2.0" operator="SYSTINET" xmlns="urn:uddi-org:api_v2"> <tModel authorizedName="admin" operator="SYSTINET" tModelKey="uuid:93335d49-3efb-48a0-acea-ea102b60ddc6"> <name>uddi-org:smtp</name> <description xml:lang="en">E-mail based web service</description> <categoryBag> <keyedReference keyName="A transport tModel is a specific type of protocol" keyValue="transport" tModelKey="uuid:c1acf26d-9672-4404-9d70-39b756e62ab4"/> </categoryBag> </tModel> </tModelDetail>
The
authorizedName
attribute is the recorded name of the
individual who published this
<tModel>
. The operator attribute is the
certified name of the UDDI registry site that owns the master copy of
the <tModel>
data. The
tModelKey
is the UUID of this
<tModel>
; it matches the
tModelKey
for the request document. The
<name>
subelement is the recorded name of
the <tModel>
; it can be used as part of a
search when doing a <find_tModel>
request.
The <description>
subelement provides a
specification’s textual description. A
<tModel>
can have an optional
<categoryBag>
or
<identifierBag>
structure as well. Finally,
a <tModel>
can contain an optional
<overviewDoc>
subelement, which contains a
URL that points to remote descriptive information.
Publishing to a UDDI registry involves any operation that would create, update, or destroy data in a UDDI registry. Here are some key technical differences between publishing and inquiring:
All publishing messages require authenticated access. The process for authentication is not defined by the UDDI specification and is specific to the operator node. Given authenticated credentials, however, your program can access any publishing message.
Publishing message requests use a different access point than do inquiry messages. The HTTP protocol was suitable for inquiry messages, but HTTPS is required for all publishing messages.
Operator nodes can impose space and registration restrictions on an
individual or company. For example, a site may limit some users to
one <businessEntity>
structure and prevent
them from inserting additional data without special permissions.
When information is inserted into an operator node, that site becomes the owner of that data’s master copy. Any subsequent updates or changes to the data must be performed at the same operator node. UDDI does not have a mechanism for resolving conflicts if duplicate entries are made at another operator node.
The Publisher API messages that require authentication are listed in Table 6-6.
Table 6-6. UDDI Publisher API messages
Authentication with an operator node is
typically straightforward. Most operator nodes implement a
name/password scheme that allows you to retrieve an authentication
token. Operator nodes that support the name/password scheme for
authentication expose their authentication interface through the
<get_authToken>
message. Operator nodes do not have to
support this scheme for authentication and can provide alternative
techniques for allowing a client to get an authentication token.
Those techniques are not documented by the UDDI specifications and
are specific to the operator node. An operator node also has specific
ways of registering new publishers and verifying their information.
The only requirement that an operator node has to adhere to is that
the authentication token returned must be a text value that can be
inserted in subsequent XML messages.
The Systinet WASP UDDI Standard has
a preconfigured username (admin
) and password
(changeit
). We can obtain an authentication token
by running the UDDISoapClient
program with the
Ch6_GetAuthToken.xml file as input and two
command-line modifications:
java -Djava.protocol.handler.pkgs=com.sun.net.ssl.internal.www.protocol
UDDISoapClient
-url https://localhost:8443/wasp/uddi/publishing/
-df Ch6
_GetAuthToken.xml
First, the JDK java.net.URL
class does not support
HTTPS as a standard protocol. The Message.send( )
method in the Apache SOAP library requires a URL
object as input, so enabling HTTPS is key. Enabling HTTP
is done by including the
-Djava.protocol.handler.pkgs=com.sun.net.ssl.internal.www.protocol
option on the command line. Using this option assumes that
you have installed the jsse.jar library. This
library is installed as part of Systinet WASP UDDI; if you use a
different UDDI package, you may have to install JSSE yourself.
Second, since we are using the Publishing API, we must access a
different URL than the default that is configured for inquiries. The
publishing URL for Systinet WASP UDDI is
https://localhost:8443/wasp/uddi/publishing/.
Using the JSSE
library directly is not for the weak of heart. In addition to setting
up JSSE and enabling HTTPS as a valid protocol, SSL requires that
your program have a valid client certificate to be used against the
server. Fortunately, Systinet WASP UDDI Standard installs a client
certificate for your use, but otherwise, you would have to create a
new certificate using Java’s
keytool
utility.
The real value of using a custom Java API such as
Systinet’s is apparent when you look at the
difficulty of using the UDDISoapClient
to generate
HTTPS messages. Two complete round-trip SOAP invocations have to be
made and you have to go through the rigmarole of configuring SSL
appropriately on the client. Systinet’s library
handles this situation cleanly by providing one method that retrieves
your authentication credentials and other methods that use the rest
of the Publishing API.
Here is the Ch6_GetAuthToken.xml document that we send to the server to request an authentication token:
<uddi:get_authToken generic="2.0" userID="admin" cred="changeit" />
The <get_authToken>
element
doesn’t have any subelements and passes the name and
password as the userID
and cred
attributes, respectively. Here’s the body of the
SOAP response:
<authToken xmlns="urn:uddi-org:api_v2" generic="2.0" operator="SYSTINET"> <authInfo> MIHLMDYbBWFkbWluMB4XDTAxMTIzMDAwNDYzNVoXDTAxMTIzMDAxNDYzNVoEDUFkbWluaXN0cmF0b3IwDQYJK oZIhvcNAQEEBQADgYEA4Cci/CbDji6RiQFneRt7gVXwX/4TA7qCZNUmTnXFJdVNFIDvp4WV+IW+/ deDCQk0GVAdsub0vkXJX3dqdDGqDsDleXwm7cDN2ENW7K/IeN9ii7/ pfbVryPtKzzbe07ETcWAoRnkcgDteC7I77VpyiqKHUqwmi5+kN10XMRXfkTw= </authInfo> </authToken>
The response document contains an
<authToken>
element that has an
<authInfo>
subelement. The value of the
<authInfo>
subelement is a key that will be
used as the authentication token on all other publishing messages. To
write a program that makes a series of updates on a UDDI registry,
you must parse the <get_authToken>
response
message and store the authentication token as a
String
object. Your program would then have to
create a second SOAP message to perform an insert, an update, or a
delete operation.
Errors can occur on any request message, whether they are part of the
inquiry API or the Publishing API.
UDDI errors are always returned as
SOAP Fault messages. (For more information on SOAP Fault messages and
their structure, refer to Chapter 4.) The
subelement of a SOAP Fault <detail>
message
is a UDDI
<dispositionReport>
document; this document is defined in
the UDDI schema. Despite being used for all error code situations,
<dispositionReport>
documents are also used
in some non-error situations as a status indicator. Non-error
<dispositionReport>
documents are returned
as part of a standard SOAP response for any UDDI
delete_
message.
UDDI SOAP Faults can be returned for
dozens of reasons: expiration of an authentication token, a server
that is busy and unable to handle requests, the use of invalid
categorization and identifiers, unsupported APIs, etc. A full listing
of error codes is contained in Appendix A of the UDDI
Programmer’s API specification. Here is an error
that I received one time when I tried to exceed my limit for
<businessEntity>
documents while using
Systinet’s WASP UDDI Standard:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Body> <ns0:Fault xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/"> <faultcode> ns0:Client </faultcode> <faultstring> ClientError </faultstring> <detail> <dispositionReport generic="2.0" operator="SYSTINET" xmlns="urn:uddi-org:api_v2"> <result errno="10160"> <errInfo errCode="E_accountLimitExceeded"> An attempt to save more data than allowed. </errInfo> </result> </dispositionReport> </detail> </ns0:Fault> </SOAP-ENV:Body> </SOAP-ENV:Envelope>
A <dispositionReport>
has a
<result>
subelement with an
errno
attribute. The
<result>
subelement also has an
<errInfo>
subelement with an
errCode
attribute. The value of the
errCode
attribute must be one of the error codes
that are identified in Appendix A of the UDDI
Programmer’s API specification. In this example, the
errCode
value is
E_accountLimitExceeded
. The value of the
<errInfo>
element is a textual explanation
of the error that can be displayed to a user.
When designing a program that interacts with a UDDI registry, your
program needs to be prepared to handle SOAP Faults and react
appropriately. If you want your program to parse UDDI responses
intelligently, use the DOM API to parse a
<dispositionReport>
structure and then
implement specialized actions to handle different
errCode
situations.
Abstraction APIs, such as the UDDI API provided by Systinet and JAXR,
capture SOAP Faults and convert their contents into a specialized
exception. This action allows you to write a program that has a
familiar try/catch block to handle SOAP Fault scenarios, rather than
using the DOM API to parse a
<dispositionReport>
.
Using the rest of the Publishing API is straightforward. If you have
the UUID of one major data structure element, you can use the
delete_
messages to destroy data in the registry.
If you want to insert or update data in a UDDI registry, construct a
valid data structure, such as a
<businessEntity>,
and then use one of the
save_
messages.
Since this chapter has already covered the major talking points of every major UDDI data structure, demonstrating Publishing APIs in full form would be repetitive. At this stage, you have all the necessary tools to work with the UDDI Programmer’s and Data Structure specifications.
When working with the Publishing API, keep a couple of points in mind:
Your program signals the difference between a creation and an update
by the value of a document’s UUID fields on a
save_
message. If a save_
message is used for updating an existing document in a UDDI registry,
the value of the existing document’s UUID is placed
in the input document’s UUID field. If a
save_
message is used to create a new document in
a UDDI registry, the UUID should be left blank. For example, if you
wanted to update a <businessEntity>
document
with a fictional UUID of 43
, then you would create
a <businessEntity>
document, fill it with
the contents you want stored in the registry, and then set this
document’s businessKey
attribute
to 43
. However, if you wanted to insert a new
registration into the registry, the businessKey
value would be ""
.
Be careful when using the delete_
and
save_
messages. If the structure you are updating
has a number of subelements, such as a
<businessEntity>
, you can inadvertently
destroy them by removing their containment. If you delete a
<businessEntity>
, it will delete all
<businessService>
and
<bindingTemplate>
elements contained within
the <businessEntity>
. It will not delete a
<businessService>
referenced by the
<businessEntity>
, which would occur only if
the <businessService>
is contained with a
different <businessEntity>
. For example, if
you want to update a <businessEntity>
document using a save_
message, you might
accidentally delete <businessService>
and
<bindingTemplate>
structures in the process.
If the existing <businessEntity>
element
stored in a UDDI registry has
<businessService>
or
<bindingTemplate>
structures, but the
<businessEntity>
document used as input to
the save_
message does not have those same
subelements, the <businessService>
and
<bindingTemplate>
subelements will be
destroyed automatically as part of the update process.
<tModel>
documents are never fully
destroyed. When you use the <delete_tModel>
message, a <tModel>
element saved in the
registry is merely hidden. Hidden documents can be located through
<get_tModelDetail>
and
<get_registeredInfo>
messages, but will not
be displayed by any find_
queries. This behavior
ensures that the details associated with any
<tModel>
are still available to anyone who
may currently implement the specifications referred by the
<tModel>
. <tModel>
documents can be unhidden by using the
<save_tModel>
message.