Web services : Q: How can I use UDDI to describe my web service?

Q: How can I use UDDI to describe my web service?
A: Refer to this Eye on Smalltalk article.
Using the Web Services feature of VisualAge Smalltalk with Universal Description Discovery and Integration (UDDI)
Technote (FAQ)
Operating System(s)
Windows, Solaris, AIX, OS/390, z/OS, Linux,
Software version
6.0, 6.0.1, 6.0.2, 6.0.3, VA Smalltalk 7.x, 8.x
Explore the capabilities of using UDDI with VisualAge Smalltalk Web services
This technote was originally published as an Eye on SmallTALK article by Paul Blizniak.
This paper introduces you to using the new Web services feature with UDDI [1] in the latest release of VisualAge Smalltalk Web services. As such, it assumes basic knowledge of UDDI, Web Services Description Language (WSDL) [2], VisualAge Smalltalk Enterprise and the Web services feature in particular.
The UDDI specification provides a platform independent way of describing, discovering and integrating business services using the Internet. The UDDI API is exposed as a Web service via WSDL. WSDL is a general purpose XML language for describing the interfaces and protocol bindings of network services. The background for this paper is the best practices document “Using WSDL in a UDDI Registry”, which can be found at the UDDI web site[1]. This paper expands on that document and shows how to use Smalltalk to both publish and find services in a UDDI registry.
Publishing the Insurance Example
Let’s assume that we want to take the existing SstWebServicesInsuranceExample that is shipped with the Smalltalk product and publish it to UDDI. The steps that follow illustrate how to do that.
Create the implementation WSDLs The UDDI web site already contains two interface WSDLs that describe both the inquiry and publish APIs for UDDI. What’s missing are the implementation WSDLs that list the network endpoints which actually implement those interfaces. There are several public UDDI registries on the Internet, or you can setup your own private UDDI node. Here is an example of an implementation WSDL for inquiries that uses the public IBM test registry:
Sample WSDL
<?xml version="1.0"?>
<definitions name="VASTWebServiceUddiV1InquireExample"
xmlns:uddi-inquiry="urn:uddi-org:inquiry" >
<import namespace="urn:uddi-org:inquiry"
<service name="IBMPublicUddiV1InquiryService">
<documentation>VAST Web Service UDDI V1 Inquire API Example</documentation>
<port name="IBMPublicUddiV1Inquiry" binding="uddi-inquiry:InquireSoap">
<soap:address location="http://www-3.ibm.com/services/uddi/testregistry/inquiryapi"/>
There are a couple of important things to remember about the above WSDL. First, notice the import statement that brings in the inquire interface WSDL for the V1 API. There is a V2 API specification, but the examples here don’t use it since it was still being defined at the time this paper was written. Second, the binding attribute of the port element must match the binding name that is defined in the imported interface WSDL.
Using this WSDL as a template, an implementation WSDL for publishing can easily be created.
Deploy the WSDLs into a container Now that you have created the implementation WSDLs, it is time to deploy them into a container so that their services can be invoked.
Sample code:
[ |aContainer |
aContainer := SstWSContainer containerNamed:'VastSampleUddiV1Container'
ifNone: [ SstWSContainer createContainerNamed: 'VastSampleUddiV1Container' ].
aContainer deploy: 'http://vasthost:63001/ibmUddiV1inquiry.wsdl'.
( aContainer serializationManager schemaNamed: 'urn:uddi-org:api' )
attributeFormDefault: 'unqualified'.
aContainer deploy: 'http://vasthost:63001/ibmUddiV1publish.wsdl'.
( aContainer serializationManager schemaNamed: 'urn:uddi-org:api' )
attributeFormDefault: 'unqualified'.
System logError: 'Deployment completed' ] fork.
The code above assumes a http server on vasthost:63001 exists to serve up the WSDLs. Notice the code following each of the deploy statements. There appears to be a problem with the UDDI V1 schema, which says that all attributes must be qualified. However, none of the API calls will work if the attributes are qualified. The workaround is to change the default behavior to ‘unqualified’, after each service is deployed. This has been fixed in the V2 schema.
Create a tModel
The UDDI tModel data structure describes compliance with a shared (usually) design or specification. In our case, this is represented by the file SstWSInsurancePolicyInterface-interface.wsdl (the interface WSDL that represents the insurance policy API). The overviewDoc field will point to this WSDL document, and the tModel will be categorized as a ‘wsdlSpec’ tModel. Since we don’t have classes that represent the many different structures used in the UDDI API, we will use mapped elements instead. See the section ‘Using the VisualAge XML Mapping Parser’ in the VisualAge Users Guide for more information on when and how to use mapped elements.
Here is the code:
[ |authToken aContainer publishService result saveTmodel tModel keyedRef catBag overview|
aContainer := SstWSContainer containerNamed: 'VastSampleUddiV1Container'.
publishService := aContainer
serviceNamed: 'IBMPublicUddiV1PublishService'
inNamespace: 'urn:uddi-org:publish_impl'.
“Get a ‘categoryBag’ element as defined by the UDDI schema”
catBag := (aContainer serializationManager
inNamespace:'urn:uddi-org:api') asMappedElement.
“Get a ‘keyedReference’ element as defined by the UDDI schema”
keyedRef := (aContainer serializationManager
inNamespace:'urn:uddi-org:api') asMappedElement.
“Set the correct values for a ‘wsdlSpec’ category”
keyedRef keyName: 'uddi-org:types';
keyValue: 'wsdlSpec';
“The following key value is the same constant in all UDDI registries”
tModelKey: 'UUID:C1ACF26D-9672-4404-9D70-39B756E62AB4'.
catBag keyedReference: (Bag with: keyedRef).
overview := (aContainer serializationManager
inNamespace:'urn:uddi-org:api') asMappedElement.
“Set the URL to where the interface WSDL can be found”
overview overviewURL: 'http://vasthost:63001/SstWSInsurancePolicyInterface-interface.wsdl'.
“Set the tModel structure”
“The empty tModelKey value indicates that we are creating a new one”
tModel := (aContainer serializationManager
inNamespace:'urn:uddi-org:api') asMappedElement.
name: 'VAST Web Services Insurance Interface';
overviewDoc: overview;
categoryBag: catBag;
tModelKey: ''.
authToken := (aContainer serializationManager
inNamespace:'urn:uddi-org:api') asMappedElement.
“Set the authToken to the correct userid and password for publishing to this UDDI node”
authToken generic: '1.0'; userID: 'wstkDemo'; cred:'wstkPwd'.
“Invoke the get_authToken operation and save the result for later”
result := publishService get_authToken: authToken.
saveTmodel := (aContainer serializationManager
inNamespace:'urn:uddi-org:api') asMappedElement.
“Set the saveTmodel structure so we can save it in UDDI”
authInfo: result authInfo;
generic: '1.0';
tModel: (Bag with: tModel).
“Invoke the save_tModel operation and inspect the result”
result:= publishService save_tModel: saveTmodel.
result tModel first tModelKey inspect.
] fork.
Notice in the code above that those elements that contain collections of other elements, as defined in the UDDI schema, must be set using a collection. For example, the ‘categoryBag’ element holds zero or more instances of ‘keyedReference’ elements, which is why you see code like this:
catBag keyedReference: (Bag with: keyedRef).
If this operation is successful, the inspector window will contain a tModelKey that was generated as a result of the save_tModel call. This value needs to be saved because we will use it later on.
Create a businessService
The businessService data structure contains one or more bindingTemplate structures. It is there that the network address of the service is listed (in the accessPoint element). It is the location attribute of the address element (in our insurance implementation WSDL file SstWSInsurancePolicyInterface.wsdl) that will be listed in the accessPoint. Also included in the bindingTemplate, we need to create a tModelInstanceInfo structure that refers to the reusable ‘wsdlSpec’ tModel we created in step #3 above. This is how you define the technical specifications for interacting with the service endpoint listed in the accessPoint element.
Since a businessService can only be created in the context of a businessEntity, we need to create that as well. In one save_business operation, we can create a new businessEntitiy, a new businessService, and a new bindingTemplate.
The following code shows how:
[ |authToken aContainer publishService result saveBusiness business tModelKey businessService bindings bindingTemp accessPoint tModelDet tModelInfo businessServices|
tModelKey := ‘UUID: the tModelKey that was generated in step #3 above’.
aContainer := SstWSContainer containerNamed: 'VastSampleUddiV1Container'.
publishService := aContainer
serviceNamed: 'IBMPublicUddiV1PublishService'
inNamespace: 'urn:uddi-org:publish_impl'.
business := (aContainer serializationManager typeNamed:'BusinessEntity' inNamespace:'urn:uddi-org:api') asMappedElement.
“The empty businessKey value signifies that we are creating a new one”
business name: 'VAST Wasteland '; businessKey: ''.
businessServices := (aContainer serializationManager
inNamespace:'urn:uddi-org:api') asMappedElement.
businessService := (aContainer serializationManager
inNamespace:'urn:uddi-org:api') asMappedElement.
name: 'SstWSInsurancePolicyInterface';
serviceKey: ''.
bindings := (aContainer serializationManager
inNamespace:'urn:uddi-org:api') asMappedElement.
bindingTemp := (aContainer serializationManager
inNamespace:'urn:uddi-org:api') asMappedElement.
bindingTemp bindingKey: ''.
accessPoint := (aContainer serializationManager
inNamespace:'urn:uddi-org:api') asMappedElement.
“Since the accessPoint element is defined in the UDDI schema as a string, we use the #object: selector to set the value (from the location attribute in the WSDL file)”
accessPoint URLType: 'http'; object: 'http://vasthost:63003/SstWSServlet'.
tModelDet := (aContainer serializationManager
inNamespace:'urn:uddi-org:api') asMappedElement.
tModelInfo := (aContainer serializationManager
inNamespace:'urn:uddi-org:api') asMappedElement.
“Set the tModelInfo with the key that describes the interfaces for the above accessPoint”
tModelInfo tModelKey: tModelKey.
“Now set the correct containment relationships using the elements created above”
tModelDet tModelInstanceInfo: (Bag with: tModelInfo).
accessPoint: accessPoint;
tModelInstanceDetails: tModelDet.
bindings bindingTemplate: (Bag with: bindingTemp).
businessService bindingTemplates: bindings.
businessServices businessService: (Bag with: businessService).
business businessServices: businessServices.
saveBusiness := (aContainer serializationManager
inNamespace:'urn:uddi-org:api') asMappedElement.
saveBusiness businessEntity: (Bag with: business).
authToken := (aContainer serializationManager
inNamespace:'urn:uddi-org:api') asMappedElement.
generic: '1.0';
userID: 'wstkDemo'; cred:'wstkPwd'.
result := publishService get_authToken: authToken.
“Invoke save_business operation and inspect the result”
authInfo: result authInfo;
generic: '1.0'.
result:= publishService save_business: saveBusiness.
result inspect.
] fork.
If this operation is successful, the inspector window will contain a businessDetail mapped element, showing the final results of saving the just registered information.
It is important to note that the above code represents the minimum information required in order to publish a business with a service. There are many optional elements in those structures that typically would be specified. For example, many of the structures contain a ‘description’ element, and/or allow you to identify/categorize them using one or more predefined taxonomies.
Using information stored in UDDI
Now that we have successfully published to UDDI, how do we look for existing services that we want to use? How do we invoke those services once we have found them? Well, there are a number of ways to query a registry. At a high level, we need to do the reverse of what we did above, which was to take our insurance policy WSDL files and ‘save’ them in UDDI. In this case, we need to get the right information out of UDDI so we can create our own implementation WSDL, and then deploy as usual.
The first thing we need to do is find the tModel that represents the interface specification of a service we want to invoke. As long as that tModel is categorized as being of type ‘wsdlSpec’, then the overviewDoc element should contain a link to the interface WSDL. It is that link that will be specified in the ‘import’ element of our implementation WSDL. The next step is to find a service that has a bindingTemplate that references the tModel we found before. The bindingTemplate contains the accessPoint element which is the network endpoint of the service (only accessPoint elements whose URLType is ‘http’ or ‘https’ are supported at this time). It is that value that will be listed in the location attribute of the address element of our implementation WSDL. We now have all the information we need to finish creating the WSDL file and deploy it.
There are a few points to remember about this scenario. Since we know the name of the tModel we’re looking for, we can find it directly. Typically, you will use a categoryBag to narrow down the returned list (be sure that one of the categories is the type ‘wsdlSpec’). Also, since you can only find a service in the context of a businessEntity, we will do a find_business call with a tModelBag first. Then we can iterate through the businesses until we find one with a service that we’re compatible with.
Here’s the code:
[ |findTmodel aContainer service result getTmodel tModelKey tModelBag findBusinessObj serviceDet|
aContainer := SstWSContainer containerNamed: 'VastSampleUddiV1Container'.
service := aContainer serviceNamed: 'IBMPublicUddiV1InquiryService' inNamespace: 'urn:uddi-org:inquiry_impl'.
findTmodel := (aContainer serializationManager typeNamed:'FindTModel' inNamespace:'urn:uddi-org:api') asMappedElement.
“Find the tModel we published before”
findTmodel generic: '1.0';
name: 'VAST Web Services Insurance Interface'.
result := service find_tModel: findTmodel.
“Grab the tModelKey from the result above and get the details on this tModel”
getTmodel := (aContainer serializationManager typeNamed:'GetTModelDetail' inNamespace:'urn:uddi-org:api') asMappedElement.
getTmodel generic: '1.0';
tModelKey: (Bag with: (tModelKey := result tModelInfos tModelInfo first tModelKey)).
result := service get_tModelDetail: getTmodel.
“Print the value of the overviewDoc element. This will be the ‘import’ in the WSDL file”
Transcript cr; show: 'The overviewURL= ', result tModel first overviewDoc overviewURL.
tModelBag := (aContainer serializationManager typeNamed:'TModelBag' inNamespace:'urn:uddi-org:api') asMappedElement.
tModelBag tModelKey: (Bag with: tModelKey).
“Find all the businesses that reference our tModel”
findBusinessObj := (aContainer serializationManager typeNamed:'FindBusiness' inNamespace:'urn:uddi-org:api') asMappedElement.
findBusinessObj tModelBag: tModelBag; generic: '1.0'.
result := service find_business: findBusinessObj.
serviceDet := (aContainer serializationManager typeNamed:'GetServiceDetail' inNamespace:'urn:uddi-org:api') asMappedElement.
“This is where we iterate thru all the businesses that were found from the call above and print out enough info to decide which service’s accessPoint we will put in our WSDL file (as the ‘location’ attribute of the ‘address’ element)”
result businessInfos businessInfo do: [:bus |
Transcript cr; cr; show: 'Business name= ', bus name.
bus serviceInfos serviceInfo do: [:services |
Transcript cr; tab; show: 'Service name= ', services name.
serviceDet serviceKey: (Bag with: services serviceKey).
servicesResult := service get_serviceDetail: serviceDet.
servicesResult businessService do: [: busService |
busService bindingTemplates bindingTemplate do: [:bind |
( 'http*' match: (accessPoint := bind getElementNamed: 'accessPoint') URLType) ifTrue: [ Transcript cr; tab; tab; show: 'AccessPoint = ', accessPoint object.]]]]].
] fork.
You should now have enough information written to your Transcript to be able to create an implementation WSDL file for a service that you want to invoke. One other thing to note about the code above is the way we get an ‘accessPoint’ element from a bindingTemplate. We must use the #getElementNamed: method since this element contains data and an attribute, but no sub-elements.
As you can see by the above examples, it can be a lot of work to do even relatively simple tasks with UDDI. It may be useful to factor the above code into reusable classes that can then be better utilized by your Smalltalk applications. When the UDDI V2 API becomes standardized, the examples above should provide a useful template from which to start exploring it. The VisualAge team continues to monitor the progress of UDDI and other standards in the industry. Although acceptance of it has been slow, we expect that it will continue to grow and become an important part in the future of Web services and e-business. References and Other Sources of Information
Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John, Design Patterns: Elements of Reusable Object-Oriented Software, Massachusetts: Addison Wesley, 1995.
Web Page URLs
[1] Universal Description, Discovery, and Integration (UDDI) http://www.uddi.org
[2] Web Services Description Language (WSDL) 1.1 http://www.w3.org/TR/wsdl
Web Services General Information http://www-106.ibm.com/developerworks/webservices/
XML Schema http://www.w3.org/XML/Schema