Deploy behavior for WCF-Custom adapter

Topics: Bindings File, Settings Management and SSO, Server Deployment
Jul 6, 2012 at 7:24 AM

Hi,

I have a custom behavior to authorize users to connect to a WCF-Custom endpoint. This is not hosted in IIS. I am not quite sure how I can deploy this custom behavior. It either needs a configuration entry to be put into machine.config or import the configuration into the receive/send handler of the WCF-Custom adapter. Is there a way to use BTDF to do this import? I am currently using BTDF to deploy every other artifact and it would be good to be able to deploy custom behaviors using this framework as well.

 

Thanks for the help.

Min

Coordinator
Jul 9, 2012 at 6:16 AM

Hi Min,

Just set up this port manually in BizTalk and import the configuration, then export the whole set of bindings to a file.  Unless I'm mistaken, you should get all of your custom configuration exported into the file.  I assume that you're familiar with the BTDF PortBindings.xml or PortBindingsMaster.xml since you're already using the Framework.  Just take that port configuration and bring it into your deployment project's bindings XML file and I'd think you should be good to go.

Thanks,
Tom

Jul 10, 2012 at 1:20 AM
Edited Aug 6, 2012 at 4:36 AM

Hi Tom,

 

Thanks for your reply. Exporting bindings was the first thing I tried. When I export, it does export out the receive location configuration to use the custom behaviour. However, It doesn’t export out the configuration of the WCF-Custom adapter. The only way to make a custom wcf behaviour visible to biztalk is to put it in machine.config or import it in the Adapter’s Send/Receive handlers transport property configuration in Platform settings. That doesn’t seem to get exported out into the bindings file.

 

I could modify the msi to put this information in machine.config but I’d much rather not do that and keep everything within biztalk and use btdf to deploy if possible.

 

Thanks again for your help.

Min  

 

From: tfabraham [email removed]
Sent: Monday, 9 July 2012 2:16 PM
To: Min Tun
Subject: Re: Deploy behavior for WCF-Custom adapter [biztalkdeployment:362234]

 

From: tfabraham

Hi Min,

Just set up this port manually in BizTalk and import the configuration, then export the whole set of bindings to a file. Unless I'm mistaken, you should get all of your custom configuration exported into the file. I assume that you're familiar with the BTDF PortBindings.xml or PortBindingsMaster.xml since you're already using the Framework. Just take that port configuration and bring it into your deployment project's bindings XML file and I'd think you should be good to go.

Thanks,
Tom

Coordinator
Aug 6, 2012 at 4:14 AM

In that case, I'm not sure if there is a tool in BizTalk or maybe in the WCF Adapter SDK that could do this automatically.  I've used WCF-Custom a number of times and never noticed a problem with getting what I needed exported to the bindings file.  If anyone does find a method to load this configuration in a scripted fashion, please post it here so it can be considered for a future release.

Thanks,
Tom

Aug 6, 2012 at 5:19 AM

Hi Tom,

I have a solution for that now. Basically I wrote a msbuild task to add the configuration information to the adapter using WMI. There are a couple of things to do

1. Define your port settings as per normal in the port bindings file and create the port without any custom behaviour.

2. I use a custom xml file to define the behaviour configuration as below. 

<AdapterConfig>
  <Handler>
    <AdapterName>WCF-Custom</AdapterName>
    <HostName>BiztalkServerApplication</HostName>
    <ConfigSection>
      <CustomProps>
        <WcfExtensions vt="8">&lt;behaviorExtensions&gt;&lt;add name=\"CustomAuthBehaviorElement\" type=\"Integration.Common.WCFBehaviours.WCFSecurityBehaviours.BiztalkServiceAuthorisationBehaviourElement, Integration.Common.WCFBehaviours, Version=#VersionNumber#, Culture=neutral, PublicKeyToken=your key here\"/&gt;&lt;/behaviorExtensions&gt;</WcfExtensions>
      </CustomProps>
    </ConfigSection>
    <IsReceiveHandler>true</IsReceiveHandler>
    </Handler>
</AdapterConfig>


3. Update the btdf project file to include the custom Task and define a target as below.

add reference assemblies
<UsingTask AssemblyFile="BiztalkDeployment.Tasks.dll" TaskName="BiztalkManagement.ReceiveHandler.UpdateAdapterCustomTransportProperties" />

add the Target needed. GetPipelineVersionNumber is a custom task that reads the version that is being deployed from somewhere

<Target Name="UpdateCustomBehaviors" DependsOnTargets="GetPipelineVersionNumber"> 
<!-- find the VersionNumber and replace with correct one --> 
<File.Replace Path="$(PortBindingsPath)\AdapterConfig.xml" OldValue="#VersionNumber#" NewValue="$(VersionNumber)"/> 
<!-- add the config file. $(portBindingsPath) is set somewhere else and will depened on how the deployment project is set up. -->
<BiztalkManagement.ReceiveHandler.UpdateAdapterCustomTransportProperties ConfigFilePath="$(PortBindingsPath)\AdapterConfig.xml"/> 
</Target> 


when installing MSBUILD is run with a /t:"UpdateCustomBehaviours" switch.

Below is the implementation for the custome Task.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Management;
using System.Xml.Linq;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

namespace BiztalkDeployment.Tasks.BiztalkManagement.ReceiveHandler
{
    public class UpdateAdapterCustomTransportProperties : Task
    {

        [Required]
        public string ConfigFilePath { get; set; }

        public override bool Execute()
        {
            try
            {
                var result = false;
                if (File.Exists(ConfigFilePath))
                {
                    //get configuration items.
                    var configItemList = ReadConfigItems();
                    var receiveHandlers = configItemList.Where(itm => itm.isReceive);

                    //update receive handlers.
                    if (receiveHandlers.Any())
                        result = ProcessHandler(receiveHandlers, "MSBTS_ReceiveHandler");

                    //update send handlers.
                    var sendHandlers = configItemList.Where(itm => !itm.isReceive);
                    if (sendHandlers.Any())
                        result = ProcessHandler(sendHandlers, "MSBTS_SendHandler");

                    Log.LogMessage("Configuration added successfully.");
                    return result;
                }

                Log.LogError("Configuration file {0} does not exist.", ConfigFilePath);
                return false;
            }
            catch (Exception ex)
            {
                Log.LogErrorFromException(ex, true);
                return false;
            }
        }

        private bool ProcessHandler(IEnumerable<ConfigItems> configList, string mgmtObject)
        {

            var configUpdated = false;
            var options = new PutOptions { Type = PutType.UpdateOrCreate };
            foreach (var itm in configList)
            {
                if (!ValidateConfigItem(itm)) continue;

                var strWQL = string.Format("SELECT * FROM {0} WHERE AdapterName=\"{1}\" AND HostName=\"{2}\"",
                                           mgmtObject, itm.AdapterName, itm.HostName);
                var searchReceiveHandler = new ManagementObjectSearcher(
                    new ManagementScope("root\\MicrosoftBiztalkServer"), new WqlObjectQuery(strWQL), null);
                var handlers = searchReceiveHandler.Get();
                if (handlers.Count > 0)
                {
                    foreach (ManagementObject handle in handlers)
                    {
                        handle["CustomCfg"] = itm.ConfigurationXml;
                        handle.Put(options);
		      handle.Dispose();
                    }

                    configUpdated = true;
                }

                searchReceiveHandler.Dispose();

            }

            return configUpdated;
        }

        private IEnumerable<ConfigItems> ReadConfigItems()
        {
            var doc = XDocument.Load(ConfigFilePath);

            var root = doc.Element("AdapterConfig");
            var handlers = from itm in root.Elements("Handler")
                           select new ConfigItems
                                      {
                                          AdapterName = itm.Element("AdapterName") == null ? string.Empty : itm.Element("AdapterName").Value,
                                          HostName = itm.Element("HostName") == null ? string.Empty : itm.Element("HostName").Value,
                                          ConfigurationXml = ConfigXml(itm),
                                          isReceive = itm.Element("IsReceiveHandler") == null ? false :
                                                                itm.Element("IsReceiveHandler").Value.Equals("true", StringComparison.OrdinalIgnoreCase)
                                      };


            return handlers;
        }

        private string ConfigXml(XElement configElement)
        {
            var elem = configElement.Element("ConfigSection");
            string str = string.Empty;
            if (elem != null)
            {
                var reader = (configElement.Element("ConfigSection").CreateReader());
                reader.MoveToContent();
                str = reader.ReadInnerXml();
            }

            return str;

        }

        private bool ValidateConfigItem(ConfigItems item)
        {
            return (!string.IsNullOrEmpty(item.AdapterName)
                    && !string.IsNullOrEmpty(item.HostName)
                    && !string.IsNullOrEmpty(ConfigFilePath));
        }

        internal class ConfigItems
        {
            public string AdapterName { get; set; }
            public string HostName { get; set; }
            public string ConfigurationXml { get; set; }
            public bool isReceive { get; set; }
        }
    }
}

I would also love to know if anyone else has run into this and has an alternate approach.

Cheers,

Min

 

Coordinator
Aug 6, 2012 at 5:46 AM

Terrific, thanks for sharing your solution here!

Tom