Wednesday, November 30, 2005

Biztalk 2004: Per-Instance pipeling - Tool

Just found this interesting article.


For each pipeline (Receive Locations and Send Ports each have a Receive and Send Pipeline respectively, but if a port uses a Request-Response message exchange pattern it will also have a corresponding pipeline for the other direction, so the Receive Port will have a Send Pipeline and the Send Port will have a Receive Pipeline) the configuration database holds another piece of configuration. In the ExplorerOM this can be found as the ReceivePipelineData (on the ReceiveLocation or SendPort object) or SendPipelineData (on the SendPort or ReceivePort object) property.

This "data" is generally empty in most cases, but can be an XML document that overrides some or all of the properties of the components configured in the pipeline (note that the XML cannot add any addtional pipeline components)

Friday, November 25, 2005

Introduction ... better late than never ...

I've been running this blog for a few months now, without proper introduction. So now, I thougth, the time is ripe.
My real name is Kenneth. I've been dragging the kennywest alias around since my first hotmail acount (which was also my first real e-mail address), which is why I used this alias for my blog as well.
I'm a consultant implementing architectures/solutions for B2B and A2A scenario's. For the moment I am using Biztalk to do this. I've also used plain Java, Seebeyond's e*Gate and Crossworlds.
Although I am working with Biztalk and .NET . I am not a, how should I put it, Microsoft supporter. I'm more into Open Source and Linux or UNIX.
I first heard about linux 8 or 9 years ago. At that time we were doing labs on a DOS box telnetting to a Linux server to learn about a database called PostgreSQL. A friend of mine had a 6 CD box containing RedHat, SuSE and Slackware distros. He told me he'd installed it and loved it very much. I decided to take it for a spin. My first impression was rather average. I wasn't impressed about the stability. X crashed from time to time, maybe my hardware was a little flaky, who knows.
About a year later I decided to buy RedHat 5.2 . Yes, at that time you'd still have to buy distros on CD. Internet was just becoming popular and I didn't have any money to spare on a dial-up or broadband connection. So I installed RedHat 5.2 . RedHat was pretty cool. I could do whatever I wanted. I was writing lab reports in StarOffice (which was included) and running Apache web server for browsing my own web site on my own box. Pretty stupid, I know, but at that time it seemed very nice. RedHat used fvwm2 as default window manager, but I didn't like it. It looked too much like Windows 95. I preferred Afterstep. Afterstep was highly configurable and I was messing in a lot of configuration files to make it look just how I wanted it to look.
A few months later I decided to buy SuSE 6.0 . Now that was a total different distro. It was German ;) No, seriously, it was much easier to install and maintain than its RedHat predecessor. SuSE also packaged with 6 CDs containing a load of software. Since I didn't have any connection to the Internet, this was an ideal solution. SuSE used YAST to configure almost everything. YAST also had an option to “save” the list of packages installed on the system. If you used this feature you could reinstall a system quite fast using this saved list. I used SuSE 6.0 a lot. I also acquired an old 486DX4 computer to use it as a server. My main computer was running Windows 95 (or was it NT) and I used the Linux server for backups. You know how thrust worthy Windows was (still is) ;)
A few years later (01/09/2000) I started working and got myself a broadband connection to the Internet. I was still using SuSE 6.0 which was becoming very outdated at that time. Upgrading to new versions of software, on the server, was hard to maintain. I had to download it from the net and compile it. I was also using a very outdated kernel (2.0.x I think) running an ipfwadm firewall to serve my internal network.
Somewhere in 2001 I got fed up with SuSE. I was reading a lot about different distros and a lot of people where talking about Debian. Debian had a superior package manager called APT which made it easier to maintain. Some people claimed, however, it was not for the faint of heart to install. In the years I'd been using SuSE and RedHat I learned a lot and I thought I was not “faint of heart”.
Installing Debian (potato at that time) was a revelation. It wasn't hard to install at all. I was very impressed with the APT tool. The stuff I read about the ease of updating and upgrading was indeed true. I kept the same machine (yes, the 486DX4) running for almost 4 years, updating/dist-upgrading every now and then without ever reinstalling it.
It was love at first sight.
6 months ago I ditched the 486DX4 (he was becoming a bit flaky) and replaced it with a PII 266. I installed Debian on it, keeping the install base as small as possible. My main desktop, however, was still running Windows NT. The reason for this is I was merely using it to burn CDs and surfing the web. I also thought Debian was not suited as a desktop OS (but well suited as a server OS). I had bad experiences burning CDs on Linux, so I wasn't prepared replacing NT.
Then I bought a new PC. The desktop was becoming very old, so I decided to invest in something new. It came preloaded with XP home. I hate XP home. It isn't even capable authenticating against a PDC I was running (Samba + LDAP). I'd had to buy the professional edition to do that.
XP lasted about a week. Then it asked me to install updates/patches ... and that was the last time I saw it.
I still read a lot about different distros. I was looking for something like Debian, but more desktop oriented. Sure, I know about compiling a kernel or other packages. But a desktop has to work out of the box, without a lot of customizing. So I read about Xandros and Libranet. I even used Libranet, but didn't like it. It seemed buggy. Xandros was superior, but it wasn't freely downloadable.
And then I read about Ubuntu.
Ubuntu is the thing I've been waiting for, for a long time. It installed like a breeze on my desktop and recognized almost every piece of hardware in my PC (a HP Pavilion 5080.be). It has everything a nice GUI distro like Mandrake or SuSE should have, but with the APT power of Debian. I'm in love ;)
On forum people tend to argue from time to time about “the best distro” or “the best desktop”. Who cares. No, really. If you like SuSE, fine use it. If you like Ubuntu, then use Ubuntu. I like Ubuntu because it has a lot of Debian in it. And Debian is, from my point of view, far superior than any other distro out there. Sure, it's not as die hard as Gentoo or Slackware but I don't want to spend a week compiling before I get to use my desktop. Slackware is the oldest distro out there, but it doesn't have APT and I am not prepared to give this up.
Anyway, this is my “Linux” story. I hope you enjoyed reading it.

Enjoy life, enjoy Linux

Tuesday, November 15, 2005

Biztalk 2004: Mime revisited ...

I had to deploy a flow in production today that has been working fine for 2 months in our test environment. The flow receives a mail from an IIS maildrop folder, gets all attachments and sends them to an orchestration. Mails are in .eml format which is actually a mime message following the RFC2557 specification.
Below you can find a sample file that enters Biztalk:

From: a@a.com
To: b@b.com
Subject: hello world
Date: Mon, 14 Nov 2005 23:31:17 -0000
MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary="----_=_NextPart_000_01C5E973.82A5C520"

------_=_NextPart_000_01C5E973.82A5C520
Content-Type: multipart/alternative; boundary=
"----_=_NextPart_001_01C5E973.82A5C520"

------_=_NextPart_001_01C5E973.82A5C520
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit

hello world, content

------_=_NextPart_001_01C5E973.82A5C520
Content-Type: text/html; charset="us-ascii"
Content-Transfer-Encoding: 7bit


<HTML>
<HEAD>
<TITLE>hello world</TITLE>
</HEAD>
<BODY>hello world, content</BODY>
</HTML>

------_=_NextPart_001_01C5E973.82A5C520--
------_=_NextPart_000_01C5E973.82A5C520
Content-Type: text/plain; name="test.txt"
Content-Disposition: attachment; filename="test.txt"

text in attachment

------_=_NextPart_000_01C5E973.82A5C520--

After decoding the above message we have one IbaseMessage having multiple IBaseMessagePart objects.
In my pipeline component I always assumed that the actual body of the mail was a BodyPart and the attachments were all the other parts. I guess I was wrong. In the above sample, the BodyPart contains the attachment.
So how can we make sure that only the attachments are sent to Biztalk and not the body of the mail? Below is the solution:

        /// <summary>

        /// Try to determine if the message part is an attachment or not.

        /// Attachments will always have a file name in the mime property

        /// namespace.

        /// </summary>

        /// <param name="messagePart"></param>

        /// <returns></returns>

        private bool IsAttachment(IBaseMessagePart messagePart)

        {

            if (messagePart == null)

            { // bail if message part is empty

                return false;

            }

 

            /*

            * Read the file name from the mime property namespace.

            */

            string fileName = messagePart.PartProperties.Read("FileName","http://schemas.microsoft.com/BizTalk/2003/mime-properties") as string;

            if (fileName != null && fileName.Length > 0)

            {

                return true;

            }

            return false;

        }


In our pipeline component (disassemble stage), we will only disassemble messages that have the FileName-property set in the mime properties namespace.
Ok, I have to admit, no rocket-science here, but maybe there are people that will find this helpful.

Saturday, October 08, 2005

Linux: Problems with Ubuntu, X and NVidia

When I started my computer the other day, X was not starting. Nothing happened. Just a cold, black screen. I had no idea what triggered this, so I decided to reinstall the NVIDIA driver for my graphics card. All went well and X was working again. After reboot, same problem.
After googling for a while, I found this discussion. Seems the Debian's/Ubuntu's own nvidia runlevel scripts are cleaning up modules they shouldn't. So the solution is to uninstall all nvidia stuf you installed from Debian/Ubuntu (with --purge option), reinstall the NVIDIA driver from the official website and switch to runlevel 2 to see the result.
Aah, don't you just love if everything falls back into place ;)

Thursday, September 22, 2005

Visual Studio .NET 2003: resx files ...

I have a project containing custom pipeline components. Each component has its own .resx-file. For some reason the .resx-file for ComponentA was included in the project as a seperate file and the .resx-file for ComponentB was only visible if you clicked the [+]-sign left of my component.
I had no idea why there was a difference between the visualization for both .resx-files. When I edited the .csproj-file, I saw why it happened:

<File
RelPath = "ComponentB.resx"
DependentUpon = "ComponentB.cs"
BuildAction = "EmbeddedResource"
/>
If you remove the DependentUpon property, the file becomes visable in your project.
Dunno how to do it without editing the project file though ...

Tuesday, September 20, 2005

Biztalk 2006 on Linux ... yes, it's possible (2)


So I finally installed the second Biztalk 2006 beta on my Ubuntu Linux box. My machine is a Pavilion 5080.be with 1GB of RAM. I thought running VMWare and Windows 2003 Server would really slow the machine down ... but it didn't.
In the above screenshot you can see my first flow (ProcessOrder) I've developed in Biztalk 2006. Let's start doing some more advanced things ;)

Tuesday, September 13, 2005

Biztalk 2006 on Linux ... yes, it's possible (1)

I'm currently running Ubuntu Hoary on a custom 2.6.13 kernel. I will be using this to run Windows 2003 Server and Biztalk 2006.
To be able to run Windows on Linux (Ubuntu), you'll need VMWare. Apparently the most recent version (5) is having problems on kernels later than 2.6.11. The VMWare network modules causes the network to lock up from time to time. You can read all about it and how to solve it here. Right now, I've managed to install 2003 Server, SQL Server 2000, Analysis services and Visual Studio 2005 beta. Later this week, I'll be installing the second Biztalk 2006 beta. Very exciting stuff ...

Excel ... aaaargh ...

I am working for a client that is using a lot of automated B2B scenarios. These include, next to normal EDI (Edifact and X12), ordering by mail. My client’s client sends orders as mail attachments. This has been working for quite some time, until recently, I got a phone call. Apparently, something was wrong in _my_ code because the requested delivery date was wrong by a day.
The requested delivery date is, well, quite special. It is an integer indicating the number of days that passed since 01/01/1900. “Why?” you might ask. No idea. But hey, no problem, I can count.
So this is the code that was parsing the “number of days passed since 01/01/1900” to a real date:

            DateTime baseDate = DateTime.ParseExact("01011900", "ddMMyyyy", null);

            int days = int.Parse(numDays);

            return baseDate.AddDays(days - 1).ToString("yyyyMMdd", null);


I didn’t quite understand why this code could lead to a wrong requested delivery date. So I asked the client’s client how I was supposed to parse this date. They provided me with a sample in Excel. Indeed the date was wrong by a day, but why. I created the following table in Excel:
numDateExcelmyCode
101/01/190001/01/1900
202/01/190002/01/1900
36429/12/190030/12/1900
36530/12/190031/12/1900
36631/12/190001/01/1901
1000018/05/192719/05/1927
2000003/10/195404/10/1954
This is what I call: "The AHA effect". When looking at the above table, the only thing you can say is: "AHA", and after a while "WTF". Anyway. It seems that our nifty spreadsheer cannot count. It "thinks" that 1900 is a leap year, but this is not the case. That's why my code's parsed date was different by a day.
Apparently Microsoft knows about this bug. It's not even a bug, it's a feature.
Really? I call it plain wrong. It seems that Lotus-1-2-3 was having this bug to, so Microsoft copied the bug for compatibility reasons.
OpenOffice does not have the above feature.
Because my client was not expecting any orders before 28/02/1900, I changed the above code to:

            DateTime baseDate = DateTime.ParseExact("01011900", "ddMMyyyy", null);

            int days = int.Parse(numDays);

            return baseDate.AddDays(days - 2).ToString("yyyyMMdd", null);


Plain stupid, I know, but what can I do ...

Monday, September 05, 2005

Linux: Swap, file systems and such ...

Just found this interesting read on KernelTrap, posted by Mr Z.


Allow me to elaborate. UNIX filesystems have a concept of "inodes" that store the body of the file, its permissions and its ownership. The inodes get linked into directories via names--aka. directory entries. The same inode can be linked into the filesystem in multiple places. (Hence the concept of a "hard link.") The filesystem keeps track of how many links an inode has, and the kernel keeps track of how many processes have opened a given inode. This concept is important, and I will come back to it.

When an executable runs, the executable's file as well as the files for all the libraries it depends on get opened. The pages for these files get mmap()'d into the process' address space as file-backed virtual memory. The memory gets marked copy-on-write, so that any changes to the mmap()'d code result in a fault, and break the file backing. In any case, the file-backed portions are backed by the contents of the inodes themselves.

Under virtual memory pressure, the kernel will have to deallocate physical pages of memory from some processes in order to allocate them to others. There are two strategies available here: Write dirty pages to swap, and discard clean pages. Clean pages are pages which have either an explicit file backing (such as program executable pages), and pages that were previously swapped, brought back in, but still have an equivalent copy in the swap partition. (This is sometimes refered to as the "swap cache," though I don't know if that designation is accurate.)

So yes, under memory pressure, some pages of an executable might get discarded and will need to be brought in later from the original executable. The grandparent wonders how that works if a user upgrades a binary while the executable runs.

Recall that there's the separation between the file's contents (the inode) and the name given to it in the file system (hard link to the inode). File descriptors are bound to inodes, not directory entries. When you "rm" a file, you remove the link between the directory and the inode. When you replace a file, say with "cp," the existing inode gets unlinked and a new inode gets linked in its place. When you "mv" a file, it gets linked in its new location, and unlinked from its old location.

The filesystem code does not reclaim the space allocated to the inode until all references to the inode drop. This includes all filesystem links and open file descriptors. Thus, when you replace a program's executable while it executes, the currently running program continues to see the old executable, even if the inode doesn't have a visible link in the filesystem. The inode will remain allocated until all of its open file descriptors get closed. Then and only then will the filesystem reclaim the storage associated with the inode.

In fact, it is this property of UNIX derived filesystems that leads to all the orphaned inodes you find in "lost+found/" after a fsck if your system gets shut down abruptly. Any inodes that were open at the point of the crash, but which did not have a hard directory link end up here.


In my experience, the NTFS file system is not as advanced as explained in the above. If you develop in M$ Visual Studio and mess a bit with the copy local setting, you'll see referenced files get locked and there's no way to overwrite them. You can somehow simulate the Unix/Linux behaviour by renaming the referenced file. If for example c:\test\a.dll is locked, just rename it to c:\test\b.dll and copy the new version of a.dll to c:\test

Friday, September 02, 2005

Biztalk: Explorer

Just found this nifty tool to manage and configure your Biztalk server running in production. It's called Biztalk Explorer and allows you to configure/manage a Biztalk 2004 without having Visual Studio installed. So this tool is ideal for administrators configuring production/QAS environments.
I know you should use binding files, but if you want to change something quickly, this is the tool you need.

Thursday, September 01, 2005

Firefox: Figures on MSDN not showing ...

When browsing some articles on Biztalk on the MSDN sites, figures were not showing (e.g.: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/bts_2004wp/html/956fd4cb-aacc-43ee-99b6-f6137a5a2914.asp) in Firefox.
I was wondering why the figures were showing in Internet Exploder and not in Firefox. Seems that Firefox is having problems with the back slashes. Well ... problems ... shouldn't be back slashes in the first place (damn Micro$ofties), right? The solution is to install Slashy.

Look mom, I can now read MSDN articles too ;)

Thursday, July 07, 2005

Biztalk: Creating documentation

Here are a few possibilities to create your documentation for Biztalk deployments.

Maybe you came accross some better options ... let me know.

Wednesday, June 29, 2005

Biztalk: Delete ReceiveLocation using WMI and VBScript

The default samples that are shipped with the Biztalk SDK do not contain code to delete a ReceiveLocation. Here is something I've written that will delete a ReceiveLocation, specified on the command line.


Please note that:
  • this is a very-very basic script (e.g. no checks on primary ReceiveLocation)

  • I know nothing about VBScript, so if you want parts of it nominated as a WTF, go ahead, make my day ;)


Option Explicit

 

RemoveRecLoc

Sub RemoveRecLoc()

    Dim objArgs: Set objArgs = WScript.Arguments

 

    'error handling is done by explicity checking the err object rather than using

    'the VB ON ERROR construct, so set to resume next on error.

    'on error resume next

 

    Dim strReceiveLocation   

    strReceiveLocation = objArgs(0)

 

    Dim InstSet, Inst

    set InstSet = GetObject ("winmgmts:\root\MicrosoftBizTalkServer").InstancesOf("MSBTS_ReceiveLocation")

 

    Dim objReceivePort

 

    'Check for error condition before continuing.

    If Err <> 0    Then

        PrintWMIErrorThenExit Err.Description, Err.Number

    End If

 

    'Report on number of receive locations found and list each one.

    wscript.echo "A Total of " & InstSet.Count & " Receive Locations were found."

    If InstSet.Count > 0 Then

        For Each Inst In InstSet

            If Inst.Name = strReceiveLocation Then

                wscript.echo "Receive Location Name  : " & Inst.Name

                Inst.Delete_()

                wscript.echo "  Deleted ..."

            End If           

        next

    End If

 

End Sub

 

'This subroutine deals with all errors using the WbemScripting object.  Error descriptions

'are returned to the user by printing to the console.

Sub    PrintWMIErrorThenExit(strErrDesc, ErrNum)

    On Error Resume    Next

    Dim    objWMIError    : Set objWMIError =    CreateObject("WbemScripting.SwbemLastError")

 

    If ( TypeName(objWMIError) = "Empty" ) Then

        wscript.echo strErrDesc & " (HRESULT: "    & Hex(ErrNum) & ")."

    Else

        wscript.echo objWMIError.Description & "(HRESULT: "    & Hex(ErrNum) & ")."

        Set objWMIError    = nothing

    End    If

 

    'bail out

    wscript.quit 0

End    Sub

Friday, June 17, 2005

Mime Decoder Pipeline Component in Biztalk 2004 (the solution)

I promised to publish a solution to read the .eml files. This solution works for both Biztalk 2004 and Biztalk 2004 SP1 installations.



Step 1: Create a custom decoder component

You can download a wizard that will create any pipeline component here. Use this to create your decoder component. Make sure you have the following design time properties defined:


  • FromRegex: this will contain the regular expression to find the sender

  • FromMatch: this will contain the index of the Group and Capture

  • ToRegex: this will contain the regular expression to find the receiver

  • ToMatch: this will contain the index of the Group and Capture

  • SubjectRegex: this will contain the regular expression to find the subject

  • SubjectMatch: this will contain the index of the Group and Capture

  • PropertyNamespace: this will contain the namespace of the properties we're promoting (don't forget to create your property schema with MessageContextPropertyBase properties)


If you're not familiar with regular expressions, you can use the following expression to match the sender:
(?i)from:\s?([^\n\r]*)
If you apply this expression to the incoming message, the sender is found in Group 1, Capture 0. You can use this tool to test your regular expressions.



Step 2: Fill the gaps
Create a private member that will contain Biztalk's default decoder:

        private MIME_SMIME_Decoder containedDecoder = new MIME_SMIME_Decoder();


Some additional members well need:

        private RegExpMatcher fromMatcher = null;

        private RegExpMatcher toMatcher = null;

        private RegExpMatcher subjectMatcher = null;


We'll see the implementation of the RegExpMatcher later on.
Initialize the decoder:

        public void InitNew()

        {

            containedDecoder.InitNew();

        }


Make sure the component is able to load its configuration:

        public virtual void Load(IPropertyBag pb, int errlog)

        {

            string regExp = ReadPropertyBag(pb, "FromRegex") as string;

            string regExpMatch = ReadPropertyBag(pb, "FromMatch") as string;

            fromMatcher = GetRegExpMatcher(regExp, regExpMatch);

            ...

        }


        private RegExpMatcher GetRegExpMatcher(string regExp, string regExpMatch)

        {

            if (StringUtils.IsNotEmpty(regExp) && StringUtils.IsNotEmpty(regExpMatch))

            {

                return new RegExpMatcher(regExp, regExpMatch);

            }

            return null;

        }


Make sure the component is able to save its configuration:

        public virtual void Save(IPropertyBag pb, bool fClearDirty, bool fSaveAllProperties)

        {

            WritePropertyBag(pb, "FromRegex", fromMatcher.RegExp);

            WritePropertyBag(pb, "FromMatch", fromMatcher.RegExpMatch);

            ...


Additional properties:

        public string FromRegex

        {

            get

            {

                if (fromMatcher != null)

                {

                    return fromMatcher.RegExp;   

                }

                return null;

            }

            set

            {

                GetRegExpMatcher(ref fromMatcher).RegExp = value;

            }

        }

 

        public string FromMatch

        {

            get

            {

                if (fromMatcher != null)

                {

                    return fromMatcher.RegExpMatch;

                }

                return null;

            }

            set

            {

                GetRegExpMatcher(ref fromMatcher).RegExpMatch = value;

            }

        }


        private RegExpMatcher GetRegExpMatcher(ref RegExpMatcher aRegExpMatcher)

        {

            if (aRegExpMatcher == null)

            {

                aRegExpMatcher = new RegExpMatcher();

            }

            return aRegExpMatcher;

        }



Step 3: Create the Execute method

        public IBaseMessage Execute(IPipelineContext pc, IBaseMessage inmsg)

        {

            /*

            * Get the original stream for the incoming message's

            * body part.

            */

            Stream stream = inmsg.BodyPart.GetOriginalDataStream();

 

            /*

            * Now, read the stream in blocks of 1024 bytes, until we

            * find the mime header. If the header is found at position

            * X, reset the stream to position X. The contained mime

            * decoder will start reading from this point forward.

            */

            int mimeHeaderIdx = -1;

            int num = 1;

            byte[] buffer = null;

            StringBuilder text = new StringBuilder();

            while (mimeHeaderIdx < 0 && num > 0)

            {

                buffer = new byte[0x400];

                num = stream.Read(buffer, 0, 0x400);

                ASCIIEncoding encoding = new ASCIIEncoding();

                text.Append(encoding.GetString(buffer, 0, num).ToUpper(CultureInfo.InvariantCulture));

                mimeHeaderIdx = text.ToString().IndexOf(MIME_HEADER);

                if (mimeHeaderIdx >= 0)

                {

                    stream.Seek(mimeHeaderIdx, SeekOrigin.Begin);

                }

            }

            IBaseMessage outmsg = containedDecoder.Execute(pc, inmsg);

            try

            {

                outmsg.Context.Promote("From", propertyNamespace, fromMatcher.Match(text.ToString()));

                outmsg.Context.Promote("To", propertyNamespace, toMatcher.Match(text.ToString()));

                outmsg.Context.Promote("Subject", propertyNamespace, subjectMatcher.Match(text.ToString()));

            }

            catch (Exception e)

            {

                throw e;

            }

            return outmsg;

        }



Step 4: Create the RegExpMatcher component
This is the class that will ease our use of regular expressions:

    public class RegExpMatcher

    {

        private int group = -1;

        private int capture = -1;

        private string regExp = null;

        private string regExpMatch = null;

        private DelimitedString delimitedString = null;

 

        public RegExpMatcher()

        {

        }

 

        public RegExpMatcher(string aRegExp, string aRegExpMatch)

        {

            if (StringUtils.IsEmpty(aRegExp))

            {

                throw new ApplicationException("Regular expression was empty.");               

            }

            if (StringUtils.IsEmpty(aRegExpMatch))

            {

                throw new ApplicationException("Regular expression match was empty.");               

            }

            regExp = aRegExp;

            regExpMatch = aRegExpMatch;

            CreateGroupAndCapture(regExpMatch);

        }

 

        private void CreateGroupAndCapture(string aRegExpMatch)

        {

            delimitedString = new DelimitedString(aRegExpMatch);

            if(delimitedString.Parts.Length < 2)

            {

                throw new ApplicationException(string.Format(@"{0} should be a delimited string having 2 parts.", regExpMatch));

            }

            group = IntUtils.ParseInt(delimitedString.Parts[0]);

            capture = IntUtils.ParseInt(delimitedString.Parts[1]);       

        }

 

        public string RegExp

        {

            get

            {

                return regExp;

            }

            set

            {

                regExp = value;

            }

        }

 

        public string RegExpMatch

        {

            get

            {

                return regExpMatch;

            }

            set

            {

                regExpMatch = value;

                CreateGroupAndCapture(regExpMatch);

            }

        }

 

        public int Group

        {

            get

            {

                if(group < 0)

                {

                    throw new ApplicationException(string.Format("Group should be an integer, found {0}.", delimitedString.Parts[0]));

                }

                return group;

            }

        }

 

        public int Capture

        {

            get

            {

                if(capture < 0)

                {

                    throw new ApplicationException(string.Format("Capture should be an integer, found {0}.", delimitedString.Parts[1]));

                }

                return capture;

            }

        }

 

        public string Match(string part)

        {

            Match m = Regex.Match(part, regExp, RegexOptions.Singleline);

            if (m.Success)

            {

                object found = m.Groups[this.Group].Captures[this.Capture];

                if (found != null)

                {

                    return found.ToString();

                }

            }

            return "";

        }

    }



Step 5: Build and deploy your component
Now you're ready to build and deploy the component. Just copy the resulting .dll to C:\Program Files\Microsoft BizTalk Server 2004\Pipeline Components and you're set.

Step 6: Use your component
After you successfully deployed your component, you're ready to use it:

  1. Add the component to your toolbox in Visual Studio

  2. Create a new Biztalk project

  3. Add a pipeline

  4. Drag your component on the decode stage of your pipeline

  5. Configure your component

  6. Test

The Daemon, the GNU and the Penguin (12)

Read all about it here.
Very interesting series. I've read to chapter 4 the other night ...

Wednesday, June 15, 2005

Mime Decoder Pipeline Component in Biztalk 2004

This is so funny. I've been struggling for a while to be able to read .eml files through Biztalk. Seems the the MIME decoder that ships with Biztalk 2004 tries to find "MIME-VERSION" in the first 1024 bytes of the .eml-file. The problem is that, in my file, the MIME-VERSION-string was near byte 1324 (or so). So the decoder tells me the file is not valid. Seems this problem is resolved in SP1. In a next post, I'll post some code ...

Wednesday, June 01, 2005

Biztalk: RIP

Biztalk died on me today after choking on an 18 MB IDOC (= flat file message from SAP) message.

A catastrophic failure occurred in the BizTalk service.
The service will shutdown and auto-restart in 1 minute.
If the database is still unavailable, this cycle will be repeated.

Error message: Parameter is incorrect
Error source: System

BizTalk host name: BizTalkServerApplication
Windows service name: BTSSvc{940AAE5E-0C6F-401E-83B2-D00BFF7792BB}

I already ran the Config Framework wizard, but untill now, no joy :(

 


Update!


I found why I was getting the above error. It seems one of the IDOCS I was using for testing lost some trailing spaces in one of its segments. This resulted in the above error ...

Monday, May 30, 2005

Xbox 360

Ok, I must have been away from this world for some time. Apparently Microsoft's new game console will be called Xbox 360. You can read all about it here.
A few years ago, when Microsoft released its first Xbox, there was some student analyzing traffic that was crossing the encrypted bus (between CPU and peripherals I think), for its thesis. As a result, different small companies released mod chips that were able to hack this bus. After mod'ing your Xbox, you were able to run copied games (not that I approve that). You could even run Linux on your Xbox. After all, the first Xbox was a normal PC, with some proprietary stuff from Microsoft.
When the new Xbox will be released, the OpenSource community will be ready ... I can't wait ;)

Thursday, May 26, 2005

The Daemon, the GNU and the Penguin

For those of you interested in the history of Unix, OS2, Minix, Linux ... (or in short those decent OSes that don't originate from Redmond) there's a series of tales you can read on Groklaw. The latest is chapter 9.

Where did my functoids go ...

Ok, yet another Biztalk post. The other night, I was working for a client, trying to get a map right. Normally I don't use these things. I know XSLT well and tend to use this instead of the clumsy Biztalk Mapper. This time, however, I was obliged to use the mapper. My colleague was using it and I had to fix his map.
Anyway, opening the toolbox startled me, as there were no functoids. After Googling for a while, it seemed I had to add Microsoft.BizTalk.BaseFunctoids.dll (which you can find near C:\Program Files\Microsoft BizTalk Server 2004\Developer Tools) to my toolbox. After that, everything was working as it should.
I'm not sure how MS Visual Studio ever forgot about the location of my functoids ...

Wednesday, May 18, 2005

Star Wars Episode III: Revenge of the sith ...

Ok, I admit it, I'm a nerd ;) This morning, at 00h10, I went to see the final part of the Star Wars saga. Better than the first 2, I must admit. Even though you know how it's going to end (you do know how it's going to end ... if not, were have you been the past 6 years or so), Lucas managed to bring enough entertainment, drama and suspension to keep me awake for almost 3 hours. So in short, a must see ... may the force be with you ...

Tuesday, May 17, 2005

Duplicate message types part II

In a recent post I was talking about duplicate message types deployed to the Biztalk management database. The following link describes duplicate message types in more detail.

Wednesday, May 04, 2005

Long classpaths in Java

Yesterday, a colleague had a problem setting the classpath for an application we use. Apparently, the length of environment variables is restricted in Win32. Unfortunately our application is using a lot of external jars, so classpaths tend to grow.
While google'ing for a solution I came accross this post.
There is another solution to solve this issue. Just add the following line to your config.sys file:

shell=command.com /e:4096

I must admit, I like the Runner.class solution though ;)

Wednesday, April 20, 2005

GAC and M$ Visual Studio .NOT

A colleague of mine was having trouble adding references to C# projects, of DLL's that are in the GAC. Apparently, M$ nows about this issue:
http://support.microsoft.com/default.aspx?scid=kb;en-us;306149


I really love the last part:

It is highly recommended that you not install your assemblies to the GAC, unless you want to share your assemblies with other applications running on that system. Also, note that it is not possible to directly reference an assembly from the GAC in your project. If you want to use an assembly from the GAC, you should drop your assemblies to a local folder, and then add a reference to the assembly from this folder.

Tuesday, April 19, 2005

Checking for duplicate message types in Biztalk

Ever been annoyed with the following message in the event viewer after deploying your freshly created Biztalk 2004 project:

There was a failure executing the receive pipeline:
"Microsoft.BizTalk.DefaultPipelines.XMLReceive"
Source: "XML disassembler"
Receive Location: "AAA.BBB.CCC"
Reason: The disassembler cannot retrieve the document specification
by using this type: "http://foo.com/bar#something".
Either the schema is not deployed correctly, or more than one schema
is deployed for the same message type.

You can check for the assemblies that are causing this by querying the Biztalk Management database:

select
d1.id, d1.msgtype, d1.docspec_name, d1.clr_assemblyname
from
bt_DocumentSpec d1,
bt_DocumentSpec d2
where
d1.id != d2.id
and d1.msgtype = d2.msgtype