Controlling BizTalk Orchestrations with PowerShell

Link. June 26, 2008. Comments [1]. Posted in: BizTalk | PowerShell

Here's a sample PowerShell script/functions to start/stop BizTalk orchestrations. This is an extended version of the Stop-Orchestration VBScript included in the BizTalk 2006 SDK, which I hope someone finds useful :-).

The script can be used to start or stop either a specific orchestration or a group of orchestrations defined in a BizTalk assembly. For example, to stop and unenlist all orchestrations in a given assembly, you could use this:

stop-orch -assembly 'MyProject.BizTalk, Version=1.0.0.0, Culture=neutral, PublicKeyToken=50b7b2906e3f8aa5' -unenlist

Here's the code for the script:

$script:bound = 2
$script:started = 4
$script:controlRecvLoc = 2
$script:controlInst = 2

function script:get-assemblyfilter([string]$assembly) {
   # The BizTalk WMI provider uses separate properties for each
   # part of the assembly name, so break it up to make it easier to handle
   $parts = $assembly.Split((',', '='))
   $filter = "AssemblyName='$($parts[0])'"
   if ( $parts.Count -gt 1 ) {
      for ( $i=1; $i -lt $parts.Count; $i += 2 ) {
         $filter = "$filter and Assembly$($parts[$i].trim())='$($parts[$i+1])'"
      }
   }
   return $filter
}
function script:find-orch([string]$name, [string]$assembly) {
   # We want to be able to find orchestrations by
   # name and/or assembly. That way we can control
   # all orchestrations in a single assembly in one call
   $filter = ""
   if ( ![String]::IsNullOrEmpty($name) ) {
      $filter = "Name='$name'"
      if ( ![String]::IsNullOrEmpty($assembly) ) {
         $filter = "$filter and $(get-assemblyfilter $assembly)"
      }
   } else {
      $filter = $(get-assemblyfilter $assembly)
   }
   get-wmiobject MSBTS_Orchestration `
      -namespace 'root\MicrosoftBizTalkServer' `
      -filter $filter
}

function start-orch([string]$name, [string]$assembly) {
   $orch = (find-orch $name $assembly)
   $orch | ?{ $_.OrchestrationStatus -eq $bound } | %{
      write-host "Enlisting $($_.Name)..."
      [void]$_.Enlist()
   }
   $orch | ?{ $_.OrchestrationStatus -ne $started } | %{
      write-host "Starting $($_.Name)..."
      [void]$_.Start($controlRecvLoc, $controlInst)
   }
}

function stop-orch([string]$name, [string]$assembly, [switch]$unenlist = $false) {
   $orch = (find-orch $name $assembly)
   $orch | ?{ $_.OrchestrationStatus -eq $started } | %{
      write-host "Stopping $($_.Name)..."
      [void]$_.Stop($controlRecvLoc, $controlInst)
      if ( $unenlist ) {
         [void]$_.Unenlist()
      }
   }
}

BizTalk Filters not Getting Imported

Link. June 21, 2008. Comments [1]. Posted in: BizTalk

... or how automatic formatting of XML files can make you miserable.

During the last few days I've been helping out a client get ready for deploying a BizTalk solution. One of those things this involved was taking the existing BizTalk Binding XML files and making minor edits to them so they would match the new environment.

I did the changes, and the BizTalk Administrator used the updated Binding Files to import them into the new BizTalk Servers. They imported without any errors at all. A couple hours later he noticed some test messages were getting incorrectly routed to the wrong send ports (there's a lot of messaging only stuff in this solution)., so he checked the send port configuration.

There were no filters defined at all! We checked the Binding files again, and yes they were clearly defined there. Why were they not getting imported?

The culprit turned out to be Visual Studio. I had edited the binding files in VS and, for several reasons, this involved doing copy-and-pasting the entire XML content of the binding files between machines. Normally this isn't a problem, but this time it was.

VS will reformat XML content when you paste it into VS. This is usually a welcomed feature, but not now. Turns out that VS reformatted the <Filter> elements of the Send port configurations like this:

<Filter>
   &lt;?xml version="1.0" encoding="utf-16"?&gt;
   &lt;Filter xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
...

See anything weird? I didn't see it either at first. The problem is the line break and extra white space caused by the indentation between the opening <Filter> element and the actual string-encoded XML of the filter expression.

Apparently, BizTalk can't deal with this at all and simply treats it as if the <Filter> element had been empty when it imports the binding file. No warnings, no errors, it simply ignores it silently.

I removed the space leaving it like this:

<Filter>&lt;?xml version="1.0" encoding="utf-16"?&gt;
   &lt;Filter xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
...

And now it was imported correctly and all the Filters were recreated successfully. This was maddening to say the least, and it's one of those pesky bugs that can make deploying BizTalk solutions an even more miserable experience than it already is. Definitely a very annoying bug in the Binding importer code.

403 Forbidden in Reporting Services

Link. June 11, 2008. Comments [1]. Posted in:

I just spent a good chunk of time fighting a problem with my SQL Server 2005 Reporting Services installation. I had not previously used it on this machine so it should've been exactly as the SQL Server (and SP2) installation program configured it.

I started by deploying a new set of reports from Visual Studio over to the server, which worked straight away. I then went to the Web-based Report manager and could see my reports deployed, but as soon as I tried actually rendering one of the reports, I'd get an error like this one:

Remote server returned an error: (403) Forbidden

Long story short, the problem was in how the /Reports application references the services in the /ReportServer application, and I fixed it by going to IIS and modifying the /ReportServer application configuration to add "ReportService2005.asmx" as a possible default content page under the "Documents" tab of the application's property page.

PipelineTesting 1.1.3.0 Released

Link. June 8, 2008. Comments [0]. Posted in: BizTalk

I just uploaded a new update to my PipelineTesting library. This one comes courtesy of Gregory Van de Wiele, who kindly made me aware of a nasty bug: The library wasn't handling schemas with no targetNamespace correctly when adding them to the pipeline context.

Because of this, you needed to specify the documentSpec name as "#root" instead of simply "root". This has now been fixed in this version and I added a new unit test to make sure it doesn't come up again. Thanks a lot Gregory!

As usual you can download the updated code/binaries from here.

Updated PS & BizTalk Posts

Link. June 4, 2008. Comments [0]. Posted in: BizTalk | PowerShell

I just took a few moments to update some of my old posts on administering BizTalk Server using PowerShell:

Here are some of the changes:

  • Reformatted code samples so that they were nicely readable using the current blog theme
  • Fixed some code that still used InvokeMethod() on WMI objects. This was the big change, since the old code was written with one of the PowerShell betas and didn't work anymore on RTM.

If anyone notices any other problems with the samples, let me know!

Duplicate Column Name When Deploying BAM Definition

Link. May 27, 2008. Comments [0]. Posted in: BizTalk

I've been playing around a bit with Business Activity Monitoring in BizTalk Server 2006 R2 and ran into a problem deploying a BAM definition XML file I had just created in Excel.

When I tried to deploy the definition, I'd get an error similar to this one:

Deploying Activity... Done.
Deploying View... ERROR: The BAM deployment failed.
Encountered error while executing command on SQL Server "NEWTON".
Column names in each table must be unique. Column name 'abc_id' in table 'bam_AbcEvents_Dim_abc' is specified more than once.

I checked my BAM definition several files (even checking IDs on the raw XML) and couldn't find any duplicates of this field. However, after struggling for about 15 minutes with it, I nailed it down: The problem was how I named a dimension!

I had an item defined in my activity named 'abc_id'. When I created my view, I created a custom dimension that combined 'abc_id' with another item and named it 'abc'. That seemed like a proper way to name it.

Problem is, when you try to deploy this, the activity tables will be created correctly, but bomb when creating the dimension table for 'abc', because bm.exe will try to create an artificial identity column for the dimension called 'abc_id'. That's how you end up with an apparent duplicate column error!~

The solution: rename your dimension in your view definition.

Net.MSMQ and Poison Messages

Link. May 27, 2008. Comments [0]. Posted in: Vista | WCF

The Net.MSMQ Binding in Windows Communication Foundation has some built-in support for detecting and handling "Poison Messages".

One of the most useful ways of handling Poison Messages in queuing systems is to get them out of the way so that following messages can still be processed (if you're not doing in-order processing), but still keep the messages around so that they are not lost.

Unfortunately, this behavior is only supported by WCF in MSMQ 4.0 with the use of a poison sub-queue, which is new in the Vista and WinServer2k8 versions of MSMQ. It's really a shame because, honestly, this could've been implemented on MSMQ 3.0 just by using a separate queue instead of sub-queues.

Instead, we're stuck with the PoisonErrorHandler sample included in the SDK. The sample provides a way to mimic this behavior by introducing an IErrorHandler implementation that detects the poison message and moves it to another queue. To be able to do this, you need to set the binding properties so that the ServiceHost listening to the queue faults when the poison message is detected. The error handler then moves the message to a new queue and starts a new service host instance to resume processing of following messages.

The Sample itself isn't all that bad, but does provide insight into a rather significant feature left out of the way the Net.MSMQ Binding reports poison messages: All it provides the error handler with is the MSMQ LookupId of the poison message. Unfortunately, this LookupId is queue-specific and the exception does not provide information on which queue or at least which endpoint was the one that received the poison message, which is a huge gaping hole in this feature.

Because of that the LookupId is useless without more information provided out-of-band [1]. The sample works around this limitation by adding an entry in the <appSettings/> section of the app.config file with the path to the queue where messages are being received and the path to the queue to move poison messages to.

This works OK, until you need a single service host listening on multiple endpoints, and then the sample won't really work anymore because you don't know which of the endpoints caused the error.

Working around the limitation

I spent some time recently playing around with WCF and ran into this same problem, so I tried to find a way of working around this limitation and still use the basic functionality the sample provided.

I found one possible workaround, which seems to work so far in my limited tests. I'm not sure yet how well it will hold, as it seems to me there's always the possibility of some race conditions here, but let me illustrate at least the basic mechanics.

First we keep the basic code of the PoisonErrorHandler class. However, instead of using appSettings to keep track of the queue we're listening to, we just try to find out dynamically which endpoint configured for the service is causing the error, and extract the path to the queue from there based on the URI of the endpoint address.

To do this, in my implementation of IErrorHandler.HandleError() I grab the current OperationContext, reach out to the ServiceHost associated with it, and then iterate over the ChannelDispatchers attached to it. The one that's in the Faulted state is very likely the one that caused the MsmqPoisonMessageException in the first place.

Code could be something like this:

private String FindFaultedQueue() {
   ServiceHostBase host = OperationContext.Current.Host;
   Uri faultedQueue = null;
   // the queue that fired the exception will be the faulted one
   foreach ( ChannelDispatcher chd in host.ChannelDispatchers ) {
      if ( chd.State == CommunicationState.Faulted ) {
         faultedQueue = chd.Listener.Uri;
      }
   }
   // translate URI into a regular MSMQ format name 
   StringBuilder fn = new StringBuilder("FormatName:DIRECT=OS:");
   fn.Append(faultedQueue.Host).Append("\\");
   string path = faultedQueue.PathAndQuery;
   if ( path.StartsWith("/private/") ) {
      fn.Append("private$\\");
      path = path.Substring("/private".Length);
   }
   path = path.Substring(1);
   fn.Append(path);
   return fn.ToString();
}

This lacks some error handling and a few other things, but it illustrates the point. Now that we have the FormatName of the queue where the poison message came from, we can try to move it to the poison message  queue. We could handle this by convention: for queue Q1, the corresponding poison message queue could be Q1Poison. The rest of the sample is pretty much the same.

I probably wouldn't use this in production, as it doesn't feel very reliable given the basic limitations imposed by the WCF implementation, but it was interesting to take a stab at. YMMV.

[1] I'll leave to you, dear reader, to wonder about the sense of providing a sample that so clearly points out such a giant hole in a product feature rather than actually fixing it. Oh wait, guess they did fix it by changing MSMQ for Vista and WinServer2k8.

Enabling Font Smoothing on NetBeans

Link. May 26, 2008. Comments [1]. Posted in: Tools

I've been working on some Java stuff lately and instead of using Eclipse as I usually do, I started working with NetBeans 6.1. It works reasonably well (much more than I expected).

However, I run into one really annoying issue: NetBeans looked great on my Ubuntu box; but when I tried running it in my Windows machine, it looked like crap. More specifically, fonts on the text editor surface were rendered without anti-aliasing so they looked horribly wrong.

I remembered then that NetBeans is a Swing app, which, until fairly recently, didn't even support using the platform's built-in sub-pixel rendering technologies (like ClearType on windows). On Java 1.5 (which I was using), however, it should support at least it's own smoothing mechanisms and those were not getting used at all on Windows.

A few searches on google turned up lots of people complaining about it and a few people saying it could be enabled (or would be fixed on Java 1.6) but no clear hints as to where to locate the actual option.

NetbeansOptions

After looking for a while finally figured out: You can enable font smoothing by going to the Tools -> Options dialog, then clicking on the Advanced Options button on the bottom left, and then selecting the Editor Settings category in the new dialog. There you'll find the elusive "Text Antialiasing" option.

I'm not sure why this was enabled by default on Linux but not on Windows, but, anyway, enabling it makes all the difference in the world.

One more thing to keep in mind: The way java renders fonts is a bit weird as well. I'm using DejaVu Sans Mono for the font and I need to use size 20 on NetBeans to get it to look around the same size of the size 15 I use on Vim (both on Windows and Linux).

Retrying Web Service Calls in BizTalk

Link. May 22, 2008. Comments [0]. Posted in: BizTalk | WCF

BizTalk Server has some pretty strong facilities for making a "best effort" at ensuring message delivery, by using the automatic retry facilities available to BizTalk Send Ports.

This functionality is available when calling Web Services as well either with the WCF or SOAP adapters, and it's very useful. However, it's not always enough by itself.

One such scenario comes up when the Web Service you're calling does not return SOAP Faults when it fails, but instead returns some kind of status/result indicator as part of a normal response message. In that case, the SOAP/WCF adapter can't tell on its own that the service call failed and retry it as necessary.

There are a few ways you can deal with this.

For example, you can handle retries manually from an orchestration, using a Loop shape to call the service, check the result and retry the call as necessary. It's not complex (though a bit cumbersome) and in many scenarios works very well.

Unfortunately, this isn't an option if you are in a messaging-only scenario, or have complex routing rules leading to the service invocation (common with direct-binding scenarios).

One possible workaround for this if you're using the WCF Adapter in R2 that I've been experimenting lately with is to use the WCF-Custom adapter and configure a custom Endpoint Behavior that adds an IClientMessageInspector implementation that checks the response message and transforms it to a fault if it indicates.

BTSWCFFault

A few things to keep in mind if you decide to try this:

  1. For your behavior to be listed on the BizTalk Administration console, you'll need to implement a BehaviorExtensionElement for it, deploy it to the GAC and register it in machine.config (or at least this was the way I did it).
  2. Inside your IClientMessageInspector.AfterReceiveReply() implementation, you might need to create a buffered copy of the message and return a new Message instance built from it to the WCF Adapter runtime; otherwise BizTalk might not be able read the message because you've already consumed it and it will be in the closed state by then.
  3. Make sure you leave the "Propagate Fault Message" option unchecked; otherwise your fault will just be submitted to BizTalk as a regular message and won't cause the send port to retry the call.

My friend Carlos suggested that another way to deal with this issue (particularly if you're not using the WCF adapter) might be by building a custom pipeline component and using it in a receive pipeline configured in the Send Port. The component would then read the response from the Web Service and throw an exception to force a failure during the response processing if necessary, thus causing the entire exchange to fail. I haven't tried this myself, but sounds like another useful idea!

A Generic WCF Service for BizTalk

Link. May 19, 2008. Comments [4]. Posted in: BizTalk | WCF

I've been playing lately with a few WCF features as well as with the WCF Adapter in BizTalk Server 2006 R2. As part of that I had a need to set up a receive location using one of the WCF HTTP-based bindings.

Normally, this isn't a big deal; you can easily use the BizTalk WCF Service Publishing Wizard to create the receive location and IIS service application for an orchestration or from a set of BizTalk schemas.

However, this time I did not want to go that way. What I really wanted was just a simple receive location that would simply receive messages and submit them over to BizTalk without tying the receive location to a specific service definition. As far as I could see, the Service Publishing Wizard didn't really have an option for this, but I was confident it could be made to work.

To be honest, I did have a set of schemas I wanted to work with and even a bunch of predefined WSDL files. However, for many reasons (including the sheer complexity of the schemas and WSDL files involved) I didn't want to get that involved in my initial BizTalk message receiver and processor.

Fortunately, turns out that doing what I wanted was fairly easy stuff. In fact, it was so easy that was able to leverage an existing service I had previously created. I basically just copied the .SVC file (and renamed it) alongside the web.config file. I explicitly ignored all the stuff that normally gets generated under the App_Data directory.

I then manually created a matching receive location in BizTalk using the WCF-BasicHTTP adapter, and this worked right away perfectly for my needs!

Just in case you've never looked at what a WCF HTTP receive location generated files look like, it's actually fairly simple stuff. The SVC file contains just a single directive as expected:

<%@ ServiceHost Language="c#" Factory="Microsoft.BizTalk.Adapter.Wcf.Runtime.BasicHttpWebServiceHostFactory, Microsoft.BizTalk.Adapter.Wcf.Runtime, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>

You can see here that it references the WCF-BasicHTTP adapter. If you wanted to use, say, the WCF-WSHttp Adapter, then you'd use Microsoft.BizTalk.Adapter.Wcf.Runtime.WSHttpWebServiceHostFactory class instead.

The config file is pretty much the default config file generated by the Service Publishing Wizard as well:

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
   <configSections>
      <section 
         name="bizTalkSettings" 
         type="Microsoft.BizTalk.Adapter.Wcf.Runtime.BizTalkConfigurationSection,
            Microsoft.BizTalk.Adapter.Wcf.Runtime, Version=3.0.1.0,
            Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
   </configSections>
   <bizTalkSettings>
      <mexServiceHostFactory debug="false">
         <receiveLocationMappings>
            <!--add markupFileName="*.svc"
               receiveLocationName="?"
               publicBaseAddress="protocol://host[:port]" /-->
         </receiveLocationMappings>
      </mexServiceHostFactory>
      <webServiceHostFactory debug="false" />
      <isolatedReceiver disable="false" />
      <btsWsdlExporter disable="false" />
   </bizTalkSettings>
   <appSettings />
   <connectionStrings />
   <system.web>
      <compilation defaultLanguage="c#" debug="false">
         <assemblies>
            <add assembly="mscorlib, version=2.0.0.0, culture=neutral,
               publickeytoken=b77a5c561934e089" />
            <add assembly="Microsoft.BizTalk.Adapter.Wcf.Common, Version=3.0.1.0,
               Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
            <add assembly="Microsoft.BizTalk.Adapter.Wcf.Runtime, Version=3.0.1.0,
               Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
         </assemblies>
      </compilation>
      <authentication mode="Windows" />
   </system.web>
   <system.serviceModel>
      <behaviors>
         <serviceBehaviors>
            <behavior name="ServiceBehaviorConfiguration">
               <serviceDebug 
                  httpHelpPageEnabled="true" 
                  httpsHelpPageEnabled="false" 
                  includeExceptionDetailInFaults="false" />
               <serviceMetadata 
                  httpGetEnabled="false" 
                  httpsGetEnabled="false" />
            </behavior>
         </serviceBehaviors>
      </behaviors>
      <services>
         <service 
            name="Microsoft.BizTalk.Adapter.Wcf.Runtime.BizTalkServiceInstance" 
            behaviorConfiguration="ServiceBehaviorConfiguration">
            <!--<endpoint
               name="HttpMexEndpoint"
               address="mex"
               binding="mexHttpBinding"
               bindingConfiguration=""
               contract="IMetadataExchange" />-->
            <!--<endpoint
               name="HttpsMexEndpoint"
               address="mex"
               binding="mexHttpsBinding"
               bindingConfiguration=""
               contract="IMetadataExchange" />-->
         </service>
      </services>
   </system.serviceModel>
</configuration>

It's extremely nice to see how the WCF adapter in BizTalk leverages a bunch of my favorite features in WCF to make this a lot simpler (compared to, say, the whole bunch of code that needed to be generated for the original SOAP adapter).

I should mention though, that part of what made it so easy was that my needs were pretty simple: I wanted a simple two-way (request/response) port, and needed no metadata (WSDL) publishing at all (as I said, I already had working WSDL files I could provide to consumers of the service).  Making it one-way wouldn't have been a problem though; as the WCF adapter handles it very gracefully as well.

Syndicate

About

Tomas Restrepo is a software developer located in Colombia, South America. His interests include .NET, Connected Systems, PowerShell and lately dynamic programming languages. More...

tomasrestrepo @ twitter My Flickr photostream My saved links on delicious My Technorati Profile

email: tomas@winterdom.com
msn: tomasr@passport.com

View my profile on LinkedIn

MVP logo

Ads


Categories

Statistics

Total Posts: 1041
This Year: 111
This Month: 0
This Week: 0
Comments: 819

Archive

Other

Copyright © 2002-2008, Tomas Restrepo.

Powered by: newtelligence dasBlog 2.2.8279.16125

Sign In