Click here to Skip to main content
15,867,568 members
Articles / Programming Languages / C#
Article

UnzipDisassembler - A custom pipeline component for BizTalk Server 2004

Rate me:
Please Sign up or sign in to vote.
4.87/5 (18 votes)
29 Jan 20069 min read 236.5K   886   44   64
This article shows how to create a custom pipeline component in BizTalk Server 2004. The component receives a zipped message, uncompresses it, and returns the .zip file contents to the BizTalk Server.

Introduction

In this article, we'll see how to create and use a custom pipeline component in BizTalk Server 2004. Our component will receive a ZIP file, uncompress it, and send its contents to BizTalk Server. Although the article seems complicated, you'll see that create a custom pipeline component is quite a simple task. Our project will use a free Zip library, the ICSharpZipLib. This library can be found at the IC#Code web site.

Creating a Custom Pipeline

A Custom Pipeline component is nothing more than a simple .NET DLL that implements a class with some predefined interfaces. This interface represents the layer between a .NET Program and then BizTalk Server. We'll start creating a new C# Class Library project called UnzipDisassembler.

Now that we created the project, we need to add our references. The first reference we'll need is that to the ZIP Library. After downloading the latest version of it, add a reference to our project.

After adding the reference to the Zip Library, we'll need a reference to a BizTalk Server library. This library can be found at the BizTalk Server install folder (\Program Files\Biztalk Server 2004, to be more precise). The name of this library is Microsoft.Biztalk.Pipeline.dll, it contains the basic interfaces we'll need to process the Zip file message.

Coding the class

Now, we'll start coding our custom pipeline component. We'll start creating the using clauses necessary for the implementation of the pipeline. The list of the using statements is:

C#
using System;
using System.IO;
using Microsoft.BizTalk.Component.Interop;
using Microsoft.BizTalk.Message.Interop;
using System.ComponentModel;
using System.Resources;
using ICSharpCode.SharpZipLib.Zip;

Now, we'll update the name of the class in the project. Change the default Class1 name to UnzipDisassemblerComponent (you can change the name of the physical file as well). The class should have a similar structure as shown below:

C#
namespace UnzipDisassembler
{
    /// <SUMMARY>
    /// Summary description for Class1.
    /// </SUMMARY>
    public class UnzipDisassemblerComponent
    {
        public UnzipDisassemblerComponent()
        {
            //
            // TODO: Add constructor logic here
            //
        }
    }
}

Implementing the Interfaces

To create a custom pipeline component, we need to implement some default interfaces. If we want to create a generic pipeline component, we need to implement IBaseComponent, IComponent, IComponentUI, and IPersistPropBag interfaces. Since we're working with the disassembler component, we'll not use the IComponent interface, we'll replace it for IDisassemblerComponent. Besides using these interfaces, we need to add some attributes to our class. These attributes are the "Component Category Attributes", that indicate that our class is a custom pipeline component, and the System.Runtime.InteropServices.Guid, that we'll use to generate a unique identifier for our custom pipeline component. You can obtain a new GUID to your component using Visual Studio: in the Tools menu, use the Create GUID option. The implementation of the class should be similar to the code shown below:

C#
[ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
[ComponentCategory(CategoryTypes.CATID_DisassemblingParser)]
[System.Runtime.InteropServices.Guid("
           6118B8F0-8684-4ba2-87B4-8336D70BD4F7")]
public class UnzipDisassemblerComponent : IBaseComponent,
    IDisassemblerComponent,
    IComponentUI,
    IPersistPropertyBag
{
    public UnzipDisassemblerComponent()
    {
        //
        // TODO: Add constructor logic here
        //
    }
}

Now that we've added the interfaces to the class, we need to implement them. We'll start with the IBaseComponent interface.

IBaseComponent

The fist interface we'll implement is the IBaseComponent. This interface has three properties that we must implement. To implement these properties, we'll use the Class Viewer. If the class viewer is not visible, go to the View menu and select Class Viewer. In the Class Viewer, expand our newly created class (UnzipDisassemblerComponent), expand Bases and Interfaces, and you'll see the four interfaces we have implemented. Expand the IBaseComponent, and you'll see the Class Viewer as shown below:

Image 1

Right click the Description property and select the Add>Override option. A new function will be created in your class. Repeat the process for the other two properties.

Each one of this properties returns some information about your custom pipeline component. The Name property returns the name of the component. The Description property returns a brief description of what you component does. The Version property returns the current version of the component. The code below has the implementation of these three properties:

C#
public string Description
{
    get    {
        return "Componente de descompactação para Biztalk";
    }
}

public string Name
{
    get{
        return "UnzipDisassemblerComponent";
    }
}

public string Version
{
    get{
        return "1.0.0.0";
    }
}

IComponentUI

This interface defines the behavior of the component in the BizTalk pipeline designed. We'll need to implement a method and a property. The Validate method is used to validate the properties that are set in the custom pipeline component, and it's called when a pipeline that uses the pipeline component is compiled. Since we're not implementing any properties here, we'll use the default behavior of this method. The Icon property represents the icon associated with the component (shown in the toolbox). In this case, we'll use the default icon, so let's leave the code as is. The complete implementation of this interface is shown below:

C#
public System.Collections.IEnumerator Validate(object projectSystem)
{
    return null;
}

public System.IntPtr Icon
{
    get
    {
        return new System.IntPtr ();
    }
}

IPersistPropertyBag

The IPersistPropertyBag interface is used to store the properties that are set in the pipeline component in the pipeline designed. We use the InitNew, Load, and Save methods. In our component, we're not using properties, so we don't need any implementation here. We just have to implement this interfaces because they are mandatory.

C#
public void GetClassID(out Guid classID)
{
    classID = classID = new Guid("6118B8F0" + 
              "-8684-4ba2-87B4-8336D70BD4F7");
}

public void InitNew()
{

}

public void Load(IPropertyBag propertyBag, int errorLog)
{
}

public void Save(IPropertyBag propertyBag, 
            bool clearDirty, bool saveAllProperties)
{
}

Note that the GetClassID property should return the same GUID used in the class attributes of the component.

IDisassemblerComponent

This interface is the most important of our component. The Disassemble method is responsible for the process of mounting the messages to BizTalk. With this component, we can receive a message as an input and generate many messages as output. As an example, we can have a single .ZIP file with multiple XML files in it. We need to consider each XML file as a single message. To do this, our disassemble component must create a list of output messages. The GetNextMessage is called just after the disassemble method. This method is responsible for returning the messages to BizTalk, and it executes as long as the method returns a message. When the method returns null, it considers that there are no more messages to be retrieved from the original assembled message. Implement the two methods of this interface using the class wizard. The code should be like the one shown below:

C#
public void Disassemble(IPipelineContext pContext, IBaseMessage pInMsg)
{

}

public IBaseMessage GetNext(IPipelineContext pContext)
{
    return null;
}

Implementing the Unzip Logic

The unzip logic will be implemented inside the Disassemble method. This method receives as an input parameter a context and a message. The context parameter represents the execution context of BizTalk Server, and the message parameter represents the zipped message itself. The message is represented by an interface called IBaseMessage. This interface has information about the message (context) as well as the body of the message, that can be obtained from the Body property. The body of the message (as well as any other "part" of the message) is represented by the IBaseMessagePart interface. This interface has some methods that allows us to read the message as an input stream. The code below obtains the body of the received message as a stream (this code should be implemented in the Disassemble method):

C#
Stream strmZipFile;

IBaseMessagePart msgPart;
msgPart = pInMsg.BodyPart;
strmZipFile = msgPart.GetOriginalDataStream();

ZipInputStream oZipStream = new ZipInputStream(strmZipFile);

Note that we're creating a ZipInputStream from the original stream. This kind of a stream is available in the Zip library, and we'll use it to uncompress the message. Since our file can have more than one uncompressed files in it, we'll store the uncompressed files generated in a Queue object. To do this, we'll implement a new instance of the Queue class in our class.

C#
System.Collections.Queue qOutputMsgs = new System.Collections.Queue();

Now, let's go back to the Disassemble method. The ZipInputStream class has a method called GetNextEntry that returns an entry to an uncompressed file inside the Zip file. Each entry represents a single file in the original Zip file. Using the code below, we'll extract this entry from the Zip file and create a new output message to BizTalk, adding it to our Queue.

C#
ZipEntry sEntry = oZipStream.GetNextEntry();
while(sEntry != null)
{
    MemoryStream strmMem = new MemoryStream();
    byte[] aBytes = new byte[2047];
    
    int inOffSet = 0;
    int outOffSet = 0;
    int iRead = oZipStream.Read(aBytes, 0, 2048);
    while(iRead != 0)
    {
        strmMem.Write(aBytes, outOffSet, iRead);
        outOffSet += iRead;
        iRead = oZipStream.Read(aBytes, inOffSet, 2048);
        inOffSet += iRead;
    }

    strmMem.Seek(0, SeekOrigin.Begin);
    msgPart.Data = strmMem;

    IBaseMessage outMsg;

    outMsg = pContext.GetMessageFactory().CreateMessage();
    outMsg.AddPart("Body", 
      pContext.GetMessageFactory().CreateMessagePart(), true);
    outMsg.BodyPart.Data = strmMem;

    qOutputMsgs.Enqueue(outMsg);
    sEntry = oZipStream.GetNextEntry();
}

Note that to create an output message to BizTalk, we're using the context object that we received as a parameter. This context accesses BizTalk's MessageFactory, that allows us to create messages to BizTalk.

Returning the messages to BizTalk

Now, we need to implement the GetNextMessage method. This method is called for each message we'll return to BizTalk. When there are no more messages, the method will return null. Since our output messages are available in our Queue, we'll use the Dequeue method to return them. When the Queue is empty, we'll return null to finish the pipeline component process. The code below implements this functionality:

C#
if(qOutputMsgs.Count>0)
    return (IBaseMessage)qOutputMsgs.Dequeue();
else
    return null;

Compiling the project

Before we compile our project, we should change some of the properties of the project. The first project we need to change is the output path of the assembly. In order to work with BizTalk, custom pipeline components should be placed in a specific BizTalk folder (the \Program Files\Microsoft BizTalk Server 2004\Pipeline Components). The properties of the project should be as shown in the picture below:

Image 2

After updating the project properties, build it.

Creating a test project

Now that out pipeline component is ready, we need to test it. To do this, we'll create a new BizTalk project that will use our custom pipeline component. Add a new BizTalk Server project in our solution, and name it PipelineTest.

In this project, add a new "Receive Pipeline" item and name in UnzipTest.btp. Open the UnzipTest.btp and check that the toolbox doesn't show our custom pipeline component. To add the component to the toolbox, select Add/Remove Items (right click on the toolbox). A new window will show up (like in the picture below). In this window, select UnzipDisassemblerComponent.

Image 3

Now that we have our component in the toolbox, drag it to the disassemble stage of the pipeline. Below this component, drag a XmlDisassembler as well. Your pipeline should have the components as shown below:

Image 4

Debugging the pipeline component

Now that our component is ready, we need to debug it. To debug a pipeline component, we'll need to use a tool that comes with BizTalk Server. This tool is called Pipeline.EXE, at it simulates the execution of a pipeline component for an input message.

Open the project properties of the pipeline component project, and select the Debugging tab. In Debug mode, select "Program", and click the "Apply" button. In the property "Start Application", use the path to the pipeline.exe tool. The most common path is shown below:

C:\Program Files\Microsoft BizTalk Server 
          2004\SDK\Utilities\PipelineTools\Pipeline.exe

In the command line, use the arguments below:

C#
C:\Labs\Work\BSCPipeline\UnzipTest.btp -d 
          C:\Order.zip -c -m C:\%messageid%.xml

The first parameter represents the path to the pipeline file that we created in the PipelineTest project (you need to provide the complete path for this to work). The -d parameter represents the input document we'll use. In our example, the input document is a .zip file. The last parameters -c and -m, represent the output options (-c represents that the output should be shown in the console, and -m shows the output path of the generated messages).

After setting these parameters, set some breakpoints in the project and run it, you should see the complete steps of the execution of the pipeline.

Conclusion

As you can see, the creation of a custom pipeline component for BizTalk Server 2004 is a simple task, it just requires some extra coding time. Hope you like the article! :)

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Brazil Brazil
Mauricio Ritter lives in Brazil, in the city of Porto Alegre. He is working with software development for about 8 years, and most of his work was done at a bank, within a home and office banking system.
Mauricio also holds MCSD, MCSE, MCDBA, MCAD and MCT Microsoft certifications and work as a trainer/consultant in some MS CTEC in his city.
Mauricio also works in his own programming site, aimed to Brazilian Developers: http://www.dotnetmaniacs.com.br

In his spare time he studys korean language...

Comments and Discussions

 
GeneralRouting Error Pin
ramakrishnacodeproject26-Apr-08 2:17
ramakrishnacodeproject26-Apr-08 2:17 
GeneralRe: Routing Error Pin
2nice19-Nov-08 2:49
2nice19-Nov-08 2:49 
GeneralQuestion about getNext() Pin
Zeus[BTY]14-Feb-08 23:06
Zeus[BTY]14-Feb-08 23:06 
QuestionHow to use this custome pipeline in the orchestration? Pin
kumar kishore kumar22-Nov-07 18:09
kumar kishore kumar22-Nov-07 18:09 
GeneralPLS UPDATE IT IF U DID ANY MISTAKES Pin
kumar kishore kumar20-Nov-07 3:01
kumar kishore kumar20-Nov-07 3:01 
GeneralRe: PLS UPDATE IT IF U DID ANY MISTAKES Pin
Mauricio Ritter20-Nov-07 3:10
Mauricio Ritter20-Nov-07 3:10 
GeneralRe: PLS UPDATE IT IF U DID ANY MISTAKES Pin
kumar kishore kumar20-Nov-07 16:38
kumar kishore kumar20-Nov-07 16:38 
GeneralRe: PLS UPDATE IT IF U DID ANY MISTAKES Pin
kumar kishore kumar21-Nov-07 15:39
kumar kishore kumar21-Nov-07 15:39 
QuestionRe: PLS UPDATE IT IF U DID ANY MISTAKES Pin
kumar kishore kumar22-Nov-07 2:15
kumar kishore kumar22-Nov-07 2:15 
QuestionHow to validate the xml file inside the unzip folder Pin
Vidhya Rao15-Oct-07 4:09
Vidhya Rao15-Oct-07 4:09 
GeneralRouting error Pin
Diditu3-Oct-07 20:19
Diditu3-Oct-07 20:19 
GeneralRe: Routing error Pin
ssay8212-Mar-09 4:59
ssay8212-Mar-09 4:59 
GeneralRe: Routing error Pin
vik32121-Jan-10 23:24
vik32121-Jan-10 23:24 
GeneralZip Library Pin
ashwini_se14-Feb-07 1:57
ashwini_se14-Feb-07 1:57 
GeneralRe: Zip Library Pin
Mauricio Ritter14-Feb-07 2:00
Mauricio Ritter14-Feb-07 2:00 
GeneralRe: Zip Library Pin
ashwini_se14-Feb-07 21:09
ashwini_se14-Feb-07 21:09 
GeneralRe: Zip Library Pin
kumar kishore kumar25-Nov-07 18:21
kumar kishore kumar25-Nov-07 18:21 
QuestionAlmost there... Pin
RichWallace3-Feb-07 19:05
RichWallace3-Feb-07 19:05 
AnswerRe: Almost there... Pin
Mauricio Ritter4-Feb-07 12:26
Mauricio Ritter4-Feb-07 12:26 
GeneralRe: Almost there... Pin
RichWallace5-Feb-07 4:40
RichWallace5-Feb-07 4:40 
GeneralRe: Almost there... Pin
Mauricio Ritter5-Feb-07 5:46
Mauricio Ritter5-Feb-07 5:46 
GeneralRe: Almost there... Pin
RichWallace5-Feb-07 9:30
RichWallace5-Feb-07 9:30 
GeneralRe: Almost there... Pin
kumar kishore kumar25-Nov-07 18:15
kumar kishore kumar25-Nov-07 18:15 
GeneralYou didn't dispose ZipInputStream Pin
Tareq Muhammad28-Dec-06 4:40
Tareq Muhammad28-Dec-06 4:40 
GeneralCongratulation Pin
BruNOFX3-Dec-06 15:32
BruNOFX3-Dec-06 15:32 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.