Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / server

Classic publisher-subscriber solution using BizTalk Server 2010 and ESB Toolkit 2.1

5.00/5 (1 vote)
8 Dec 2011CPOL4 min read 37.3K   843  
This article attempts to provide an alternative solution for the classic publisher-subscriber problem using ESB toolkit.

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.

Publish-Subscribe-CaseStudy.PNG

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.

MyCustomerContact_XML.PNG

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.

Publish-Subscribe-Itinerary.PNG

  1. 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.
  2. ESB Message Extender: This shape is used to transform the custom contact message into a canonical contact.
  3. 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.
  4. ESB Off-Ramp Extender: The off ramp extender is used to archive the canonical message into the archive folder.
  5. 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?

MyContact-Canonical.PNG

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

C#
// Itinerary - Initialize
itineraryWrapper = new Microsoft.Practices.ESB.Itinerary.SerializableItineraryWrapper();
itineraryStepWrapper = 
    new Microsoft.Practices.ESB.Itinerary.SerializableItineraryStepWrapper();

// Itinerary - Assign
itineraryWrapper.Itinerary = 
    Microsoft.Practices.ESB.Itinerary.ItineraryOMFactory.Create(InboundMsg);
itineraryStepWrapper.ItineraryStep = itineraryWrapper.Itinerary.GetItineraryStep(InboundMsg);

// Itinerary - Extract
hasNextService = itineraryWrapper.Itinerary.HasNextService();
resolvers = itineraryStepWrapper.ItineraryStep.ResolverCollection;

// Resolver - Count
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.

ODX-Sec1.PNG

C#
// Move to retrieve first resolver
resolver = resolvers.Current;

// Pass the resolver configuration to the Resolver mgr for resolution
resolverDictionary = 
    Microsoft.Practices.ESB.Resolver.ResolverMgr.Resolve(InboundMsg, resolver);

// set transport properties
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.

C#
//Retrieve map type from resolution structure
mapCLRType = System.Type.GetType(mapType);
transform(OutboundMsg) = mapCLRType(InboundMsg);
OutboundMsg(*) = InboundMsg(*);

Microsoft.Practices.ESB.Adapter.AdapterMgr.SetEndpoint(resolverDictionary, OutboundMsg);
// Set delivery port address
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.

ODX-Sec2.PNG
C#
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:

XML
    <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

C#
//Retrieve map type from resolution structure
mapCLRType = System.Type.GetType(mapType);

//Executing a map using transform keyword
transform(OutboundMsg) = mapCLRType(InboundMsg);

NOTE: The 'mapCLRType' is a variable of type 'System.Type'.

Using the 'MapHelper.TransformMessage' API Call

C#
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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)