Click here to Skip to main content
Click here to Skip to main content
Go to top

UnzipDisassembler - A custom pipeline component for BizTalk Server 2004

, 29 Jan 2006
Rate this:
Please Sign up or sign in to vote.
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:

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:

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:

[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:

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:

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:

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.

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:

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):

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.

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.

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:

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:

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.

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:

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:\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! Smile | :)

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

Share

About the Author

Mauricio Ritter
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

 
GeneralGreat tool! Thank You PinmemberJürgen Hof22-Nov-12 4:34 
QuestionNeed Help In custom Receive pipeline PinmemberPallaviIsTheBest10-Nov-12 5:58 
QuestionMultiple Files in Zip File Pinmemberkallepalli8-May-12 5:19 
GeneralMy vote of 5 PinmemberAyyappan.a@Tcs.com30-Apr-11 19:20 
good work.. thanks Smile | :)
GeneralWrong local header signature GetNextEntry() Pinmembermayito228-Oct-09 10:33 
GeneralInvalid offset/count combination Pinmemberramakrishnacodeproject28-Apr-08 4:43 
GeneralRe: Invalid offset/count combination PinmemberJohn Paul Jones9-Jun-08 3:56 
GeneralRe: Invalid offset/count combination PinmemberMember 211717129-Sep-08 23:43 
GeneralRe: Invalid offset/count combination PinmemberRoland170321-Jul-09 1:32 
GeneralRe: Invalid offset/count combination PinmemberHelderSoares14-Jun-12 8:39 
GeneralRouting Error Pinmemberramakrishnacodeproject26-Apr-08 2:17 
GeneralRe: Routing Error Pinmember2nice19-Nov-08 2:49 
GeneralQuestion about getNext() PinmemberZeus[BTY]14-Feb-08 23:06 
QuestionHow to use this custome pipeline in the orchestration? Pinmemberkumar kishore kumar22-Nov-07 18:09 
GeneralPLS UPDATE IT IF U DID ANY MISTAKES Pinmemberkumar kishore kumar20-Nov-07 3:01 
GeneralRe: PLS UPDATE IT IF U DID ANY MISTAKES PinmemberMauricio Ritter20-Nov-07 3:10 
GeneralRe: PLS UPDATE IT IF U DID ANY MISTAKES Pinmemberkumar kishore kumar20-Nov-07 16:38 
GeneralRe: PLS UPDATE IT IF U DID ANY MISTAKES Pinmemberkumar kishore kumar21-Nov-07 15:39 
QuestionRe: PLS UPDATE IT IF U DID ANY MISTAKES Pinmemberkumar kishore kumar22-Nov-07 2:15 
QuestionHow to validate the xml file inside the unzip folder PinmemberVidhya Rao15-Oct-07 4:09 
GeneralRouting error PinmemberDiditu3-Oct-07 20:19 
GeneralRe: Routing error Pinmemberssay8212-Mar-09 4:59 
GeneralRe: Routing error Pinmembervik32121-Jan-10 23:24 
GeneralZip Library Pinmemberashwini_se14-Feb-07 1:57 
GeneralRe: Zip Library PinmemberMauricio Ritter14-Feb-07 2:00 

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

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

| Advertise | Privacy | Mobile
Web03 | 2.8.140916.1 | Last Updated 29 Jan 2006
Article Copyright 2006 by Mauricio Ritter
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid