This post continues the series on creating custom pipeline components. The previous posts in the series can be found here: post 1, post 2.
In order to complete the exercise of creating the archive component, we need to add the code to actually perform the archival of the message. For this example, the requirements of the archive component is to persist an exact copy of the message coming into BizTalk (whether flat file, xml, binary or even an encrypted) in it’s original form to the local filesystem.
To start off, we need to create a method to write an inputstream to the filesystem outputstream. The following CopyStream method does this…
protected void CopyStream(Stream input, Stream output)
{
int BUFFER_SIZE = 4096;
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead;
try
{
bytesRead = input.Read(buffer, 0, BUFFER_SIZE);
while (bytesRead > 0)
{
output.Write(buffer, 0, bytesRead);
bytesRead = input.Read(buffer, 0, BUFFER_SIZE);
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
// rewind input stream
if (input.CanSeek)
input.Position = 0;
}
}
The next method needed is one that takes a BizTalk IBaseMessage and uses the CopyStream method from above to write the message to the filesystem.
protected void WriteToFile(IBaseMessage message, string fileName)
{
Stream msgStream = message.BodyPart.GetOriginalDataStream();
FileStream fileStream = null;
try
{
fileStream = new FileStream(fileName, FileMode.OpenOrCreate);
this.CopyStream(msgStream, fileStream);
}
catch (Exception ex)
{
throw ex;
}
finally
{
if (fileStream != null)
fileStream.Close();
if (msgStream.CanSeek)
msgStream.Position = 0;
}
}
The final step is to implement the Execute method that utilizes the WriteToFile method we just created. We will use the InterchangeID as the filename, and since it is basically a GUID it guarantees the filename will be unique. This is a string automatically set by the Messaging Engine for each message that arrives on the server and it defines the unique ID that is used to group the documents that resulted from the same interchange message.
We also want to the archive file to include the original filename, otherwise it might be difficult for a user to determine which message came from which file when a directory is filled with just GUIDs. So what we will do is look at the original filename property and prepend it to the InterchangeID with a underscore between them. You will notice that depending on the adapter (FILE, FTP) the file comes in on, we use a different schema. In addition, if the file comes in from any other adapter, the filename property won’t be available, so we skip the prepend step.
/// <summary>
/// Implements IComponent.Execute method.
/// </summary>
/// <param name=”pc”>Pipeline context</param>
/// <param name=”inmsg”>Input message</param>
/// <returns>Original input message</returns>
/// <remarks>
/// IComponent.Execute method is used to initiate
/// the processing of the message in this pipeline component.
/// </remarks>
public Microsoft.BizTalk.Message.Interop.IBaseMessage Execute
(Microsoft.BizTalk.Component.Interop.IPipelineContext pContext,
Microsoft.BizTalk.Message.Interop.IBaseMessage pInMsg)
{
IBaseMessage passedMessage = pInMsg;
string archiveFileName = null;
// get the interchange id from the message
string interchangeID = (string)pInMsg.Context.Read(“InterchangeID”,
“http://schemas.microsoft.com/BizTalk/2003/system-properties”);
// if the transport type if file or ftp, get the incoming filename to use
// as part of the archive filename (for easier identification)
string filePath = null;
string adapterType = (string)pInMsg.Context.Read(“InboundTransportType”,
“http://schemas.microsoft.com/BizTalk/2003/system-properties”);
if (adapterType == “FILE”)
{
filePath = (string)pInMsg.Context.Read(“ReceivedFileName”,
“http://schemas.microsoft.com/BizTalk/2003/file-properties”);
}
else if (adapterType == “FTP”)
{
filePath = (string)pInMsg.Context.Read(“ReceivedFileName”,
“http://schemas.microsoft.com/BizTalk/2003/ftp-properties”);
}
archiveFileName = interchangeID + “.out”;
if (filePath != null)
{
archiveFileName = Path.GetFileName(filePath) + “_” + archiveFileName;
}
// write the archive file
WriteToFile(pInMsg, Path.Combine(this._ArchivePath, archiveFileName));
// this way, it’s a passthrough pipeline component
return
pInMsg;
}
#endregion
When used, the archive component will create files with filenames similar to the following examples:
If Transport Type = FTP or FILE:
MyTestFile.txt.pgp_{2E50A0DD-395B-4629-A345-7CB0FDDE4727}.out
Any other Transport Type:
{B9D33010-BE49-490B-ABFA-E3B63CAFCDAE}.out
The next article in this series goes over the steps to using the custom Archive pipeline component in your BizTalk application.
3 Comments
Great article! One quibble. In the Public Execute method you declare IBaseMessage passedMessage = pInMsg but you don’t actually do anything with it?
I would recommend:
IBaseMessagePart bodyPart = pInMsg.BodyPart;
if (bodyPart != null) {
//Insert your code here
}
Good job!
Hi! I was surfing and found your blog post… nice! I love your blog.
Cheers! Sandra. R.
Hi, where we are capture the xml document finally, earlier you have used path some thing like , but you never usee that path while send file to that path can you please send that code which is working
early reposnse appreciate