Introduction
This article describes an implementation on BizTalk 2006/R2/BizTalk 2009 where a received message is split and sent to many different destinations. The number of destinations is not known before runtime. BizTalk reads information about the receivers from the message and sends a message to each target system. BizTalk receives a response from each of the targets and aggregates them into a response message which is sent back to the sender.
One example where this implementation can be used is when a web application needs to compare prices of a product from different providers. The web application sends a request to the broker and receives back a response that includes the prices from the different providers.
Overview
This implementation contains two different orchestrations. A parent orchestration receives the message, counts the number of receivers, creates a copy of the message, and starts a child orchestration for each recipient.
The child orchestration reads the receiver's ID from the message and uses that to find transport information and address from a database. All information about the receivers is stored in a database, only the receiver's ID is included in the message. A separate .NET assembly is used to get this information. The child orchestration configures a dynamic send port based on the information about the receiver, and sends the message to the target system. It also receives a response from the target system.
The parent orchestration uses a self-correlating binding port to receive the responses from the child orchestration. It adds all responses into one message and when all responses are received, it calls a pipeline to aggregate the messages into an envelope and then it sends the envelope with all responses back to the sender.
The messages
There are four different messages included in this implementation, and each has its own schema. One schema is for the main request which is received by the broker, and one for the main response which is sent back to the sender. There is also one schema for the recipient request which is sent to the target systems and one for the recipient response that the target systems send back to the broker.
The parent orchestration
In this case, the message that BizTalk receives contains one record for a main receiver and one or many records for copy destinations. The message is received by a static receive port and a receive shape activates the orchestration. The orchestration's Transaction Type is set to be Long Running and the Timeout is set to be one day.
Count the receiver destinations
The first thing to do is to count the copy destinations. This is done in an expression shape. The total number is stored in a variable named intTotalRecipients
.
intTotalRecipients = System.Convert.ToInt32(xpath(msgMainRequestIN,
"count(/*[local-name()='Request' and namespace-uri()='http://Stm.MessageBroker" +
".Schemas.MainRequest']/*[local-name()='CopyDest' and " +
"namespace-uri()='http://Stm.MessageBroker.Schemas.MainRequest'])"));
intRecipientNumber = 0;
Construct an individual message for each destination
The individual messages are created in a loop which runs as long as the variable intRecipientNumber
is less than or equal to the total number of recipients. The variable intRecipientNumber
is used to hold the current recipient and it is increased by one at the end of the loop. First, a message is created for the main destination. This is done in a construct shape which simply maps the received main request to the individual recipient request if intRecipientNumber
is equal to 0.
For each copy destination, a helper message is first constructed. This message is based on a simple schema with only two fields, and it contains the value of the current recipient and the value of intRecipientNumber
. (The other field is not used.) Both are distinguished fields. The message assignment expression looks like this:
xmlDocSplitHelper.LoadXml("<splitmainrequesthelper " +
"xmlns:ns0="http:
"<recipientnumber />10</recipientnumber /><recipientstotal />" +
"10</recipientstotal /></splitmainrequesthelper />");
msgSplitMainRequestHelper = xmlDocSplitHelper;
msgSplitMainRequestHelper.RecipientNumber = intRecipientNumber;
This message is, together with the main request, used as input in the mapping to the recipient request in the construct shape where the recipient request is constructed. (A map with two input schemas can be created when choosing to create a new map in a transform shape.) In this map, Index functoids are used to get the values from the correct copy destination record. Input to the Index functoids are the value from the helper message (the current value of intRecipientNumber
) and the value from the copy destination record (copy destination ID).
At this point, the individual request is correctly constructed:
Start the child orchestration and wait for responses
A self-correlating binding port is used to receive the responses from the child orchestration. The port identifier is ReceiveFromChildPort
and the port type is FromChildPortType
. This port and the recipient request that is constructed above are used as parameters when starting the child orchestration. The orchestration engine then generates a correlation token on the message that is particular to the orchestration instance. This provides the capability of getting responses back to the parent orchestration instance without using a correlation set.
The variable intRecipientNumber
is set to 0 and a new loop is used to receive responses through the port ReceiveFromChildPort
. The intRecipientNumber
is increased at the end of the loop and the loop runs until intRecipientNumber
is equal to intTotalRecipients
and all the responses are received.
Aggregate the responses and send a response back to the sender
The responses that are received are added to a variable MessagesToAggregate
of type SendPipelineInputMessages
in the expression shape in the loop above.
MessagesToAggregate.Add(msgRecipientResponse);
intRecipientNumber = intRecipientNumber + 1;
When the loop is finished, this variable contains all the responses. Then a new message AggregatedMessage
of type XmlDocument
is constructed. This is done by calling a send pipeline to aggregate the responses. This send pipeline contains an XMLassembler
where the main response schema is specified as an envelope schema and the recipient response schema is specified as a document schema. To make this work, it is important that the envelope schema contains an Any Element to include the recipient responses. The expression in the construct shape to construct the AggregatedMessage
and call the send pipeline looks like this:
AggregatedMessage = null;
Microsoft.XLANGs.Pipeline.XLANGPipelineManager.ExecuteSendPipeline(
typeof(Stm.MessageBroker.Pipelines.XMLAggregate),MessagesToAggregate,AggregatedMessage);
The AggregatedMessage
is then sent back to the sender.
The child orchestration
The child orchestration has two orchestration parameters: a message parameter which is the recipient request message, and a port parameter which has the same port type as the self-correlating binding port in the parent orchestration, FromChildPortType
.
Get information about the receiver and configure the dynamic send port
All information about the different receivers is stored in a database. In this example, there is a table named Partners that contains the fields PartnerID, PartnerName, and TransportType. Then there is a table for each possible transport type. A FILEInfo table contains all the necessary information for configuring a dynamic FILE port for the partners with the transport type FILE. A similar table also exists for the partners with transport type SMTP. An external assembly helps to get this information from the database. This assembly provides a Transport
object, and in the orchestration, there is a variable named objTransport
of this type. This assembly is not a part of the broker, so no explanation will be given in this article. However, the source code is included in the download. Mind that this assembly is just a simple example of how to get this data.
The first expression shape gets the receiver's ID from the distinguished field and initializes the objTransport
object.
strPartnerId = msgRecipientRequest.Receiver.Id;
objTransport.InitializeByPartnerId(strPartnerId);
When calling the InitializeByPartnerId
method, the external assembly looks up in the Partners table to find the correct transport type. It then looks up in the correct table and fills the objTransport
properties with information. The objTransport
object contains properties for all transport types.
Based on the objTransport.TransportType
property, a new recipient request message is constructed and the dynamic send port is configured. In the case of SMTP, the message assignment shape in the construct shape looks like this:
SndRequestTargetPort(Microsoft.XLANGs.BaseTypes.Address) = objTransport.Address;
SndRequestTargetPort(Microsoft.XLANGs.BaseTypes.TransportType) = objTransport.TransportType;
msgOUT = msgRecipientRequest;
msgOUT(*) = msgRecipientRequest(*);
msgOUT(SMTP.SMTPHost) = objTransport.SMTPHost;
msgOUT(SMTP.SMTPAuthenticate) = objTransport.SMTPAuthenticate;
msgOUT(SMTP.EmailBodyTextCharset) = objTransport.EmailBodyTextCharset;
msgOUT(SMTP.EmailBodyText) = "Message from BizTalk";
msgOUT(SMTP.MessagePartsAttachments) = 1;
msgOUT(SMTP.Username) = objTransport.Username;
msgOUT(SMTP.Password) = objTransport.Password;
msgOUT(SMTP.From) = "stmbiztalk@gmail.com";
msgOUT(SMTP.Subject) = "Test from biztalk";
The constructed message is then sent through the dynamic send port to the target system.
Receive the response from the target and send it to the parent orchestration
The send shape that sends the message to the target initializes a correlation set based on the MsgId in the recipient request and the OriginalMsgId field in the recipient response. These are promoted fields. The recipient response needs to contain the original recipient request's MsgId in order to correlate. (This MsgId is made unique for each recipient in the mapping from the main request to recipient request.) A receive shape receives the response and follows up this correlation set.
A send shape sends the response back to the parent orchestration through a send port with Port Type FromChildPortType
. Note that this is the same port type that was defined and used to receive responses in the parent orchestration and also used as port parameter in the child orchestration.
General exception handler
A general exception handler is added in order to send a constructed response back to the parent orchestration if an error occurs or if no responses are received within the timeout limit. Catching exceptions can be done in several ways. In this case, a simple response with some error information is created and sent to the parent orchestration.
Test and installation
All necessary source code to test this implementation is included in a solution in the download. The database is not included, but it is easy to see how the database is designed in the source code to the external .NET assembly.
The .NET assembly needs to be built and installed in GAC. All other projects in the solution need to be deployed to BizTalk.