Using the ESB Guidance: Dynamic Transformation and Routing

 

In my last entry, I showed how developers could use our new ESB guidance to submit generic messages into BizTalk and have that message routed to a Service Endpoint based on a UDDI entry.  Today, I thought I'd expand on that example and add in another common scenario that occurs within an ESB architecture, namely "Dynamic Transformations". (If you haven't seen the last post, its probably best to check it out first as I'll be building on the concepts introduced there)

 

The Goal

 

Submit a generic message to the ESB and have it dynamically transformed and then have the new message dynamically routed to a Service Endpoint that is defined in a UDDI entry. The client application needs to be able to specific the transformation map to use as well as the name of the Service. 

 

The Solution

 

The first thing I did was to build out a test client that I could use to submit message to the ESB.  I needed to be able to specify the body of my message, the map I want to use for the transformation and the name of the Service that I want the message submitted to.

 

 

 In my last post I spoke about the ESB On Ramp Web Service and show how you could submit generic XML message to it. I also outlined about how to submit message processing instructions to the ESB by using SOAP headers attached to that SOAP message.  To achieve dynamic transformation and routing, we'll again need to use the this On Ramp and we'll again attach message processing instructions via the SOAP Headers.   Therefore, the first thing I did after creating my test client was to add a Web Reference to the ESB On Ramp Web Service.

 

 

Next, I created a function that would handle all interaction with the OnRamp Web Service.  To achieve both dynamic transformation followed by dynamic routing, I needed to use four of the SOAP headers.

 

  1. ProcessingInstruction - (Allows me to specify the first processing task for the ESB to perform)
  2. Itinerary - (I'll talk more about this in a minute)
  3. MapType - (Allows me to specify the fully qualified name of the map I want BizTalk to use in the transformation)
  4. EndpoingUddiLabel - (Allows me to specify the name of the Service that I want message routed to)

 

private string Submit_Transform_Route(string strService, string strMessage, string strMap)

{

// Get an instance of the On Ramp Service

ESB.OnRamp ramp = new ESB.OnRamp();

 

// Assign security credentials

ramp.Credentials = new System.Net.NetworkCredential("username", "password");

 

// Assign values to the headers we need to perform the UDDI based routing

ramp.EsbSoapHeadersValue = new ESB.EsbSoapHeaders();

 

//Tell the ESB that the first task is to "Transform" the message

ramp.EsbSoapHeadersValue.ProcessingInstruction = "TRANSFORM";

 

//Let the ESB know what all of the steps I want performed are

ramp.EsbSoapHeadersValue.Itinerary = "TRANSFORM,ROUTE";

 

//Tell the ESB which Map to use in the transform

ramp.EsbSoapHeadersValue.MapType = strMap;

 

ramp.EsbSoapHeadersValue.EndpointUddiLabel = strService;

 

// Get the message to send

XmlDocument doc = new XmlDocument();

doc.LoadXml(strMessage);

 

try

{

//Send the message to the OnRamp and return the result

ramp.Receive(doc);

return "Message Submitted Successfully.";

}

catch (System.Exception ex)

{

return "Error: " + ex.Message;

}

}

 

You'll notice that its almost identical to the function I developed for my last post. In that function I had to set three of the EsbSoapHeaders to achieve UDDI based routing for my message.  I had to set the ProcessingInstruction = "ROUTE", the Itineray to "ROUTE" and the EndPointUddiLabel to the name of the Service I wanted the message routed to.

 

For this example, I actually wanted the ESB to perform multiple and sequential tasks on my message. (i.e. transform it first and then route it).  Since the "ProcessingInstruction" header only allows me to specify a single task for the  ESB to perform,  I needed to find some header that would let me submit a list of instructions.   This is where the "Itinerary" header comes into play. It is this header that allows me to pass in a comma delimited list of tasks which the ESB will perform one after the other.

 

Once I had this function built, all I had to do was call it from the onClick where I passed in the Map name and the Service name.

 

private void button1_Click(object sender, EventArgs e)

{

            Cursor.Current = Cursors.WaitCursor;

            btnSubmit.Enabled = false;

 

            string result = Submit_Transform_Route(txtServiceName.Text, txtOriginalMessage.Text, txtMapName.Text);

 

            btnSubmit.Enabled = true;

            Cursor.Current = Cursors.Default;

            MessageBox.Show(result, "Result", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly);

}

 

Its important to understand that you can only use a Map that has already been deployed into your BizTalk server.  So you do need to pre-develop the Map before you can use it. For my example here, I just used one of the prebuilt samples that comes with the SB guidance.  Also, you have to provide the fully qualified name of the map.

 

 

How it Works

 

The way this scenario works is very similar to the Dynamic Routing scenario I explained in my last post.  Our message is submitted to the OnRamp Web Service, it promotes all of the SOAP headers that we provided up into the standard BizTalk Message Context.   Within BizTalk, we then have our "Agents" (i.e. Orchestrations) that are setup to subscribe to our message. Each Agent subscribes to a message based on the "ProcessingInstruction" property.  In my example, I set the ProcessingInstruction equal to " TRANSFORM". This resulted in my message being picked up by the DynamicTransformation.odx orchestration. (Found in the Microsoft.BizTalk.ESB.Agents.Transform project). It is this orchestration that will perform the transformation for us.

 

This transformation orchestration does a couple of things for us.

  1. It checks to see if we have specified a map for us. If we haven't it attempts to determine the map name based on information sent in the SOAP headers.
  2. Once a map name is determined, the transformation is performed for us.
  3. Advance the itinerary for us.

 

"Advance the itinerary"??? What's that mean? We'll lets take a look ...

 

Itineraries

 

Things to understand:

  1. Message processing tasks are performed by "Agents" which are really just BizTalk Orchestrations
  2. Agents subscribe to messages based on the "ProcessingInstruction" property. I.e. The Transform agent only picks up messages with a ProcessingInstruction set to "TRANSFORM". The delivery agent only to messages with "ROUTE"
  3. Itineraries are comma delimited list of processing instructions that will eventually be moved up into the ProcessingInstruction property as each step is completed.

 

After an agent picks up a message from the messageBox and completes its core task, it is required to examine the current message itinerary. If there are additional steps that need to be completed, the agent is responsible for extracting the next step out of the itinerary and moving it up into the ProcessingInstruction property. The message is then published into the messageBox where another agent can potentially pick it up based on the new processing instruction.  It is this mechanism that allows us to "chain" together our agents and complete multiple tasks on our message as it moves through the ESB.

 

Lets take a look at a portion of the Transform agent orchestration.

 

 

 

In this image, we can see the 2 key tasks that the agent is performing. In the "MessageAssignment" step, the actual transformation is performed. The code that performs the dynamic transform is:

 

tMapType = System.Type.GetType(FullyQualifiedMapType);

transform (OutboundMessage) = tMapType(InboundMessage);

 

The second key box is the "Advance Itinerary"  one. It contains the following code:

 

OutboundMessage(*) = InboundMessage(*);

itineraryStep = Microsoft.BizTalk.ESB.Helpers.ItineraryHelper.Advance(OutboundMessage(Microsoft.BizTalk.ESB.Itinerary));

OutboundMessage(Microsoft.BizTalk.ESB.Itinerary) = itineraryStep.Itinerary;

OutboundMessage(Microsoft.BizTalk.ESB.ProcessingInstruction) = itineraryStep.NextStep;

 

As you can see, this code advances the itinerary forward one step by calling the ItineraryHelper class. The ProcessingInstruction property is then updated to contain this next step.  For our example, the "TRANSFORM" instruction I manually assigned to the ProcessingInstruction is replaced with the second Itinerary task "ROUTE".  Once this code runs, a new message is published back to the messageBox where our Routing agent picks it up and perform the dynamic routing. (If you want details on how that agent will run, check out the previous post)  This is one way in which Dynamic Transformation and Dynamic Routing can be achieved using the ESB guidance.

 

Extending Beyond this Example:

 

The ESB guidance currently ships with two default agents, the Transform agent and the Delivery agent. It's important to understand that you can extend your ESB beyond this basic functionality by creating you own Agent orchestrations that subscribe to your own custom ProcessingInstructions.  The guidance contains a set of guidelines on how to develop your own agents so that you can develop your own custom and potentially complex ESB tasks.

 

Now, if you are interested in leveraging the ESB guidance for your organization, we have currently released it to a core set of Microsoft Partners. You can find a list of these partners at https://www.microsoft.com/biztalk/solutions/soa/esbpartners.mspx