Consuming Web Services through UDDI, SOAP and Weblets
Introduction
Web Services in the Microsoft .NET Framework make it easy to write components that communicate using HTTP GET, HTTP POST, and SOAP. Consumers of a Web Service do not need to know anything about the platform, object model, or programming language used to implement the service; they only need to understand how to send and receive SOAP messages (HTTP and XML).
Since the decentralized nature of Web Services enables both the client and the Web Service to function as autonomous units, there are countless ways to consume a Web Service. For example, a call to a Web Service can be included in a Web application, a middleware component, or even another Web Service.
This article provides a general overview of UDDI and describes how to consume web services through UDDI, SOAP and light-weight components (referred to here as Weblets).
Introduction to UDDI
UDDI, acronym for Universal Description Discovery and Integration is the name of a group of web-based registries that expose information about a business or other entity and its technical interfaces (API's). UDDI provides a way for businesses to publish information about their own services and find services they need from other businesses.
UDDI Business Registry
The UDDI Business Registry is a global, public, online directory that gives businesses a uniform way to describe their services, discover other companies' services, and understand the methods necessary to conduct e-business with a particular company. UDDI Project recently released second version of the UDDI Specification (on June 2001). The key features of the new version are support for complex organizations, improved internationalization, additional categorization and identifier schemes, and richer search capabilities. The UDDI Business Registry is an implementation of the UDDI version 2 specifications. Public UDDI registries are now fully operational.
Is there a charge for using the UDDI Registry?
There is no charge for using the services published in the registry. The goal of the free business registry is to enable companies to do business with new or current partners easily over the Web using XML.
Operator Sites
Operator Sites are the websites that maintain the public UDDI Business Registry. Microsoft and IBM are the companies that serve as operators of the registry at present. Ariba, which was also one of the former registry operator has now shifted to be a registrar. Hewlett-Packard Co. has signed on as an operator, and its site is expected to go live by the end of the year. The two live sites for the registry are located at http://uddi.microsoft.com and http://www.ibm.com/services/uddi. Companies can register with any node. Though the registry sites will be in physically different locations, they will contain the same information. And also the common set of SOAP APIs are supported by all the nodes.

How UDDI Works
- Software companies, standards bodies, and programmers populate the registry with descriptions of different types of services, they support.
- UDDI Business Registry assigns a programmatically unique identifier to each service and business registration.
- Marketplaces, search engines and business applications query the registry to discover services at other companies.
- Business uses this data to facilitate easier integration with each other over the Web.
Technical Overview
The Universal Description, Discovery and Integration (UDDI) specifications consist of an XML schema for SOAP messages, and a description of the UDDI API specification. Together, these form a base information model and interaction framework that provides the ability to publish information about a broad array of Web Services.
Four Data Structure Types in the UDDI Registry
The core information model used by the UDDI registries is defined in an XML schema. The UDDI XML schema defines four core types of information. These are: business information, service information, binding information, and information about specifications for services.

Registry Data
There are two types of information that can be registered in the UDDI Base Registry.
- Business Registration details
- Service Type Registration details
Business Registration
Conceptually, the information provided in a UDDI business registration consists of three components: 'white pages' including address, contact, and known identifiers; 'yellow pages' including industrial categorizations based on standard taxonomies; and 'green pages', the technical information about services that are exposed by the business. Green pages include references to specifications for Web Services, as well as support for pointers to various file and URL based discovery mechanisms if required.

Service Type Registration
The Service Type Registration contains the following info:
- Pointer to the namespace where service type is described.
- Identifier for who published the service.
- Identifier for the service type registration called a tModelKey, that is used as a signature by the websites that implement those services.
Design and Architecture of Registry APIs
Accessing UDDI Registry programmatically may be accomplished via APIs that comes with the UDDI SDK Toolkit. The programming APIs for UDDI are based on Extensible Markup Language (XML). There are two types of APIs in the UDDI
- Publishers API
- Inquiry API
The UDDI Programmers API Specification defines approximately 30 SOAP messages that are used to perform inquiry and publishing functions against any UDDI compliant service registry.
Publishers API
They define the calls needed for interactions between the programs and the UDDI registry for the purpose of storing or changing data in the registry. Authenticated access is required to use publishers API.
API calls to save information in registry
save_business
save_service
save_binding
save_tModel
API calls to delete information from registry
delete_business
delete_service
delete_binding
delete_tModel
API calls that takes care of security
get_authToken
discard_authToken
Inquiry API
API calls to search the registry
find_business
find_service
find_binding
ind_tModel
API calls to get details from registry
get_businessDetail
get_serviceDetail
get_bindingDetail
get_tModelDetail
SOAP Messaging
The APIs interact with the operator sites using SOAP through standard HTTP-POST protocol.
The SOAP messages can be traced by setting the trace property of the RequestManager object to true and specifying the trace path where the trace messages are to be stored.
Dim reqmgr as new UDDI10.RequestManager() reqmgr.trace = true reqmgr.tracepath="c:\Uddi\Uddi_Soap"
Example:
The following figure shows the SOAP message that travels across the wire on calling the Inquiry API method, find_business().

The UDDI server responds with a BusinessList object on receiving this UDDI SOAP request as, shown in the figure below.

Response SOAP message containing BusinessList on invoking find_business() inquiry API
Error Handling
The RequestManager object has three properties that are used for reporting the errors: UDDIErrno, UDDIErrorCode and UDDIErrorText. If any error occurs while processing the UDDI API calls, a SOAP Fault will be returned to the caller containing these values. Errors that are not reported by way of SOAP Faults are reported using the dispositionReport structure.
Reporting Success
Successful process of the request is indicated via UDDIErrno: 0 and UDDIErrorCode: E_success.
Reporting Errors with the disposition Report structure
All application errors are communicated via the use of the SOAP FAULT structure. The following figure shows how an error generated on behalf of invalid key is reported to the user, using DispositionReport structure.

SOAP message containing fault payload on encountering error
Reporting critical errors
Critical errors like connection broken unexpectedly while interacting with the registry or some other technical errors that occurred while processing the request are reported with the Error number 10500 and fault code, E_FatalError.
Error reporting Account Limit Exceeded
One can publish a maximum of four businesses in the UDDI Registry with his user account. So if he tries to publish more than that, UDDI reports with a error number 10610 and fault code E_accountLimitExceeded
UDDI and .NET
Microsoft's .Net framework aims to transform applications into services that get swapped over the Web. But if there are no Web sites available for exchanging services, the idea will miss its target. Thus, Microsoft is integrating support for UDDI into the .NET Platform, insuring that standards and industry cooperation are the basis for the next generation Internet.
Consumption of a .NET webservice through UDDI
Topic:Dynamic consumption of webservices published in the UDDI Registry by an ASP.NET client
Let us see a demo on how an Asp.NET client uses the UDDI Registry to dynamically access the web services published in it.
Step 1
Searching the UDDI Registry for businesses
The web page below, Search1.aspx searches the UDDI (Test) registry for the specified search text and search type and brings the list of businesses in the registry that matches the specified search criteria.
The inquiry API call find_business() is used to get the business list from the UDDI registry matching the search text. In this case, it lists companies that match the search text 'Calsoft'. There is only one business matching the name 'Calsoft' in the UDDI Registry, and that is listed as shown in the page.

The code snippet that searches the registry and fetches the list of businesses matching the search criteria is as follows:
Protected Sub Submit_Click(ByVal sender As
System.Object,ByVal e As System.EventArgs)
Dim Search_Text, Search_Type As String
Dim env As New UDDIEnv.Envelope()
Dim reqmgr As New UDDIEnv.RequestManager()
Dim rspenv As New UDDIEnv.Envelope()
Dim inqreq As New UDDI10.find_business()
Dim inqres As New UDDI10.businessList()
Dim result As UDDI10.businessInfo
On Error Resume Next
'Get the Search Text and Search Type
Search_Text = Request.Form("tSearchText")
Search_Type = Request.Form("dSearchType")
'Set the envelope to the find_business()
template env.Plugin = inqreq
'Set the businessname to Search_Text
inqreq.name = trim(Search_Text)
'Set the mode to Test thereby uses
http://test.uddi.microsoft.com
reqmgr.Mode = UDDIEnv.UDDIMode.test
'Calls UDDI registry for the specified type
'and brings the Business List matching the text
rspenv = reqmgr.UDDIRequest(env)
'On successful getting the Business List
If (reqmgr.UDDIErrno = 0) Then
rspenv.Plugin = inqres
Dim business_count As Integer
business_count = 1
If (inqres.businessInfos.count >= 1) Then
Do While business_count <= inqres.
businessInfos.count
result = inqres.businessInfos.businessInfo.
Item(business_count)
'Displays the Business Name, result.name
Loop
Else
'Displays No Businesses found matching the
search criteria
End If
Else
Response.Write("Error Text : " &
reqmgr.UDDIErrorText)
End If
If (Err.Number <> 0) Then
Response.Write(Err.Description)
End If
End Sub
Step 2
Finding whether the business exposes any compatible webservices
The following web page (ConvertDocument1.aspx) is reached on clicking the Business Name "Calsoft" in the previous page. This page gets the business details associated with the business "Calsoft", using the inquiry API call, get_businessDetail().
Then, it lists the services that are exposed by the business and says whether it is compatible to be invoked. This application supports only DotNet webservices to be called

ConvertDocument1.aspx (Lists the services exposed in the specified business)
Looking at the above page, you can see that the business by name, "Calsoft" exposes four services: Document Conversion, Math Service, E-Mail Web Service and File Upload of which only the first three are compatible.
The code snippet that corresponds to the above page ConvertDocument1.aspx, which brings the list of web services exposed by the Business is as below:
Protected Sub ConvertDocument1_Load(ByVal Sender
As System.Object,ByVal e As System.EventArgs)
If Not IsPostback Then ' Evals true first
time browser
hits the page
Dim envdom As New MSXML2.DOMDocument()
'Set the RequestManager to Test
Repository http://test.microsoft.com
reqmgr.Mode = UDDIEnv.UDDIMode.test
'Get the BusinessDetail corresponding to
the BusinessKey
envdom.loadXML("
"
& request.querystring("BusinessKey") &
"
")
envdom.save("c:\getbusinessDetail.xml")
env.Load("c:\getBusinessDetail.xml")
'Send the request getBusinessDetail() to
Registry
getResp = reqmgr.UDDIRequest(env)
'On No error from the Registry
If reqmgr.UDDIErrno = 0 Then
'Associate a businessDetail object with
the response
getResp.Plugin = bizDet
'now get the detail data desired using the
document object
Dim business As UDDI10.businessEntity
Dim service As UDDI10.businessService
Dim service_desc As UDDI10.Description
Dim binding_template As UDDI10.bindingTemplate
Dim service_url As String
If (bizDet.businessEntity.Count > 0) Then
no_services = bizDet.businessEntity(1).
businessServices.count
If no_services = 0 Then
'Display No Service Exists in the Business
Else
For serviceindex = 1 To no_services
Display the Service Name and Description
Next
no_bindings = service.bindingTemplates.count
For bindingindex = 1 To no_bindings
binding_template =
service.bindingTemplates.bindingTemplate.
Item(bindingindex)
'Fetch the url of the service
service_url = binding_template.accessPoint.
accessPoint.ToString()
'From the Url, identify whether the service
'is compatible to be accessed and display
Next
End If
End If
End If
End Sub
Step 3
Accessing the WebService
In step 2, we got the list of web services exposed by the business. The inquiry API call, get_businessDetail() in the previous step gets the binding details of the service, which will have the access point URL of the service in it. The access point Url of the "Document Service" is http://203.129.222.82/ado.net/PdfService/Service1.asmx.
Case 1: Accessing "Document Service" exposed by Calsoft
The webservice "Document Service" converts the documents from various formats to PDF format. It exposes one method namely "Convert Document". The method accepts the Url of the document to be converted as one of its parameters. It then uses an API call, UrlDownloadToFile() to download the document passed as Url location by the client, converts it to PDF, hosts it and passes the Url of the converted PDF file back to the client.
To summarize the steps to be followed to access this service:
- Create ROPE 1.0 Proxy object
- Call LoadServiceDescription of this object to get the service description (SDL) of this service.
- Load the service description to a XMLDOM object.
- Parse the XML file loaded in the DOM object and extract the following information to invoke the service:
- Web methods exposed by the service
- The input parameter name of the web methods and their data type
- The return data type of the web method
- This service, "Document Service" exposes only one web method named
- Enter the input values to the parameters of the method "ConvertDocument" as per their data types.
- The SOAP request message is built using the XMLDOM object, that will contain the method "ConvertDocument" of the webservice carrying the input parameters and their values entered by the client as shown below.
The service description of the service "Document Conversion" is shown in the figure below:

Having known the above information, the web page (InvokeService.aspx) is built as shown in the figure below.

Dim rope as new rope.proxy()
Dim xmldom as MSXML2.DOMDocument() xmldom.loadXML(rope.ServicesDescription)
"ConvertDocument", that accepts six parameters: JobId (int),Source_Url (string), Dest_Ip (string),Dest_Port (string), DocType (string) and Pdf_Filename (string).
Create an instance of XMLDOM object
Set xmldom = CreateObject("Microsoft.XMLDOM")
'Build the SOAP Request Payload
Set pi = xmldom.createProcessingInstruction
("xml", " version=""1.0""")
xmldom.insertBefore pi, xmldom.childNodes.Item(0)
Set objXMLroot = xmldom.createNode
(1,"SOAP:Envelope",
"http://schemas.xmlsoap.org/soap/envelope/")
xmldom.appendChild (objXMLroot)
objXMLroot.setAttribute
"xmlns:soapspenc",
"http://schemas.xmlsoap.org/soap/encoding/"
objXMLroot.setAttribute
"xmlns:xsi",
"http://www.w3.org/1999/XMLSchema-instance"
objXMLroot.setAttribute
"xmlns:xsd","http://www.w3.org/1999/XMLSchema"
Set objxmlbody = xmldom.createElement("SOAP:Body")
xmldom.documentElement.appendChild (objxmlbody)
Set objxmlmethod1 = xmldom.createNode
(1,"ConvertDocument", "http://tempuri.org/")
objxmlbody.appendChild (objxmlmethod1)
Set objxmlparam1 = xmldom.createNode
(1, "JobId", "http://tempuri.org/")
objxmlparam1.Text = Trim(tJobid.Text)
objxmlmethod1.appendChild (objxmlparam1)
Set objxmlparam2 = xmldom.createNode
(1,"Source_Url", "http://tempuri.org/")
objxmlparam2.Text = Trim(tSource.Text)
objxmlmethod1.appendChild (objxmlparam2)
Set objxmlparam3 = xmldom.createNode
(1,"Dest_Ip", "http://tempuri.org/")
objxmlparam3.Text = Trim(tDest_ip.Text)
objxmlmethod1.appendChild (objxmlparam3)
Set objxmlparam4 = xmldom.createNode
(1, "Dest_Port", "http://tempuri.org/")
objxmlparam4.Text = Trim(tDest_port)
objxmlmethod1.appendChild (objxmlparam4)
Set objxmlparam5 = xmldom.createNode
(1, "Pdf_Filename", "http://tempuri.org/")
objxmlparam5.Text = Trim(tPdf_Filename.Text)
objxmlmethod1.appendChild (objxmlparam5)
Set objxmlparam6 = xmldom.createNode
(1, "DocType", "http://tempuri.org/")
objxmlparam6.Text = Trim(tDoctype.Text)
objxmlmethod1.appendChild (objxmlparam6)
Dim XMLHTTP As New MSXML2.XMLHTTP()
'Open connection to the .Net Service
XMLHTTP.Open("POST", service_url, False)
'Set the SOAP-Specific HTTP Headers
XMLHTTP.setRequestHeader
("SOAPAction",
"http://tempuri.org/ConvertDocument")
XMLHTTP.setRequestHeader("Content-Type",
"text/xml")
'Post the SOAP Request to the .Net Service
XMLHTTP.send(xmldom.XML)
xmldom.loadXML(XMLHTTP.responseText)
So, the Url of the document that is to be converted is sent as a string parameter to the method "ConvertDocument" of the web service.
- POST the SOAP request to the webservice using the XMLHTTP object.
- The web service processes the SOAP request and sends the SOAP response back to the client. In this case it converts the document passed by the client to PDF format and on successful processing, it returns a SOAP response containing the Pickup Url of the converted Pdf file back to the client.
- Get the response SOAP payload received from webservice using the responseText property of the XMLHTTP object. Load it to a XMLDOM object.
- Parse the XMLDOM object having the SOAP response payload received from the webservice and extract the result tag which carries the result of the method.
- In this case, the result type is a structured data type of name Reply containing the Pickup Url of the converted Pdf file, which is shown in the text area in the above figure. In case if there is any error encountered in processing the SOAP request, the service responds with a Fault payload which will be informed to the user.
- With the Url of the converted Pdf file, the client can use HTTP-GET to pickup the document.

InvokeService.aspx (Displays the structured response returned from the webservice on calling the method ConvertDocument)
Thus the web services published in the UDDI Registry can be dynamically consumed.
Introduction to Weblets
Weblets can be defined as lightweight components that may be used consume web services. On Microsoft platforms weblets may be implemented as COM/Active-X components. Java platforms may use Beans (Java Beans, Enterprise Java Beans) or just Java classes.
A Weblet acts as an interface between a web service and a client application and enables the client application to access the web service very easily. All the programming nuances of setting up and working with UDDI, XML and Description Languages are made transparent, and the weblet functions like a proxy to the web service. One may drag and drop (embed) or simply instantiate a weblet from a client application and then call its methods to access the web service. Weblets need not have user interfaces, though many times it may be useful to have one.
Weblets may be used to aggregate multiple web services. A service provider may obtain weblets from multiple service providers, aggregate/embed them into a single weblet and then provide it to its customers. These Weblets may be downloaded on the fly and installed, sensitive to updates. For e.g. on Microsoft platforms, a .CAB file may be used, with the codebase pointing to an online repository. Similarly for Java classes.
Why do we need Weblets?
Weblets make access to Web Services componentizable and easier to manage. After all, why should every programmer write code against these interfaces to invoke services, when a prewritten weblet would suffice?
Weblets make aggregation or use of multiple services to be delegated to the client-side, wherever it makes sense. For example, a weblet could perform login and user authentication, a purchase order, and shipping in sequence using web services that offer authentication, purchase transactions and shipping respectively.
Weblets make creation of thin-client applications easier. A word processor client may use a disk storage service weblet for storing and retrieving files, a print service weblet for printing, a spreadsheet weblet for embedding a spreadheet, a billing service weblet for keeping track of usage and billing and so on.. These thin-client applications can thus provide access to software and online services, on a rental basis if required.
Weblets also help avoid additional investment in training and maintaining resources. An enterprise wishing to integrate a web service into its MIS can use its existing programmer pool to incorporate the weblet.
Consumption of a .NET webservice from VB application through WEBLETS
Let's go through the steps involved in consuming a .Net webservice from a VB application through weblets.
Step 1:
Developing the .NET Web Service
For this demo, let's consider the webservice, "Document Service" which is described in the previous section (Section 6).
Step 2:
Developing the Weblet
Let's say the company that developed the webservice "Document Service" (which is described in the previous section) also develops a weblet, in this case, a COM component, "prWeblet.DocumentService" corresponding to this web service. The weblet uses XMLDOM and XMLHTTP objects to interact with the webservice.
The code snippet that corresponds to this weblet is shown below.
'Define the structured response from the webservice
Public Type Reply
JobId As Integer
Pickup_Url As String
Trans_Date As Date
Status As String
Error_Message As String
Error_Description As String
End Type
Public errorflag As Boolean
Public error_desc As String
Public Soap_Request_Payload As String
Public Soap_Response_Payload As String
'Method ConvertDocument with the same
signature as the webmethod'
"ConvertDocument" of the Document Service
Public Function ConvertDocument
(ByVal JobId As Integer,
ByVal Source_Url As String,
ByVal Dest_Ip As String,
ByVal Dest_Port As String,
ByVal DocType As String,
ByVal Pdf_Filename As String)
As Reply Dim rep As Reply
On Error GoTo Err_Handler
'Create an instance of XMLDOM object
Set xmldom = CreateObject("Microsoft.XMLDOM")
'Build the SOAP Request Payload using XMLDOM
'.
'Store the SOAP Request Payload
Soap_Request_Payload = xmldom.xml
'Create an instance of XMLHTTP object
Set XMLHTTP = CreateObject("Microsoft.XMLHTTP")
'Build Service URL from specified
Destination and Port
service_url = "http://" & Trim(Dest_Ip) &
":" & Trim(Dest_Port) &
"/Ado.Net/PrintService/Service1.asmx"
'Open connection to the .Net Service
XMLHTTP.Open "POST", service_url, False
'Set the SOAP-Specific HTTP Headers
XMLHTTP.setRequestHeader "SOAPAction",
http://tempuri.org/ConvertDocument
XMLHTTP.setRequestHeader "Content-Type",
"text/xml"
'Post the SOAP Request to the .Net Service
XMLHTTP.send (xmldom)
' Store the Soap Response Payload
Soap_Response_Payload = XMLHTTP.responseText
'Load the Soap Response into XMLDOM object
Dim xmldomfile As Object
Set xmldomfile =
CreateObject("Msxml2.DOMDocument")
xmldomfile.loadXML (Soap_Response_Payload)
'Parse the XMLDOM object, xmldomfile and
extract the result
' .
'Return the result
ConvertDocument = rep
Exit Function
Err_Handler: errorflag = True
error_desc = Err.Description
End Function
As you can see in the above code, the weblet contains one method "ConvertDocument" with the same signature as that of the webmethod of the service. The core functionality of this weblet is summarized below.
Step 1
Builds the SOAP request to the webservice using XMLDOM object
Step 2
Sends the SOAP request to the webservice using XMLHTTP object
Step 3
Loads the SOAP response from the webservice to a XMLDOM object
Step 4
Parses that XMLDOM object and extracts the result returned from the webservice. If case of any error, returns the fault description returned by the webservice
Step 3
Accessing the webservice from a thin Client through Weblets
The client application can be anything that supports COM activation. Here we'll take a VB application.
All that the VB application needs to do is to create an instance of the weblet corresponding to the webservice that is required, and simply call the methods in the weblet. The weblet invokes the corresponding method in the webservice, gets the result and passes it back to the client.
The following code snippet shows how the VB application that consumes the "Document Service" through weblet.
'Create an instance of the Weblet to
DocumentService
Dim docserviceobj As New prWeblet.DocumentService
Dim docserviceresult As prWeblet.Reply
'Call the method ConvertDocument of the Weblet,
which the Web
'Method ConvertDocument of the .NET WebService,
"Document Conversion"
docserviceresult = docserviceobj.ConvertDocument(
CInt(Trim$(tJobId.Text)), Trim(tSource.Text),
Trim(tDest_ip.Text), Trim(tDest_port.Text),
Trim$(tDoctype.Text), Trim$(tPdf_Filename.Text))
'Get the result
If docserviceobj.errorflag = False Then
rJobId = docserviceresult.JobId
rPickUpUrl = docserviceresult.Pickup_Url
rStatus = docserviceresult.Status
rTransDate = docserviceresult.Trans_Date
rErrMsg = docserviceresult.Error_Message
rErrDesc = docserviceresult.Error_Description
Else
'Display the error returned by the Weblet
on calling the WebService
MsgBox docserviceobj.error_desc
End If
As you see in the code above, the client instantiates the weblet "prWeblet.DocumentService". Then it calls the method "ConvertDocument", passing the input parameters as entered by the client in the screen (Figure 12).

The input values passed by the thin VB Client to invoke the web service "Document Service"
The weblet forms the request in the format that the web service requires, and invokes the corresponding webmethod of the "Document Service".
The webservice processes the request and returns its response to the weblet. In this case it downloads and converts the document supplied as a URL by the client to a Pdf format and responds with a structured variable named Reply containing JobId, Pickup_Url (the Url where the client can pickup the converted Pdf document), Status (Transaction Success or Failed), Date of Transaction and error message if any.
The weblet decodes this XML result set and returns them to its caller. Fig 13 & 14 show the application at work.

Below is the screen that displays the converted Pdf file returned by the webservice.

The client can also trace SOAP payloads that travels across the wire in this transaction. And again, these payloads are easily obtained from the weblet.

SOAP Request and Response packets that travels on the wire in the transaction being displayed by the client
Thus the implementation of the code necessary to consume the webservice is completely hidden from the client application.
Summary
In this article we have attempted to explain the basic concepts as well as programming details of UDDI and its use with Web Services. We have also introduced the concept of Weblets, supplementing it with a working example.
Web Services are the next big wave to hit the IT industry. A new way of delivering software and services is on the horizon. UDDI, SOAP, XML and Weblets will form important software infrastructure to make it happen.
Resources
- Official site of the UDDI community
- Microsoft Operator Site
- http://uddi.microsoft.com
- IBM Operator Site
- http://www-3.ibm.com/services/uddi


