Tallan's Technology Blog

Tallan's Top Technologists Share Their Thoughts on Today's Technology Challenges

MABS EAI Bridge LoB Lookup (Part 2 of 2)

Dan Field

Last week month (sorry about that!), I wrote a post about using MABS to access a LoB system (in the example, SQL Server) behind several layers of firewalls (here).

We looked at the following tasks

  1. Creating the BizTalk services
  2. Setting up BizTalk Adapter Services in a local (or IaaS) environment to run a stored procedure in SQL Server
  3. Creating a sample table and stored procedure
  4. Creating a ServiceBus namespace with ACS
  5. Create the Relay to the LOB system
  6. Creating an EAI bridge to access the LoB system

This week, we’ll look at these tasks:

  1. Testing and debugging the bridge with a Visual Studio add on
  2. Writing a custom component to call the LoB adapter in a EAI Bridge Stage and parse the response
  3. Having the component send an email notification using an Office 365 server

Using the MessageSender application from Microsoft is handy for a quick test (and for some sample code), but to do more serious work there’s a Visual Studio add on for debugging and sending test messages to EAI bridges called the BizTalk Service Explorer.  It’s available here: https://visualstudiogallery.msdn.microsoft.com/1f75a6a6-a54e-44eb-8b11-1b5ea8928754 (this can also be found through the Visual Studio Extensions manager).  This will add a new item to the Server Explorer window in Visual studio:

Server Explorer 1

Right click on the highlighted item, and click “Add New BizTalk Service”.  Here you’ll have to provide the service URL and the ACS information about the service, as well as a “Friendly Name” used to display the service in the Server Explorer:

add a biztalk service

(if you can’t remember your ACS information, see my last post linked at the top of this entry; hopefully you saved it somewhere, otherwise you can get it from the Azure portal).

Once you enter that information, you will see your assemblies, bridges, certificates, schemas, and transforms deployed to MABS.  There are two particularly helpful functions for bridges: sending a test message and debugging the bridge:

Right click on SQLREQUEST (created last time), and click “Send Test Message…”  In this dialogue, you can load and send a test message and get the response (here, I got back a response of valid because asdf is a valid serial number in my local database):

Send test message

Debugging this bridge is a similar process, but it’s not really doing a whole lot yet.  The next phase of this project is to call the LoB bridge from a one way bridge to make a routing decision.

 

There is no built in way to do that in MABS currently; a two way bridge’s output cannot be routed to a one way destination.  However, various stages of a bridge can all be extended with custom .NET code, and it’s possible to send a message to a bridge and get the response using a custom .NET module.  This is something like a BizTalk Pipeline Component.  I wrote a C# library to place in a new EAI bridge.  The library has two classes:

  • MessageSender class (based off of Microsoft MessageSender application, but refactored to take string inputs instead of files; source included at end of this post)
  • SetPropertiesInspector, which implements IMessageInspector.  This class reads some promoted properties, sends a message to the LoB system, parses the response, and promotes some new properties for routing.

propertiesinspector code

Note that the http scheme is used, not https; this is to ensure that the ACS lookup will happen correctly.  Obviously, a “real” integration would have more sophisticated message parsing, perhaps using an XDocument to load and parse the data.

The bridge itself needs to be created and configured as well.  I dropped a new XmlOneWayBridge on to the MessageFlowItinerary design surface, and two Queues:

Design Surface

 

On the properties for the connector to the first queue, I added the filter condition “Valid=’True'”, and on the other, “Valid=’False'” (see the last post for more about filter conditions).  Valid is a property that gets promoted by the custom component.

 

For the Bridge configuration, I have the following: the message type is a new Request Schema that has a serial number and other information as well.  In the enrich stage, I added two XPath property definitions to promote the serial number node and another node:

enrich stage

  1. Click Enrich
  2. Open the Property Definitions
  3. Add/Edit your properties
  4. Enter the information; Xpath is to promote properties by XPath expression.

Finally, I set up the “On Exit Inspector”to refer to the Fully Qualified Name of my signed assembly:

exit inspector

  1. Select the outter Enrich stage box
  2. Open the properties for the On Exit Inspector
  3. Provide the fully qualified name of the class in the assembly.  You can also pass parameters to the assembly in here if desired.

Deploy your solution (ensure that the custom assembly is referenced and set to Copy Local by the MABS project) as before.

Deployment make take a minute or two to complete, but now we can debug the bridge.  Right click the bridge in Server Explorer and instead of sending a test message, this time we’ll debug; each stage will have new information to show, including showing the message body and any properties getting promoted.  This example is really only working with the requestMessageExtractor stage, so I’ll show those.  First, the “Before requestMessageExtractor”: (two properties have been promoted by earlier stages):

debug 1

 

The XPath promotions come next:

debug 2

Then the Exit Inspector portion of this stage; our new properties are promoted, including “Valid” to true because asdf is a valid serial number in this case; I would not ordinarily promote the outgoing and return messages, but for debugging it’s helpful:

debug 3

And that’s that!  The custom component can do other things with the message or message data if desired.  This solution has the data going to a queue, but the data could also be sent via Exchange 365, like so:

sendmail

There’s a pretty wide range of possibilities here!

 

Finally, here’s the MessageSender class:

 

using System;
using System.Collections.Specialized;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Mime;
using System.ServiceModel.Channels;
using System.Text;
using System.Web;
using System.Xml;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Collections.Generic;

namespace LoBLookup
{

 /// <summary>
 /// MessageSender is used to send a message to a deployed bridge end point
 /// </summary>
 public class MessageSender
 {
 /// <summary>
 /// Send message bytes to the runtime adresss of the deployed bridge end point
 /// </summary>
 /// <param name="acsNamespace">ACS namespace</param>
 /// <param name="issuerName">Issuer name for the specified namespace</param>
 /// <param name="issuerKey">Issuer key for the specified namespace</param>
 /// <param name="runtimeAddress">Runtime address of the deployed bridge</param>
 /// <param name="msg">String of the message to be sent</param>
 /// <param name="contentType">Content type of the message</param>
 /// <returns></returns>
 public static string SendMessage(string acsNamespace, string issuerName, string issuerKey, string runtimeAddress, string msg, string contentType)
 {
 string runtimeToken;
 UriBuilder endpointToGetAcsTokenForBuilder = new UriBuilder(runtimeAddress);
 endpointToGetAcsTokenForBuilder.Scheme = Uri.UriSchemeHttp;
 endpointToGetAcsTokenForBuilder.Port = -1;
 runtimeToken = GetAccessControlToken(endpointToGetAcsTokenForBuilder.ToString(), issuerName, issuerKey, acsNamespace);
 return SendMessageToBridge(runtimeAddress, msg, runtimeToken, contentType);
 }

 /// <summary>
 /// Get the Access Control token for the Service Bus URI
 /// </summary>
 /// <param name="endpointUri">Represents the End Point URI</param>
 /// <param name="issuerName">Issuer name for the Service Bus URI</param>
 /// <param name="issuerKey">Issuer key for the Service Bus URI</param>
 /// <returns>Access Control token</returns>
 private static string GetAccessControlToken(string endpointUri, string issuerName, string issuerKey, string acsNamespace)
 {
 string acsAddress = GetAcsAddress(acsNamespace);
 return GetAcsToken(acsAddress, issuerName, issuerKey, endpointUri);
 }

 /// <summary>
 /// Get the ACS address from the ACS namespace
 /// </summary>
 /// <param name="acsNamespace">Represents ACS Namespace</param>
 /// <returns>ACS Address</returns>

 private static string GetAcsAddress(string acsNamespace)
 {
 //UriBuilder acsUri = new UriBuilder(Uri.UriSchemeHttps + "://" + acsNamespace + "." + "accesscontrol.windows.net");
 //return acsUri.ToString();
 return "https://" + acsNamespace + ".accesscontrol.windows.net:443/";
 }

 /// <summary>
 /// Gets the ACS token for the specified Service Bus URI
 /// </summary>
 /// <param name="acsAddress">Represents ACS address</param>
 /// <param name="issuerName">Issuer name for the specified Service Bus namespace</param>
 /// <param name="issuerKey">Issuer key for the specified Service Bus namespace</param>
 /// <param name="appliesToAddress">Represents Service Bus URI</param>
 /// <returns>ACS Token</returns>
 private static string GetAcsToken(string acsAddress, string issuerName, string issuerKey, string appliesToAddress)
 {
 HttpClient client = new HttpClient();

 HttpContent content = new FormUrlEncodedContent(new Dictionary<string, string>
 {
 {"wrap_name", issuerName},
 {"wrap_password", issuerKey},
 {"wrap_scope", appliesToAddress}
 });

 var message2 = client.PostAsync(acsAddress + "WRAPv0.9/", content).Result;
 string response = message2.Content.ReadAsStringAsync().Result;
 //string response = Encoding.UTF8.GetString(responseBytes);

 // Extract the SWT token and return it.
 return response
 .Split('&')
 .Single(value => value.StartsWith("wrap_access_token=", StringComparison.OrdinalIgnoreCase))
 .Split('=')[1];

 }
 

 /// <summary>
 /// Sends message
 /// </summary>
 /// <param name="address">Represents the runtime address of the bridge end point</param>
 /// <param name="msg">Represents the string of the message to be sent</param>
 /// <param name="token">Represents ACS token</param>
 /// <param name="contentType">Content type of the message</param>
 /// <returns>Success/Failure message of the Send operation</returns>
 private static string SendMessageToBridge(string address, string msg, string token, string contentType)
 {
 string response;
 //WebClient webClient = new WebClient();
 //webClient.Headers[HttpRequestHeader.Authorization] = "WRAP access_token=\"" + HttpUtility.UrlDecode(token) + "\"";
 //webClient.Headers["Content-Type"] = contentType;
 //byte[] uploadData = webClient.UploadData(address, "POST", messageBytes);
 //response = Encoding.UTF8.GetString(uploadData);
 HttpClient c = new HttpClient();

 c.DefaultRequestHeaders.Add("Authorization", "WRAP access_token=\"" + HttpUtility.UrlDecode(token) + "\""); //Authorization = new AuthenticationHeaderValue("WRAP access_token", HttpUtility.UrlDecode(token));
 //c.DefaultRequestHeaders.Add("Content-type", "application/xml");

 //string msg = Encoding.UTF8.GetString(messageBytes);
 HttpContent content = new StringContent(msg);
 content.Headers.ContentType = new MediaTypeHeaderValue("application/xml");

 var message2 = c.PostAsync(address, content).Result;
 response = message2.Content.ReadAsStringAsync().Result;
 return response;
 }

 }
}

1 Comment. Leave new

Hi Tallen
Have you any idea how to implement a debatch solution of a large file csv by MABS?
I Have tried to debatch by envelope xsd schema but with no success! It’s very frustrating not to reproduce a very simple behavior of Biztalk server inside MABS

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

\\\