Custom Receive Pipeline for Unzip File in BizTalk

Hello everyone, today I will be sharing a guide on how to create custom pipeline code for BizTalk. This code will be used to unzip files and copy the same zip file to a local directory. After this, we will proceed to unzip the XML files and then route them in the BizTalk orchestration. One of the benefits of using BizTalk XLangs classes is that we can use context promote value in the expression shape of the orchestration. In the end, you can find the complete source code with an example available for download.

Here are the steps to process a unzip file in BizTalk orchestration using a custom pipeline component:

1. Open Microsoft Visual Studio as an administrator user and click on File > New > Project. In the Installed Templates, click on BizTalk Projects, then Empty BizTalk Server Project and name it “BizTalkLive.Unzip.Disassemble”. Click OK.

2. Now, right-click on your solution and add a new C# class library project named “BizTalkLive.UnZip”. Rename the class1 as “UnZip”.

3. Add the dll reference of Ionic to use the unzip method in the pipeline disassembler stage. Also, add the reference Microsoft.BizTalk.Pipeline to use the pipeline method. At the end of this post, you will be able to download Ionic.dll or it is an open-source library, which you can download from another website.

using Microsoft.BizTalk.Message.Interop;

using Microsoft.BizTalk.Component.Interop;

usig Ionic.Zip;

4. Add the following attribute and GUID. Remember, a GUID is a unique value across the network. You can create your own unique GUID or use an existing one. To create a new GUID, click on the Tools menu in VS and then click on Create GUID. See the following picture for reference.

namespace BizTalkLive.UnZip

{

    //Attributes of class

    [ComponentCategory(CategoryTypes.CATID_PipelineComponent)]

    [ComponentCategory(CategoryTypes.CATID_DisassemblingParser)]

 

    //Generate unique identifier

    [System.Runtime.InteropServices.Guid(“F7EA6794-5E97-4E20-9C4B-EA9C3FD10FA9”)]

5. In order to enhance the capabilities of the UnZip class, it is recommended to use the following interfaces: IBasecomponent, IComponentUI, IDisassemblerComponent, and IPersistPropertyBag. Additionally, two string methods should be created: PropertyNamespace and ShemaMessageType. These methods are used to add message types used in the XML file and PropertyNamespace to promote context message respectively. Both methods are utilized in the receive pipeline disassembler stage.

public class UnZip : IBaseComponentIComponentUIIDisassemblerComponentIPersistPropertyBag

    {

        private string _ShemaMessageType;

        private string _PropertyNamespace;

        public string ShemaMessageType

        {

            get

            {

                return _ShemaMessageType;

            }

            set

            {

                _ShemaMessageType = value;

            }

        }

6. Let’s move to the stage of IDisassemblerComponent since you already have a basic understanding of IBasecomponent, IComponentUI, IDisassemblerComponent, and IPersistPropertyBag.

public void Disassemble(IPipelineContext pc, IBaseMessage inmsg)

        {

            //Microsoft.BizTalk.CAT.BestPractices.Framework.Instrumentation.TraceManager.WorkflowComponent.TraceInfo(“TSAUnZipDecode : Unzip process has started at ” + DateTime.Now.ToLongDateString());

            Byte[] TextFileBytes = null;

            string FileStatus = “0”;

            string extension = “”;

            string strReceivePortName = “”;

            string strReceivedFileName = “”;

            string _filename = “”;

 

            IBaseMessagePart msgBodyPart1 = inmsg.BodyPart;

 

            Stream msgBodyPartStream1 = msgBodyPart1.GetOriginalDataStream();

 

            MemoryStream memStream1 = new MemoryStream();

            byte[] buffer1 = new Byte[1024];

 

            int bytesRead1 = 1024;

            while (bytesRead1 != 0)

            {

                bytesRead1 = msgBodyPartStream1.Read(buffer1, 0, buffer1.Length);

                memStream1.Write(buffer1, 0, bytesRead1);

            }

 

            IBaseMessage outMessage1;

            outMessage1 = pc.GetMessageFactory().CreateMessage();

            outMessage1.AddPart(“Body”, pc.GetMessageFactory().CreateMessagePart(), true);

            memStream1.Position = 0L;

            outMessage1.BodyPart.Data = memStream1;

            if (this.ShemaMessageType != null)

            {

                outMessage1.Context.Promote(“MessageType”“http://schemas.microsoft.com/BizTalk/2003/system-properties”this.ShemaMessageType);

 

            }

            outMessage1.Context.Promote(“FileExtension”, PropertyNamespace, “.zip”);

 

            IBaseMessageContext context = inmsg.Context;

            strReceivePortName = context.Read(“ReceivePortName”“http://schemas.microsoft.com/BizTalk/2003/system-properties”).ToString();

            strReceivedFileName = context.Read(“ReceivedFileName”“http://schemas.microsoft.com/BizTalk/2003/file-properties”).ToString();

            strReceivedFileName = strReceivedFileName.Substring(strReceivedFileName.LastIndexOf(“\\”) + 1);

            strReceivedFileName = ((string)strReceivedFileName).Replace(“.zip”“”);

            outMessage1.Context.Promote(“ReceivedFileName”“http://schemas.microsoft.com/BizTalk/2003/file-properties”, strReceivedFileName);

            outMessage1.Context.Promote(“ReceivePortName”“http://schemas.microsoft.com/BizTalk/2003/system-properties”, strReceivePortName);

 

            //outMessage1.Context = PipelineUtil.CloneMessageContext(inmsg.Context);

             OutFiles.Enqueue(outMessage1);

The code mentioned above uses the interfaces IBaseMessage and IBaseMessageContext to receive the port name and file name. It also adds the original zip message to the Enqueue method of System.Collections.Queue and promotes message context FileExtension and MessageType. The aim is to store the original zip file in a local folder using message routing.

7. In the Disassemble method, the code unzips the zip file and enqueues the xml file in System.Collections.Queue. It also promotes the message type of the xml and some property promotion value, which can be used in the expression shape of the orchestration.

IBaseMessagePart msgBodyPart_XML = inmsg.BodyPart;

 

            IBaseMessage outMessage;

 

            if (msgBodyPart_XML != null)

            {

                Stream msgBodyPartStream = msgBodyPart_XML.GetOriginalDataStream();

                msgBodyPartStream.Position = 0L;

 

                if (msgBodyPartStream != null)

                {

                    using (ZipInputStream zipInputStream = new ZipInputStream(msgBodyPartStream))

                    {

                        ZipEntry entry = zipInputStream.GetNextEntry();

                        while (entry != null)

                        {

                            Stream memStream = new MemoryStream();

                            byte[] buffer = new Byte[4096];

 

                            int bytesRead = 4096;

                            while (bytesRead != 0)

                            {

                                bytesRead = zipInputStream.Read(buffer, 0, buffer.Length);

                                memStream.Write(buffer, 0, bytesRead);

                            }

 

                            //Creating custom context property to hold extension of file

                            extension = entry.FileName.Substring(entry.FileName.IndexOf(“.”)).ToUpper();

                            _filename = entry.FileName.ToString();

 

                            if (extension == “.XML”)

                            {

                                FileStatus = “1”;

                                outMessage = pc.GetMessageFactory().CreateMessage();

                                outMessage.AddPart(“Body”, pc.GetMessageFactory().CreateMessagePart(), true);

                                memStream.Position = 0L;

                                outMessage.BodyPart.Data = memStream;

 

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

                                if (this.ShemaMessageType != null)

                                {

                                    outMessage.Context.Promote(“MessageType”“http://schemas.microsoft.com/BizTalk/2003/system-properties”this.ShemaMessageType);

 

                                }

                                _filename = ((string)_filename).Replace(“.xml”“”);

                                outMessage.Context.Promote(“ReceivedFileName”“http://schemas.microsoft.com/BizTalk/2003/file-properties”, _filename);

                                outMessage.Context.Promote(“FileStatus”, PropertyNamespace, FileStatus.ToString());

                                outMessage.Context.Promote(“FileExtension”, PropertyNamespace, extension.ToLower());

 

                                OutFiles.Enqueue(outMessage);

 

                            }

                            else

                            {

                                FileStatus = “0”;

                                //outMessage.Context.Promote(“FileStatus”, PropertyNamespace, FileStatus.ToString());

                                //outMessage.Context.Promote(“FileExtension”, PropertyNamespace, extension.ToLower());

                            }

 

                            entry = zipInputStream.GetNextEntry();

                        }

 

                    }

 

                }

 

            }

        }

 

        public IBaseMessage GetNext(IPipelineContext pContext)

        {

            if (OutFiles.Count > 0)

            {

                return (IBaseMessage)OutFiles.Dequeue();

            }

            else

            {

                return null;

            }

        }

 In the above code, there is an IBaseMessage method called GetNext() which dequeues all messages one by one in the pipeline. This allows us to easily route the message either in an orchestration or direct to a send port.

8. Right-click on the same project, BizTalkLive.UnZip, and then click on “Property”. After clicking on the “Sign” tag, add a new strong name file. Now, right-click on the same project and build it.

9. Go to the folder location and navigate to bin\Debug. Install the Ionic and UnZip DLL in GAC. You can reference how to install a DLL in GAC by following the instructions in this link: Install DLL into GAC.

10. To move to the next step, right-click on the solution of the same project and add a new class library of C# called “BizTalkLive.ContextPropertyAccessor“. This will call the property promoted value in BizTalk orchestration expression shape. Please see the following code, then build this project, and finally install the DLL in GAC.

using Microsoft.XLANGs.BaseTypes;

using Microsoft.XLANGs.Core;

using System;

using System.Collections;

 

namespace BizTalkLive.ContextPropertyAccessor

{

    public class ContextAccessor

    {

        public static Hashtable GetContext(XLANGMessage message)

        {

            try

            {

                foreach (Segment segment in Service.RootService._segments)

                {

                    IDictionary fields =

                        Context.FindFields(

                          typeof(XLANGMessage)

                        , segment.ExceptionContext);

 

                    foreach (DictionaryEntry field in fields)

                    {

                        XMessage msg = (field.Value as XMessage);

                        if (msg == null)

                            continue;

 

                        if (String.Compare(msg.Name, message.Name) != 0)

                            continue;

 

 

                        return msg.GetContextProperties();

                    }

                }

            }

            catch (Exception ex)

            {

                // do not provoke failure

                throw ex;

            }

 

            return new Hashtable();

        }

 

        public static string GetContextProperty(XLANGMessage message, string PropertyName, string PropertyNamespace)

        {

            string Property = “False”;

            try

            {

 

                Microsoft.XLANGs.BaseTypes.XmlQName qname = new XmlQName(PropertyName, PropertyNamespace);

                foreach (Segment segment in Service.RootService._segments)

                {

                    IDictionary fields =

                        Context.FindFields(

                          typeof(XLANGMessage)

                        , segment.ExceptionContext);

 

                    foreach (DictionaryEntry field in fields)

                    {

                        XMessage msg = (field.Value as XMessage);

                        if (msg == null)

                            continue;

 

                        if (String.Compare(msg.Name, message.Name) != 0)

                            continue;

 

                        if (msg.GetContextProperties().ContainsKey(qname))

                        {

                            // get the property from GetContextProperties

                            Property = msg.GetContextProperties()[qname].ToString();

                        }

                        else if (msg.GetContentProperties().ContainsKey(qname))

                        {

                            // get the property from GetContentProperties

                            Property = msg.GetContentProperties()[qname].ToString();

                        }

 

                        return Property;

 

                    }

                }

            }

            catch (Exception ex)

            {

                throw ex;

                // do not provoke failure

                // probably best to add some logging here

            }

 

            return Property;

        }

    }

}

11. Reference both BizTalkLive.UnZip and BizTalkLive.ContextPropertyAccessor dlls in the BizTalk project “BizTalkLive.Unzip.Disassemble”.

12. To add a new receive pipeline named “Rcv_UnZipDisassemble“, follow these steps: 1. Click on the toolbox of the receive pipeline. 2. Right-click and select “Choose Items”. 3. Select the “BizTalk Pipeline Component” tag and click the “Browse” button on the bottom right. 4. Choose “BizTalkLive.UnZip.dll” from the already installed GAC assembly. 5. A new toolbox named “unzip_Disassembler” will be added to the pipeline toolbox. Select it and place it on the disassemble stage. 6. Add an XML disassembler after placing the toolbox.

13. To proceed further, add two schemas – one property schema and one schema for the XML file that is unzipped from the zip file. You can refer to the following picture for a better understanding, or download the complete source from the link provided at the end of this post, which will help you to create both schemas easily.

14. Firstly, please refer to the picture below which shows the orchestration flow design. The process starts with receiving a message at the receive location, which is done using a custom unzip receive pipeline. After this, an expression shape is used to obtain the file status value by promoting the schema property in the XLANGs context message. Finally, the logical file port is configured to send the XML file to the local directory.
15. To complete the last part of the BizTalk project, you need to unzip the custom pipeline disassembler. Right-click on the solution, set the same name of the BizTalk project as its solution name, and then deploy it. After that, create a receive port, followed by a receive location. Then, select the receive pipeline and set two disassemble components, one for the property schema’s target namespace and the other for the message type of the XML schema used in the zipped XML files. Please refer to the following image for a visual guide.

16. To achieve the desired outcome, you need to create two send ports. The first send port should route the same ZIP file to a local directory using the filter condition that was promoted in the UnZip disassemble method i.e. file extension ‘a d’ receive port. The second send port should be used to copy all XML files to a local folder. Please refer to the picture below for better understanding.

To download the Ionic DLL, please click on this link – Download Ionic DLL.

To download the complete project source code, please click on this link – Download Source Code.

Post a comment

Leave a Comment

Scroll to Top