wsHttpDualBinding - a non-interoperable binding

Based upon a user request, I'll explain why wsDualHttpBinding (a system-provided binding in WCF) is not an interoperable binding. This blog entry will explain the service endpoint code, client code, generated WSDL and the SOAP messages exchanged based upon the DualHttp Binding Sample that is bundled with the Windows SDK samples. This is also an update to an earlier attempt to explain wsDualHttpBinding.

In the sample, I replaced the default wsDualHttpBinding with an equivalent custom binding (after removing the security) as:

<bindings>
  <customBinding>
    <binding name="Binding1">
      <reliableSession />
      <compositeDuplex />
      <oneWay />
      <textMessageEncoding
                     messageVersion="Soap12WSAddressing10" 
                     writeEncoding="utf-8" />
      <httpTransport authenticationScheme="Anonymous"
                     bypassProxyOnLocal="false"
                     hostNameComparisonMode="StrongWildcard"
                     proxyAuthenticationScheme="Anonymous"
                     realm=""
                     useDefaultWebProxy="true" />
    </binding>
  </customBinding>
</bindings>

The wsDualHttpBinding, also known as Composite Duplex or Full Duplex Binding, provides a bi-directional communication between Client and Endpoint. In a single direction communication, the 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 a service can invoke on the client is called as callback interface.

The sample in Windows SDK is a trivial calculator service where primitive Math operations are performed in a running session on the "primary" interface and results are returned on the "callback" interface.

Service Endpoint Code

Let's understand the service endpoint code first. The "primary" service endpoint interface looks like:

[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples", 
                 SessionMode=SessionMode.Required,
                 CallbackContract=typeof(ICalculatorDuplexCallback))]
public interface ICalculatorDuplex
{
  [OperationContract(IsOneWay=true)]
  void Clear();
  [OperationContract(IsOneWay = true)]
  void AddTo(double n);

  ...
}

Three points to note in this code:

  • A duplex contract requires a session, because a context must be established to correlate the set of messages being sent between client and service. Even though this is specified using SessionMode=SessionMode.Required attribute but the default value of SessionMode=SessionMode.Allowed (equivalent of not specifying) will be sufficient as well. This is because all Duplex bindings in .NET maintain a transport session.

  • Callback interface is specified using CallbackContract attribute.

  • All the methods are declared as One-way operations otherwise the response can be returned on the transport back channel itself. The documentation on this particular binding is limited.

The "callback" interface is defined as:

public interface ICalculatorDuplexCallback
{
  [OperationContract(IsOneWay = true)]
  void Result(double result);

  ...
}

In order for a service endpoint to establish a connection with the "callback" interface on client, a CallbackChannel is obtained from the OperationContext in the implementation of the "primary" interface as:

public class CalculatorService : ICalculatorDuplex
{
  double result;
  ICalculatorDuplexCallback callback = null;

  public CalculatorService()
  {
    result = 0.0D;
    callback = OperationContext.Current.GetCallbackChannel<ICalculatorDuplexCallback>();
  }

Another variable is initialized to return the running result. The implementation of each method in the "primary" interface then invokes a method on the "callback" interface to return the running result as:

public void AddTo(double n)
{
  result += n;
  callback.Result(result);
}

Generated WSDL

Now let's look at the generated WSDL fragments. The policy assertion elements are:

<wsp:All>
  <wsrm:RMAssertion xmlns:wsrm="http://schemas.xmlsoap.org/ws/2005/02/rm/policy">
    <wsrm:InactivityTimeout Milliseconds="600000" /> 
    <wsrm:AcknowledgementInterval Milliseconds="200" /> 
  </wsrm:RMAssertion>
  <cdp:CompositeDuplex xmlns:cdp="http://schemas.microsoft.com/net/2006/06/duplex" /> 
  <ow:OneWay xmlns:ow="http://schemas.microsoft.com/ws/2005/05/routing/policy" /> 
  <wsaw:UsingAddressing /> 
</wsp:All>

The wsrm:RMAssertion and wsaw:UsingAddressing elements are bound to a known namespace and their behavior is clearly documented. However the specification of cdp:CompositeDuplex and ow:OneWay elements are unclear at this time. This does not allow any WSDL-based interoperability whenever these elements are included.

All methods from the "primary" and the "callback" interface are generated in one wsdl:portType. The methods from the "primary" interface are generated as One-way operations. But methods from the "callback" interface are generated as Notification operation. For example, one of the methods from "callback" interface looks like:

<wsdl:operation msc:isInitiating="true" msc:isTerminating="false" name="Result">
  <wsdl:output
      wsaw:Action="http://Microsoft.ServiceModel.Samples/ICalculatorDuplex/Result"
      message="tns:ICalculatorDuplex_Result_OutputCallbackMessage"/>
</wsdl:operation>

JAX-WS, the core of Metro, supports only Request-Response and One-way operations. This is the second place where WSDL-based interoperability will not work with any JAX-WS-based WSDL import tool, such as wsimport. Moreover, the WSDL-to-Java mapping defined by the JAX-WS specification requires each wsdl:portType map to a single Java interface. This WSDL design pattern requires two interfaces to be generated from a single wsdl:portType.

There are some other elements in namespace prefix bound to "http://schemas.microsoft.com/ws/2005/12/wsdl/contract" and their purpose is also unclear. Rest of the WSDL is pretty straight-forward.

Client side code

On the client side, svcutil (WSDL importing tool for .NET 3.0) generates the "primary" and "callback" interface from the WSDL. The "callback" is implemented as:

public class CallbackHandler : ICalculatorDuplexCallback
{
  public void Result(double result)
  {
    Console.WriteLine("Result({0})", result);
  }

  public void Equation(string eqn)
  {
    Console.WriteLine("Equation({0})", eqn);
  }
}

This client instance is initialized with the callback implementation as:

class Client
{
  static void Main()
  {
    // Construct InstanceContext to handle messages on callback interface
    InstanceContext instanceContext = new InstanceContext(new CallbackHandler());

    // Create a client with given client endpoint configuration
    CalculatorDuplexClient client = new CalculatorDuplexClient(instanceContext);

And then the client invokes the service endpoint normally as shown below:

// Call the AddTo service operation.
double value = 100.00D;
client.AddTo(value);

...

SOAP messages

Lets look at the SOAP messages exchanged between client and endpoint now. The first call from the client to an endpoint triggers a protocol handshake for establishing a session. The CreateSequence protocol message looks like:

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
  <s:Header>
    <a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/rm/CreateSequence</a:Action>
    <a:ReplyTo>
      <a:Address>http://iamfine.sfbay.sun.com/Temporary_Listen_Addresses/bfd8c103-b0f9-4c65-9cb6-fbebb7d1517b/4e0cdb31-2451-4fb6-84b8-dc286e5f26c8</a:Address>
    </a:ReplyTo>
    <a:MessageID>urn:uuid:51918652-9a78-4ba3-82f5-e68ecd664d42</a:MessageID>
    <a:To s:mustUnderstand="1">http://localhost:8888/</a:To>
  </s:Header>
  <s:Body>
    <CreateSequence xmlns="http://schemas.xmlsoap.org/ws/2005/02/rm">
      <AcksTo>
        <a:Address>http://iamfine.sfbay.sun.com/Temporary_Listen_Addresses/bfd8c103-b0f9-4c65-9cb6-fbebb7d1517b/4e0cdb31-2451-4fb6-84b8-dc286e5f26c8</a:Address>
      </AcksTo>
      <Offer>
        <Identifier>urn:uuid:b1116e69-f1dd-45b0-8495-129645038160</Identifier>
      </Offer>
    </CreateSequence>
  </s:Body>
</s:Envelope>

The WCF runtime uses the Windows HTTP.SYS library to host an endpoint at the address specified in a:ReplyTo. This address is used for all subsequent messages sent on the callback channel. This message is used to create a session for the "primary" interface. The message also carries an offer, in the SOAP Body, to create a "callback" interface session.

The CreateSequenceResponse protocol message returns "primary" interface session identifier and also accepts the offered "callback" session. The message looks like:

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
  <s:Header>
    <a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/rm/CreateSequenceResponse</a:Action>
    <a:RelatesTo>urn:uuid:51918652-9a78-4ba3-82f5-e68ecd664d42</a:RelatesTo>
    <a:To s:mustUnderstand="1">http://iamfine.sfbay.sun.com/Temporary_Listen_Addresses/bfd8c103-b0f9-4c65-9cb6-fbebb7d1517b/4e0cdb31-2451-4fb6-84b8-dc286e5f26c8</a:To>
  </s:Header>
  <s:Body>
    <CreateSequenceResponse xmlns="http://schemas.xmlsoap.org/ws/2005/02/rm">
      <Identifier>urn:uuid:d483898c-4bd3-4077-ba04-07a9010ab27f</Identifier>
      <Accept>
        <AcksTo>
          <a:Address>http://localhost:8888/</a:Address>
        </AcksTo>
      </Accept>
    </CreateSequenceResponse>
  </s:Body>
</s:Envelope>

Now, because of the way each method is implemented (invoking callback.Result(result) method at the end of each "primary" operation), a response to a request received by an endpoint is returned over the callback channel. This happens under-the-cover even though all messages in the "primary" interface are defined as One-way operations.

The behavior is quite analogous to a Request-Response operation primitive. I wonder what are the usecases of wsDualHttpBinding ?

Summary

Finally, I summarize the reasons that makes wsDualHttpBinding a non-interoperable binding:

  1. The specifications of cdp:CompositeDuplex and ow:OneWay are not available and these elements will thus be ignored by the Metro WSDL importing tool.

  2. The operations from "callback" interface are mapped as Notification operation in the WSDL. This operation primitive is not supported by Metro.

  3. On the service endpoint, all the operations from "primary" and "callback" interface are mapped to a single wsdl:portType. On the client side, wsdl:portType is mapped to separate "primary" and "callback" interfaces. The Java-to-WSDL mapping defined by the JAX-WS specification allows one-to-one mapping between Java interface and wsdl:portType.

Technorati: webservices interoperability wcf metro jax-ws wsit

Comments:

[Trackback] There you have it - very insightful article by Arun.

Posted by Christian Weyer: Smells like service spirit on August 02, 2007 at 04:08 PM PDT #

[Trackback] Arun Gupta recently asked what the use cases are for WSDualHttpBinding over here. To save you some time,...

Posted by Scott Seely on August 04, 2007 at 09:53 AM PDT #

Thanks Arun for the explanation. Below I've included a couple of use cases dealing with the callback capability inherent in the wsDualHttpBinding which I sent you privately but wanted to share with your blog readers as well: 1) One of my use cases deals with a job request that takes an unknown about of time to process a job (in the host) due to load or complexity. A service request is made by the client and a Boolean value is immediately returned on the job request being accepted or denied. If accepted the requestor (client) knows that at some point in the future its (the client's) callback method will be called with the results of the job request from the host (of course there is a watchdog timer just in case). I have this working great with wsDualHttp in a WCF to WCF environment currently and have a requirement to add a lightweight Java WSIT client. 2) Another possible use case, that I have not yet done but plan on, deals with a publish/subscribe scenario where a client subscribes to receive certain events. The client would receive these events through its callback.

Posted by Chuck Rianhard on August 06, 2007 at 06:09 AM PDT #

[Trackback] Interesting post&nbsp;(via Christian)

Posted by Mike Taulty's Blog on August 06, 2007 at 10:02 AM PDT #

I guess we should all just start using Java instead. ;)

Posted by MSGuy on January 28, 2008 at 09:13 AM PST #

Could any one tell me whether WSHttpBinding is Interoparable with java?

is there any example on how to do that.

Posted by raj on September 20, 2008 at 08:42 PM PDT #

Raj,

http://blogs.sun.com/arungupta/entry/glassfish_v2_beta3_and_vista is an example that shows how a WCF reliable endpoint is interoperable with Metro stack. A much more extensive set of test results (from Microsoft plugfest) are available at:

http://weblogs.java.net/blog/haroldcarr/archive/2008/03/metro_web_servi_2.html

Posted by Arun Gupta on September 21, 2008 at 12:56 AM PDT #

[Trackback] Clear back in August 2007 Arun Gupta wrote a blog entry with the title "wsHttpDualBinding - a non-interoperable binding". In point of fact, that binding is interoperable. My current entry explains the details.

Posted by Harold Carr's Blog on April 09, 2009 at 03:36 AM PDT #

The latest article on this topic is here:

http://weblogs.java.net/blog/haroldcarr/archive/2009/04/metro_interoper.html

This article isn't correct.

Posted by David on October 10, 2011 at 01:21 AM PDT #

Post a Comment:
Comments are closed for this entry.
About

profile image
Arun Gupta is a technology enthusiast, a passionate runner, author, and a community guy who works for Oracle Corp.


Java EE 7 Samples

Stay Connected

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today