"BizTalk Server 2004: Receieve Pipeline woes v2" by Charles Young, I guess description for "Seekable streams and MAPS" is incorrect.

Posted at: 2/19/2007 at 7:32 PM by saravana

Charles Young has written this article "Receive Pipeline Woes V2"  few months back. It's one of the very good articles out there, which explains some of the insights of BizTalk Server internals. But under the heading "Seekable Streams" it appears that the description is not correct. Recently, I came across a strange problem with custom pipeline component and Biztalk MAPs.

My custom pipeline component does very simple task something like reading the incoming message, logging it in the database and putting the message back into the stream using MemoryStream. I can't use the DATA property of IBaseMessage, because I'm using HTTP adapter which gives a Network stream that's not Seekable. Accessing "Position" property or changing the "Seek" position will throw an exception ("method not implemented"). So, I need to populate a local MemoryStream by buffering the input stream in order to do any manipulation on the complete message I received via HTTP adapter inside my pipeline component.

Charles Young in his article (under Seekable Streams heading) says "The first of the two problems is that BizTalk 2004 fails to execute inbound maps over seekable streams.   It is as simple, and as brutal, as that.   If, after a pipeline has completed its work, it returns a message whose body part contains a seekable stream, and if BizTalk matches an inbound map to the MessageType property of the message, the map will always fail with a “The root element is missing” error.   If the stream is non-seekable, and positioned at the beginning of the stream, the map will succeed (unless, of course, there is some other problem)."

I've just put together a sample here, which shows the problem is due to the way you handle stream and the way you flush the stream inside your pipeline component and its nothing to do with whether the stream is seekable or not.

The sample application (Download it here) contains the following artifacts

1.LogMessageWithStreamFlush (Pipeline Component)

2. LogMessageWithoutStreamFlush (Pipeline Component)

3. Receive.LogMessageWithStreamFlush (Biztalk Receive Pipeline)

4. Receive.LogMessageWithoutStreamFlush (Biztalk Receive Pipeline)

5. Tester.XSD, Developer.XSD and Tester2Developer Biztalk Map.

In the sample application Biztalk receive pipelines are configured with appropriate pipeline component (i.e .Receive.LogMessageWithStreamFlush BizTalk pipeline component will have LogMessageWithStreamFlush pipeline component in the Decode stage). The custom receive pipelines receives the incoming message, reads it completely into a local buffer, and then puts the message back into the stream via MemoryStream. A map will be applied on the receive port to transform the message.

Download the Sample. 

To setup the project

1. Copy the zip file to C:\BTSSamples and open the solution SeekableStreamsAndMaps.sln

2. Compile and Deploy the project from Visual Studio.

3. Copy the pipeline component dll "DDL.Samples.SeekableStreamAndMaps.PipelineComponents.dll" to "Pipeline Components" folder under Biztalk 2004/2006 installation directory (normally under c:\program files\microsoft biztalk server 2004(2006).

4. Open Biztalk Adminstrator console. Right-click on the application "SeekableStreamsAndMaps" and import the binding file "binding.xml" from the sample download.

5. Restart biztalk host instance.

Configuration:

Two receive ports (FILE Adapter) are created one with "Receive.LogMessageWithStreamFlush" pipeline and another with "Receive.LogMessageWithoutStreamFlush" pipeline component. Both the Receive ports has got the map "Tester2Developer" which transforms the incoming "Tester" message into "Developer" message.

A send port (FILE Adapter) is created with a "Filter" expression subscribed to messages received via any of the Receive ports we configured in the previous steps.

Execution:

1. Copy and paste the sample message "Tester.xml" (from FileDrop\MSG_SAMPLES) into the folder MSG_IN_WITH_STREAM_FLUSH, you'll see the expected transformed output "Developer" in the output folder "MSG_OUT"

2. Copy and paste the same sample message into the folder MSG_IN_WITHOUT_STREAM_FLUSH, then you'll see the exception as shown below in the event viewer

The Messaging Engine failed while executing the inbound map for the message coming from source URL:"C:\BTSSamples\SeekableStreamsAndMaps\FileDrop\ MSG_IN_WITHOUT_STREAM_FLUSH\*.xml" with the Message Type "http://www.digitaldeposit.net/samples#Tester". Details:"Root element is missing."

The exception is same as what's Charles Young mentioned in his article.

So, What's the difference between two pipeline components:

Below is the code from Execute method of the pipeline component LogMessageWithStreamFlush, which works fine without any issue.

string message = GetMessage(inmsg);

//Log the message to DB or Filesystem or anywhere you like
System.Diagnostics.Debug.WriteLine(message);

//Promote MessageType
string btsNamespace =  "
http://schemas.microsoft.com/BizTalk/2003/system-properties"
inmsg.Context.Promote( "MessageType",  btsNamespace, "http://www.digitaldeposit.net/samples#Tester");  //Hardcoded for the demo

//Write the message back into inmsg stream
MemoryStream memStream = new MemoryStream();
StreamWriter sw = new StreamWriter(memStream);
sw.Write(message);
sw.Flush();
memStream.Flush();
memStream.Position = 0;

inmsg.BodyPart.Data = memStream;
inmsg.BodyPart.Data.Position = 0;

return inmsg;

In the second pipeline component(the one which throws the exception LogMessageWithoutStreamFlush ), I've just commented the two statements which Flushes the stream sw.Flush() and memStream.Flush(). You can see from the above code, I've used MemoryStream to put the message back into the IBaseMessage body part. MemoryStream is fully seekable, you can put the position of the Stream anywhere within the boundary since I got memory as the backend data store. So, it proves the reason why MAP is failiing with "Root element is missing" is purely because we didn't flush the stream, which resulted in empty content of the stream when the MAP starts to pull it.

NOTE: This method is quite expensive and should not be used for high volume systems. As Charles Young explained in his article you should wrap the original stream in a new stream object that processes the data on the fly during read operation, making it stateless. But in my case we were expecting one message every 1 hour once, so its not worth spending so much time on writing a proper stream handing pipeline component.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags: |  Categories: BizTalk General
Actions: Email this article Email | Kick it! | DZone it! | Save to del.icio.us | Technorati Links
Post Information: Permanent LinkPermalink | CommentsComments(4) | Comments RSS

Comments

Friday, April 27, 2007 12:25 PM
Yossi Dahan
Great post Saravana.
I always wondered about this statement and never took the time to check it out.

Thanks.
Tuesday, May 29, 2007 5:22 PM
Charles Young
Hi Saravana

Just seen your post. Sorry I missed it at the beginning of the year.

The issue about maps not working over seekable streams was a genuine issue in the initial release of BizTalk 2004, and was confirmed by Lee Graber (Microsoft BizTalk team member, now working on the SQL team). It was fixed ages ago in SP1 for Biztalk 2004. That's the trouble with blogs. They get out of date Smile.

I don't think I ever checked BizTalk 2006 with regard to the second problem (a logical bug in the Xml Disassembler). As I recall, it wsn't fixed in BTS2004 SP1. I suspect that it remains unfixed.

Your example does nicely make the point that you should always, always, always flush your buffers! Failure to do so is a very common cause of bugs. I advocated this as one of the 'Ten Commandments' at geekswithblogs.net/cyoung/articles/13490.aspx. In your sample, failure to do so means that all or part of the content is not written out to the stream, so the XML is mangled or missing when the stream is pulled at the map.

Thanks

Charles
Tuesday, May 29, 2007 5:58 PM
Saravana Kumar
Hi Charles,
Thanks for responding to this post. I agree with you completely, it's hard to keep all the old blog posts upto date. I tried to leave a comment in your blog, but there is some strange issue with your geekswithblog url. The problem is still there, it crashes the whole browser instance.

Saravana
Wednesday, May 30, 2007 6:49 PM
Charles Young
Thanks. I've recently started seeing intermittent crashes as well. This only seems to happen with IE7 on Vista (Firefox works OK, and so does IE7 on my XP machine at home). It's only started since GeeksWithBlogs upgraded from .Text to SubText. It only appears to affect 'articles' rather than 'posts'. I've spent some time tracking this down and found that it seems to have something to do with blogmaps. Don't know why it only affects atricles. Anyway, I've removed blogmaps (my map was incorrect, anyhow!) and everything appears to be working OK.

Also, to avoid further confusion, I've put a comment in red on the original Pipeline Woes article to say that the problem was fixed in BTS2004 SP1.

Thanks for the feedback.

Add comment


(Will show your Gravatar icon)  

  Country flag

biuquote
  • Comment
  • Preview
Loading