Thursday August 02, 2007
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:
The specifications of cdp:CompositeDuplex
and ow:OneWay are not available and these elements will
thus be ignored by the Metro WSDL importing tool.
The operations from "callback" interface are mapped as Notification operation in the WSDL. This operation primitive is not supported by Metro.
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
Posted by Arun Gupta in webservices | Comments[8]
|
|
|
|
|
Today's Page Hits: 5215
Total # blog entries: 999
| « November 2009 | ||||||
| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
1 | 2 | 4 | 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 | ||||||
Posted by Christian Weyer: Smells like service spirit on August 02, 2007 at 11:08 PM PDT #
Posted by Scott Seely on August 04, 2007 at 04:53 PM PDT #
Posted by Chuck Rianhard on August 06, 2007 at 01:09 PM PDT #
Posted by Mike Taulty's Blog on August 06, 2007 at 05:02 PM PDT #
I guess we should all just start using Java instead. ;)
Posted by MSGuy on January 28, 2008 at 05:13 PM 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 21, 2008 at 03:42 AM 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 07:56 AM PDT #
Posted by Harold Carr's Blog on April 09, 2009 at 10:36 AM PDT #