Having a means of transporting data between Web services is only half the story. Without interface descriptions for our Web services, they are about as useful as any other undocumented API very little! While in theory we could simply examine the message schemas for a Web service and figure out for ourselves how to interoperate with it, this is a difficult and error-prone process and one which could be safely automated if Web services had recognizable interfaces. Fortunately, WSDL provides this capability and more for Web services.
The Web Service Description Language or WSDL pronounced "Whiz Dull" is the equivalent of an XML-based IDL from CORBA or COM, and is used to describe a Web service's endpoints to other software agents with which it will interact. WSDL can be used to specify the interfaces of Web services bound to a number of protocols including HTTP GET and POST, but we are only interested in WSDL's SOAP support here, since it is SOAP which we consider to support the (logical) Web services network. In the remainder of this chapter we explore WSDL and show how we can build rich interfaces for Web services that enable truly dynamic discovery and binding, and show how WSDL can be used as the basis of other protocols and extended to other domains outside of interface description.
A WSDL interface logically consists of two parts: the abstract parts that describe the operations the Web service supports and the types of messages that parameterize those operations; and the concrete parts that describe how those operations are tied to a physical network endpoint and how messages are mapped onto specific carrier protocols which that network endpoint supports. The general structure of a WSDL document is shown in Figure 3-25.
Figure 3-25. WSDL structure.
The foundation of any WSDL interface is the set of messages that the service behind the interface expects to send and receive. A message is normally defined using XML Schema types (though WSDL allows other schema languages to be used) and is partitioned into a number of logical parts to ease access to its contents.
Messages themselves are grouped into WSDL operation elements that have similar semantics to function signatures in an imperative programming language. Like a function signature, an operation has input, output, and fault messages where WSDL supports at most a single input and output message, but permits the declaration of an arbitrary number of faults.
The portType is where what we think of as a Web service begins to take shape. A portType is a collection of operations that we consider to be a Web service. However, at this point the operations are still defined in abstract terms, simply grouping sets of message exchanges into operations.
The binding section of a WSDL interface describes how to map the abstractly defined messages and operations onto a physical carrier protocol. Each operation from each portType that is to be bound to a specific protocol (and thus ultimately be made available to the network) is augmented with binding information from the binding part of the WSDL specification WSDL supports SOAP, HTTP GET and POST, and MIME to provide a protocol-specific version of the original portType declaration.
Finally, a port is declared that references a particular binding, and along with addressing information is wrapped together into a service element to form the final physical, network addressable Web service.
As we saw in Figure 3-25, the abstract components of a WSDL description are the types, message, and portType elements, while the concrete elements are binding and service.
The split between abstract and concrete is useful, because it allows us to design interfaces in isolation from eventual deployment environments, using only the abstract definitions in WSDL. Once we are happy with the abstract aspects of the Web service interface, we can then write the concrete parts to tie the service down to a specific location, accessible over a specific protocol.
The Stock Quote WSDL Interface
Having seen WSDL from a theoretical perspective, we can concretize that theory by considering a specific example. The classic Web services application is the stock ticker example where a Web service provides stock quotes on request. Throughout the remainder of this discussion, we shall use a simple Web service which supports a single operation that has an equivalent signature to the following C# code:
double GetStockQuote(string symbol);
We examine WSDL stage by stage and show how we can turn this simple method signature into a true Web service interface.
The opening element of any WSDL document is definitions, which is the parent for all other elements in the WSDL document. As well as acting as a container, the definitions element is also the place where global namespace declarations are placed.
Figure 3-26. The WSDL definitions element.
<wsdl:definitions targetNamespace="http://stock.example.org/wsdl" xmlns:tns="http://stock.example.org/wsdl" xmlns:stockQ="http://stock.example.org/schema" xmlns:wsdl="http://www.w3.org/2003/02/wsdl"> <!-- Remainder of WSDL description omitted --> </wsdl:definitions>
A typical WSDL definitions element takes the form shown in Figure 3-26, where the element declares the target namespace of the document, a corresponding prefix for that namespace, and a namespace prefix for the WSDL namespace (or alternatively it is also common to use the WSDL namespace as the default namespace for the whole document). Other namespaces may also be declared at this scope, or may be declared locally to their use within the rest of the document. Good practice for declaring namespaces to WSDL documents is to ensure the namespaces that are required for the abstract parts of the document are declared at this level, while namespaces required for the concrete parts of a WSDL document (like the bindings section) are declared locally to make factoring and management of WSDL documents easier.
The Types Element
The types element is where types used in the interface description are defined, usually in XML Schema types, since XML Schema is the recommended schema language for WSDL. For instance in our simple stock quote Web service, we define types that represent traded stocks and advertise those types as part of its WSDL interface as illustrated in Figure 3-27.
Figure 3-27. Defining types in a WSDL interface.
<wsdl:definitions ... > <wsdl:import namespace="http://stock.example.org/schema" location="http://stock.example.org/schema"/> <wsdl:types xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="stock-quote"> <xs:complexType> <xs:sequence> <xs:element name="symbol" ref="stockQ:symbol"/> <xs:element name="lastPrice" ref="stockQ:price"/> </xs:sequence> </xs:complexType> </xs:element> <!-- Other schema type definitions --> <wsdl:types> </wsdl:definitions>
Before writing the types section, we first import some types declared by an external schema that make the types within that schema available to this WSDL document to build on. Those schema types (symbol and price) are used to create a new complex type (stock-price) which the WSDL interface will use to advertise its associated Web service.
The orthodox view is to use XML Schema to provide constraints and type information in Web services-based applications. However it is not necessarily the case that XML Schema is the right choice for every application domain, particularly those domains that have already chosen a different schema language on which to base their interoperability infrastructure. Recognizing this requirement, WSDL 1.2 supports the notion of other schema languages being used in place of the recommended XML Schema. Although the WSDL 1.2 specification does not provide as wide coverage for other schema languages, it does allow for their use within WSDL interfaces.
Once we have our types, we can move on to the business of specifying exactly how consumers can interact with the Web service. The message declarations compose the types that we have defined (and those that we are borrowing from other schemas) into the expected input, output and fault messages that the Web service will consume and produce. If we take our simple stock ticker Web service as an example, we can imagine a number of messages the Web service would be expected to exchange as shown in Figure 3-28.
Figure 3-28. The message elements.
<wsdl:message name="StockPriceRequestMessage"> <wsdl:part name="symbol" element="stockQ:symbol"/> </wsdl:message> <wsdl:message name="StockPriceRespnseMessage"> <wsdl:part name="price" element="stockQ:StockPriceType"/> </wsdl:message> <wsdl:message name="StockSymbolNotFoundMessage"> <wsdl:part name="symbol" element="stockQ:symbol"/> </wsdl:message>
As we see in Figure 3-28, a WSDL message declaration describes the (abstract) form of a message that a Web service sends or receives. Each message is constructed from a number of (XML Schema) typed part elements which can come from the types part of the description or an external schema that has been imported into the same WSDL document and each part is given a name to ease the insertion and extraction of particular information from a message. The name given to a part is unconstrained by WSDL but it is good practice to make the part name descriptive as one would when naming programming language variables.
In this example we have three possible messages: StockPriceRequestMessage, StockPriceResponseMessage, and StockSymbolNotFoundMessage, each of which carries some information having to do with stock prices and, because it is good practice to do so, whose name is indicative of its eventual use in the Web service.
A portType defines a collection of operations within the WSDL document. Each operation within a portType combines input, output, and fault messages taken from a set of messages like those defined in Figure 3-28.
In the example shown in Figure 3-29, the StockBrokerQueryPortType declares an operation called GetStockPrice which is designed to allow users' systems to ask for the price of a particular equity.
The input to this operation is provided by a StockPriceRequestMessage message. The contents of this message are understood by the implementing Web service, which formulates a response in an output StockPriceRequestMessage message that contains the details of the stock price for the equity requested.
Figure 3-29. Defining portType elements.
<wsdl:portType name="StockBrokerQueryPortType"> <wsdl:operation name="GetStockPrice"> <wsdl:input message="tns:StockPriceRequestMessage"/> <wsdl:output message="tns:StockPriceResponseMessage"/> <wsdl:fault name="UnknownSymbolFault" message="tns:StockSymbolNotFoundMessage"/> </wsdl:portType>
Any exceptional behavior is returned to the caller through a fault called UnknownSymbolFault which is comprised from a StockSymbolNotFoundMessage message. Note that portType fault declarations have an additional name attribute compared to input and output messages, which is used to distinguish the individual faults from the set of possible faults that an operation can support.
Of course not all operations are so orthodox with a single input, output, and fault message, and so we have a variety of possible message exchange patterns described by the operation declarations within a portType, as follows:
Note that WSDL 1.2 changes the syntax of the portType declaration, renaming it interface. It also supports a useful new feature in the form of the extends attribute, which allows multiple interface declarations to be aggregated together and further extended to produce a completely new interface. For example, consider the situation where our simple stock Web service needs to evolve to support basic trading activities in addition to providing stock quotes. Using the extends mechanism, a new interface can be created which possesses all of the operations from each interface that it extends, plus any additional operations the developer chooses to add to it as exemplified in Figure 3-30.
The only potential pitfall when extending an interface is where names clash. For instance, an extending interface should take care not to call its operations by the same name as operations from any interface that it extends unless the operations are equivalent. Furthermore, the designer of a new interface that extends multiple existing interface declarations must take care to see that there are no name clashes between any of the interface declarations as well as with the newly created interface.
Figure 3-30. Extending interface definitions.
<wsdl:message name="BuyStockRequestMessage"> <wsdl:part name="symbol" element="stockQ:symbol"/> <wsdl:part name="amount" element="xs:positiveInteger"/> <wsdl:part name="bid" element="stockQ:StockPriceType"/> </wsdl:message> <wsdl:message name="BuyStockResponseMessage"> <wsdl:part name="symbol" element="stockQ:symbol"/> <wsdl:part name="amount" element="xs:positiveInteger"/> <wsdl:part name="price" element="stockQ:StockPriceType"/> </wsdl:message> <wsdl:message name="BidRejectedMessage"> <wsdl:part name="symbol" element="stockQ:symbol"/> <wsdl:part name="amount" element="xs:positiveInteger"/> <wsdl:part name="bid" element="stockQ:StockPriceType"/> <wsdl:part name="asking" element="stockQ:StockPriceType"/> </wsdl:message> <wsdl:interface name="StockBrokerQueryPurchaseInterface" extends="tns:StockBrokerQueryInterface" > <wsdl:operation name="BuyStock"> <wsdl:input message="tns:BuyStockRequestMessage"/> <wsdl:output message="tns:BuyStockRequestMessage"/> <wsdl:fault name="UnknownSymbolFault" message="tns:StockSymbolNotFoundMessage"/> <wsdl:fault name="BidRejectedFault" message="tns:BidRejectedMessage"/> </wsdl:interface>
The bindings element draws together the portType and operation elements into a form suitable for exposing to the network. Bindings contain information that dictates how the format of the abstract messages is mapped onto the features of a particular network-level protocol.
While WSDL supports bindings for a number of protocols including HTTP GET and POST, and MIME, we are primarily interested in the SOAP binding for our simple stock quote portType from Figure 3-29, which is presented in Figure 3-31.
Figure 3-31. A SOAP binding.
<wsdl:binding name="StockBrokerServiceSOAPBinding" type="tns:StockBrokerQueryPortType"> <soap:binding styleDefault="document" transport="http://www.w3.org/2002/12/soap/bindings/HTTP/" encodingStyleDefault="http://stock.example.org/schema" /> <wsdl:operation name="GetStockPrice"> <soap:operation soapAction="http://stock.example.org/getStockPrice"/> <wsdl:input> <soap:body use="literal"/> </wsdl:input> <wsdl:output> <soap:body use="literal"/> </wsdl:output> <wsdl:fault> <soap:fault name="StockSymbolNotFoundMessage"/> </wsdl:fault> </wsdl:operation> </wsdl:binding>
The binding shown in Figure 3-31 binds the abstract portType defined in Figure 3-29 to the SOAP. It states how each of the message components of the operation defined in the StockBrokerQueryPortType is mapped onto its SOAP equivalent.
Starting from the top, we see a name for the binding (which is later used to tie a binding to a physical network endpoint) and the portType for which this binding is specified.
We then use elements from the WSDL SOAP binding specification to declare a binding for SOAP document-style exchanges, which is expressed as the default mode for this binding through the styleDefault="document" attribute. The encoding of the documents exchanged is defined by the stock broker schema encodingStyleDefault="http://stock.example.org/schema". The fact that the service uses document-style SOAP and has its own schema means that it is a document-literal Web service.
Finall,y we see that the binding is for SOAP over the HTTP protocol as specified by the transport="http://www.w3.org/2002/12/soap/bindings/HTTP/" attribute. Each of these options is set as the default for the entire binding though both the style and encoding can be changed, if necessary, on a per-message basis.
This binding contains a single operation, namely GetStockPrice, which maps each of the input, output, and fault elements of the GetStockPrice operation from the StockBrokerQueryPortType to its SOAP on-the-wire format. The soapAction part of the operation binding is used to specify the HTTP SOAPAction header, which in turn can be used by SOAP servers as an indication of the action that should be taken by the receipt of the message at runtime which usually captures the name of a method to invoke in a service implementation.
The soap:body elements for both the wsdl:input and wsdl:output elements provide information on how to extract or assemble the different messages inside the SOAP body. Since we have chosen literal encoding and document style for our messages (via the use="literal" and styleDefault="document" attribute), each part of a corresponding message is simply placed as a child of the soap:body element of the SOAP envelope. Had we been using RPC-style SOAP, then the direct child of the soap:body would be an element with the same name as the operation, with each message part as a child, as per SOAP RPC style, as contrasted with document style in Figure 3-32.
Figure 3-32. Example SOAP RPC-style "Wrapping" element.
<!-- RPC style --> <soap:body> <GetStockPrice xmlns:gsp="http://stock.example.org/wsdl" xmlns:stockQ="http://stock.example.org/schema"> <stockQ:symbol>MSFT</stockQ:symbol> </GetStockPrice> </soap:body> <!-- Document style --> <soap:body> <stockQ:symbol xmlns:stockQ="http://stock.example.org/schema"> MSFT </stockQ:symbol> </soap:body>
Of course, the value of SOAP is not only that it provides a platform-neutral messaging format, but the fact that the mechanism is extensible through headers. To be of use in describing SOAP headers, the WSDL SOAP binding has facilities for describing header content and behavior. For example, imagine that the query operation for which we have already designed a SOAP binding in Figure 3-31 evolves such that only registered users can access the service and must authenticate by providing some credentials in a SOAP header block as part of an invocation. The WSDL interface for the service obviously needs to advertise this fact to users' applications or no one will be able to access the service.
The WSDL fragment shown in Figure 3-33 presents a hypothetical soap:header declaration within the wsdl:input element which mandates that a header matching the same namespace as the userID message (as declared earlier in the document) is present, and will be consumed by the ultimate receiver of the incoming SOAP message.
Figure 3-33. Describing SOAP headers.
<wsdl:message name="UserID" targetNamespace="http://security.example.org/user"> <wsdl:part name="signature" type="xs:string"/> <wsdl:part name="session" type="xs:anyURI"/> </wsdl:message> <wsdl:input> <soap:body use="literal"/> <soap:header use="literal" message="tns:UserIDMessage"/> </wsdl:input> <wsdl:output xmlns:sec="http://security.example.org/user"> <soap:body use="literal"/> <soap:headerfault message="sec:UserID" part="signature"/> </wsdl:output>
Correspondingly, a soap:headerfault element is present in the wsdl:output element to report back on any faults that occurred while processing the incoming header. If a fault does occur while processing the header, this soap:headerfault element identifies the user's signature that caused the problem. This information, which amounts to a "user unknown" response, can then be used at the client end to perhaps prompt the end user to re-enter a pass phrase.
Note that an error such as an incorrect signature is propagated back through the header mechanism and not through the body, since the SOAP specification mandates that errors pertaining to headers must be reported likewise through header blocks.
The services element finally binds the Web service to a specific network-addressable location. It takes the bindings declared previously and ties them to a port, which is a physical network endpoint to which clients bind over the specified protocol.
Figure 3-34 shows a service description for our stockbroker example. It declares a service called StockBrokerService, which it defines in terms of a port called StockBrokerServiceSOAPPort. The port is itself defined in terms of the StockBrokerServiceSOAPBinding binding, which we saw in Figure 3-31, and is exposed to the network at the address http://stock.example.org/ to be made accessible through the endpoint specified at the soap:address element.
Figure 3-34. A service element declaration.
<wsdl:service name="StockBrokerService"> <wsdl:port name="StockBrokerServiceSOAPPort" binding="tns:StockBrokerServiceSOAPBinding"> <soap:address location="http://stock.example.org/"/> </wsdl:port> </wsdl:service>
Managing WSDL Descriptions
While the service element is the final piece in the puzzle as far as an individual WSDL document goes, that's not quite the end of the story. For simple one-off Web services, we may choose to have a single WSDL document that combines both concrete and abstract parts of the interface. However, for more complex deployments we may choose to split the abstract parts into a separate file, and join that with a number of different concrete bindings and services to better suit the access pattern for those services.
For example, it may be the case that a single abstract definition (message, portType, and operation declarations) might need to be exposed to the network via a number of protocols, not just SOAP. It might also be the case that a single protocol endpoint might need to be replicated for quality of service reasons or perhaps even several different organizations each want to expose the same service as part of their Web service offerings. By using the WSDL import mechanism, the same abstract definition of the service functionality can be used across all of these Web services irrespective of the underlying protocol or addressing. This is shown in Figure 3-35 where MIME, HTTP, and SOAP endpoints all share the same abstract functionality yet expose that functionality to the network each in their own way. Additionally, the SOAP protocol binding has been deployed at multiple endpoints which can be within a single administrative domain or spread around the whole Internet and yet each service, by dint of the fact that they share the same abstract definitions, is equivalent.
Figure 3-35. Including abstract WSDL descriptions for concrete endpoints.
If a WSDL description needs to include features from another WSDL description or an external XML Schema file, then the import mechanism is used. It behaves in a similar fashion to the XML Schema include feature where it can be used to include components from other WSDL descriptions. We have already seen how the WSDL import mechanism is used in Figure 3-27 where the XML Schema types from the stockbroker schema were exposed to the stock broking WSDL description, as follows:
<wsdl:import namespace="http://stock.example.org/schema" location="http://stock.example.org/schema"/>
The import feature of WSDL means that a WSDL description can leverage existing XML infrastructure previously defined schemas for in-house documents, database schemas, existing Web services, and the like without having to reproduce those definitions as part of its own description.
As Web services technology has advanced and matured, WSDL has begun to form the basis of higher-level protocols that leverage the basic building blocks that it provides, to avoid duplication of effort. Many of the technologies that we are going to examine throughout this book extend WSDL via such means to their own purpose. However, where SOAP offers header blocks as its extensibility mechanism for higher-level protocols to use, WSDL offers extension elements based on the XML Schema notion of substitution groups (see Chapter 2).
In the WSDL schema, several (abstract) global element declarations serve as the heads of substitution groups. In addition, the WSDL schema defines a base type for use by extensibility elements as a helper to ensure that the necessary substitution groups are present in any extensions. While it is outside the scope of this book to present the WSDL schema in full, there exists in the schema extensibility elements which user-defined elements can use to place themselves at any point within a WSDL definition. There are extensibility elements that allow extensions to appear at global scope, within a service declaration, before the port declaration, in a message element before any part declarations and any other point in a WSDL description, as shown in Figure 3-36.
Figure 3-36. WSDL substitution group heads.
For example, the soap elements that we have seen throughout the bindings section of our WDSL description are extensibility elements. In the schema for those elements, they have been declared as being part of the substitution group bindingExt which allows them to legally appear as part of the WSDL bindings section.
Additionally, third-party WSDL extensions may declare themselves as mandatory with the inclusion of a wsdl:required attribute in their definitions. Once a required attribute is set, any and all validation against an extended WSDL document must include the presence of the corresponding element as a part of the validation.