Building Clients That Use Industry Standard WSDL
March 5, 2002
Our past two columns have focused on using standardization as a way of describing and implementing Web Services. First, we wrote the WSDL describing how to discover and order pencils. The discovery and order WSDL documents were then registered by PencilSellers.org at http://uddi.microsoft.com. Using the WSDL interface definitions, we implemented the interfaces as Web Services. These Web Services were hosted at http://www.pencilsellers.org and http://www.coldrooster.com. The live endpoints were registered with UDDI to allow clients that knew how to use the interfaces to discover the existence of those endpoints either at design time or run time. The discovery and usage of endpoints that implement both the discovery and order service is the subject of this week's column.
In particular, we will be looking at how to build a client that knows how to use UDDI to discover only those businesses that implement both interfaces. Once the client can discover those interfaces, it can provide an interface that allows a user to search for items and to place orders. The major topics of the column are:
- Creating the proxy based on WSDL found in UDDI.
- Writing code (using the Microsoft® UDDI SDK) that finds businesses that implement both endpoints.
- How to use those discovered endpoints in the client.
Some extra links are included at the very end of the article pointing to other items that you will need in order to use the sample.
Creating the Proxies
The first thing that the client developer needs to do is to create proxies for both the Discovery and Order bindings. This is a little awkward, because the UDDI link within Microsoft® Visual Studio® .NET's Add Web Reference feature only allows searching for existing endpoints, not for WSDL-based tModels that have no associated implementations. To do this, we can use the Add Web Reference feature, but we have to do so in a slightly different way. The following steps will allow you to use the Add Web Reference dialog when adding the Web Reference for the Discovery service stored in UDDI.
With your project open, on the Project menu, click Add Web Reference.
In the address bar, type http://uddi.microsoft.com.
In the Search area on the home page, click Advanced Search.
We know that the Web Service we are searching for begins with "http://pencilsellers.org/". Place that string in the "Search for" field. Select "tModel by name" in the combo box. (Shown in Figure 1.)
Figure 1. Searching for the Discovery interface
When you click the ">" button, two tModels come up:
After each tModel name, there is a link that reads simply, "(details)". This is also shown in Figure 2. Click this details link for the discovery tModel.
Figure 2. The search results
The next screen has a number of sections. The first section on the page says "tModel detail." Within that section you can see the UUID (same as a GUID) that uniquely identifies the tModel. Copy this value, including the UUID: portion, to somewhere for future reference. I recommend using a combination of cut and paste with Notepad—it's very easy to transpose values when writing down a string that is this long.
Find the section labeled Overviews. This has a URL pointing to the WSDL document. Click the link to http://www.pencilsellers.org/WSDL/PencilDiscovery.WSDL.
At this point, the WSDL has been pulled in. Click Add Reference and the proxy will be added to your project.
Repeat steps 1-9 until all interfaces have been successfully added to your project. For our example, this means repeating the steps in order to add the Order proxy to the project.
At this point, your project has two proxy classes that have absolutely no idea where a physical endpoint even exists. Believe it or not, that's exactly what we wanted. The client will now have to find endpoints through UDDI. It will do so using those tModel ID values that you wrote down just a few moments ago.
Finding Endpoints in UDDI
For the user interface, we would like to provide the user with a list of businesses to interact with. Each business will have related pieces of information: the endpoint of the Discovery and Order interface operated by that business. To store that information, I created a class: PencilSellerEndpoint. The class exposes three properties that store the information:
- BusinessName: String
- OrderEndpoint: Uri
- DiscoveryEndpoint: Uri
Using this class, another entity could easily maintain a list of identifying information about a business that implements both tModels. For this example, the client form has a private member variable that maintains an array of PencilSellerEndpoints.
The actual information-gathering happens using the Microsoft UDDI SDK. The SDK documentation is a little thin right now. The good news is that the SDK is simply a .NET-based client for the UDDI Specification. By grabbing a copy of the UDDI Version 2.0 API Specification from http://www.uddi.org, you can figure out almost everything. Names of queries and the data returned are all standardized, allowing developers to take advantage of a real industry standard with all the documentation that comes with it.
To find the endpoints, we need to send a query to UDDI. To find the related endpoints, the client needs to find all businesses that implement both the Order and Discovery Web Service. The sample client handles discovery in the GetSellerEndpoints method. What follows is a dissection of that routine. To find only businesses that implement both interfaces, the code has to issue a find_business UDDI query. The query does an exact name match on the two UUIDs. The tModel UUIDs are stored in the client application for use when building the search query. These UUIDs are stored in constants as:
Const DiscoveryUUID As String = _ "UUID:C6DFDB28-9E83-4350-805B-54256670E420" Const OrderUUID As String = _ "UUID:8248AA8E-C977-4F1D-AF22-C72CF295B0A2"
The code uses these UUIDs to find businesses implementing the corresponding tModels. The following query will locate those businesses:
' Look for the information over at Microsoft's ' public UDDI site. Inquire.Url = "http://uddi.microsoft.com/inquire" Dim findBus As New Microsoft.Uddi.FindBusiness() ' Set up the find to look only for businesses ' that implement both the Discovery and Order ' bindings. findBus.FindQualifiers.Add( _ Api.FindQualifierEnum.exactNameMatch) findBus.TModelKeys.Add(DiscoveryUUID) findBus.TModelKeys.Add(OrderUUID) ' Get the list. Dim busList As BusinessList = findBus.Send()
With the list in hand, the code makes sure that we did not get an empty result set. Each business contains a list of the services they implement. To find out the location endpoint and the UUID of the tModel the service implements, the code needs to execute a get_serviceDetail UDDI call. This is done while looping through the list of services contained by each business.
For Each bus In busList.BusinessInfos m_pencilSellerEndpoints(index) = New _ PencilSellerEndpoint() m_pencilSellerEndpoints(index).BusinessName = _ bus.Name cboPickBusiness.Items.Add(bus.Name) For Each aSvc In bus.ServiceInfos getSvcDetail = New GetServiceDetail() getSvcDetail.ServiceKeys.Add(aSvc.ServiceKey) svcDetail = getSvcDetail.Send() FillInEndpoints(m_pencilSellerEndpoints(index), _ svcDetail) If Not (m_pencilSellerEndpoints(index). _ DiscoveryEndpoint Is Nothing) And Not _ (m_pencilSellerEndpoints(index).OrderEndpoint _ Is Nothing) Then ' We've found a pair of endpoints. ' move on to the next aSvc Exit For End If Next ' Now that the data is stored, increment the index being _ ' manipulated. index = index + 1 Next
A given service can implement more than one binding. This means that the code needs to search through all the bindings contained by the service until it finds the ones it is looking for. This search happens in the FillInEndpoints method. If one service does implement both bindings, the code will move on to the next business. Otherwise, the code continues to investigate services. The search simply iterates through all binding templates related to the service. In UDDI terms, a binding template describes which tModels a particular Web Service implements.
Private Sub FillInEndpoints(ByRef theEndpoint As _ PencilSellerEndpoint, ByRef svcDetail As ServiceDetail) ' Let's do some checking to see if everything looks ' OK. If (svcDetail.BusinessServices Is Nothing) Then Return If (svcDetail.BusinessServices.Count < 1) Then Return If (svcDetail.BusinessServices(0).BindingTemplates _ Is Nothing) Then Return If (svcDetail.BusinessServices(0).BindingTemplates.Count _ < 1) Then Return ' Just store the first endpoint that ' this business implements. Dim bindingTemp As Binding.BindingTemplate For Each bindingTemp In svcDetail.BusinessServices(0). _ BindingTemplates ' A couple more checks to see if everything looks right ' If we exit, something went wrong (these items should ' not be empty). If (bindingTemp.TModelInstanceDetail.TModelInstanceInfos _ Is Nothing) Then Exit For End If If (bindingTemp.TModelInstanceDetail.TModelInstanceInfos. _ Count < 1) Then Exit For End If ' Figure out if the binding maps ' to the Discovery or Order WSDL. ' Then, store in the appropriate ' member variable. If (bindingTemp.TModelInstanceDetail. _ TModelInstanceInfos(0).TModelKey.ToUpper() = _ DiscoveryUUID.ToUpper()) Then theEndpoint.DiscoveryEndpoint = New _ Uri(bindingTemp.AccessPoint.Text) ElseIf (bindingTemp.TModelInstanceDetail. _ TModelInstanceInfos(0).TModelKey.ToUpper() = _ OrderUUID.ToUpper()) Then theEndpoint.OrderEndpoint = _ New Uri(bindingTemp.AccessPoint.Text) End If Next End Sub
To use these discovered endpoints, the client application maps the business name to the endpoints. When it comes time to call a Web method, the code looks up the binding endpoint for a particular business and sets the Url property for the proxy object. The code can then call any Web methods associated with the binding on that particular endpoint.
Dim theEndpoint As PencilSellerEndpoint = _ GetPencilSellerEndpoint(Me.cboPickBusiness.Text) Dim svc As New org.pencilsellers.www.DiscoveryBinding() ' Set the Url and call the method. svc.Url = theEndpoint.DiscoveryEndpoint.ToString()
The GetPencilSellerEndpoint function just looks up the PencilSellerEndpoint object that has the particular business name. The end result of this work is a client application that can find and use any existing implementations of the Discovery and Order bindings. Figure 3 shows the client created for this example.
Figure 3. PencilSellers.org Discovery and Order client
UDDI can be used by Web Service client applications to discover implementations of Web Services that a client application knows how to use. Some thought has to go into the client in order to use UDDI effectively. The payback is extra flexibility for your applications.
- Microsoft UDDI SDK: In order to run the example, you will need to have the UDDI SDK installed.
- UDDI Programmer's API Specification: The documentation in the Microsoft UDDI SDK is in beta as of this writing. A lot of information is missing. Luckily (for us) the only documentation we really need is the UDDI API Specification. Microsoft's UDDI implementation must implement the specification, so the documentation for that specification is valid for implementations by Microsoft, IBM, or anyone else.
At Your Service
Scott Seely is a member of the MSDN Architectural Samples team. Besides his work there, Scott is the author of SOAP: Cross Platform Web Service Development Using XML (Prentice Hall—PTR) and the lead author for Creating and Consuming Web Services in Visual Basic (Addison-Wesley).