The WSDL Service Contract in Detail

The WSDL document, which is XML, is structured as follows:

The biggest section in a WSDL is typically the types section because an XML Schema tends to be wordy. An example from Amazon, introduced shortly, illustrates. For now, the WSDL (see Example 4-11) for the RandService is only about a page or so in size.

Example 4-11. The dynamically generated WSDL for the RandService

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
             xmlns:tns="http://rand/"
             xmlns:xsd="http://www.w3.org/2001/XMLSchema"
             xmlns="http://schemas.xmlsoap.org/wsdl/"
             targetNamespace="http://rand/" name="RandServiceService">
  <types>
    <xsd:schema>
      <xsd:import namespace="http://rand/"
                  schemaLocation="http://localhost:8888/rs?xsd=1"></xsd:import>
    </xsd:schema>
  </types>
  <message name="next1">
    <part name="parameters" element="tns:next1"></part>
  </message>
  <message name="next1Response">
    <part name="parameters" element="tns:next1Response"></part>
  </message>
  <message name="nextN">
    <part name="parameters" element="tns:nextN"></part>
  </message>
  <message name="nextNResponse">
    <part name="parameters" element="tns:nextNResponse"></part>
  </message>
  <portType name="RandService">
    <operation name="next1">
      <input message="tns:next1"></input>
      <output message="tns:next1Response"></output>
    </operation>
    <operation name="nextN">
      <input message="tns:nextN"></input>
      <output message="tns:nextNResponse"></output>
    </operation>
  </portType>
  <binding name="RandServicePortBinding" type="tns:RandService">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http"
                  style="document"></soap:binding>
    <operation name="next1">
      <soap:operation soapAction=""></soap:operation>
      <input>
        <soap:body use="literal"></soap:body>
      </input>
      <output>
        <soap:body use="literal"></soap:body>
      </output>
    </operation>
    <operation name="nextN">
      <soap:operation soapAction=""></soap:operation>
      <input>
        <soap:body use="literal"></soap:body>
      </input>
      <output>
        <soap:body use="literal"></soap:body>
      </output>
    </operation>
  </binding>
  <service name="RandServiceService">
    <port name="RandServicePort" binding="tns:RandServicePortBinding">
      <soap:address location="http://localhost:8888/rs"></soap:address>
    </port>
  </service>
</definitions>

The first three WSDL sections (types, message, and portType) present the service abstractly in that no implementation details are present. The binding and service sections provide the concrete detail by specifying, for example, the type of transport used in the service as well as the service endpoint.

The portType is of particular interest because it characterizes the service in terms of operations, not simply messages; operations consist of one or more messages exchanged in a specified pattern. The two areas of immediate interest in the WSDL for a programmer writing a client against a service would be the portType and the service; the portType section informs the programmer about what calls can be made against the service, and the service section gives the service endpoint, the URL through which the service can be reached.

XML is not fun to read, but the basic profile WSDL for the RandService is not unduly forbidding. Perhaps the best way to read the document is from top to bottom.

This section contains or links to an XML Schema or equivalent. (In the case of Java, the schema is a separate document shown in Example 4-3; in the case of DotNet, the schema is included in the WSDL.) To understand how the schema relates to its WSDL, consider this segment of the XML Schema from Example 4-3:

<xs:element name="nextNResponse" type="tns:nextNResponse">             1
</xs:element>
...
<xs:complexType name="nextNResponse">                                  2
  <xs:sequence>
    <xs:element name="return"                                          3
                type="xs:int" minOccurs="0" maxOccurs="unbounded">
    </xs:element>
  </xs:sequence>
</xs:complexType>

The xs:element in line 1 has a specified type, in this case tns:nextNResponse. The type is the complexType in line 2. XML Schema has built-in simple types such as xsd:int and xsd:string, but XML Schema is also extensible in that new complex types can be added as needed. The complexType in this case is for the nextNResponse message that the service returns to the client. Here is that message from the WSDL in Example 4-11:

<message name="nextNResponse">
  <part name="parameters" element="tns:nextNResponse"></part>  1
</message>

The message has an element attribute (line 1) with tns:nextNResponse as the value; tns:nextNResponse is the name of the element in line 1 of the XML Schema. The WSDL, in defining a message, points back to the XML Schema section that provides the data type for the message.

The complexType section of the WSDL indicates that a nextNResponse message returns zero or more integers (XML type xs:int). The zero leaves open the possibility that the service, in this case written in Java, might return null instead of an actual array or equivalent (e.g., List<Integer>). At this point a human editor might intervene by changing the minOccurs in line 3 from 0 to 1. (If the minOccurs attribute were dropped altogether, the value would default to 1.) The dynamically generated WSDL may not capture the intended design of a service; hence, the WSDL may need to be edited by hand.

Each message element in the WSDL points to an element and, more important, to a complexType in the WSDL’s XML Schema. The result is that all of the messages are typed. The RandService exposes two operations and each follows the request/response pattern; hence, the WSDL has four message elements: two for the next1 and nextN requests and two for the corresponding responses named next1Response and nextNResponse, respectively.

This section contains one or more operation elements, each of which defines an operation in terms of messages defined in the immediately preceding section. For example, here is the definition for the nextN operation:

<operation name="nextN">
  <input message="tns:nextN"></input>
  <output message="tns:nextNResponse"></output>
</operation>

The input message precedes the output message, which signals that the pattern is request/response. Were the order reversed, the pattern would be solicit/response. The term input is to be understood from the service’s perspective: an input message goes into the service and an output message comes out from the service. Each input and output element names the message defined in a message section, which in turn refers to an XML Schema complexType. Accordingly, each operation can be linked to the typed messages that make up the operation.

This section and the next, service, provide implementation details about the service. In theory, but rarely in practice, there are several options or degrees of freedom with respect to the service that the WSDL defines, and a binding section selects among these options. One option for a SOAP-based service such as the RandService is the SOAP version: 1.1 or 1.2. SOAP 1.1 is the default in Java; hence, the one and only binding section is for SOAP 1.1. In DotNet, a dynamically generated WSDL usually has two binding sections: one for SOAP 1.1 and the other for SOAP 1.2. However, the very same DotNet WSDL typically has only one service endpoint or URL; this means the same deployed service is for SOAP 1.1 and SOAP 1.2, thereby signaling that no difference between the two SOAP versions comes into play for the service.

There are three other options to be considered: transport (line 1) and style (line 2) are two of the three. Here is the first subelement in the binding section, a subelement that makes choices on these two options:

<soap:binding transport="http://schemas.xmlsoap.org/soap/http"  1
              style="document"></soap:binding>                  2

The transport value is a URI that ends with soap/http, which can be summed up as SOAP over HTTP. Another option would be SMTP (Simple Mail Transport Protocol) or even TCP (Transmission Control Protocol, which underlies HTTP), but in practice, HTTP is the dominant transport. HTTP in this context includes HTTPS. The other option (line 2) concerns the service style, in this case set to document. A web service in document style always has an XML Schema or equivalent that types the service’s constituent messages. The other choice for style is misleadingly named rpc, which is short for remote procedure call. The name is misleading because a document-style service such as the RandService can and typically does follow the request/response pattern, which is the RPC pattern. In the context of a WSDL, rpc style really means that messages themselves are not typed, only their arguments and return values are typed. The WSDL for an rpc style service may have no types section at all or only an abbreviated one. In modern SOAP-based services, document style dominates and represents best practice. Indeed, both Java and DotNet toyed for a time with the idea of dropping support altogether for rpc style. The issue of rpc style will come up again later but only briefly.

The document style deserves to be the default. This style can support services with rich, explicitly defined Java data types such as Employee or ChessTournament because the service’s WSDL can define, for the XML side, the required types in an XML Schema. Any service pattern, including request/response, is possible under the document style.

The last option concerns use, more accurately called encoding, because the choice determines how the service’s data types are to be encoded and decoded. The WSDL has to specify how the data types used in an implementation language such as Java are to be serialized into and deserialized out of WSDL-compliant types—the types laid out in the WSDL’s XML Schema or equivalent (see Example 4-12). For example, Java and Ruby have similar but subtly different data types. In a conversation based on SOAP messages, a conversation in which the SOAP remains transparent, the two languages would need the ability to serialize from instances of native types to XML and to deserialize from XML to instances of native types.

The attribute

use = 'literal'

means the service’s type definitions in the WSDL literally follow the WSDL’s schema. The alternative to literal is named encoded, which means that the service’s type definitions come from implicit encoding rules, typically the rules in the SOAP 1.1 specification. However, the use of encoded does not comply with WS-I (Web Services Interoperability) standards.

This section brings the pieces together. Recall that a WSDL has but one portType section but may have multiple binding sections. The service element has port subelements, where a port is a portType linked to a binding; hence, the number of port subelements equals the number of binding sections in the WSDL. In this example, there is one binding and, therefore, one port subelement:

<port name="RandServicePort" binding="tns:RandServicePortBinding">
   <soap:address location="http://localhost:8888/rs"></soap:address> 1
</port>

The address subelement specifies a location (line 1), whose value is commonly called the service endpoint. A web service with two significantly different bindings (for instance, one for HTTP and another for SMTP) would have different location values to reflect the different bindings.

The foregoing examination of the WSDL, and in particular its XML Schema, prompts an obvious question: Which Java data types bind to which XML Schema data types? Table 4-1 summarizes the bindings.

The bindings in Table 4-1 are automatic in the sense that, in a JAX-WS service, the SOAP infrastructure does the conversions without application intervention. Conversions also are automatic for arrays of any type in Table 4-1. For example, an array of BigInteger instances converts automatically to an array of xsd:integer instances, and vice versa. Programmer-defined classes whose properties reduce to any type in Table 4-1 or to arrays of these likewise convert automatically. For example, an Employee class that has properties such as firstName (String), lastName (String), id (int), salary (float), age (short), hobbies (String[ ]), and the like would convert automatically to XML Schema types. The upshot is that the vast majority of the data types used in everyday Java programming convert automatically to and from XML Schema types. The glaring exception is the Map—a collection of key/value pairs. However, a Map is readily implemented as a pair of coordinated arrays: one for the keys, the other for the values.

The source for the RandService class begins as follows:

@WebService
public class RandService {
...

The default style, document, could be overridden with an additional annotation:

@WebService
@SOAPBinding(style = Style.RPC) // versus Style.DOCUMENT, the default
public class RandService {
...

The RandService is simple enough that the difference would be transparent to clients against the service. Of interest here is how the different styles impact the underlying SOAP messages.

Consider a very simple SOAP-based service with operations named add, subtract, multiply, and divide. Each operation expects two arguments, the numbers on which to operate. Under the original SOAP 1.1 specification, a request message for document style—what is now called unwrapped or bare document style—would look like Example 4-13:

The Body of the SOAP message contains two elements at the same level, the elements tagged num1 and num2; each element is a child of the soapenv:Body element. The glaring omission is the name of the operation, for instance, add. This name might occur instead, for example, in the request URL:

http://some.server.org/add

It is peculiar that the SOAP envelope should contain the named arguments but not the named operation. Under rpc style, however, the operation would be the one and only child of the Body element; the operation then would have, as its own child elements, the arguments. Here is the contrasting SOAP message in rpc style or, what now comes to the same thing, wrapped document style (Example 4-14).

The add element (line 1) now acts as a wrapper for the argument elements, in this case num1 and num2. The wrapped convention, unofficial but dominant in SOAP frameworks, gives a document-style service the look and feel of an rpc-style service—at the message level. The document style still has the advantage of a full XML Schema that types the messages. In Java as in DotNet, the default style for any SOAP-based service is wrapped document; hence, a service such as RandService, with only the @WebService annotation, is wrapped document in style. This style is often shortened to wrapped doc/lit: wrapped document style with literal encoding.

The wsimport utility produces, from a WSDL document, code that directly supports client calls against a web service. This same code can be used, with a few adjustments, to program a service. This section illustrates with a simple example.

Here are two operations for a temperature conversion service written in C#:

[WebMethod]
public double c2f(double t) { return 32.0 + (t * 9.0 / 5.0); }
[WebMethod]
public double f2c(double t) { return (5.0 / 9.0) * (t - 32.0); }

The c2f operation converts from centigrade to fahrenheit and the f2c method converts from fahrenheit to centigrade.

DotNet, by default, generates a WSDL with SOAP 1.1 and SOAP 1.2 bindings. This temperature conversion service is simple enough that the two bindings have the same implementation. In general, however, the wsimport utility can handle multiple bindings with the -extension flag. Assuming that the WSDL for the service is in the file tc.wsdl, the command:

% wsimport -p tempConvert -keep -extension tc.wsdl

generates the usual artifacts: Java .class files that represent the c2f and f2c request messages and their corresponding responses, together with various support files. Of interest here is the interface—the Java file that represents the portType section of the WSDL. Here is the file, cleaned up for readability:

package tempConvert;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;

@WebService(name = "ServiceSoap",
            targetNamespace = "http://tempConvertURI.org/")
@XmlSeeAlso({
    ObjectFactory.class
})
public interface ServiceSoap {
    @WebMethod(operationName = "c2f",
               action = "http://tempConvertURI.org/c2f")
    @WebResult(name = "c2fResult",
               targetNamespace = "http://tempConvertURI.org/")
    @RequestWrapper(localName = "c2f",
                    targetNamespace = "http://tempConvertURI.org/",
                    className = "tempConvert.C2F")
    @ResponseWrapper(localName = "c2fResponse",
                     targetNamespace = "http://tempConvertURI.org/",
                     className = "tempConvert.C2FResponse")
    public double c2F(
        @WebParam(name = "t",
                  targetNamespace = "http://tempConvertURI.org/")
        double t);
    @WebMethod(operationName = "f2c",
               action = "http://tempConvertURI.org/f2c")
    @WebResult(name = "f2cResult",
               targetNamespace = "http://tempConvertURI.org/")
    @RequestWrapper(localName = "f2c",
                    targetNamespace = "http://tempConvertURI.org/",
                    className = "tempConvert.F2C")
    @ResponseWrapper(localName = "f2cResponse",
                     targetNamespace = "http://tempConvertURI.org/",
                     className = "tempConvert.F2CResponse")
    public double f2C(
        @WebParam(name = "t",
                  targetNamespace = "http://tempConvertURI.org/")
        double t);
}

The ServiceSoap interface, like any interface, declares but does not define methods, which in this case represent service operations. If the semantics of these two operations c2f and f2c are understood, then converting this wsimport artifact to a web service is straightforward: