Blog Stats
  • Posts - 5
  • Articles - 0
  • Comments - 4
  • Trackbacks - 12

 

Friday, July 22, 2005

Pipeline for extracting Processing Instructions

In my last post I wrote about how one can extract InfoPath processing instructions from a message inside an orchestration. While it seems to work all right, the idea generally has nothing to do with business. A better place for doing this would be a pipeline. So I wrote a simple pipeline which is basically identical to the default XMLReceive with one exception - in the decode stage it has a component that does work of extracting processing instructions from an XML document and subsequently writing them into the message context.
Solution files can be downloaded here.

A few notes about the component which I called PIExtractor:
1. It is designated for the decode stage. I think it is quite an appropriate place for it :-).
2. It extracts processing instructions that are immediate children of the root (/). That is, any processing instructions that are descendants of the document element are not extracted.
3. It places processing instructions into the XMLNORM.ProcessingInstruction property which is a standard property so you don't need to deploy a custom property schema. XMLNORM.ProcessingInstruction is basically used for outgoing messages so it seems all right to make use of it for ingoing messages.
4. It does not promote XMLNORM.ProcessingInstruction but writes it. Which means that you cannot use this propery as a filter expression for subscribers. The property is not promoted mostly for the reason that processing instructions may be longer than 256 characters and promoted properties are limited to 256 characters in length while written properties have no length limitation. Anyway, I can hardly imagine why one should filter on Processing Instructions :-).
5. To handle large XML documents relatively efficiently, PIExtractor uses XPathDocument and XPathNavigator. XPathDocument has a bug (feature?) - its constructor closes the passed stream. To avoid this I wrote a simple wrapper class around Stream with an empty implementation of the Close method.
6. Working with streams in pipelines is somewhat complicated. So the output message is cloned from the input message and then its context is altered. No copying streams are performed - I didn't want to degrade performance with redundant allocation of memory.

The component along with the pipeline were tested in the BizTalk 2004 SP1 environment.

Download files.

Tuesday, June 14, 2005

InfoPath Processing Instructions

If you worked with InfoPath you must know about processing instructions that it uses to treat an XML file as an InfoPath form. If your orchestration receives such a file, alters its data and then sends it out, you should keep these processing instructions to be able to view your file as an infopath form. InfoPath (SP1) uses 2 PI's:

 

<?mso-infoPathSolution name="urn:schemas-microsoft-com:office:infopath:Form1:http---rgs-ru-Form1" href="file:///C:\YourInfoPathForm\manifest.xsf" solutionVersion="1.0.0.60" productVersion="11.0.6357" PIVersion="1.0.0.0" ?>

 

The value of “href” depends on the location of your xsn or manifest (template) file. If you use simple assignment when constructing the out message, processing instructions will be copied into the out message automatically. If you use map you have an option of copying PI's on the properties sheet of the map grid. Unfortunately this option won't help if your map has more than one input scheme. In such a case you may copy PI's explicitly in the expression shape as described in the Blogger's guide. There's a drawback however. If you redeploy/relocate your InfoPath form template (xsn) you'll have to modify the orchestration. Not the best option, especially taking into account the fact that PI's are contained in the message.

Here's the solution I propose. In your orchestration declare a string variable (strProcessingInstructions) that will store the processing instructions. Put an atomic scope and declare an XmlNodeList (nodeList) and XmlNode (node) variables in it. We need a scope with the atomic transaction type because XmlNodeList and XmlNode are not serializable. Put an Expression shape into the scope and type the following in it:

 

nodeList = xpath(InputMessage, "processing-instruction()");

if (nodeList.Count > 0) { node = nodeList.Item(0); strProcessingInstructions = strProcessingInstructions + node.OuterXml; }

if (nodeList.Count > 1) { node = nodeList.Item(1); strProcessingInstructions = strProcessingInstructions + node.OuterXml; }

if (nodeList.Count > 2) { node = nodeList.Item(2); strProcessingInstructions = strProcessingInstructions + node.OuterXml; }

if (nodeList.Count > 3) { node = nodeList.Item(3); strProcessingInstructions = strProcessingInstructions + node.OuterXml; }

if (nodeList.Count > 4) { node = nodeList.Item(4); strProcessingInstructions = strProcessingInstructions + node.OuterXml; }

 

The list of if's may be as long as you think the number of processing instructions in the message are. In the case of InfoPath the number is 2 so the last 3 conditions are redundant. As another option we could use a Loop shape and get rid of the hardcoded number of if's but in my case a Loop shape would be too much :-).

Now when constructing the out message, put the following lines in a Message assignment shape:

 

OutMessage(XMLNORM.ProcessingInstructionOption) = 1;

OutMessage(XMLNORM.ProcessingInstruction) = strProcessingInstructions;

 

Here you go. Your processing instructions will be transferred to the out message and the document will look as as infopath form.

Wednesday, April 06, 2005

DebugBreak that really breaks

I've recently submitted an article about DebugBreak (and, in a way, ASSERTs), why it does not work sometimes and how you can improve it to get it working always. The article is available on the CodeProject through this url http://www.codeproject.com/debug/DebugBreakAnyway.asp.

Wednesday, March 23, 2005

Exception of type Microsoft.SharePoint.SoapServer.SoapServerException was thrown

Today I've encountered an interesting issue with the SharePoint. I wrote a code to call a couple of SharePoint web services. But it did not work. SoapException occurred with the message "Exception of type Microsoft.SharePoint.SoapServer.SoapServerException was thrown.". Having played a little bit with the code I understood that the same exception occured whenever I called some SharePoint web method. I tried GetListCollection method of the Lists web service - it should work, i've used it often - but it would not work either. I had a look at the Detail property of the SoapException and saw "Method not found: System.Collections.IEnumerator Microsoft.SharePoint.SPListCollection.GetEnumerator". Now I started recalling that we'd had the same problem a long time ago when we used foreach loop to traverse a collection of sites or something else - it doesn't matter anyway, all SharePoint collections inherit the SPBaseCollection which implements the interface System.Collections.ICollection. The code then had compiled successfully but in runtime when it ran up to the method with the foreach, the application crashed with a runtime exception saying "Method not found: System.Collections.IEnumerator Microsoft.SharePoint.SPListCollection.GetEnumerator.". It looked like the collection object did not have the method GetEnumerator in runtime...

I searched in Google and found a few messages from people having the same problem but no one could answer them. I decided to dig it up to understand what really happened. I examined Microsoft.SharePoint.dll (from GAC) and STSSOAP.dll (it's in C:\Program Files\Common Files\Microsoft Shared\web server extensions\60\ISAPI\BIN\). STSSOAP.dll contains SharePoint web services (Lists.asmx, Dws.asmx, etc.). Nothing was suspicious. SPBaseCollection really implements ICollection and its metod GetEnumerator. Also STSSOAP.dll references Microsoft.SharePoint.dll with assembly version 11.0.0.0. No application or publisher policy that might override versions existed.

I ran once again my console application that called SharePoint web service, launched Process Explorer and looked at modules loaded into w3wp.exe. Microsoft.SharePoint.dll assembly had version 11.0.0.0 and was loaded from the GAC. OK, I looked at STSSOAP.dll and here it was. There were two copies of it loaded into w3wp.exe with the same assembly version - 11.0.0.0! They were different in File Versions. The first one was 11.0.6361.0 (file version) and loaded from the Temporary ASP.NET Files (ASP.NET copies web application assemblies over there) and the second assembly was 11.0.4920.0 and loaded from the GAC.

I compared these assemblies and they were different in size: the 6361 was 200K, the 4920 - 180K. Both assemblies had the same Assembly Version - 11.0.0.0! And both referenced Microsoft.SharePoint.dll with Assembly Version 11.0.0.0. Moreover, the 6361 assembly was copyrighted all-right but the 4920 was marked "Unpublished work.". Cool, isn't it? :-) By the way, it's a good idea to put strings like "Unpublished work" before your application is released, but I guess you shouldn't distribute it before you remove such a string... :-) Anyway, after I had removed this unpublished assembly from the GAC everything worked like it should.

Now I wonder where it came from...

The very beginning...

Hi, Everyone!
My name is Stas Kondratiev and I've recently worked with the most amazing :-) Microsoft's product - BizTalk Server 2004. Despite the fact that you'll get a standard documentation out-of-the-box after installing BizTalk, it's not enough to get understanding why everything works the way it does. So here's the list of the best (IMO) information on the Internet:

So put yourself into the amazing and interesting world of BizTalk and take care!

Oh, by the way, if you can read Russian you're welcome to visit my Russian blog.

 

 

Copyright © Stas Kondratiev