Skip to main content

Indigo Duplex Bindings

Posted by arungupta on May 16, 2005 at 1:26 PM PDT

In part
1
of the series, I gave a short tutorial on Indigo
bindings. In this entry, I'll describe Indigo Duplex bindings and related code
sample.

Indigo Duplex Binding enables bi-directional communication between a service
and client. The steps
in Indigo documentation
are scattered unless I look at the Duplex
Contract sample
. In this entry I'm listing all the steps and tried
explaining my understanding at each step with certain questions I'm still trying
to answer.

In a single direction communication, client can invoke a set of operations on
a service and this interface is referred as "primary" interface in
Duplex binding. The set of operations that service can invoke on client is
called as "callback" interface. To enable bi-directional
communication, here is what I need to do on the service side:

  1. Link the "primary" and "callback" interface by setting
    CallbackContract property in the "primary" interface to the type of
    the "callback" interface.
  2. Get the CallbackChannel from OperationContext
    specifying the "callback" interface

Here is the service endpoint interface showing the linking using CallbackContract
in line (01):

(01) [ServiceContract(CallbackContract=typeof(ICalculatorCallback)]
(02) public interface ICalculator {
(03)   [OperationContract]
(04)   double add(double n1, double n2);
(05) }
(06)
(07) public interface ICalculatorCallback {
(08)   [OperationContract]
(09)   void display(string line);
(10) }

Line (02) to (05) is the "primary" interface and line (07) to (10)
is the "callback" interface. Here is the service endpoint
implementation:

(01) public class CalculatorService : ICalculator {
(02)   CalculatorCallback callback = null;
(03)   public CalculatorService() {
(04)     callback = OperationContext.Current.GetCallbackChannel<ICalculatorCallback>();
(05)   }
(06)
(07)   public double add(double n1, double n2) {
(08)     callback.display(“Adding “ + n1 + “ and “ + n2);
(09)     return n1 + n2;
(10)   }
(11) }

Line (03) to (05) is the constructor and retrieves the callback channel,
passing the generic type ICalculatorCallback. Line
(08) invokes the "callback" interface. The "callback" interface
is implemented on the client side but the
programming model requires it to be defined on the server side explicitly. Here
is the
binding configuration file:
(01) <configuration>
(02)   <system.serviceModel>
(03)     <services>
(04)       <service serviceType=”Calculator”>
(05)         <endpoint
(06)          address=””
(07)           contractType=”ICalculator”
(08)          bindingType=”wsProfileDualHttpBinding”/>
(09)       </service>
(10)     </services>
(11)   </system.serviceModel>
(12) </configuration>

Line (08) shows the WsProfileDualHttpBinding is
used and that's what enables duplex binding on the service. Once the service is
deployed in IIS, here is an Indigo generated wsdl:portType:

(01) <portType>
(02)   <wsdl:operation name="Add">
(03)     <wsdl:input wsa:Action="..." message="tns:ICalculator_Add_InputMessage"/>
(04)     <wsdl:output wsa:Action="..." message="tns:ICalculator_Add_OutputMessage"/>
(05)   </wsdl:operation>
(06)
(07)   <wsdl:operation name="Display">
(08)     <wsdl:output wsa:Action="..." message="tns:ICalculator_Display_OutputCallbackMessage"/>
(09)     <wsdl:input wsa:Action="..." message="tns:ICalculator_Display_InputCallbackMessage"/>
(10)   </wsdl:operation>
(11) </portType>

The methods on "callback" interface, Display in our case, are
described in WSDL 1.1 using the following patterns:

  1. wsdl:operation on line (07) resembles WSDL 1.1 solicit-response
    operation, i.e. the sequence is wsdl:output and wsdl:input
    instead of wsdl:input and wsdl:output in the
    conventional request-response
    operation.
  2. The input message, on line (08), is suffixed with _InputCallbackMessage
    and output message, on line (09), with _OutputCallbackMessage.
wsdl:port within wsdl:service looks like:

(01) <wsdl:port name="WSProfileDualHttpBinding_ICalculator_port" binding="i0:WSProfileDualHttpBinding_ICalculator">
(02)   <soap:address location="http://wsstar/duplex/service.svc" /> 
(03)     <EndpointReference xmlns="http://schemas.xmlsoap.org/ws/2004/08/addressing"> 
(04)       <Address>http://wsstar/duplex/service.svc</Address> 
(05)     </EndpointReference>
(06) </wsdl:port>

Line (03) to (05) shows an EndpointReference
within wsdl:port as an extensibility element. Now,
lets take a look at the client code:

(01) public class CalculatorClient {
(02)   static void Main() {
(03)     ServiceSite site = new ServiceSite(CalculatorCallback);
(04)     CalculatorProxy proxy = new CalculatorProxy(site, "CalculatorEndpoint");
(05)     double result = proxy.add(10.00D, 20.00D);
(06)     Console.writeLine("Result is: " + result);
(07)   }
(08) }
(09)
(10) public class CalculatorCallback : ICalculatorCallback {
(11)   public void Display(string line) {
(12)     Console.writeLine(line);
(13)   }
(14) }

Line (01) to (08) is the client class and line (10) to (14) is the
implementation of the "callback" interface defined on the service. Most of the client code is simple except ServiceSite class,
in line (03), which
seems to perform the magic of enabling the callback. ServiceSite is
initialized by passing a reference to the implementation of "callback"
interface and then it is passed to the proxy constructor. Service endpoint is
invoked, via proxy, on line (05). I could not find much
documentation
on how ServiceSite enables duplex contract but here
is the console output on the client-side:

(01) Adding 10.00 and 20.00
(02) Result is: 30.00

So the request goes from client to the service endpoint, line (08) in service
endpoint implementation above is invoke that displays line (01) in the console,
response comes back to client and then line (02) from the client code, in line
(06), is displayed.

Here is the SOAP header of the request message from client to service
endpoint:

<s:Header>
  <a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/rm/CreateSequence</a:Action>
  <a:ReplyTo>
    <a:Address>http://wsstar:8000/Temporary_Indigo_Addresses/...698064</a:Address>
  </a:ReplyTo>
  <a:MessageID>uuid:1adf8d20-bad9-47a1-90aa-8510c3b0bfec;id=0</a:MessageID>
  <a:To s:mustUnderstand="1">http://localhost/duplex/service.svc</a:To>
</s:Header>

The particular header element to notice is <a:ReplyTo> that
sends a HTTP address to service endpoint and thus enabling callback. ServiceSite
extends from CommunicationObject, but unfortunately it is not a
hyperlink (how I miss Javadoc
style documentation) and I could not find any other related documentation so I
still need to understand the magic behind how this header is generated. So far
my understanding is that specifying ServiceSite seems to generate
an HTTP endpoint, hosted on IIS, on port 8000 in
the context root Temporary_Indigo_Addresses. Is
this supposed to work on a non-Windows platform ?

Anyway, here is the SOAP header of the response message from service to client endpoint:

<s:Header>
  <a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/rm/CreateSequenceResponse</a:Action>
  <a:RelatesTo>uuid:1adf8d20-bad9-47a1-90aa-8510c3b0bfec;id=0</a:RelatesTo>
  <a:To s:mustUnderstand="1">http://wsstar:8000/Temporary_Indigo_Addresses/...698064</a:To>
  <RedirectTo s:mustUnderstand="1" xmlns="http://schemas.xmlsoap.org/ws/2004/06/addressingex">
    <a:EndpointReference>
      <a:Address>http://wsstar/duplex/service.svc</a:Address>
      <Via>http://wsstar:8000/Temporary_Indigo_Addresses/...26df92</Via>
    </a:EndpointReference>
  </RedirectTo>
</s:Header>

Note that the <a:To> header uses the
header from the SOAP request message and thus enabling callback.

Related Topics >>