Message oriented interoperability between WCF channels and Oracle Application Server WSIF
Overview
A few weeks ago, in an SOA forum, someone inquired about which technologies to use to achieve untyped interactions with Web Services. Untyped interactions are interactions in which the Service Contract (WSDL, Policies, etc) is not available at design time. This is a classic Enterprise Service Bus (ESB) scenario in which multiple and generic interactions with Web Services are needed and in which generating specific proxies per Web Services is not a practical solution. Going back to my conversation, my response to the question was that the Windows Communication Foundation (WCF) channel model in .NET and the Web Services Invocation Framework (WSIF) in J2EE are technologies that can address those types of scenarios. Surprisingly, when somebody else asked me about .NET-J2EE interoperability references for those technologies, mainly WSIF, I could not find a good example for them to reference.
After this introduction you can guess what this post is all about. I intend to illustrate the techniques used in WSIF and WCF channels to achieve interoperable solutions.
Low-level Interoperability
From the technology standpoint both WCF channels and WSIF reside at the lowest level of the Web Service stack. In other words, this is the component in the Web Service technology responsible for sending and receiving messages from Web Services. Contrary to WCF channels which are exclusive to WCF, WSIF is the foundation of numerous J2EE-based Web Service stacks such as Oracle Application Server, WebSphere Application Server and WSO2 (this is J2SE). WSIF is also the foundation of some integration products such as Oracle BPEL Process Manager, Oracle ESB, and WebSphere Integration Server, among others. For the purposes of the demonstrations included on this article we are going to focus on Oracle Application Server.
Although WCF channels and WSIF reside at the lowest level of the Web Service technology stack, they still present interoperability challenges in terms of contract design and serialization. The difference from the consumer standpoint relies on the fact that the developer has complete control of the serialization process despite having no type information available at design time. On the other hand, the consumer has to be intimately familiar with the contract of the service in order to create the proper messages.
Even though this article does not intend to deep dive into the WCF or WSIF concepts, a high level overview of the technologies is required in order to walk through the samples.
WSIF overview
WSIF provides standard interfaces to interact with Web Services using alternative protocols to the classic SOAP over HTTP approach. The protocols used can be either transport (TCP, UDP) or technology (EJB, JMS). WSIF relies on WSDL bindings in order to reference the different providers. The following picture illustrates a high overview of the WSIF architecture
Figure 1: WSIF Architecture overview
As the figure illustrates WSIS implements an extensible provider model in order to abstract the different transports. A common kernel layer is responsible for determining the required provider for executing the operation based on the WSDL binding. The SOAP provider must be used for standard Web Services interactions. The extensible WSIF provider model is used by multiple J2EE Web Service technologies as the mechanism for supporting multi-transport and multi-binding Web Services. WCF Channels would be considered the equivalent to that model.
WCF Channels overview
WCF endpoints communication with other applications using a communication stack called the channel stack. Basically, every component of the channel stack abstracts the communication aspects of the layers below it and exposes that abstraction only to the layer above it. The lowest level in the channel stack is the transport channel which sends and receives messages using a transport protocol like TCP or UDP.
Figure 2: WCF Channel stack overview
The main communication unit between channels is the Message object. Above the transport channel could be a number of protocols channels responsible for performing additional functionalities against the message object like encryption, transformation, etc. The main components for channel interactions are Channel Factories on the client side and Channel Listeners on the Service side. In particular, WCF channel listeners are combined with different types of channels in order to receive messages. IReplyChannel is commonly used for receiving request messages and sending back reply messages.
The following sections will illustrate how to create interactions between the WCF channel and WSIF models.
Programming with the WCF Service Channel Layer
For the purposes of this example we will use the following WCF contract which simulates a mathematical operation.
[DataContract(Name="Add", Namespace="http://tempuri.org/")] public class Add { private int fparam1; private int fparam2; [DataMember] public int param1 { get { return fparam1; } set { fparam1 = value; } } [DataMember] public int param2 { get { return fparam2; } set { fparam2 = value; } } } [DataContract(Name = "AddResponse", Namespace = "http://tempuri.org/")] public class AddResponse { private int fAddResult; [DataMember] public int AddResult { get { return fAddResult; } set { fAddResult = value; } } } |
The next step is to create a WCF listener that waits for a message and returns a response. In order to receive a message we are going to use an IReplyChannel instance. Also, for interoperability purposes we will bind the listener using the basicHttpBinding.
private static void OpenListener() { BasicHttpBinding binding = new BasicHttpBinding(); IChannelListener<IReplyChannel> listener = binding.BuildChannelListener<IReplyChannel>(new Uri(WCF Endpoint Url…), new BindingParameterCollection()); listener.Open(); IReplyChannel channel = listener.AcceptChannel(); channel.Open(); rest of the code… } |
Then we have to add the required logic to receive a message. This is accomplished by calling the ReceiveRequest method of the IReplyChannel class.
RequestContext request = channel.ReceiveRequest(); Message message = request.RequestMessage; Add data = message.GetBody<Add>(); |
After that, we are ready to build and send the response message. We will use the Reply operation of the IReplyChannel class.
AddResponse result = new AddResponse(); result.AddResult = data.param1 + data.param2; Message replymessage = Message.CreateMessage(message.Version, "http://tempuri.org/", result); //Send the response… request.Reply(replymessage); |
Finally, we must release the resources allocated to the messages, channels and listeners.
message.Close(); request.Close(); channel.Close(); listener.Close(); |
This is basically all the logic we need to put in place to create a WCF channel listener that implements a request-response message exchange pattern. Now, we will create a WSIF client that interacts with the WCF channel.
Creating an Oracle App Server client using WSIF
WISF relies heavily on WSDL descriptions for its interaction with the service side. For that reason, we have to create a WSDL that contains the required type of information to interact with the WCF channel. An important difference from the classic “proxy” approach is that in this scenario the WSDL is interpreted at runtime and no type information is required at design time.
The first step is to create the WSIF client artifacts required to interact with the WCF listener. We are going to accomplish that using the service, operation and port classes available in WSIF.
WSIFServiceFactory factory= WSIFServiceFactory.newInstance(); WSIFService service= factory.getService(WCF Service WSDL URL…, null, null, "http://tempuri.org/", "IMyService"); WSIFPort port= service.getPort("BasicHttpBinding_IMyService"); WSIFOperation operation = port.createOperation("Add"); |
The next step is to create the messages required to interact with the WCF listener.
WSIFMessage input = operation.createInputMessage(); WSIFMessage output = operation.createOutputMessage(); WSIFMessage fault = operation.createFaultMessage(); |
Next, we must add the logic for populating the request message. Given that our WCF Service uses document-literal encoding, we will build the message using the SOAPElement class instead rather than the XML-RPC classes.
SOAPFactory msgFactory= SOAPFactory.newInstance(); SOAPElement opElement= msgFactory.createElement("Add", "ns0", "http://tempuri.org/"); SOAPElement param1Element= msgFactory.createElement("param1", "ns0", "http://tempuri.org/"); SOAPElement param2Element= msgFactory.createElement("param2", "ns0", "http://tempuri.org/"); param1Element.addTextNode("11"); param2Element.addTextNode("12"); opElement.addChildElement(param1Element); opElement.addChildElement(param2Element); //Set the message part… input.setObjectPart("parameters", opElement); |
Finally, we need to send the message and receive the response. For a request-response interaction we use the executeRequestResponseOperation method of the WSIFOperation class.
operation.executeRequestResponseOperation(input, output, fault); SOAPBodyElement result= (SOAPBodyElement)output.getObjectPart("parameters"); |
The following SOAP message is produced by the WSIF client.
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <s:Header xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <To s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://192.168.1.207:8081/MathWCF/Service.svc</To> <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://tempuri.org/IMyService/Add</Action> </s:Header> <env:Body> <ns0:Add xmlns:ns0="http://tempuri.org/"> <ns0:param1>11</ns0:param1> <ns0:param2>12</ns0:param2> </ns0:Add> </env:Body> </env:Envelope> |
After the WCF listener code executes, the following SOAP message is produced.
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Header> <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://tempuri.org/</Action> </s:Header> <s:Body> <AddResponse xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://tempuri.org/"> <AddResult>23</AddResult> </AddResponse> </s:Body> </s:Envelope> |
Where are we?
This article has illustrated how to achieve interoperability between WCF channels and the WSIF implementation in Oracle Application Server. Both technologies allow creating interactions with Web Services with no type information required at design time. WCF Channels and WSIF represent key components of the messaging layer for WCF and Oracle App Server Web Services. Achieving interoperability between those technologies opens the door for a lot of complex messaging scenarios.