Using the BizTalk Server R2 BAM interceptor for WF
For some time now I have been pushing the use of BizTalk BAM outside of BizTalk Server implementations. BizTalk BAM provides a generic interceptor framework to track relevant data on business processes. BizTalk Server is just one of the many products that can leverage that framework in order to add visibility into Business Processes. Finally, with the release of BizTalk R2, Microsoft provides BAM interceptor implementations for WCF and WF. Using those interceptors we can extend the classic BAM features like aggregation, tracking and alerting into generic foundations like WF and WCF.
To utilize BAM with WCF-WF we need to use two main components:
- bm.exe, an enhanced version of the BAM deployment utility extended to modify interceptor configurations including add, remove, update, and list functionalities.
- CommonInterceptorConfiguration.xsd, the Common Interceptor Foundation configuration XML schema. At minimum, all interceptor configurations must validate against this schema.
The WF BAM interceptor combines the above mentioned components with a custom tracking service that logs data to the BAMPrimaryImport database. Let’s explore a classic sample. I plan to explain the details of the different components of this example in following posts. Our workflow is a simple State Machine workflow that simulates a PO Process.
The observation model for this example was built using the Excel BAM plugin that ships with BizTalk Server. The model contains just one activity and a set of dimensions for further aggregation in the generated OLAP cubes. The following is the XML generated by the Excel plugin.
<?xml version="1.0" encoding="UTF-16"?> <BAMDefinition xmlns="http://schemas.microsoft.com/BizTalkServer/2004/10/BAM"> <Activity Name="POActivity" ID="IDD38284A4F64745409E88DC7BF608D414"> <Checkpoint Name="Item" ID="IDF57663E311524757851437C1D9B8A183" DataType="NVARCHAR" DataLength="500"/> <Checkpoint Name="Quantity" ID="IDADEABF22B6C6416789C3ADF2D4CD2E59" DataType="INT"/> <Checkpoint Name="Received" ID="IDED14A7F26EC7477DA4ED63B8C08CBB86" DataType="DATETIME"/> <Checkpoint Name="Approved" ID="IDC1BADE06C3B04AC99BDDF544E84033BB" DataType="DATETIME"/> <Checkpoint Name="Shipped" ID="ID20C1465442184553929AB6B0EE49796C" DataType="DATETIME"/> <Checkpoint Name="Denied" ID="IDE88C30D7ADE5440B8F19191C288D6D74" DataType="DATETIME"/> </Activity> <View Name="POView" ID="IDDCA141B0E6E94D879B04CF6C4E8ED62A"> <ActivityView Name="ViewPOActivity" ID="IDB0C4AA0BE204425590FB1704829D6E9F" ActivityRef="IDD38284A4F64745409E88DC7BF608D414"> <Alias Name="Approved" ID="ID6CF7A7B9BB4140218259616AA698587D"> <CheckpointRef>IDC1BADE06C3B04AC99BDDF544E84033BB</CheckpointRef> </Alias> <Alias Name="Denied" ID="ID4B6715A4FF434D61AD6B55B62DCE80EE"> <CheckpointRef>IDE88C30D7ADE5440B8F19191C288D6D74</CheckpointRef> </Alias> <Alias Name="Item" ID="IDAB2C1362300142B884A6F567D5AEEFDB"> <CheckpointRef>IDF57663E311524757851437C1D9B8A183</CheckpointRef> </Alias> <Alias Name="Quantity" ID="IDCE258F95A63B4177B383FC83F7923A1A"> <CheckpointRef>IDADEABF22B6C6416789C3ADF2D4CD2E59</CheckpointRef> </Alias> <Alias Name="Received" ID="ID6C75FDA099DE462EB145ABF00CA2AE4D"> <CheckpointRef>IDED14A7F26EC7477DA4ED63B8C08CBB86</CheckpointRef> </Alias> <Alias Name="Shipped" ID="IDE59DA0F05E7A428DA444CD2ACFA75ABE"> <CheckpointRef>ID20C1465442184553929AB6B0EE49796C</CheckpointRef> </Alias> </ActivityView> </View> <Cube Name="POView" ID="ID1C2CF921025A47AC96A2F3382168FB08" CreateOlapCube="false" ActivityViewRef="IDB0C4AA0BE204425590FB1704829D6E9F"> <Measure Name="TotalReceived" ID="ID04E27CBA4B2F4813B45BA59C3EA0BC2D" AliasRef="IDCE258F95A63B4177B383FC83F7923A1A" AggregationFunction="Sum"/> <Measure Name="TotalApproved" ID="ID915659F57AB9462BAE0E037F76CF2DF6" AliasRef="IDCE258F95A63B4177B383FC83F7923A1A" AggregationFunction="Sum"/> <TimeDimension Name="ApprovedDim" ID="ID97905E2E89734CBCA0169AC06934BE1B" TimeStampAliasRef="ID6CF7A7B9BB4140218259616AA698587D"> <TimeLevel>Year</TimeLevel> <TimeLevel>Quarter</TimeLevel> <TimeLevel>Month</TimeLevel> </TimeDimension> <TimeDimension Name="ReceivedDim" ID="ID61B667B6CF9F4409900B4F49EC3B6033" TimeStampAliasRef="ID6C75FDA099DE462EB145ABF00CA2AE4D"> <TimeLevel>Year</TimeLevel> <TimeLevel>Quarter</TimeLevel> <TimeLevel>Month</TimeLevel> </TimeDimension> </Cube> <Extension> <OWC xmlns:x="urn:schemas-microsoft-com:office:excel">
</OWC> </Extension> </BAMDefinition> |
After generating this XML file, the next step is to deploy the observation model using the following syntax:
< installation path >\Program Files\Microsoft BizTalk Server 2006\Tracking\BM.exe deploy-all -definitionfile:< definitionfile.xml >
Once the bm.exe is completed, we need to create the interceptor configuration file which states how to map specific Workflow events and properties to BAM activities. Watch for more details about WF interceptor files in future postings. The interceptor configuration file for our example looks like the following.
<?xml version="1.0" encoding="utf-8" ?> <ic:InterceptorConfiguration xmlns:ic="http://schemas.microsoft.com/BizTalkServer/2004/10/BAM/InterceptorConfiguration" xmlns:wf="http://schemas.microsoft.com/BizTalkServer/2004/10/BAM/WorkflowInterceptorConfiguration"> <ic:EventSource Name="Workflow1" Technology="WF" Manifest="StPO.Workflow1, StPO, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/> <ic:BamActivity Name="POActivity"> <!--Workflow Initiated--> <ic:OnEvent Name="MyWorkflowEvent" Source="Workflow1" IsBegin="true"> <ic:Filter> <ic:Expression> <wf:Operation Name="GetWorkflowEvent" /> <ic:Operation Name="Constant"> <ic:Argument>Created</ic:Argument> </ic:Operation> <ic:Operation Name="Equals" /> </ic:Expression> </ic:Filter> <ic:CorrelationID> <ic:Expression> <wf:Operation Name="GetContextProperty"> <wf:Argument>InstanceId</wf:Argument> </wf:Operation> </ic:Expression> </ic:CorrelationID> <ic:Update DataItemName="Received" Type="DATETIME"> <ic:Expression> <wf:Operation Name="GetContextProperty"> <wf:Argument>EventTime</wf:Argument> </wf:Operation> </ic:Expression> </ic:Update> </ic:OnEvent> <!--PO Received...--> <ic:OnEvent Name="POReceived" Source="Workflow1"> <ic:Filter> <ic:Expression> <wf:Operation Name="GetActivityName" /> <ic:Operation Name="Constant"> <ic:Argument>OrderReceived</ic:Argument> </ic:Operation> <ic:Operation Name="Equals" /> </ic:Expression> </ic:Filter> <ic:CorrelationID> <ic:Expression> <wf:Operation Name="GetContextProperty"> <wf:Argument>InstanceId</wf:Argument> </wf:Operation> </ic:Expression> </ic:CorrelationID> <ic:Update DataItemName="Quantity" Type="INT"> <ic:Expression> <wf:Operation Name="GetWorkflowProperty"> <wf:Argument>Quantity</wf:Argument> </wf:Operation> </ic:Expression> </ic:Update> <ic:Update DataItemName="Item" Type="NVARCHAR"> <ic:Expression> <wf:Operation Name="GetWorkflowProperty"> <wf:Argument>ItemName</wf:Argument> </wf:Operation> </ic:Expression> </ic:Update> </ic:OnEvent> <ic:OnEvent Name="POApproved" Source="Workflow1"> <ic:Filter> <ic:Expression> <wf:Operation Name="GetActivityName" /> <ic:Operation Name="Constant"> <ic:Argument>POApproved</ic:Argument> </ic:Operation> <ic:Operation Name="Equals" /> </ic:Expression> </ic:Filter> <ic:CorrelationID> <ic:Expression> <wf:Operation Name="GetContextProperty"> <wf:Argument>InstanceId</wf:Argument> </wf:Operation> </ic:Expression> </ic:CorrelationID> <ic:Update DataItemName="Approved" Type="DATETIME"> <ic:Expression> <wf:Operation Name="GetContextProperty"> <wf:Argument>EventTime</wf:Argument> </wf:Operation> </ic:Expression> </ic:Update> </ic:OnEvent> <ic:OnEvent Name="POShipped" Source="Workflow1"> <ic:Filter> <ic:Expression> <wf:Operation Name="GetActivityName" /> <ic:Operation Name="Constant"> <ic:Argument>POShipped</ic:Argument> </ic:Operation> <ic:Operation Name="Equals" /> </ic:Expression> </ic:Filter> <ic:CorrelationID> <ic:Expression> <wf:Operation Name="GetContextProperty"> <wf:Argument>InstanceId</wf:Argument> </wf:Operation> </ic:Expression> </ic:CorrelationID> <ic:Update DataItemName="Shipped" Type="DATETIME"> <ic:Expression> <wf:Operation Name="GetContextProperty"> <wf:Argument>EventTime</wf:Argument> </wf:Operation> </ic:Expression> </ic:Update> </ic:OnEvent> <ic:OnEvent Name="PODenied" Source="Workflow1"> <ic:Filter> <ic:Expression> <wf:Operation Name="GetActivityName" /> <ic:Operation Name="Constant"> <ic:Argument>PODenied</ic:Argument> </ic:Operation> <ic:Operation Name="Equals" /> </ic:Expression> </ic:Filter> <ic:CorrelationID> <ic:Expression> <wf:Operation Name="GetContextProperty"> <wf:Argument>InstanceId</wf:Argument> </wf:Operation> </ic:Expression> </ic:CorrelationID> <ic:Update DataItemName="Denied" Type="DATETIME"> <ic:Expression> <wf:Operation Name="GetContextProperty"> <wf:Argument>EventTime</wf:Argument> </wf:Operation> </ic:Expression> </ic:Update> </ic:OnEvent> </ic:BamActivity> </ic:InterceptorConfiguration> |
After that we need to deploy the interceptor file using one of the enhancements to the bm.exe tool.
< installation path >\Program Files\Microsoft BizTalk Server 2006\Tracking\BM.exe deploy-interceptor -filename:< icfile.xml >
The final and most important step is to add the BAM tracking service to the WF runtime. The following shows the required code which must be added to the WF host. The additional code is highlighted below.
using System.Threading; using System.Workflow.Runtime; using System.Workflow.Runtime.Hosting; using Microsoft.BizTalk.Bam.Interceptors.Workflow; #endregion namespace StPO { class Program { static void Main(string[] args) { using(WorkflowRuntime workflowRuntime = new WorkflowRuntime()) { AutoResetEvent waitHandle = new AutoResetEvent(false); System.Collections.Specialized.NameValueCollection serviceParams= new System.Collections.Specialized.NameValueCollection(); serviceParams.Add("InterceptorConfigurationPollingInterval", "5"); serviceParams.Add("ConnectionString", "Integrated Security=SSPI;Data Source=.;Initial Catalog=BAMPrimaryImport"); workflowRuntime.AddService(new BamTrackingService(serviceParams)); CODE… } } } } |
While running the workflow, the data gets saved into the BAMPrimaryImport database and aggregated in OLAP representations. At this point we can use some of the existing BAM end-user tool to get a real-time representation of the workflow activity. The following figure shows a view of the BAM Portal rendering the views of the observation model of our target workflow.
The source code for this example will be available on www.AdapterWorx.com in the next few days.