BizTalk Utilities CV ,   Jobs ,   Code library  
 
Home Page
WCF, WS, SOAP
SOAP Headers
Deserializing an Object Graph
Serializing an Object Graph
The SoapFormatter
Runtime Object Serialization
Importance of a UDDI based Service Registry
BizTalk and WCF: Part V, Publishing Operations Patterns
BizTalk and WCF: Part IV, Attachment Patterns
BizTalk and WCF: Part III, Transaction Patterns
BizTalk and WCF: Part II, Security Patterns
BizTalk and WCF: Part I, Operation Patterns
Accessing UDDI using Apache Scout APIs
Storing state in an XML property bag
UDDIExplorer
XslTransport for PocketSOAP
Kafka - XSLT SOAP Toolkit
VB Web services Proxy Generator
Using msxml3 with Web Storage System
Getting started with SOAP
Calling an Web service from a Managed C++ project
<< Uncategorized
XALAN >>

By :Mark Wilson
I am the creator of TopXML. I am available for international and local (Australia) contracts. I am a Solution Architect/Business Analyst. I have worked in IT in several countries (NZ, Australia, South Africa, UK) building and training teams for government and very large non-governmental organizations. I am ex-Microsoft Consulting Services. I wrote the first book on Microsoft XML published in 2000 called XML Programming with VB and ASP. Most recently I have been building tools for the SEO industry. Ask me for a 37 point SEO health-checkup for your website.
First posted :03/24/2008
Times viewed :320

 

Retrieving Message Headers

Retrieving the SOAP headers from a message is by far more complex than adding them. This is mostly because the mechanism to retrieve headers is designed for executing method calls through the .NET remoting infrastructure rather than for transmitting serialized objects. Nevertheless we will outline how to retrieve headers from messages like the ones we have seen so far in this chapter. In chapter 14 we learn about creating SOAP messages in RPC format instead of the document format and we will see a different technique to access headers in a message.

We saw that one version of the Deserialize() method allows to register a delegate to handle the header section of the SOAP message. Deserialize() will call the registered method after it parsed the <SOAP-ENV:Header> section of the message, but before it deserializes the content of the <SOAP-ENV:Body>. Deserialize() will pass the handler method an array of Header objects, very much like the array we originally passed to Serialize() to produce the headers. We can iterate over the Header array and process each header entry one-by-one. By doing this we will find that there is an extra element called __methodName in the array. It contains the name of the first child element of the <SOAP-ENV:Body>. Remember how I said, that reading headers is very much designed around the parsing RPCs? Here is the first piece of evidence to back that statement. A message in RPC format contains the name of the method to execute as the first body element. In our scenario, however, this pseudo header does not help us at all.

So much for the theory, let’s get back to the code example. We need a class to process header entries and extract the transaction identifier. We also have to check for other header entries because the SOAP specs mandate that an application has to abort processing and discard a message if it can not act on a required entry, i.e. an entry with the mustUnderstand attribute set to “1”. Both of these requirements are implemented in the HeaderProcessor class. This class exposes a single public method, HandleHeaders, to receive and process the message headers. We are going to register HandleHeaders via a delegate with Deserialize() to receives the headers. HandleHeaders processes each header entry and throws an exception if it finds a required entry it does not understand. When it finds the TransactionId header it stores ID’s value in a public property for later access.

Listing Classes to process header entries from a SOAP message.

public class HeaderException : ApplicationException

{

  public HeaderException( string msg ) : base(msg) {}

}

[Serializable]

public class HeaderProcessor : ISerializable

{

  public object HandleHeaders( Header[] headers ) |#1

  {

    foreach( Header h in headers )   |#2

    {                                |

      ProcessHeader(h);              |

    }                                |

    return this;                             |#3

  }

  public int TransactionId;

  private void ProcessHeader( Header header )

  {

    if( header.Name.CompareTo("TransactionId") == 0 )

    {

      TransactionId = (int)header.Value;

    }

    else if( header.MustUnderstand == true )              |#4

    {                                                     |

      throw new HeaderException( String.Format(           |

        "Don’t understand {0}", header.Name) );           |

    }                                                     |

  }

  public HeaderProcessor ()  {}

  // Required for all classes implementing ISerializable

  public HeaderProcessor ( SerializationInfo info,

    StreamingContext context )  { /* empty */  }

  // Method defined by ISerializable

  public void GetObjectData( SerializationInfo info,

    StreamingContext context ) {  /* empty */  }

}

(annotation) <#1 Register this method with the SoapFormatter to process the headers of the message.>

(annotation) <#2 Process each header.>

(annotation) <#3 The header handler has to return an object that implements ISerializable.>

(annotation) <#4 Abort processing if the message contains any required headers that we do not understand .>

That was straight forward so far, now here comes the rough part, rough mostly because we are operating a little bit outside the area of intended use for HeaderHandlers. Deserialize() will not recreate the object in the message body when a HeaderHandler is registered. Instead it will return the object returned from the HeaderHandler.  Any attempt to recreate the serialized object in our HandleHeaders() method will fail, however, because we cannot access the type information. The only information available about the type of the root object in the message is the plain class name in the pseudo-header __methodName. Since we do not get any information about the class’ namespace or its assembly, we are unable to create an object of the correct type. That is another piece of evidence that HeaderHandlers are only designed for deserializing RPC messages. SOAP RPC servers do not need any type information to invoke a method call, because they only expose an interface at a given URL.

Implementing a HeaderHandler

The SoapFormatter requires the HeaderHandler to return an object that implements the ISerializable interface. After the HeaderHandler exits the SoapFormatter will call the deserialization constructor to initialize the object with before Deserialize() returns it to the caller.

BUG WARNING: We have to return a valid object from the header handler method. The SoapFormatter will throw an exception if we return null (this behavior is specific to the SoapFormatter, its sibling, the BinaryFormatter behaves different.)

The SerializationInfo passed into the deserialization constructor contains entries describing most of the SOAP message, but it still does not provide enough information to reliably reconstruct the root of the object graph. The entries in the SerializationInfo object are listed.

Table The SerializationInfo passed to an object after headers were processed with a HeaderHandler contains more data than just the serialized object graph.

SerializationInfoEntry

Type

Description

__methodName

string

The name of the first element in the <Body>. In an RPC messages it is the name of the invoked message. In a document message it is the class name of the root of the serialized object graph.

__keyToNamespaceTable

Hashtable

A Dictionary of all attributes found in the message and their values.

__paramNameList

ArrayList

The field names of the root object or parameter names of an RPC call. Retrieve the values by these names from the SerializationInfo.

<field name>

object

The values of the fields listed in the __paramNameList.

__fault

At this point we should concede that a HeaderHandler was not intended to reconstruct the root object of a serialized object graph. To work around this limitation we can call Deserialize() twice, once to retrieve and process the message headers and once to retrieve the serialized object, but this solution carries a pretty severe performance penalty.

We can finish this section with a modified version of the ReceiveObject() method first shown in listing 13.5. It shows how we can call Deserialize() once with the HeaderProcessor class from listing 13.8 to process the message headers and a second time to retrieve the object stored inside the message. Note that we have to reposition to the stream to the beginning of the SOAP message after the first call to Deserialize() because streams are always positioned after the last read position.

Listing A modified Receive() method to retrieve message headers before processing the object

private void ProcessStream( Stream stream )

{

  HeaderProcessor processor = new HeaderProcessor();

  long streamPosition = stream.Position;

  try

  {

    processor = (HeaderProcessor)_Formatter.Deserialize(     |#1

      stream, new HeaderHandler(                             |

        processor.HandleHeaders ) );                         |

                          |

    stream.Position = streamPosition;  |#2

   

    IQueuedJob job = _Formatter.Deserialize( stream ) as IQueuedJob;

    if( null != job )

    {

      job.Run();

    }

  }

  catch(HeaderException ex)                           |#3

  {                                                   |

      Console.WriteLine( "Incompatible Headers:" );   |

      Console.WriteLine( ex.Message );                |

  }                                                   |

}

 (annotation) <#1 Call Deserialize once to retrieve the headers from the message. >

 (annotation) <#2 Read the message a second time to retrieve the object in the body, because we cannot reliably deserialize it with the information in the context object.>

(annotation) <#3 Do not process the message if we did not understand mandatory headers in the message.>


Rate this article on a scale of 1 to 10

Your vote :  


 

Recent Jobs

Software Developers Needed in Charl
Sr. Software Engineer - Analytics
Immediate Mainframe openings for Ch
Immediate TANDEM-TAL openings for C
Immediate ASP.NET/C# Openings for C

View all Jobs (Add yours)
View all CV (Add yours)



help desk support
swimming pool contractor
conference call
water softener
Teleconference
Host Department NOLIMIT Web Hosting
MSN
sunglasses


    Email TopXML  

Front Page Daily Stuff TopXML Forum XML blogs XML Newsgroups BizTalk Biztalk Utilities Biztalk Utilities Tutorial B2B SAP XML Microsoft .NET Dotnet System XML Soapformatter SQLXML XMLserializer XQuery PHP PHP SimpleXML PHP XML Dom PHP XML RPC PHP XSLT Java Java Java XML Xalan Microsoft ASP ASP Schemas XML SQL Server XML XMLDom XSL XSL Tutorial XSLT Stylesheets General Javascript CSS XHTML WAP