Web Services Addressing, SOAP and Endpoint References:o

A Worked Example

Henry S. Thompson
7 Nov 2005

1.   Introduction

Since we started discussing EPRs in the TAG, I've been struggling for lack of a completely filled-in example. I've now produced one, backed by running code (trivial, almost all auto-generated). I'm short on time, but I thought I'd at least get a preliminary version out there so people can see an end-to-end illustration.

I've been helped by a sample and tutorial that comes with the Apache WSRF toolkit, but my example is much simpler than the example they start with.

2.   The service

A version of our old friend the stockquote service, but treating the stock itself as an EndpointParameter. Only one operation at the moment, namely LastTradePrice, but others could easily be added.

Here's the WSDL, with some comments inline.

<?xml version='1.0'?>
<definitions targetNamespace="http://example.com/stockquote" xmlns:ex="http://example.com/stockquote" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsa="http://www.w3.org/2005/08/addressing" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns="http://schemas.xmlsoap.org/wsdl/">
    <types>
       <schema targetNamespace="http://example.com/stockquote" xmlns="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">

The request element is empty, as the only relevant 'parameter' is stock symbol, which is part of the EPR:

<element name="TradePriceRequest">
              <complexType/>
           </element>

The response is pretty trivial:

<element name="TradePrice">
              <complexType>
                  <sequence>
                      <element name="price" type="float"/>
                  </sequence>
               <attribute name="id" type="ID"/>
              </complexType>
           </element>
       </schema>
    </types>

The messages are trivially based on the elements/types above:

<message name="GetLastTradePriceInput">
        <part name="body" element="ex:TradePriceRequest"/>
    </message>

    <message name="GetLastTradePriceOutput">
        <part name="body" element="ex:TradePrice"/>
    </message>

The portType is what's actually exposed as the name of the service:

<portType name="StockQuotePort">
    <operation name="GetLastTradePrice">

The WSA/Soap binding story includes rules for construction Action names from the namespace, portType and operation names. I've actually explicitly assigned the same URI as those rules would have come up with:

<input message="ex:GetLastTradePriceInput" wsa:Action="http://example.com/stockquote/StockQuotePort/GetLastTradePriceRequest"/>
      <output message="ex:GetLastTradePriceOutput" wsa:Action="http://example.com/stockquote/StockQuotePort/GetLastTradePriceResponse"/>
    </operation>
  </portType>

The binding is completely predictable except for the wsa:UsingAddressing bit, which means messages are required to use WS-Addressing-style SOAP headers.

<binding name="soapBinding" type="ex:StockQuotePort">
   <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
   <wsa:UsingAddressing wsdl:required="true"/>
   <operation name="GetLastTradePrice">
         <soap:operation style="document"/>
         <input>
            <soap:body use="literal"/>
         </input>
         <output>
            <soap:body use="literal"/>
         </output>
   </operation>
  </binding>

The service is again completely predictable:

<service name="StockQuote">
    <port name="StockQuotePort" binding="ex:soapBinding"> 
      <soap:address location="http://example.com/stockquote"/>
    </port>
  </service>
</definitions>

Note that the one thing that's missing from the above is any indication that there's a Reference Parameters that's required for the service to work. This seems bizarre to me, but maybe I'm missing something. In particular, it appears that in WSDL 2.0 you may provide type information and other constraints which properties must satisfy if present, but not, as far as I can tell, that they are required.

3.   The Endpoint Reference

Here's where we do find out about the reference parameter:

<?xml version='1.0'?>
<wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/08/addressing" xmlns:sq="http://example.com/stockquote">
 <wsa:Address>http://localhost:8080/wsrf/services/StockQuotePort</wsa:Address>
 <wsa:ReferenceParameters>
  <sq:ResourceIdentifier>IBM</sq:ResourceIdentifier>
 </wsa:ReferenceParameters>
</wsa:EndpointReference>

Note, somewhat bizarrely, that the ResourceIdentifier element which carries the crucial parameter, is in the example's stockquote namespace. This despite the fact that the toolkit which builds the service framework from the WSDL requires that it be called ResourceIdentifier. . .

4.   The SOAP message

Now we can put this all together. The EPR 'identifies' a service. But a service may have multiple functionalities, i.e. operations, so to construct a message for e.g. POSTing via HTTP, we need to combine the service identity from the EPR with a wsa:Action which identifies an operation:

<?xml version='1.0'?>
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sq="http://example.com/stockquote">

The SOAP header is where all the action is. We get the primary destination indicator (wsa:To), which is where we POST the whole thing to, and the crucial Reference Parameter, from the EPR. We get the operation designator (wsa:Action), from the WSDL.

<Header xmlns:wsa="http://schemas.xmlsoap.org/ws/2005/08/addressing">
      <wsa:To mustUnderstand="1">http://localhost:8080/wsrf/services/StockQuotePort</wsa:To>
    <wsa:Action mustUnderstand="1">http://example.com/stockquote/StockQuotePort/GetLastTradePriceRequest</wsa:Action>
    <sq:ResourceIdentifier wsa:IsReferenceParameter="true">IBM</sq:ResourceIdentifier>
   </Header>

In this example the body is completely redundant:

<Body>
    <sq:TradePriceRequest/>
   </Body>
</Envelope>

5.   The result

Using the toolkit and its wizard, I built a service which actually implements the above WSDL, and if you send it the above message, i.e. POST the SOAP message above to http://localhost:8080/wsrf/services/StockQuotePort, the following comes back:

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <soapenv:Header>
    <wsa:Action soapenv:actor="http://schemas.xmlsoap.org/soap/actor/next"
                soapenv:mustUnderstand="0"
                xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing"
>http://schemas.xmlsoap.org/ws/2004/03/addressing/anonymous</wsa:Action>
    <wsa:To soapenv:actor="http://schemas.xmlsoap.org/soap/actor/next"
            soapenv:mustUnderstand="0"
            xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/03/addressing"
>http://schemas.xmlsoap.org/ws/2004/03/addressing/anonymous</wsa:To>
   </soapenv:Header>
   <soapenv:Body>
    <stoc:TradePrice xmlns:stoc="http://example.com/stockquote">
     <stoc:price>3.75</stoc:price>
    </stoc:TradePrice>
   </soapenv:Body>
  </soapenv:Envelope>