[Cache from http://www.alphaworks.ibm.com/tech/uddi4py; please use this canonical URL/source if possible.]
Using the UDDI Public Registry with Python
Julio Ruano
Software Engineer, IBM
June 2002
The Universal Description, Discovery and Integration (UDDI) Registry serves as a central location for publishing/acquiring information about various businesses, the services that they provide, and any technical interfaces they may use. Users can access the registry via an Internet graphical interface or programmatically through SOAP messaging. In this article Julio Ruano introduces UDDI4Py, a Python package that facilitates the transport and processing of the various SOAP messages that the UDDI 2.0 API supports. It available for download at IBM alphaWorks, and its usage as well as some examples are provided below.
Software systems are moving away from tightly bundled integrated programs, to ones that are loosely related and rely on open communication standards.At the heart of this evolution is the Web Service, defined as a unit of sofware that performs specific task(s), and can be assembled as a distributed application or to function as certain business process(es) [1]. It's main advantage is that it conforms to accepted standards, and open messaging interfaces which help interoperability with other services. This allows developers to easily interchange services as needed without worrying about having to write tedious code for the sole purpose of making them work together. Central to the idea of swapping programs in and out of these open distributed systems is the ability to discover and/or make available needed services. The UDDI Public Registry serves as a central location on the Internet to allow developers to incorporate and/or expose available services (information) to integrate into Web applications.
The Python scripting language fits very easily into this new Web services development environment. The standard Python system comes with a variety of modules well suited towards Internet development. Programmers can easily create robust Internet software using tools for http, ftp, smtp transport, XML processing, socket and URL connections, and much more. The interpreter is also available for a wide variety of platforms from the PalmOS to Linux on the mainframe, and programs are highly portable. Python is object-oriented, which gives it a significant advantage when compared to other scripting languages (PERL, Rexx, etc...). It is very helpful to model XML data as objects, and this is easily implemented using Python's object-oriented constructs. The Python web site is a good starting place to get all of the information needed to get started using this powerful language. There are also many other articles pertaining to Python development hosted on the IBM developerWork's Web Services Zone.
In this article I will provide an introduction to UDDI4Py, explaining set-up and usage of the package. I am assuming that the reader is familiar with using Python, and also has some experience with the UDDI API. The Resources section contains links to all of the information needed to fulfill these prerequisites.
UDDI4Py Installation
The following are required:
python -V |
You should see
the following reply:
Python 2.2.1 |
If this is not the case, you should download and install the 2.2.1 (or most recent) version from the Python website.
python find_business.py |
<businessList generic="2.0"
operator="www.ibm.com/services/uddi" truncated="false" xmlns="urn:uddi-org:api_v2">
<businessInfos> <businessInfo businessKey="FDFDBBA0-A7D3-11D5-A30A-002035229C64"> <name xml:lang="en"> IBM WSTK Tutorial </name> <description xml:lang="en"> IBM WSTK Tutorial </description> <serviceInfos> <serviceInfo businessKey="FDFDBBA0-A7D3-11D5-A30A-002035229C64" serviceKey="12F63B90-A7D4-11D5-A30A-002035229C64"> <name xml:lang="en"> NasdaqQuotes </name> </serviceInfo> </serviceInfos> </businessInfo> </businessInfos> </businessList> |
Linux
As of this time, the UDDI4Py has not been tested under Linux.
Using UDDI4Py
The UDDI4Py package is organized into several modules, each of which help with the construction and processing of UDDI SOAP messages to and from the Registry. In order to facilitate these XML transactions, the toolkit consists of Python classes which represent corresponding XML defined structures of the API. Each Python class contains an appropriate toDOM() method, which converts the underlying Python object into a DOM structure. This helps the transition of the Python object --> to a DOM structure --> to an XML string format (and also from XML string --> to DOM --> to Python object). The Python classes are grouped together as follows, according to certain shared characteristics:
All Publish API messages require authenticated access information, whereas the Inquiry API messages do not. It is also necessary to connect to the Publish API via an SSL connection.
I will now walk you through a simple find_business request, using a UDDI4Py simple client as an example. You should consult the UDDI Version 2.0 API Specification as a reference for the message arguments sent to and response XML received from the respective API's.
Listing 1: A UDDI SOAP request to find business.
<?xml version="1.0" encoding="UTF-8" ?>
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"> <Body> <find_business generic="2.0" xmlns="urn:uddi-org:api_v2"> <name>some business name</name> <categoryBag> <keyedReference keyName="some keyName" keyValue="some keyValue" tModelKey="some tModelKey" /> </categoryBag> </find_business> </Body> </Envelope> |
The first line describes this as being an XML defined document with its character encoding as Unicode. The next line lets the XML processor know that this is a SOAP envelope that wraps a UDDI specific request inside the <Body/> tag. The UDDI specific elements begin at line 4, where the <find_business/> tag denotes the API request. This tag also has two important attributes namely "generic" and "xmlns". The "generic" attribute tells the UDDI API that this is a version 2.0 request, where the "xmlns" attribute states that all of the contents of the find_business element are contained within the "urn:uddi-org:api_v2" namespace. The <name/> tag contains the name of the business you wish to search for. The content of the <categoryBag/> tag, denotes established keyedReferences which are used as categorizations for this business.
Listing 2: Expected XML response from the find_business request made above.
<?xml version="1.0" encoding="UTF-8" ?>
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/"> <Body> <businessList generic="2.0" xmlns="urn:uddi-org:api_v2" operator="www.ibm.com/services/uddi" truncated="false"> <businessInfo businessKey="some business key"> <name xml:lang="en"> some business name </name> <description xml:lang="en"> some business description </description> <serviceInfos> <serviceInfo businessKey="some business key" serviceKey="some service key"> <name> some service name </name> </serviceInfo> </serviceInfos> </businessInfo> </businessInfos> </businessList> </Body> </Envelope> |
The UDDI return elements again begin at line 4 with the <businessList/> tag. This denotes the start of the list of businesses found from the name in the request. There can be multiple matches because the search on the value of name is done from left to right. For example, the name "IBM" can match IBM, IBM Software, IBM Global Services, etc... The <businessInfo/> tag at line 5 denotes the information (description, name, etc...) for a single business. The <serviceInfos/> tag at line 12 contains one or more <serviceInfo/> elements that define a specific service for the business (A business does not have to define a service and this can be blank).
It is becoming quite clear from this simple example that it is tedious to deal with the creation and processing of the XML used for communicating with the UDDI API. The UDDI4Py library abstracts the internal handling of XML and provides a set of classes with methods to deal with this messaging.
Example 1: A complete program to send and process a find_business UDDI SOAP request.
# ****************
# Import the modules. # **************** from UDDI4Py.client import * from UDDI4Py.requestDOM import * from UDDI4Py.responseDOM import * # ********************************
# *********************************************
# Construct a proxy object used to send
a response object to the
# Construct a findBusiness request object.
This object maps to the
# Set the name for the name of the business
to search for.
# These next statements use constructs
that are defined by the UDDI
# Set the tModelKey for the keyedReference.
# Set the keyName for the keyedReference.
# Set the keyValue for the keyedReference
# Construct a categoryBag object.
# Set the keyedReferences to the keyRef
object created above.
# Set the categoryBag for the request
object.
# Send the request object to the API
and store the response DOM object in a
# Construct a businessList object from
the businessList DOM in
# Display the response as XML format.
All the objects defined by the
# Catch any errors or in particular a UDDIError object.
|
The above syntax should seem fairly familiar, if not please check the Resources section for some links to Python documentation. It is hopeful that the inline comments guide the flow of the logic for the program listed above. The basic steps for using the toolkit are:
1.) Instantiate a UDDIProxy obejct for communication
to the API's with the appropriate URL's.
2.) Instantiate an appropriate request object to
interface with the respective API.
3.) Set the message parameters for the request object
that is being used.
4.) Use the UDDIProxy object to send the request
object to the appropriate API and store the response in a local variable.
5.) Construct the appropriate response object using
the responseDOM stored in step 4. Make sure that the correct response
object
is instantiated,
as it should correspond to the return message explicitly stated in the
UDDI Specification for the request
message that was
sent.
6.) Process data using access methods provided in
the response object.
7.) The steps listed above should also be wrapped
in a try/except block in order to catch any unexpected UDDIError's.
** It is important to note that Python is a loosely-typed language, meaning that there is no strict type enforcement. In other words, a method that is expecting a string, can be passed an integer, list of objects, or anything. If this happens, a nasty exception will be raised when the string that was expected is actually referenced within the method at runtime (this same principle can be extended to improper referencing of return types). For the example above, the setNameStrings method is expecting a list of string values only. Anything else that is passed in will cause an exception to be raised at runtime (which may lead to countless hours of wasted time bug tracking). I have taken great care to explicitly state the parameter types (and return types) for each and every method where it is not very clear from the code. It is very convenient to pass values blindly around without worrying about their types, if special care is taken to ensure that the correct types are used. Please take the time to look over the methods you wish to use (examing argument and return types), so that you do not cause invalid types to be referenced. **
Example 1a: Result of running the program in example 1.
d:\Python21\UDDI4Py\examples\article\python listing2.py
<businessList generic="2.0" operator="www.ibm.com/services/uddi" truncated="false" xmlns="urn:uddi-org:api_v2"> <businessInfos> <businessInfo businessKey="DCE11330-78DA-11D6-8723-000629DC0A2B"> <name xml:lang="en"> Python Test Business </name> <description xml:lang="en"> This is a test business for UDDI4Py illustration. </description> <serviceInfos> <serviceInfo businessKey="DCE11330-78DA-11D6-8723-000629DC0A2B" serviceKey="013AB050-78DC-11D6-8723-000629DC0A2B"> <name xml:lang="en"> Python Test Service </name> </serviceInfo> </serviceInfos> </businessInfo> </businessInfos> </businessList> |
The user can also use all of the get methods provided by the businessList class in order to access member data. For the listing2 example, we could have easily done something like the following:
Listing 3: Example of using other access methods to retrieve data (Reference to code in example 1).
# Access the businessInfo data contained within
the businessList response object.
# The businessList contains a businessInfos structure, which contains an array of # businessInfo structures. businessInfo = response.getBusinessInfos().getBusinessInfoArray()[0] print businessInfo |
The above statement will print the businessInfo XML string format. The user can continue to extract data from the businessInfo object using any of its access methods.
Example 1 illustrated the simple technique for finding information from the registry. Often the user wants to publish data to the registry, and typically this involves sending a sequence of inquiries in order to use the results to build a request for a publish service. The next example serves as a template to demonstrate the technique for publishing information to the Registry. All of the Publish API messages require authenticated access, and as such, in order to use any of these API's you must have a registered account with a UDDI Operator Node. IBM provides a V2 Business Registry Beta where you can register for a free account and use any of the UDDI Publish API's.
Example 2: A complete program to publish updated business information.
# ****************
# Import the modules. # **************** from UDDI4Py.client import * from UDDI4Py.requestDOM import * from UDDI4Py.responseDOM import * # ********************************
# **********************************************
# These next steps illustrate a request to get an
authToken
# Please insert the appropriate user ID provided
by the operator
# Please insert the appropriate password for this
account.
# Send the request object to the API, and construct
an authToken
# These next steps illustrate a request to get a
complete businessDetails for
# Construct a getBusinessDetail request object.
# Set the businessKey request parameter.
# Send the request object to the API, and construct
a businessDetail
# Get the businessEntity object using the access
method provided
# Store in a local variable the first description
in the array.
# Set the description to something new.
# Set the businessEntity to this new description.
# These next steps illustrate a request to save the
business which
# Insert into the businessEntityArray request parameter,
the updated
# Set the authentication information from the authToken
that we
# Send the request object to the API, and construct
a businessDetail
# Display the response as XML format. All the objects
defined by the
# Catch any errors or in particular a DispositionReport UDDIError object.
|
If you notice, the listing above follows the same distinct technique outlined in example 1. Each request object that is created is meant to retrieve data that is needed to build the final saveBusiness request message object. It is a typical pattern for all publish requests since there needs to be an initial code section to retrieve the authentication information.
It is important to note that although the previous example was updating existing data in the registry, the same procedure can be followed in order to save new data to the registry. The only difference between saving new versus updating data, is that the initial keys are not set. This indicates to the registry that the data does not exist and to generate a unique key to identify this data.
Example 3: A complete program to publish new business information.
# *******************
# Import the modules. # ******************* from UDDI4Py.client import * from UDDI4Py.requestDOM import * from UDDI4Py.responseDOM import * # **************************************
# ************************************************************
# These next steps illustrate a request to get an
authToken
# Please insert the appropriate user ID provided
by the operator
# Please insert the appropriate password for this
account.
# Send the request object to the API, and construct
an authToken
# Construct a saveBusiness request object.
# Construct a businessEntity object.
# Construct a description object, and set it's text
description.
# Set the description array for the business object
to the description.
# Construct a name object, and set it's text name.
# Set the names array for the business object to
the name.
# Construct a businessService object.
# Set the description object's text to reflect the
service
# Set the description array for the service object
to the description.
# Set the name object's text to reflect the service
name.
# Set the names array for the service object to the
name.
# Construct a businessServices object.
# Set the businessServices array for the businessServices
object to the
# Set the businessServices object for the business
object to the
# Set the businessEntityArray for the request object
to the business.
# Set the authentication information from the authToken
that we
# Send the request object to the API, and construct
a businessDetail
# Display the response as XML format. All the objects
defined by the
# Catch any errors or in particular a UDDIError object.
|
You should also take notice from the previous example that not only is a new business being saved, but also a new service for this business is saved. This is accomplished by not specifying a key for the service object that is created for the business. The publishing of this new service could have be done in a separate call to the save_service API, but for this example it is intended to show how sometimes multiple requests can be combined into a single request to the registry.
The examples provided thus far have demonstrated the publishing of business and service information to the registry. Users of the UDDI Registry also have the capability of publishing technical references to Web interfaces (specifications) that they define. These technical references are called tModels by the UDDI Specification, and the interfaces that they reference are typically Web Services. The publishing of tModels to the registry follows the same outline as described in the previous publish examples. In using the UDDI Registry for publishing tModels, users make the discovery and integration of their Web Services easily accessible.
Example 4: A complete program to publish new a new tModel.
# *******************
# Import the modules. # ******************* from UDDI4Py.client import * from UDDI4Py.requestDOM import * from UDDI4Py.responseDOM import * # **************************************
# ************************************************************
# These next steps illustrate a request to get an
authToken
# Please insert the appropriate user ID provided
by the operator
# Please insert the appropriate password for this
account.
# Send the request object to the API, and construct
an authToken
# Construct a saveTModel request object.
# Construct a tModel object.
# Construct a description object, and set it's text
description.
# Set the description array for the business object
to the description.
# Construct a name object, and set it's text name.
# Set the names array for the business object to
the name.
# Set the tModelArray for the request object to the
tModel.
# Set the authentication information from the authToken
that we
# Send the request object to the API, and construct
a tModelDetail
# Display the response as XML format. All the objects
defined by the
# Catch any errors or in particular a UDDIError object.
|
There are a few other UDDI constructs that in conjunction with tModels, enable users of the registry to bind to Web Services through their applications. The bindingTemplate, tModelInstanceInfo, InstanceParms, and other constructs are described in more detail in the UDDI Programmer's guide. The process of discovering and publishing these structures using the UDDI Registry follow the same procedures outlined in the examples above.
These examples should provide you with the basis for formulating and processing the messages sent to and recieved from the UDDI API. So go visit the UDDI website to get a copy of the latest API Specification, get registered for an account with the IBM Operator Node, and get started publishing and integrating Web services with your Python applications.
Implementation Notes
The UDDI API is an XML based specification, and as such, the most natural
way to represent these messages is to model them using DOM structures.
A DOM structure kept in memory allows for an easy traversal to add data
as needed. It also provides for a manageable way to retrieve needed data
contained within the structure. The Python objects created using the UDDI4Py
toolkit, all use an underlying DOM to abstract the XML handling from the
user.
It is also important to note that XML adheres to the Unicode standard for text representation. The UDDI Specification supports this standard (since the defined messages are XML markup), and requires all requests to be UTF-8 encoded. Python provides support for Unicode data via the Unicode object. A Unicode string can be constructed in Python as follows:
Listing 4: Constructing Unicode data using Python.
description = u"Unicode test description \u00DC" |
The small u in front of the string indicates that this is a Unicode string. The \u00DC indicates a Unicode-Escape character to be inserted with an ordinal value of 0x00DC (the \ character). This Unicode string can now be set as a description for any one of the appropriate UDDI constructs. This allows users to publish data to the registry using any international character set. Please keep in mind that while working with character data outside the 7-bit ASCII range, if you try to display or write this data out to file, you may get the following result (depending on the default encoding used by your system):
Listing 5: Using character data not supported by default encoding.
# The response object contains Unicode character data.
print response |
Output (Exception thrown):
UnicodeError: ASCII encoding error: ordinal not in range(128) |
I have assumed in Example 1 and 2, that I was not working with Unicode data. The exception above would have been thrown upon execution of the print statement, had this not been the case. To protect your applications from this, you may want to do the following when using methods that depend on the default system encoding (such as print):
Listing 6: Protect against invalid encoding errors for character data.
# Convert the response to a Unicode string object, and display it using
utf-8 encoding.
print unicode(response).encode("utf-8") |
For a complete instruction on using Unicode strings in Python, please visit the website.
The Python Web services revolution
Python is evolving as a natural way to develop Web services. A number
of modules are bundled with the standard interpreter, as are hosted as
open source on developerWorks.
There is also a zone
on the developerWorks website that is completely dedicated to providing
information and resources on using and creating Web services. It is hopeful
that the UDDI4Py toolkit becomes a necessary indegredient to the Python
Web services recipe.
References
[1] Web
services architect, Part I: An introduction to dynamic e-business.
IBM DeveloperWorks 09-April-2001. Dan Gisolfi (IBM).
About the author
Julio Ruano is a Software Engineer working for the IBM UDDI Public
Node team. He has a BS in Computer Science from Florida International University
in Miami, Florida. As a member of the UDDI team, he is responsible for
the development and implementation of the API, and its conformance to the
specification. He is working hard trying to make developers aware of the
power and significance that the Python language brings to the Web services
arena. You can reach him at ruano@us.ibm.com.