BizTalk Messaging Archive Custom Solution

BizTalk Messaging Archive Custom Solution

Lately one of the questions asked on BizTalk Server Forums intrigued me. The question was how one could archive a message including its message context.

Archiving received message
 
As soon as a message reaches BizTalk it can go through one of the default pipelines (XMLReceive,PassThruReceive) and a message context is added to incoming message.
                                                 Message Context
The XMLReceive pipeline has a XmlDisassembler pipeline component on the disassembling stage.

Pipeline
Whenever an Xml message is received via the XmlReceive pipeline the XmlDisassembler will do the following tasks:

  • Promote the “MessageType” context property by taking the combination of TargetNamespace and Root Element in the format of: Targetnamespace#RootElement. So one of context properties BizTalk will set (promote) is the MessageType.
Name: MessageType – Namespace: http://schemas.microsoft.com/BizTalk/2003/system-properties – http://BizTalk.Archiving.BankTransaction#Transaction
  • Remove Envelopes and disassemble the interchanges
  • Promote the content properties from interchange and individual document into message context based on the configured distinguished fields and promoted properties.

I will discuss here a custom disassembler pipeline component that will promote the “MessageType” context property and archive the message context and body to file. It will not perform the other two default actions by XmlDisassembler pipeline component.
Pipeline component is targeted for disassembler stage of the receive pipeline. The component category is CATID_DisassemblingParser will be set above the class amongst other attributes. Since the component is targeted at the disassembling stage the IDisassemblerComponent needs to be implemented. This interface has two methods, Disassemble and GetNext. In the Disassemble method you will find the implementation for archiving the received message.

        /// 
/// Implements IDisassemblerComponent.Disassemble method.
///

///

Pipeline context
///

Input message.
/// Message
///
/// IComponent.Execute method is used to initiate
/// the processing of the message in pipeline component.
///
public void Disassemble(IPipelineContext pContext, IBaseMessage pInMsg)
{
//Trace
System.Diagnostics.Debug.WriteLine("1. Pipeline Disassemble Stage");

//Create XmlDocument object
XmlDocument xmlDoc = new XmlDocument();

//Create a copy of the message
IBaseMessage archiveMessage = pInMsg;

//Trace
System.Diagnostics.Debug.WriteLine("2. Call GetMessagePayLoad()");

//Get Message PayLoad
xmlDoc = GetMessagePayLoad(archiveMessage);

//Trace
System.Diagnostics.Debug.WriteLine("3. Message PayLoad :" + xmlDoc.OuterXml);

// Promote MessageType in order to the Biztalk to have a unique key for evaluating the subscription
archiveMessage.Context.Promote("MessageType", "http://schemas.microsoft.com/BizTalk/2003/system-properties", xmlDoc.DocumentElement.NamespaceURI + "#" + xmlDoc.DocumentElement.LocalName.ToString());

//Debug
System.Diagnostics.Trace.WriteLine("4. Call ReadContextProperties");

//Get the context properties and assign them to contextProperties archiveMessage
string contextProperties = ReadContextProperties(archiveMessage);

//Debug
System.Diagnostics.Trace.WriteLine("5. Context Properties: " + contextProperties);

//Get the message content (BodyPart)
string messageBody = xmlDoc.OuterXml;

//Debug
System.Diagnostics.Trace.WriteLine("6. Message Body: " + messageBody);

//Debug
System.Diagnostics.Trace.WriteLine("7. Write to output file");

//Write output
using (StreamWriter outfile = new StreamWriter(_ArchiveLocation + System.Guid.NewGuid().ToString() + "_Message" + ".txt"))
{

//Debug
System.Diagnostics.Trace.WriteLine("8. File Location :" + _ArchiveLocation);

outfile.Write(contextProperties + " " + Environment.NewLine + messageBody);

//Debug
System.Diagnostics.Trace.WriteLine("9. Write to output file");
}

//Debug
System.Diagnostics.Trace.WriteLine("10. Pipeline Disassemble Stage Exit");


//Return orginal message
IBaseMessage outMessage;
outMessage = pContext.GetMessageFactory().CreateMessage();
outMessage.AddPart("Body", pContext.GetMessageFactory().CreateMessagePart(), true);
IBaseMessagePart bodyPart = pInMsg.BodyPart;
Stream originalStream = bodyPart.GetOriginalDataStream();
originalStream.Position = 0;
outMessage.BodyPart.Data = originalStream;

outMessage.Context = PipelineUtil.CloneMessageContext(pInMsg.Context);

_qOutMessages.Enqueue(outMessage);

//Return orginal message to queue
_qOutMessages.Enqueue(outMessage);
}

///
/// Default method
///

///

Context
/// null
public IBaseMessage GetNext(IPipelineContext pContext)
{
if (_qOutMessages.Count > 0)
{
IBaseMessage msg = (IBaseMessage)_qOutMessages.Dequeue();
return msg;
}
else
return null;

}

///
/// Read the context properties of the message
///

///

IBaseMessage archiveMessage
/// string containing all context properties
private string ReadContextProperties(IBaseMessage archiveMessage)
{
string name;
string nmspace;
string contextItems = "";

for (int x = 0; x < archiveMessage.Context.CountProperties; x++)
{
archiveMessage.Context.ReadAt(x, out name, out nmspace);
string value = archiveMessage.Context.Read(name, nmspace).ToString();
contextItems += "Name: " + name + " - " + "Namespace: " + nmspace + " - " + value + "\r\n";
}

return contextItems;
}

///
/// Method extract message into XMLDocument
///

///

IBaseMessage
/// XML Document
private XmlDocument GetMessagePayLoad(IBaseMessage archiveMessage)
{
IBaseMessagePart bodyPart = archiveMessage.BodyPart;
Stream originalStream = bodyPart.GetOriginalDataStream();

XmlDocument XMlDoc = new XmlDocument();
XMlDoc.Load(originalStream);

return XMlDoc;
}

The complete code can be found through MSDN Code Gallery here.

Buy versus Build

This is just a custom solution that took me a couple hours to develop and test. To add more functionality would mean more development work. A custom solution can bring a tremendous amount of flexibility and power. However it will also cost a fair amount of time to develop and some more time to maintain it (i.e. changes). An alternative can be buying one of the off-shelve products for archiving BizTalk messages like BizTalk Message Archiving Pipeline Component. This product has a great deal of features, offers support and has a license model.

BizTalk Tracking for Archiving

BizTalk offers tracking capabilities, which can be used to archive messages for a short period of time. Basically BizTalk tracking is created in such a way that you can use tracking for troubleshooting purposes not really for archiving. You can use tracking for archiving purposes, but then you need to be aware of fact that you have to configure the tracking and purging BizTalk database job correctly and move your tracking data! Why the job is so important is clearly explained in MSDN Archiving and purging the BizTalk Tracking Database:

As BizTalk Server processes more and more data on your system, the BizTalk Tracking (BizTalkDTADb) database continues to grow in size. Unchecked growth decreases system performance and may generate errors in the Tracking Data Decode Service (TDDS). In addition to general tracking data, tracked messages can also accumulate in the MessageBox database, causing poor disk performance.

By placing the backups somewhere else you can later on extract tracked messages from it. Tracked messages are compressed in BizTalk tracking database and you can extract these programmatically. Thiago Almeida has written post on how to do that. So again you need a custom solution to view the message body and its context.

You can use BizTalk tracking for archiving purposes, yet you need to offload the data from time to time to another another database. Retention on BizTalk databases is limited, because of the growth of data that eventually will impact BizTalk Server performance. In my personal opinion (view) I would not use BizTalk tracking for archiving purposes.

Archiving send message

In this post you have seen the implementation of the receive side of archiving a message. At send side a similar process can be performed to archive the message that is send by BizTalk to another system, application or service. Normally when a message is send by BizTalk, one of the default pipelines (XMLSend, PassThruSend) is used. With the XmlSend pipeline the reverse of what in a XmlReceive pipeline happens. The XMLSend has a XmlAssembler component in the Assemble stage. Whenever an Xml message is send via the XMLSend pipeline the XmlAssembler will do the following tasks:

  • XML Assembler Builds envelopes as needed and appends XML messages within the envelope.
  • Populates content properties on the message instance and envelopes. 

The custom assembler component will neither of these, it will only archive the message to file. Pipeline component is targeted for assembler stage of the send pipeline. The component category is CATID_AssemblingParser will be set above the class amongst other attributes.

[ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
[ComponentCategory(CategoryTypes.CATID_AssemblingSerializer)]
[System.Runtime.InteropServices.Guid(“728609D1-8282-4D73-B4D3-4792D6580537”)]

Since the component is targeted assembling stage the IAssemblerComponent needs to be implemented. This interface has two methods, Assemble and AddDocument. In the AddDocument method you will find the implementation for archiving the send message.

        /// 


        /// Implements IAssemblerComponent.Assemble method.
        ///

        ///

Pipeline context
        /// Message
        ///
        /// IComponent.Assemble method is used to
        ///
        public IBaseMessage Assemble(IPipelineContext pipelineContext)
        {
            if (_qOutMessages.Count > 0)
            {
                IBaseMessage msg = (IBaseMessage)_qOutMessages.Dequeue();
                return msg;
            }
            else
                return null;
        }

        ///
        /// IAssembler.AddDocument Method
        ///

        ///

Pipeline context
        ///

Message send out
        public void AddDocument(IPipelineContext pContext, IBaseMessage pInMsg)
        {
            //Trace
            System.Diagnostics.Debug.WriteLine("1.  Pipeline Assemble Stage");

            //Create XmlDocument object
            XmlDocument xmlDoc = new XmlDocument();

            //Create a copy of the message
            IBaseMessage archiveMessage = pInMsg;

            //Trace
            System.Diagnostics.Debug.WriteLine("2.  Call GetMessagePayLoad()");

            //Get Message PayLoad
            xmlDoc = GetMessagePayLoad(archiveMessage);

            //Trace
            System.Diagnostics.Debug.WriteLine("3.  Message PayLoad :" + xmlDoc.OuterXml);

            //Debug
            System.Diagnostics.Trace.WriteLine("4. Call ReadContextProperties");

            //Get the context properties and assign them to contextProperties  archiveMessage
            string contextProperties = ReadContextProperties(archiveMessage);

            //Debug
            System.Diagnostics.Trace.WriteLine("5. Context Properties: " + contextProperties);

            //Get the message content (BodyPart)
            string messageBody = xmlDoc.OuterXml;

            //Debug
            System.Diagnostics.Trace.WriteLine("6. Message Body: " + messageBody);

            //Debug
            System.Diagnostics.Trace.WriteLine("7. Write to output file");

            //Write output
            using (StreamWriter outfile = new StreamWriter(_ArchiveLocation + System.Guid.NewGuid().ToString() + "_Message" + ".txt"))
            {

                //Debug
                System.Diagnostics.Trace.WriteLine("8. File Location :" + _ArchiveLocation);

                outfile.Write(contextProperties + " " + Environment.NewLine + messageBody);

                //Debug
                System.Diagnostics.Trace.WriteLine("9. Write to output file");
            }

            //Debug
            System.Diagnostics.Trace.WriteLine("10. Pipeline Disassemble Stage Exit");

            //Return orginal message
            IBaseMessage outMessage;
            outMessage = pContext.GetMessageFactory().CreateMessage();
            outMessage.AddPart ("Body", pContext.GetMessageFactory().CreateMessagePart(), true);
            IBaseMessagePart bodyPart = pInMsg.BodyPart;
            Stream originalStream = bodyPart.GetOriginalDataStream();
            originalStream.Position = 0;
            outMessage.BodyPart.Data = originalStream;

            outMessage.Context = PipelineUtil.CloneMessageContext(pInMsg.Context);
           
            _qOutMessages.Enqueue(outMessage);
        }

The complete code can be found through MSDN Code Gallery here.

Conclusion

This is a very basic and straight forward custom solution to archive messages going in and out of BizTalk Server. It can be leveraged to create a more sophisticated archiving solution. There are however some considerations that have to be taken into account.

  • When you expect a high volume of messages flowing in and out of BizTalk a lot of disk I/O will be the result when using these pipelines for archiving messages. So basically a high disk contention can occur when writing archived messages to same disk as where the BizTalk instance resides. A good approach would be having the archived messages stored on a separate dedicated disk.
  • This solution shows writing archived messages to file, yet you can also choose to store them in a database or send the archived messages to a queue where they are picked up to be stored elsewhere. In the end you have to decide where you want your archived messages stored, for how long (retention) and possibly how to retrieve them if you want to look up a certain archived message later on. The latter will be rather difficult when having archived messages on file.
  • Another thing you have to consider is data inside the messages. Is it data everyone can see or is sensitive data? Storing the files either on file or in database that easily accessible by others may not be a good option.

Having a solid, robust archiving solution in place for your BizTalk messages is an easy walk in the park. You’ll need a good design up front that is fit for purpose. I hope that with this post you have some food for thought.

My new course is up on Pluralsight!

If you’ve been wondering where I am been – I’ve been putting down my thoughts about HTML5 development with native mobile apps – and its now live at Pluralsight! http://www.pluralsight-training.net/microsoft/Courses/TableOfContents?courseName=native-mobile-apps-with-html5 If you want to try it out you can start … Continue reading
Blog Post by: jon

BizTalk with ReST

BizTalk with ReST

There is a great article posted by Microsoft which explains how BizTalk can be configured to call a ReST service. You can find it here. Althought this article is based on BizTalk 2010 (.net 4.0), I have tested this with BizTalk 2009 (.net 3.5) and it works. Check it out!
Blog Post by: DipeshA

TechDays 2012: JavaScript aussi sur le serveur et jusque dans le cloud?

The session about server side JavaScript is available online with its slides and its video.
Here are the links (NB: the content is in French)
La session “JavaScript aussi sur le serveur et jusque dans le cloud?” est disponible en ligne

http://aka.ms/wd3afq

JavaScript becomes more and more important and Microsoft makes its execution very efficient. This session is about the use cases of this language on the server side on the Microsoft Platform. This will be shown with Node.js but also Hadoop Map/Reduce on Windows Server and Windows Azure.

JavaScript prend de plus en plus d’importance et Microsoft rend son ex%u00e9cution tr%u00e8s efficace. Nous verrons dans cette session les cas d’utilisation de ce langage sur la plateforme Microsoft au niveau du serveur que ce soit par exemple avec Node.js ou Hadoop Map/Reduce sur Windows Server et Windows Azure.

Benjamin

Blog Post by: Benjamin GUINEBERTIERE

Failover clustering doesn’t mean 100% uptime

When we discuss about high availability scenario, we typically think about windows failover clustering. In a BizTalk world we commonly use failover clustering in few places. The first and foremost is the SQL server clustering, and clustering other resources like host instances, enterprise single sign on etc.

 
One of the common misconceptions people got with failover clustering is, they presume 100% uptime is guaranteed and the failover is seamless. But the reality is, having a fail over cluster simply reduces the time it takes to bring the service up and running. Still there will be intermittent period without that dependent service. One big advantage of fail over clustering is, that intermittent period could be just few seconds instead of few minutes or hours to manually bring the resource online.
 
Let’s dive bit more deep into the issue with a SQL server clustering scenario.
 
When a SQL Server instance is clustered, the sessions do not stay connected to SQL server during failover. Although failing over is quicker than a reboot, the instance must shut down and start back again dropping all the connections. All the normal recovery processes that SQL server goes through, rolling back uncommitted transactions and writing committed transactions to disk, also happens during failover. Any scheduled jobs that are running when failover occurs do not start back up when SQL server restarts. The time that SQL server takes to fail over is similar to restarting it on the same server without a reboot.
 
The good thing about clustering is that in case of a hardware failure, downtime is only minutes or seconds. The application can be up and running quickly after a failover. But any uncommitted transactions will be lost if it’s not handled by the application properly.
 
BizTalk Server is designed keeping this in mind and majority of the time you will be able to recover from this failure either automatically or by instances getting suspended and some one manually resuming it. But if you are dealing with the database directly, then you need to keep this restriction in mind.
 
Keeping the restriction in mind, you should not failover your cluster manually during the peak business hours. Fail over clustering is there either for controlled fail over during outage hour or during disaster like hardware failure.

Testing your RFC hosted in BizTalk from SAP

Testing your RFC hosted in BizTalk from SAP

In a recent blog post I discussed how you would host an RFC in BizTalk that SAP could call.  I thought it would be helpful if I also included a post that would discuss how you can test your BizTalk Hosted RFC.  Like most BizTalk Developers, I wanted to be able to perform some tests on my own without bothering our SAP team.  So by following these steps I was able to perform some tests on my own.

NOTE: Performing the following steps will require developer permissions in SAP. I suspect that with this kind of access I could get myself in all kinds of trouble.  Luckily for me, our SAP Security team didn’t ask me too many questions when I requested access.  Smile

 

Testing

  • Execute the SE37 “ABAB Function Modules” transaction.
  • Type in the name of your Function Module.  In my case it is ZISU_RFC_ADD and then click the Test/Execute button highlighted in Red or press F8

image

  • We now have the opportunity to provide our inputs.  If you recall from my previous post, this RFC will accept two integer inputs and then provided the sum in the response message.  Once we have our two inputs populated we can click on the Execute button (highlighted in red) or click the F8 button.

image

 

  • Our result will now be displayed on screen and we will discover how long it took to get the result.

image 

Exceptions

So what happens if BizTalk is not available when you perform one of these tests?  I was curious as well so I simply disabled my receive location, executed my test and received the following result.

image

Its not pretty but it describes that the “program BizTalk_Z_Add” is not registered.  This is saying that our listener that is using this Program ID is not online. This is a similar error message that we receive during a failed Connected Client test from SM59.

Conclusion

SAP can be a very large and daunting system at times but the more and more I get into it the more comfortable  I feel about its ability to integrate with other systems.  Tools like this one (SE37) and the IDOC resubmit transaction(WE19) can be a BizTalk dev’s best friend when integrating with SAP.

Rabbit Server: Error: Host name in Upper-case

This is a small thing, but very annoying.

I didn’t find it in Internet, so decided to blog it here.

My environment: Windows Server 2008 R2 x64; Rabbit 2.7.1.

I installed the RabbitMQ Server and tried to interact with it using the rabbitmqctl.bat script. This script requires to use the host name:

>rabbitmqctl -n rabbit@Win2K8R2 status

I was getting the error again and again:

“Error: unable to connect to node rabbit@Win2K8R2: nodedown”

I used the exact string provided me by script (see above in red) –

– nodes and their ports on Win2K8R2:

and the error text was –

Status of node rabbit@Win2K8R2

When I changed the host name to upper-case, error was gone.

>rabbitmqctl -n rabbit@WIN2K8R2 status

Conclusion:

Use only upper-case letters in the host name in rabbitmqctl.bat script!

BizTalk Community series: Introducing Ruth Resende

BizTalk Community series: Introducing Ruth Resende

During the MVP Summit I had a chat with our sole female on the BizTalk MVP program, Ruth Resende. I have met her before at the MVP Summit 2011 and at this summit I got the chance to catch up with again. I interviewed her for my BizTalk Community Series that brings active BizTalk community members to the foreground. Last couple of years Ruth has done an amazing job in the Portuguese speaking BizTalk community. Below you will find a picture of us together during the MVP Summit 2012.
iPhone 240
Ruth Resende is a 30 years old women from Brazil. She lives in Cubatao (a city near Santos) and works in Sao Paulo at Itgroup Consulting since 2008. For 8 years and counting she has been in the IT arena. Ruth started as a Delphi developer before one of her friends dragged her into the Microsoft world. Once in this world Ruth soon started working with BizTalk and became very active in the Brazilian community.

How did Ruth become so active in the Brazilian BizTalk community? Well one day she was configuring the HTTP adapter a couple of times and found the English tutorial hard to understand. Her English as she states is not very good. This brought her on the idea to translate the tutorial in Portuguese. By then she also found out that there was almost no BizTalk related information available in Portuguese. So she started writing blog posts in Portuguese on different BizTalk topics. Her manager noticed her activities and asked her to work with a team that writes BizTalk related material for BizTalk Brasil (a community blog). All activities combined (including MSDN activities) resulted in her being awarded MVP by Microsoft (see her profile here). Note she was the first women ever in Brazil awarded MVP.

When Ruth works on BizTalk projects she is more on the development and administration side. During development there are some features of BizTalk she really likes. Amongst these are the Schema Flat File Wizard, and Well-Formed XML Wizard, which both in her view improve productivity. In general Ruth finds BizTalk a great Microsoft product. However I quote:

I really like work with Biztalk, but we are loosing market in Brazil. Over the years, the clients are disappearing. I hope next year Microsoft could reborn some strategy to Biztalk.”
 
Ruth likes to reads books, watch movies, animations and TV Series in her spare time. She not the sporty type, but she does like to play video games and as she states I quote: “Video games count as a sport :)” As you can see on the picture she is huge fan of the Brazilian soccer team (Canarinhos).
Ruth likes to share her experiences with BizTalk on the BizTalk Brasil blog. She states and I quote:

What I write on the blog are my on experiences, and my intention is reduce effort of someone who experienced the same problem.I hope that I can help the community, make the BizTalk better known in the world.”
 
I like to thank Ruth for her time and contributions to the community. Ruth your English is fine!