BizTalk Server 2009 : Implementing Dynamic Parallel Orchestrations

Microsoft BizTalk Server orchestrations allow parallel business execution branches, using the native Parallel Actions shape. However, the number of branches is static: to add an execution branch, you need to modify and recompile an orchestration.

1. When to Use Them

The behavior for Parallel Actions shapes doesn’t fit in scenarios where you know only at runtime the number of execution branches that you can spawn. An example is the travel agent service scenario described at www.w3.org/TR/ws-arch-scenarios/, where a travel agent requests in parallel a list of flights for each airline included in a customer list. This sample can be generalized to scenarios where a client application sends a request to a broker that splits it into individual requests for similar target systems; then the broker collects the responses from the target systems and aggregates them into a single response for the client (see Figure 1).

Figure 1. Sample broker implementation

 

The Recipient List pattern is explained by the Enterprise Integration Patterns site as the following:

A Content-Based Router allows us to route a message to the correct system based on message content. This process is transparent to the original sender in the sense that the originator simply sends the message to a channel, where the router picks it up and takes care of everything.

In some cases, though, we may want to specify one or more recipients for the message. A common analogy are [sic] the recipient lists implemented in most e-mail systems. For each e-mail message, the sender can specify a list of recipients. The mail system then ensures transport of the message content to each recipient. An example from the domain of enterprise integration would be a situation where a function can be performed by one or more providers. For example, we may have a contract with multiple credit agencies to assess the credit worthiness of our customers. When a small order comes in we may simply route the credit request message to one credit agency. If a customer places a large order, we may want to route the credit request message to multiple agencies and compare the results before making a decision. In this case, the list of recipients depends on the dollar value of the order.

In another situation, we may want to route an order message to a select list of suppliers to obtain a quote for the requested item. Rather than sending the request to all vendors, we may want to control which vendors receive the request, possibly based on user preferences.

How do we route a message to a list of dynamically specified recipients?



Define a channel for each recipient. Then use a Recipient List to inspect an incoming message, determine the list of desired recipients, and forward the message to all channels associated with the recipients in the list.

The logic embedded in a Recipient List can be pictured as two separate parts even though the implementation is often coupled together. The first part computes a list of recipients. The second part simply traverses the list and sends a copy of the received message to each recipient. Just like a Content-Based Router, the Recipient List usually does not modify the message contents.[]

[] www.enterpriseintegrationpatterns.com/RecipientList.html

This section describes a dynamic parallel implementation of this pattern with a BizTalk orchestration.

An alternative approach would have been using the Publish-Subscribe and Message Filter patterns. We don’t describe here this alternative approach, because this implementation could be more resource consuming in terms of database queries to resolve the filter conditions, and more error prone while setting filter conditions on the channels.

2. Broker Implementation Overview

Our implementation of the broker requires using two different orchestrations.A parent orchestration builds the list of recipients, based on the received document. The parent orchestration uses the Start Orchestration shape to launch a child orchestration for each recipient. The child orchestration executes the actual flow of messages with the recipient. We assume that all the recipients share a common workflow model and schema documents; otherwise you wouldn’t need such a dynamic invocation model, because a manual activity would be needed to introduce each additional recipient! The child orchestration makes use of dynamic port binding to send messages to each different recipient.

The parent orchestration collects the results returned by each child and builds an aggregated response document. The parent orchestration makes use of a self-correlating binding port to receive the responses from the started child orchestrations.

In Exercise 1, we concentrate on the general design and on the orchestration mechanisms involved in the dynamic parallelism implementation; we don’t give a complete implementation sample including schemas, maps, ports, and helper Microsoft .NET Framework objects. A working knowledge of the basic orchestration development tasks is required.

EXERCISE 1: CREATING THE IMPLEMENTATION

Create the Parent Orchestration

The following are the steps required to create the parent orchestration as shown in Figure 2 for the solution within Visual Studio

Figure 2. Parent orchestration main blocks


  1. Define the schemas for the Customer Request, Customer Response, Recipient Request, and Recipient Response.

  2. Promote a property in the Recipient Request schema that can be used to build the dynamic address of the recipient.

  3. Define one message for each of the mentioned schemas, that is, a CustomerRequest, a CustomerResponse, a RecipientRequest, and a RecipientResponse message.

  4. Drag a Receive shape to receive a CustomerRequest message from a client application.

  5. Define variables to control the two loops of the parent orchestration: the SpawnChild loop and the CollectResponses loop.

  6. Drag an Expression shape that you use to calculate the recipient list from the CustomerRequest message and initialize the SpawnChild loop control variable. The customer request message must contain the number of messages to spawn. The SpawnChild variable will contain this number.

  7. Drag a Loop shape for the SpawnChild loop and define the Boolean looping control expression.

  8. Drag a Loop shape for the CollectResponses loop and define the Boolean looping control expression.

  9. Drag a Send shape to return the CustomerResponse message to the client application.

  10. Drag two ports to be used as PortFromCustomer and PortToCustomer; their actual properties depend on the particular scenario and are not relevant to the discussion.

  11. Drag a port to be used as PortFromChild. In the Port Configuration Wizard, define the following properties:

    1. In the Select a Port Type tab, choose to create a new port type named TypePortFromChild with the communication pattern One-Way.

    2. In the Port Binding tab, choose “I’ll always be receiving messages on this port” as port direction of communication.

    3. Choose Direct as port binding and then Self Correlating. []

Next, create the SpawnChild loop whose steps are defined here and shown in Figure 3.

Figure 3. Parent orchestration loops


  1. Drag a Message Assignment shape inside the SpawnChildLoop shape; in the enclosing Construct Message shape, define RecipientRequest as the message to be constructed.

  2. In the Message Assignment expression, you will build the RecipientRequest message from the CustomerRequest message according to the current loop cycle; you will probably want to use a helper .NET component to build the message.[] You will also update an orchestration variable with the number of spawned children.

  3. Drag a Start Orchestration shape below the ConstructMessage shape. Leave it unconfigured for the moment.

Once the preceding steps are completed, the final step is to create the wait responses loop as defined here:

  1. Drag a Receive shape inside the WaitResponsesLoop shape. Define RecipientResponse as the message that will be received by this shape.

  2. Drag a Message Assignment shape below the Receive shape; in the Construct Message shape, define CustomerResponse as the message to be constructed.

  3. In the Message Assignment expression, you will build[] the CustomerResponse message aggregating the RecipientResponse message received in the current loop cycle. You will also update an orchestration variable with the number of received responses that will have to match the number of spawned children to exit the loop.

Create the Child Orchestration

Once the parent orchestration is created, the next step is to create the child orchestration. The steps for this are defined here and the orchestration is shown in Figure 4.

Figure 4. Child orchestration


You will reuse the Recipient Request and Recipient Response schemas defined before.

  1. In the Orchestration View section, right-click Orchestration Parameter and choose New Port Parameter; assign to this port parameter the port type TypePortFromChild, defined previously in the parent orchestration; assign to this port parameter the identifier PortToParent; change the communication direction of this port parameter to Send.

  2. Right-click Orchestration Parameter again and choose New Message Parameter; assign to this message parameter the message type RecipientRequest and the identifier MsgFromParent.

  3. Define one TargetResponse message that uses the Recipient Response schema.

  4. Drag a port to be used as PortToTarget. In the Port Configuration Wizard, define the following properties:

    1. In the Select a Port Type tab, choose to create a new port type named TypePortToTarget with the communication pattern Request-Response.

    2. In the Port Binding tab, choose “I’ll be sending a request and receiving a response” as port direction of communication.

    3. Choose Dynamic as port binding and then choose a receive pipeline and a send pipeline suitable for your Recipient Request and Recipient Response schemas.

  5. Drag a Send shape onto the Orchestration Designer surface and name it SendToTarget; configure this shape to send the MsgFromParent message to the PortToTarget port.

  6. Drag a Receive shape onto the Orchestration Designer surface that you will name ReceiveFromTarget; configure this shape to receive the TargetResponse message from the PortToTarget port.

  7. Drag a Send shape onto the Orchestration Designer surface that you will name SendToParent; configure this shape to send the TargetResponse message to the PortToParent port.

  8. Drag an Expression shape at the top of the orchestration that you will name BuildTargetAddress; use this expression to assign the URI to the dynamic port PortToTarget[] based on the value of a promoted property[] in the MsgFromParent message.

Bind the Parent Orchestration to the Child Orchestration

To complete the exercise, you need to add the following additional configuration to the parent orchestration:

  1. Double-click the Start Orchestration shape to open its configuration box. In the Select the orchestration you want to start combo box, select the child orchestration. The Orchestration Parameters grid is automatically updated with the right matches between the variables in scope of the parent orchestration and the parameter name of the child orchestration: the PortFromChild variable is matched with the PortToParent parameter; the RecipientRequest variable is matched with the MsgFromParent parameter.

  2. In the Properties pane of the orchestration, change the transaction type to Long Running and set a value for the timeout; otherwise, in case a child orchestration is terminated abnormally, the parent orchestration would wait indefinitely for a response.

 

[] Alternatively you could use a BizTalk map to create the new message—either approach will work.

[] This can be accomplished a number of ways. A potential choice would be to use an aggregating pipeline, collect all the messages to be aggregated, and call the pipeline with the array of messages. An implementation of an aggregating pipeline is given in the BizTalk SDK under the Program Files\Microsoft BizTalk Server 2009\SDK\Pipelines directory. Another choice would be to create a .NET component to accept the messages as they are received and aggregate them together.

[] The code for the Expression shape will look something like PortToTarget(Microsoft.XLANGs.Base-Types.Address) = “Http://wsOrders/Interface.asmx”.

[] This implementation requires that the address of where the messages are to be sent is known ahead of time. In the original message that was received by the parent orchestration, an element must exist that contains this address. This value must be promoted into the context via either the schema definition or a distinguished field.

The previous exercise shows you how you can use orchestrations to solve a real-world problem. Let’s look at another issue that often arises when processing messages—dealing with order.