Click here to Skip to main content
Click here to Skip to main content

SoapNULL Transport for WSE 3.0

, 8 Dec 2005
Rate this:
Please Sign up or sign in to vote.
This article describes an implementation, concept and usage of the NULL transport for WSE 3.0.

Contents

Introduction

The SoapNull transport is a lightweight connectivity layer between the SoapReceiver and SoapSender under the same appDomain. It is a simple bridge that allows using the same connectivity model to encapsulate a business layer from the physical deployment schema (tiers). The transport "wire" between the service and its consumer can be LONG (across the network), SHORT (across appDomains on the same machine) or NULL (within the appDomain) - based on the configuration schema.

This article describes an implementation, concept and usage of the NULL transport for WSE 3.0. I assume the reader is familiar with WSE 3 technology. More details about the WSE messaging stack can be found in my SoapMSMQ transport article.

Features

  • Request message pattern (OneWay)
  • Request - ReplyTo message pattern (2x OneWay)
  • RequestResponse message pattern
  • Service and client must be hosted in the same appDomain
  • No formatter needed
  • SoapEnvelope cloning and dispatching directly to the input channel

Configuration

The soap.null transport does not require any special configuration other than adding it into the transports collection.. The following code snippet shows a host CONFIG file where a <transports> section has been populated by the soap.null transport:

<configuration>
 <configSections>
  <section name="microsoft.web.services3" 
    type="Microsoft.Web.Services3.Configuration.WebServicesConfiguration, 
     Microsoft.Web.Services3,Version=3.0.0.0,Culture=neutral,
     PublicKeyToken=31bf3856ad364e35"/>
 </configSections>
 <microsoft.web.services3>
   <messaging>
     <transports>
       <add scheme="soap.null" 
         type="RKiss.WseTransports.SoapNullTransport, SoapNULL, 
         Version=3.0.0.0, Culture=neutral, 
         PublicKeyToken=5c954de69e82238f" />
     </transports> 
   </messaging> 
 </microsoft.web.services3>
</configuration>

Endpoint transport address

The soap.null transport URI address format is rather simple and requires schema and host sections only. Note that the host must always be a localhost: soap.null://localhost.

The following examples demonstrate the usage of soap.null addressing:

  1. Send a message to the endpoint with address urn:myReceiver via a null transport:
     EndpointReference epr = 
         new EndpointReference(new Uri("urn:myReceiver"));
     epr.Via = new Uri(@"soap.null://localhost"));
     SoapSender Sender = new SoapSender(epr);
     Sender.Send(message);
  2. Send message to the endpoint:
     EndpointReference epr = 
       new EndpointReference(new Uri("soap.null://localhost/myReceiver"));
     SoapSender Sender = new SoapSender(epr);
     Sender.Send(message);

Implementation

The concept of the soap.null transport is based on the fast SoapEnvelope message delivery to the appropriate SoapInputChannel. The message travels within the same appDomain, therefore streaming its image via the physical channel is not necessary. Note that the abstract SoapInputChannel class contains an event driven queue for storing SoapEnvelope references.

The following picture shows a concept of the soap.null transport and its position in the messaging infrastructure:

The Level 1 message workflow is started by a SoapSender that calls the SoapOutputChannel.Send method transparent to the SoapTransport. Only the SoapNullOutputChannel knows how to call its tightly coupled transport. When the call comes to the transport layer, the message is cloned and passed to the dispatcher that is responsible for delivering the message to the appropriate SoapInputChannel..

The following code snippet shows an implementation of the major transport part as shown in the above picture:

[Editor comment: Line breaks used to avoid scrolling.]

public void Send(SoapEnvelope message, 
                   EndpointReference endpoint)
{
  try
  {
    // 1. enqueue a copy of an outgoing 

    // message into the input channel

    SoapEnvelope message2 = 
             new SoapEnvelope(message.SoapVersion);

    // 2. encoding

    message2.Encoding = message.Encoding;

    // 3. clear Body created in the constructor 

    // (we have to use a reflection for this action) 

    message2.GetType().GetField("_body", bindingFlags).
                                SetValue(message2, null);

    // 4. import (copy) Envelope XmlElement

    message2.Envelope.ParentNode.ReplaceChild(
        message2.ImportNode(message.Envelope, true), 
                                      message2.Envelope);

    // 5. post load (it will create a Body, Header and Context) 

    message2.PostLoad();

    // 6. setup request headers

    message2.Context.Addressing.SetRequestHeaders(this.Via, 
                                                     this.Via);
    
    // 7. dispatch message to the properly channel

    if ((!base.DispatchMessage(message2) && (message2.Fault == null)) 
        && ((message2.Context.Addressing.RelatesTo == null) || 
        (message2.Context.Addressing.RelatesTo.RelationshipType!=
                                WSAddressing.AttributeValues.Reply)))
    {
      SoapEnvelope envelope = base.GetFaultMessage(message2, 
        new AddressingFault(AddressingFault.DestinationUnreachableMessage, 
        AddressingFault.DestinationUnreachableCode));
        
      if ((envelope.Context.Addressing.Destination != null) && 
            !envelope.Context.Addressing.Destination.TransportAddress.
                                       Equals(WSAddressing.AnonymousRole))
      {
          ISoapOutputChannel channel = 
              SoapTransport.StaticGetOutputChannel(
                         envelope.Context.Addressing.Destination);
          channel.Send(envelope);
      }
    }
  }
  catch (Exception ex)
  {
    string strError = string.Format(
                      "[{0}].Send error = {1}, epr={2}", 
                      UriScheme, ex.Message, 
                      endpoint.Address.Value.AbsoluteUri);
      
    throw new Exception(strError);
  }
}

There are more ways of cloning the SoapEnvelope, for instance, using InnerXml/OuterXml properties, ImportNode etc. Based on the cloning mechanism and size of the message payload, the transport layer can significantly increase or decrease the performance of the message exchange.

SoapEnvelope cloning requires to clone the Header, Body and the Context elements. The WSE 3 messaging namespace has a public method performing the PostLoad on the SoapEnvelope CLR object. Using this "horse" method, we need to clear the empty Body XmlElement created during the SoapEnvelope constructor. There is no public method for this action, therefore we are using a reflection method - see step #3 in the above code snippet.

Once we have a cleaned SoapEnvelope, the envelope XmlElement can be replaced by the import node of the original envelope element - step # 4. We finished message processing in the layer by calling the base transport DispatchMessage method. Of course, there is also a possibility that the dispatcher cannot deliver the message to the endpoint. In this case, the soap.null transport will create a FaultMessage based on the message addressing headers.

Note for WSE 2.0 migration: The PostLoad method in the WSE 2SP3 namespace is not public; therefore it is necessary to use the following reflection to invoke it:

[Editor comment: Line breaks used to avoid scrolling.]

BindingFlags bindingFlags2 = BindingFlags.InvokeMethod | 
               BindingFlags.NonPublic | BindingFlags.Instance;
message2.GetType().GetMethod("PostLoad", bindingFlags2).
                                       Invoke(message2, null);

Usage

The WSE messaging infrastructure is divided into three layers. I have described these layers in detail in my previous article. Please feel free to review the article here. The top layer - Level 2 is represented by the SoapClient and SoapService that are tightly coupled to the business layer using a CLR/Messaging pattern. This model enables encapsulation of the business layer from the physical layer of connectivity in a transparent manner. Based on the transport type, the workflow will work in the same way within the appDomain or between different machines. The only difference is in the message transport time and in the usage of resources such as memory (no deserialization/serialization processing). Using soap.null transport, we can minimize the transport time and unify the connectivity model (application framework) across the tiers and layers - see for more details about the connectivity model in my article WSE ServiceBus.

The soap.null transport enables encapsulating layers in a tier. The following examples show the usage of WSE messaging and the position of soap.null transport in the business workflow:

Example 1 - Caching data

This example shows the service used for caching a data in a data slot loaded from an external source. The client has fast access to the cached data via a soap.null transport. This solution is suitable for front-end tier:

Example 2 - Async workflow

This example shows encapsulating an asynchronous workflow into the sync and async activities. The sync activity (pre-processor) is hosted under the same appDomain as the SoapClient. The pre-processor is connected to its consumer via the soap.null transport. The message exchange pattern can be used in the Request-ReplyTo fashion, where the client will decide how the pre-processor will forward the message to the next service. The picture also shows a layering of the business workflow into three layers such as business client (root of the workflow), pre-processor and processor services. Note that another client based on the business needs can use the pre-processor service.

Example 3 - WS transfer

The WS-transfer spec is a good candidate for using the soap.null transport. Please feel free to review WS-transfer implementation in my article. It is written for WCF (Indigo) communication model, but the design pattern is common for WSE messaging, as well. As the following picture shows, the Resource Factory can be connected to the client via soap.null transport, which enables fast creation (factorizing) of a resource. Note that the factory (knowledge base of the resources) can be pre-loaded during appDomain start-up.

The model works in two steps. In the first step, the client is asking the Resource Factory for access to the resource representation (body). Subsequently, the client invokes an operation to obtain a resource representation. The factory is responsible for the operation endpoint included in its transport. In a special case all the three layers can be hosted under the same appDomain.

Testing

The SoapNULL transport can be tested with a simple console program such as a client and server included in the download package. There are tests for all messaging patterns including background processing.

The following screen snap shows the client/server message exchange patterns. Please ensure that your machine has the following resources, before you run the test:

  • .NET 2.0
  • WSE 3.0 installed
  • SoapNULL transport (MSI package)

Conclusion

The soap.null transport represents a special custom transport with a "null wire" between the service and its consumer. Its features enable efficiently using the same across boundary connectivity model and encapsulating a business workflow into logical layers within the same appDomain.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Roman Kiss
Software Developer (Senior)
United States United States
No Biography provided

Comments and Discussions

 
Questionsoap.null for WCF? PinmemberDeyan Petrov9-Aug-07 2:38 
AnswerRe: soap.null for WCF? PinmemberRoman Kiss9-Aug-07 5:37 
GeneralRe: soap.null for WCF? PinmemberDeyan Petrov9-Aug-07 6:09 
GeneralRe: soap.null for WCF? PinmemberRoman Kiss9-Aug-07 6:22 
Generalgreat, but one question PinmemberWin32nipuh22-May-07 21:38 
Generalbravo PinmemberAdrian Bacaianu1-Jan-06 5:56 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140814.1 | Last Updated 8 Dec 2005
Article Copyright 2005 by Roman Kiss
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid