Writing a custom HostApps ESB adapter provider to integrate ESB toolkit with a Mainframe

Adapter provider framework is how the ESB toolkit configures the off-ramps to send requests using dynamic send ports. An adapter provider is an implementation of the base abstract class BaseAdapterProvider. The adapter provider is simply a component that is executed as part of the send pipeline to add the required context properties on the message before it is processed by the adapter on the dynamic send port to send the message.

HostApps adapter is BizTalk way to communicate with Mainframe programs using CICS integration. To use this adapter as part of the BizTalk ESB toolkit then you will need to allow dynamic ports to be able to send requests to this adapter. The ESB way to do this is by implementing a custom adapter provider.

The steps to implement a new custom adapter provider are simple. The following are the steps required to implement a HostApps adapter provider:

  1. 1- Create a new C# class library DLL and strongly sign the DLL.
  2. 2- Add references to the following DLLs

a. Microsoft.BizTalk.Messaging.dll

b. Microsoft.BizTalk.Pipeline.dll

c. Microsoft.Practices.ESB.Adapter.dll

d. Microsoft.Practices.ESB.Itinerary.Pipelines.dll

e. Microsoft.XLANGs.BaseTypes.dll

  1. 3- Add a new class with any name and inherit from BaseAdapterProvider
  2. 4- Override the public properties AdapterName and AdapterContextPropertyNamespace and return any name for your adapter provider. The following is an example for the HostApps adapter provider.

        public override string AdapterName

        {

            get

            {

                return "HostApps";

            }

        }

 

        public override string AdapterContextPropertyNamespace

        {

            get

            {

                return "HostApps";

            }

        }

 

  1. 5- Override the method SetEndpoint and do all your processing to add context properties required by the adapter. In the case of the HostApps adapter you need simply to add an undocumented context property called “AdapterConfig” with all properties required including the assembly mapping. The following is an example for full configuration to be set in this property

.

<Config xmlns:xsi=\"https://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"https://www.w3.org/2001/XMLSchema\">

  <uri>Connection String</uri>

  <PersistentConnections>Yes</PersistentConnections>

  <Transactions>Yes</Transactions>

  <SecurityOverrides>No</SecurityOverrides>

  <Tracing>No</Tracing>

  <AdvancedOverrides>No</AdvancedOverrides>

  <AssemblyMappings>&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;&lt;mappings&gt;&lt;mapping&gt;&lt;assembly&gt;&lt;![CDATA[C:\\Program Files\\Microsoft Host Integration Server 2013 SDK v1.0\\ApplicationIntegration\\WindowsInitiated\\InstallationVerification\\TiHostDefinitions\\NetClnt1\\bin\\NetClnt1.DLL]]&gt;&lt;/assembly&gt;&lt;connectionString&gt;&lt;![CDATA[CodePage=37;Name=TrmLink;TimeOut=0;SecurityFromClientContext=False;IPAddress=127.0.0.1;TCPPorts=7508;ConcurrentServerTransactionId=MSCS]]&gt;&lt;/connectionString&gt;&lt;/mapping&gt;&lt;/mappings&gt;</AssemblyMappings>

</Config>

  1. 6- You can implement the way to take all these properties any way you want but the below is a full example of how to implement this.

 

        public override void SetEndpoint(Dictionary<string, string> resolverDictionary, Microsoft.BizTalk.Message.Interop.IBaseMessageContext pipelineContext)

        {

            try

            {

                if (resolverDictionary == null)

                    throw new ArgumentNullException("resolverDictionary");

                if (pipelineContext == null)

                    throw new ArgumentNullException("pipelineContext");

                // Set the end point

                base.SetEndpoint(resolverDictionary, pipelineContext);

                // Get the endpoint configuration

                string endpointConfig = resolverDictionary["Resolver.EndpointConfig"];

                // Split the values

                // endpoint in the following format "key1=val1#key2=val2#....";

 

                // Here the assembly mapping will be the last element

                // We used the '&' instead of the ';' due to that AssemblyMappings will contains ';'

 

                Config cng = new Config();

                string elemname, elemvalue;

                int i;

                var configelements = endpointConfig.Split('&');

                foreach (string elem in configelements)

                {

                    i = elem.IndexOf('=');

                    if (i == -1) continue;

                    elemname = elem.Substring(0, i).Trim();

                elemvalue = elem.Substring(i + 1).Trim();

                    switch (elemname)

                    {

                        case "Uri":

                            cng.uri = elemvalue;

                            break;

                        case "PersistentConnections":

                            cng.PersistentConnections = elemvalue;

                            break;

                        case "Transactions":

                            cng.Transactions = elemvalue;

                           break;

                        case "SecurityOverrides":

                            cng.SecurityOverrides = elemvalue;

                            break;

                        case "AdvancedOverrides":

                     cng.AdvancedOverrides = elemvalue;

                            break;

                        case "AssemblyMappings":

                            cng.AssemblyMappings = elemvalue;

                            break;

                        case "Tracing":

                            cng.Tracing = elemvalue;

                            break;

                    }

                }

 

                StringBuilder Builder = new StringBuilder();

                StringWriter Writer = new StringWriter(Builder);

                XmlSerializer serializer = new XmlSerializer(cng.GetType());

                serializer.Serialize(Writer, cng);

                Writer.Flush();

                string configStr = Builder.ToString();

                string AdapterConfigNamespace = "https://microsoft.com/HostApplications/TI/WIP/Properties";

 

                pipelineContext.Write("AdapterConfig", AdapterConfigNamespace, configStr);

            }

            catch(Exception ex)

            {

            }

        }

 

  1. 7- Where the Config class is a just a helper class to allow serialization of the properties to XML and it is as follows.

    public class Config

    {

        public Config()

        {

            uri = "Connection String";

            PersistentConnections = "Yes";

            Transactions = "Yes";

            SecurityOverrides = "No";

            Tracing = "No";

            AdvancedOverrides = "No";

            AssemblyMappings = "";

        }

        public string uri { get; set; }

        public string PersistentConnections { get; set; }

        public string Transactions { get; set; }

        public string SecurityOverrides { get; set; }

        public string Tracing { get; set; }

        public string AdvancedOverrides { get; set; }

        public string AssemblyMappings { get; set; }

    }

 

  1. 8- Then create the manifest file as follows to allow development inside the itinerary designer.

<adapterPropertyManifest adapterName="TI">

  <aliases>

    <alias name="tiPropertySchemas" value="NBE.MW.Common.Schemas, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b504e32a0298c59a" />

  </aliases>

  <properties>

    <property name="uri" type="HostApps.uri" description="Always set to Connection String" assembly="tiPropertySchemas" />

    <property name="PersistentConnections" type="HostApps.PersistentConnections" description="" assembly="tiPropertySchemas" />

    <property name="Transactions" type="HostApps.Transactions" description="" assembly="tiPropertySchemas" />

    <property name="SecurityOverrides" type="HostApps.SecurityOverrides" description="" assembly="tiPropertySchemas" />

    <property name="Tracing" type="HostApps.Tracing" description="" assembly="tiPropertySchemas" />

    <property name="AdvancedOverrides" type="HostApps.AdvancedOverrides" description="" assembly="tiPropertySchemas" />

    <property name="AssemblyMappings" type="HostApps.AssemblyMappings" description="" assembly="tiPropertySchemas" />

  </properties>

</adapterPropertyManifest>

 

  1. 9- Copy the manifest file to the folder “C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\Extensions\Microsoft.Practices.Services.Itinerary.DslPackage” on any development machine.
  2. 10- Build the DLL and then GAC it.
  3. 11- Add the following line to the Esb.Config file

<adapterProvider name="HostApps" type="MyDll.AdapterProviders.HostAppsAdapterProvider, MyDll.AdapterProviders, Version=1.0.0.0, Culture=neutral, PublicKeyToken=32323232" moniker="HostApps" />

 

  1. 12- Now open the itinerary designer and you will be able to see the new adapter provider in the list of adapters for the static resolver to configure the required properties. Also you can use the same configuration using any dynamic resolver such as BRE resolver.

Happy BizTalking J