BizTalk XPATHReader

Code snippet for using the XPATHReader in Microsoft.BizTalk.XPATHReader.dll.  You’ll find this assembly in the GAC on any BizTalk Server installation.  XML below is compatible with the XPATH’s in the PromoteContext method.

<Msg>
  <Id>732</Id>
  <CreateDate>2016-05-30T09:55:37.257</CreateDate>
<Msg>

Add the below code to a pipeline component then a reference to Microsoft.BizTalk.Streaming.dll and Microsoft.BizTalk.XPATHReader.dll.

using Microsoft.BizTalk.Streaming;
using Microsoft.BizTalk.XPATH;

private void PromoteContext(IBaseMessage pInMsg)
{
  ReadOnlySeekableStream vs = new ReadOnlySeekableStream(pInMsg.BodyPart.Data);
  XmlReader reader = XmlReader.Create(vs);

  XPathCollection xPathCollection = new XPathCollection();
  int idQuery = xPathCollection.Add("/*[local-name()='Msg']/*[local-name()='Id']");
  int createDateQuery = xPathCollection.Add("/*[local-name()='Msg']/*[local-name()='CreateDate']");

  XPathReader xPathReader = new XPathReader(reader, xPathCollection);

  while (xPathReader.ReadUntilMatch())
  {
    if (xPathReader.Match(idQuery))
    {
      pInMsg.Context.Promote("Id", "https://Schema.PropertySchema", xPathReader.ReadString());
    }

    if (xPathReader.Match(createDateQuery))
    {
      pInMsg.Context.Promote("CreateDate", "https://Schema.PropertySchema", xPathReader.ReadString());
    }
  }

  // Set the ForwardOnlyStream to a seekable stream
  pInMsg.BodyPart.Data = vs;
  pInMsg.BodyPart.Data.Seek(0, SeekOrigin.Begin);
}
Posted in BizTalk Server | Leave a comment

Streaming Get Message Type

Code snippet for grabbing the message type from an seekable stream.  Tip use the ReadOnlySeekableStream from the Microsoft.BizTalk.Streaming.dll.

string messageType = string.Empty;

using (XmlReader xmlReader = XmlReader.Create(memoryStream))
{
   if (xmlReader.MoveToContent() == XmlNodeType.Element)
      messageType = xmlReader.NamespaceURI + "#" + xmlReader.Name.Replace(xmlReader.Prefix + ":",string.Empty);
}
Posted in BizTalk Server | Leave a comment

ESB Toolkit Management Portal Resubmit with Itinerary

Edit the MessageViewer.ascx.cs BindGrid method grabbing the itinerary from context and store in an invisible label for later use.

private void BindGrid()
        {
            IExceptionService client = null;
            try
            {
                client = PortalHelper.GetExceptionService();

                List<usp_select_ContextPropertiesResult> contextProperties = client.GetContextProperties(new Guid(Request.QueryString["esb:MessageID"]));
                
                contextPropertiesView.DataSource = contextProperties;
                contextPropertiesView.DataBind();

                // Get Itienrary from context properties and write to visible=false label
                if (contextProperties.Exists(delegate(usp_select_ContextPropertiesResult prop) { return prop.Name == "ItineraryHeader"; }))
                {
                    usp_select_ContextPropertiesResult itineraryContextProperty = contextProperties.Find(delegate(usp_select_ContextPropertiesResult prop) { return prop.Name == "ItineraryHeader"; });
                    this.lblItineraryHeader.Text = itineraryContextProperty.Value;
                }

Edit the ResubmitMessage method of MessageViewer.ascx.cs. Things to note is reading the itinerary from the label and then just deserializing it to the Itinerary Object. Note in the below POC code the MessageResubmitter.ResubmitWCF method has been modified to return the exception.Message to display on the UI and also take an extra parameter, of type Itinerary.

private bool ResubmitMessage(string resubmitUrl, out string responseCode, out string responseMessage)
        {
            bool result = false;    // returned.
            if (resubmitUrl.ToUpper().Equals("WCF ONRAMP"))
            {
                // resubmit message and capture the result
                System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
                doc.LoadXml(((TextBox)this.MessageView.FindControl("messageBodyBox")).Text);

                // Derserialize ItineraryHeader
                System.Xml.Serialization.XmlSerializer reader = new System.Xml.Serialization.XmlSerializer(typeof(Itinerary));
                StringReader sr = new StringReader(this.lblItineraryHeader.Text);
                Itinerary itinerary = (Itinerary)reader.Deserialize(sr);

                string response = MessageResubmitter.ResubmitWCF(itinerary, doc);
                if (response == string.Empty)
                {
                    result = true;
                    responseCode = "202";
                    responseMessage = "Successfully sumbitted to WCF OnRamp";
                }
                else
                {
                    result = false;
                    responseCode = "500";
                    responseMessage = "Failure re-submitting to WCF OnRamp : " + response;
                }
            }

For the deserialization code above to work I had to edit the Itinerary Type to include the XmlRootAttribute as below. You don’t need to do this for SOAP as the ProcessItinerary.Itinerary type already has this.

[System.CodeDom.Compiler.GeneratedCodeAttribute("svcutil", "3.0.4506.30")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "http://schemas.microsoft.biztalk.practices.esb.com/itinerary")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "http://schemas.microsoft.biztalk.practices.esb.com/itinerary", IsNullable = false)]
public partial class Itinerary

Edited MessageResubmitter.ResubmitWCF below.

public static string ResubmitWCF(Itinerary itinerary, XmlDocument doc)
      {
         try
         {

            ProcessRequestClient onRamp = new ProcessRequestClient();
            onRamp.SubmitRequest(itinerary, doc.OuterXml);
            return string.Empty;
         }
         catch (Exception ex)

That is pretty much it for the portal enhancements. Create a WCF-WSHTTP receive location with config as per the below security tab, which should match up with the config in the ESB Portal web.config.

Screen Shot 2015-11-04 at 9.41.08 pm

Your receive pipeline will look something like the following.  Ignore the Reset Itinerary State component.  It’s some POC code I wrote that generically resets an itinerary state for a specific project.  If you want to resume the itinerary at the point where it failed all you need do is drop an ESB Itinerary component as the first component followed by an ESB Dispatcher to start executing your itinerary.  The ESB Itinerary component basically takes the itinerary from the inbound message and places it in the ItineraryHeader context property ready for the ESB Dispatcher.

Screen Shot 2015-11-04 at 9.43.10 pm 1

That it!  Too easy…

Posted in BizTalk Server | Leave a comment

BizTalk Shrink Log Script

Sample script to get a dev/test env going again if you logs chew up the disk. Set recovery mode to simple of each db to cleanup and then shrink the log and set the reco very mode back to full.

USE master
ALTER DATABASE BizTalkDTADb SET RECOVERY SIMPLE WITH NO_WAIT
ALTER DATABASE BizTalkMsgBoxDb SET RECOVERY SIMPLE WITH NO_WAIT
Use BizTalkDTADb
DBCC SHRINKFILE (N'BizTalkDTADb_log' , 0, TRUNCATEONLY)
GO
Use BizTalkMsgBoxDb
DBCC SHRINKFILE (N'BizTalkMsgBoxDb_log' , 0, TRUNCATEONLY)
GO
USE master
ALTER DATABASE BizTalkDTADb SET RECOVERY FULL WITH NO_WAIT
ALTER DATABASE BizTalkMsgBoxDb SET RECOVERY FULL WITH NO_WAIT
GO
Posted in BizTalk Server | 1 Comment

WCF Service ASP.NET Authorization Manager

Below sample using the System.ServiceModel ServiceAuthorizationManager and UrlAuthorizationModule from System.Web to provide IIS allow/deny authorization to a WCF Service.

Add the below class,

public class ASPNetAuthorizationManager : ServiceAuthorizationManager
    {
        protected override bool CheckAccessCore(OperationContext operationContext)
        {
            // Get the calling user
            System.Security.Principal.WindowsPrincipal wp = new System.Security.Principal.WindowsPrincipal(ServiceSecurityContext.Current.WindowsIdentity);

            // Use IIS authorization rules
            if (!System.Web.Security.UrlAuthorizationModule.CheckUrlAccessForPrincipal(operationContext.Host.Extensions.Find<System.ServiceModel.Activation.VirtualPathExtension>().VirtualPath, wp, "GET"))
                throw new AddressAccessDeniedException("Access Denied : " + operationContext.Host.Description.Name);

            // If we get to here return true and grant access
            return true;
        }
    }

Register the service behavior in the web.config as below,

<behaviors>
      <serviceBehaviors>
        <behavior name="serviceBehavior">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceAuthorization principalPermissionMode="UseWindowsGroups" serviceAuthorizationManagerType="ASPNetAuthorizationManager, MyWCFServices" />

Posted in BizTalk Server | Leave a comment

WCF ASP.NET Authorization Manager for BizTalk

ASP.NET style IIS authorizations using allow/deny users config from web.config for a BizTalk generated WCF Service. Plug this service behaviour into the receive location.

References

  • System.ServiceModel.dll
  • System.ServiceModel.Web.dll
  • System.Web.dll
using System;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Configuration;
using System.Threading.Tasks;
using System.ServiceModel.Web;
using System.Collections.Generic;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Description;
using System.ServiceModel.Configuration;

namespace WCFBehaviors.BizTalk.Framework
{
    public class ASPAuthorizationInspector : IDispatchMessageInspector
    {
        public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
        {
            try
            {
                // Deny anonymous users
                if (ServiceSecurityContext.Current.IsAnonymous)
                    throw new Exception();
            }
            catch
            {
                return false;
            }

            // Get the calling user
            System.Security.Principal.WindowsPrincipal wp = new System.Security.Principal.WindowsPrincipal(ServiceSecurityContext.Current.WindowsIdentity);

            // Use authorizations from web.config
            if (!System.Web.Security.UrlAuthorizationModule.CheckUrlAccessForPrincipal(instanceContext.Host.Extensions.Find<System.ServiceModel.Activation.VirtualPathExtension>().VirtualPath, wp, "GET"))
                throw new AddressAccessDeniedException("Access Denied : " + instanceContext.Host.Description.Name);

            return true;
        }

        public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
        {
            // Handle uncaught exceptions from AfterReceiveRequest
            if (correlationState == null) correlationState = false;

            if (!(bool)correlationState)
            {
                // reply with authorization error when correlationState = false
                reply = Message.CreateMessage(MessageVersion.Soap11, 
                                                MessageFault.CreateFault(FaultCode.CreateSenderFaultCode("Unauthorized", "http://BizTalk.Framework/wcf/Authorization"), 
                                                                            new FaultReason("Tee user is not authorized to access this service.")), 
                                                                            reply.Headers.Action);
            }
        }
    }

    public class ASPAuthorizationBehavior : IServiceBehavior
    {
        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<System.ServiceModel.Description.ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters){}

        public void Validate(System.ServiceModel.Description.ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase) { }

        public void ApplyDispatchBehavior(System.ServiceModel.Description.ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
        {
            foreach (ChannelDispatcher cDispatcher in serviceHostBase.ChannelDispatchers)
                foreach (EndpointDispatcher eDispatcher in cDispatcher.Endpoints)
                    eDispatcher.DispatchRuntime.MessageInspectors.Add(new ASPAuthorizationInspector());
        }
    }

    public class ASPAuthorizationBehaviorElement : BehaviorExtensionElement
    {
        protected override object CreateBehavior(){return new ASPAuthorizationBehavior();}

        public override Type BehaviorType{get { return typeof(ASPAuthorizationBehavior); }}
    }
}

Register the WCF Behavior Extension in the Framework and Framework64 machine.config.

<system.serviceModel>
        <extensions>
            <behaviorExtensions>
			<add name="ASPAuthorizationBehavior" type="WCFBehaviors.BizTalk.Framework.ASPAuthorizationBehavior, WCFBehaviors.BizTalk.Framework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=AAAA04cd3275BBBB"/>
Posted in BizTalk Server | Leave a comment

BizTalk Tip : Number of Orchestrations Tracking Data Query

Quick query to give number of orchestration instances over the last week. Grouped by orchestration name.

USE [BizTalkDTADb]

SELECT
	bts_Orchestration.nvcFullName,
	count(bts_Orchestration.nvcFullName) 'COUNT'
FROM
	dta_DebugTrace with (READPAST) JOIN dta_ServiceInstances on dta_DebugTrace.uidServiceInstanceID = dta_ServiceInstances.uidServiceInstanceId
								   LEFT JOIN BizTalkMgmtDb.dbo.bts_Orchestration on dta_ServiceInstances.uidServiceId = bts_Orchestration.uidGUID
WHERE 
	dta_ServiceInstances.dtEndTime &gt;= DATEADD(WEEK, -1, GETDATE())
GROUP BY bts_Orchestration.nvcFullName
ORDER BY 'COUNT' desc
Posted in BizTalk Server, SQL Server | Leave a comment