Are you New to BizTalk Enterprise Service Bus (ESB) 2.1 Toolkit?
If yes, please read up on my 'Hello World' to ESB article which takes you on an introductory route.
Case Study: A Simple Publisher-subscriber Scenario
Consider a scenario where a Contact information needs to be published to several subscribers. The problem is to send a contact update message to multiple subscribers. The classic way of solving this problem is to, have a message broker orchestration place several messages onto the message box. These messages would then be consumed by the Send port subscriptions or other orchestrations as deemed fit in the tailored solution.
In this article, I am trying to suggest a different solution using the ESB toolkit.
Consider a scenario where a custom contact update message needs to be sent to multiple subscribers. The subscribers here are two CRM systems. The idea is to first transform the message into a canonical form (like a generic contact). This canonical contact is then transformed into the respective CRM specific formats.
The Custom Contact Message
The Custom contact
message is NOT in canonical
form, this is the RAW XML received from the application. This message has to be transformed into a canonical form using a transformation map.
Understanding the Itinerary
What are the Advantages of Using an Itinerary in this Case?
- A new subscriber can be added to the Itinerary without any modifications to the existing BizTalk deployment configuration.
- The subscriber end points can be changed using the BRE rules.
- The subscriber data formats (maps) can be changed using the BRE rules.
- ESB On-Ramp: An ESB on-ramp receives the message. This is the custom contact message which needs to be transformed into a canonical form.
- ESB Message Extender: This shape is used to transform the custom contact message into a canonical contact.
- ESB Orchestration Extender: This shape is used to process the canonical contact using an orchestration and dispatch it, using a list of subscribers. The resolvers list down the list of subscribers.
- ESB Off-Ramp Extender: The off ramp extender is used to archive the canonical message into the archive folder.
- ESB Off-Ramp: A BizTalk Server dynamic send port, which is also referred to as an ESB off-ramp is used to place the canonical message into the archive folder.
How are the Transformation Maps Located from the Itinerary?
The transformation maps are located using a BRE resolver policy
from the Itinerary shape resolver configuration. The BRE policy would look like this, notice the maps fully qualified name.
BRE Policy : ContactCanonicalMap
//##Conditions
If Context_Receive_Port_Name == "ContactInfo" and _
Context_Receive_Location_name == "ContextLocation:FILE"
//##Actions
Set Transform_Type To "MyCustomerSchema.CustomerContact_To_ContactCanonical_Map, _
MyCustomerSchema, Version=1.0.0.0, Culture=neutral, PublicKeyToken=2710b190689828fa"
The BizTalk ESB Compatible 'Dispatcher' Orchestration
Step 1: Message Box Binding
A logical receive port, bound directly to the message box is required for receiving ESB messages. The Contact canonical message is received by the orchestration.
Step 2: Coding the Meat of the Orchestration
The Orchestration has been broken up into two sections for brevity. One for resolver processing and another for advancing the Itinerary.
Initialization
itineraryWrapper = new Microsoft.Practices.ESB.Itinerary.SerializableItineraryWrapper();
itineraryStepWrapper =
new Microsoft.Practices.ESB.Itinerary.SerializableItineraryStepWrapper();
itineraryWrapper.Itinerary =
Microsoft.Practices.ESB.Itinerary.ItineraryOMFactory.Create(InboundMsg);
itineraryStepWrapper.ItineraryStep = itineraryWrapper.Itinerary.GetItineraryStep(InboundMsg);
hasNextService = itineraryWrapper.Itinerary.HasNextService();
resolvers = itineraryStepWrapper.ItineraryStep.ResolverCollection;
System.Diagnostics.Trace.WriteLine("Resolver count: " +
System.Convert.ToString(resolvers.Count), eventSource);
Section 1: Iterate Through all the Resolvers
This is the most critical piece of the orchestration. The idea is to iterate through all the resolvers and assign the transformed message to the dynamic send port.
resolver = resolvers.Current;
resolverDictionary =
Microsoft.Practices.ESB.Resolver.ResolverMgr.Resolve(InboundMsg, resolver);
transportLocation = resolverDictionary.Item("Resolver.TransportLocation");
transportType = resolverDictionary.Item("Resolver.TransportType");
mapType = resolverDictionary.Item("Resolver.TransformType");
Custom Logic: You can add custom piece of logic to determine suitable subscribers
based on certain rules.
mapCLRType = System.Type.GetType(mapType);
transform(OutboundMsg) = mapCLRType(InboundMsg);
OutboundMsg(*) = InboundMsg(*);
Microsoft.Practices.ESB.Adapter.AdapterMgr.SetEndpoint(resolverDictionary, OutboundMsg);
RoutingDynamicPort(Microsoft.XLANGs.BaseTypes.Address) = transportLocation;
RoutingDynamicPort(Microsoft.XLANGs.BaseTypes.TransportType) = transportType;
Section 2: Advance the Itinerary to the Next Step
This operation shall send the message back to the Itinerary to perform additional steps. If the Itinerary has no other additional steps, the operation won't be executed.
OutboundMsg.Body = InboundMsg.Body;
OutboundMsg(*) = InboundMsg(*);
itineraryWrapper.Itinerary.Advance(OutboundMsg, itineraryStepWrapper.ItineraryStep);
itineraryWrapper.Itinerary.Write(OutboundMsg);
System.Diagnostics.EventLog.WriteEntry(eventSource, "Message posted back to ESB.");
Step 3: Adding Entries in esb.config
Locate the esb.config under C:\Program Files (x86)\Microsoft BizTalk ESB Toolkit 2.1, find the XML tag itineraryServices
and add the following:
<itineraryservice id="cbd59fc2-e8e0-4d00-ad73-8590938911b2" stage="None"
scope="Orchestration" type="ContactOperation.Dispatcher, ContactOperation,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=2710b190689828fa"
name="ContactOperation.Dispatcher" />
TakeAway: What are the Two Ways to Quickly Transform a Message using ESB Toolkit?
Using the Transform XLANG Keyword
mapCLRType = System.Type.GetType(mapType);
transform(OutboundMsg) = mapCLRType(InboundMsg);
NOTE: The 'mapCLRType'
is a variable of type 'System.Type'
.
Using the 'MapHelper.TransformMessage' API Call
transformedData = Microsoft.Practices.ESB.Transform.MapHelper.TransformMessage
(InboundMsg.Body.OuterXml, MetadataMsg.Body.TransformationType);
NOTE: The 'transformedData'
is a variable of type 'System.String'
.
Some Points to Ponder
- The use of Orchestrations within
Itineraries
is NOT recommended. The design of Itineraries
does NOT encourage the use of orchestrations. Looking forward to suggestions for a better solution, which avoids the use of Orchestrations in Itineraries for the case presented in this article. - Any elegant design clearly depicts the end-to-end message flow processing, which is from the source to the destination. The
Itineraries
are one way of looking at it. By using Itineraries, we are modeling the message flow from end-to-end. - Every message flowing through the ESB would have an Itinerary attached to it.
- The use of BRE resolvers enables the flexibility of maps used in the message flow process.
References for Further Reading