In the world of Web services, a business describes its services (activities or functionality) by using an XML-based structure called WSDL (Web Service Description Language).
When a service consumer wants to interact with a Web service, they merely download that service's WSDL file. The file tells what the service offers and how to interact with it.
In this article, I'll design a reusable WSDL Java client that can download any WSDL, parse it to read interface details, author service invocation requests, and collect information coming back from the WSDL service. I'll also explain WSDL's syntax, grammar, namespace rules, etc.
The interaction between the WSDL client and host service has four parts:
| |
 |
Figure 1. Here's the four step procedure for a WSDL-based Web service.
|
- Downloading the WSDL file. Start with a WSDL file URL. In some cases, you will know the location of the file you want, but you can also get the URL from a UDDI registry (please see the article "Constructing a UDDI Client, Calling the UDDI Registry"). An HTTP request is passed to the target URL, which returns a WSDL file. To download the WSDL file, use the HTTPConnection class shown in Listing 1.
- Processing the WSDLfile. Remember, the WDSL file is in XML, so you need an XML parser to process it. As in the UDDI article, you'll be using Enhydra's kXML. kXML's smaller footprint allows the WSDL client to work inside an applet as well as Java Foundation Classes/Swing containers. The WSDLClient class (Listing 2) processes the WSDL file.
- Authoring the service invocation request. Based on the information gathered in Step 2, the WSDL client formulates an XML request. Simple Object Access Protocol (SOAP) is the most popular transport facility available for sending Web service invocation requests. WSDL only tells the details of services, but once they are known, SOAP transports the requests as XML messages. The WSDL client uses the same SOAP-related classes that developed for the UDDI client in the previous article (SoapTransport.class in Listing 3, SoapCall.class in Listing 4, and HttpConnection.class in Listing 1).
- Processing the SOAP response: A class named SoapTransport extracts the response string from the SOAP envelope.
Figure 1 shows the sequence of these four events broken down into sub-steps. Step 4 is purely a SOAP issue is outside the scope of this article. For more on using SOAP, please refer to Resources (right).
| |
 |
Figure 2. The arrangement of classes within the WSDL client.
|
Figure 2 shows the arrangement of classes within the WSDL client.
The WSDLAppletClient class is a generic applet that provides a simple GUI as shown in Figure 3. The container must interact with three of our classes:
- The WSDLClient downloads a WSDL file from the specified URL, downloads it, parses it, and populates its internal data structures.
- The Operation class (Listing 6) creates the SOAP requests based on the operations described in the WSDL file. WSDL-based Web services consist of Operations. Think of a Web service as a Java or C++ class and Operations as its public methods. The WSDL parsing task is nothing but creating Operation objects.
- The Parameter class (Listing 7) builds and retrieves individual parameters (values) from the SOAP messages passed between the WSDL client and the Web service for each operation. Information about what parameters are required for a particular operation comes from the WSDL file. While parsing the WSDL file to create Operation objects, you also create the necessary Parameter objects for each operation.
| |
 |
Figure 3. This is an applet-based GUI to test the WSDL client.
|
Downloading the WSDL File
The HTTPConnection class makes an HTTP request to the URL, where the WSDL file is located. The WSDLClient class is the main processing client; it downloads the WSDL file, processes it, and populates the operation and message class objects. The GetOperations() method of the WSDLClient class returns a Vector of operation-type objects, allowing the container application to invoke an individual operation.
Processing the WSDL File
The WSDL file contains service information in the <service> element. Listing 8 shows a sample WSDL file with one <service> element containing the following children:
- The <description> element contains the description of the service in simple English.
- The <port> element serves as one end of the communication channel. Its remote end sits on the other side of the Internet listening to your requests. It contains a "binding" attribute, whose value (TemperatureBinding) refers to the "name" attribute of a <binding> element. The <port> element also contains a <SOAP:address> element (readers familiar with XML namespaces will understand that soap:address means the <address> element belongs to the SOAP namespace and is not part of WSDL schema). This <SOAP:address> element specifies the location of a SOAP server in the form of a URL.
Listing 8 also contains the following elements: a <binding> element and a <portType> element. The <binding> specifies the details of the SOAP binding. The <portType> element describes details of the WSDL interface. The <portType> element contains one operation named "getTemp." Because these two elements are separate, you can design WSDL interfaces independent of their SOAP deployments.
| |
 |
Figure 4. : Here the user has downloaded a WSDL file containing a setMessage method. This required one parameter type string.
|
The <port>, <binding>, and <portType> elements are a set, but there may often be more than one. Listing 9 shows another WSDL file that contains a set of these elements.
The client's job is to jump from a <port> element to the matching (they have the same value in their name attributes) <binding> element. The <binding> element furnishes the information needed for the next step.
The WSDLClient in Listing 2 takes either of the following types of WSDL file URLs:
- http://www.mywebservice.com/mywsdl.wsdl: This is simply a WSDL URL. In this case, read the <service> element and its <port> child, and jump to the matching <binding>. If there is no <service> element, jump to the first instance of a <binding> element.
- http://www. mywebservice.com/mywsdl.wsdl#mybindingfragmentname: This URL contains a fragment identifier, which is the "name" attribute of the <binding> element. Because you don't need to read the <service> element to get the <binding> element name, you can jump directly to the desired <binding>.
Whatever path you take, you have to reach a <binding> element. The <binding> element has an attribute type specifying the matching <portType> element name. Listing 8 contains only one <portType> element, whose name attribute has value "TemperaturePortType." This matches the type attribute of the <binding> element.
PortType is the WSDL interface and each operation is a method in that interface. The <portType> element in Listing 8 contains one <operation> element named "getTemp".
Get the <Message> Element
Each of the WSDL methods has input parameters and return values. So the <operation> contains <input> and <output> tags. <input> tags represent input parameters and <output> tags represent return values. Listing 8 contains one each of these elements. Notice that both types of element tag have "message" attributes.
The value of the "message" attribute of the <input> element is "getTempRequest." Look for a <message> element with the same "name" attribute value somewhere in the WSDL file. In Listing 8, the message element with the name attribute value "getTempRequest" lies close to the beginning of the file. But it can be present anywhere in the WSDL file as an immediate child of <definitions> root element. The <message> element is meant to describe the data type of <input> element.
Similarly, there is a <message> element whose name attribute value matches the message attribute of <output> element. These message attribute values of <input> and <output> elements are actually referring to <message> elements. Each <message> element will have one or more <part> elements. Each part has name and type attributes. Think of each <part> as variable declarations in Java. Together, these <part> elements can be used to specify user-defined data types for messages.
Why match the name attribute value of <message> element with the message attribute value of <input> and <output> elements? Because <input> and <output> elements use their corresponding <message> elements to define what data types will actually travel over the Internet via SOAP. The <message> elements define the data types for the messages. Without finding the matching <message> element, you will not know what data types the Web service is expecting from you and what you can expect from the Web service in return.
WSDL uses messages to send "parameters" and "operation" (method) calls. Any number of messages and return (output) messages can be sent with a method invocation call.
| |
 |
Figure 5. The response from the WSDL implementation/SOAP server.
|
WSDL Parsing Strategy
The parsing should go something like this:
- Find the <service> element and get the <binding>, <portType> element names as well as the SOAP server address.
- Jump to the <binding> element by matching the binding name that was found in the <service> element, and get the SOAP action attribute value.
- Find the <operation> element within the <binding> element and get its name, encoding style, and namespace values.
- Jump to the <portType> element, by matching the portType name found in the <service> element, and get the <operation> element by matching the operation name found in <binding>.
- Get the <operation> element's child <input> element and the "message" attribute value.
- Jump to the <message> element by matching the name found in the <input> element.
- Get the <part> elements found in the <message> element and retrieve the name and "type" attribute values from each <part>.
- Repeat steps 5 to 7 for the <output> child element.
- Repeat steps 3 to 7 for each <operation> element found in <binding>.
Parsing involves the use of Enhydra's kXML (refer to the parse() method in Listing 2). The complete WSDL file passes to a reader object, which then passes to an XMLParser object. The next step is to create a document object through this object. Calling the parse() method of the document loads the complete WSDL structure in DOM. Next, parse through the WSDL tags to construct the required operation and parameter objects.
Authoring the Service Invocation Request
After parsing the complete WSDL file, load it into a WSDLClient object. As shown in Figure 4, each <operation> becomes an object of the Operation class and each <message> element becomes an object of the Parameter class. The Operation class is also capable of coordinating with SOAP and HTTP classes to send method invocation calls.
| |
 |
Figure 6. One object of the WSDLClient holds several Operation objects. Each Operation object holds input and output Parameter objects.
|
Take a look at the following three methods of the Operation class:
- SetValue()
- Send()
- GetBody()
These three methods provide an easy way of authoring SOAP service invocation requests for any Operation object. Once the WSDL files are parsed and their data structures (which are essentially Operation objects that contain Parameter objects) are loaded, call the setValue() method of the Operation class to provide parameter values. To send the SOAP method invocation request, call the send() method of the Operation class. Internally, the Send() method uses the getBody() method to author the SOAP Body. It then sends the request, waits for the SOAP response, and returns it when received. There's no need to worry about SOAP request authoring because the WSDL client hides SOAP-related functionality from its users.
Using the Applet Container
Listing 5 is an applet-based container for the WSDL client. It represents the GUI shown in Figure 3.
To test this applet:
- Type the URL of the WSDL file in the text box labeled "Enter WSDL file URL". For example type http://www.mywebservice.com/mywsdl.wsdl.
- Type the SOAP server address in text box labeled "Enter SOAP server URL". For example type http://www.mywebservice.com/soap. This information may also be contained within the WSDL file (within the SOAP:address element). In that case, entering the full URL is optional.
- Press the "Download WSDL" button. This populates the method list with service method names.
- Select a method from the methods list for making a call. The parameters required are shown in the parameter window.
- Enter the parameter values in the parameter window.
- Press the "Invoke Method" button. This executes the call. The server response of your call is shown in the output window.
Bilal Siddiqui is an Electronics Engineer, an XML consultant, and the co-founder of WaxSys, a company focused on simplifying e-Business. He is a technology evangelist and frequently-published technical author. You can contact Bilal at bsiddiqui@waxsys.com.