ESB itinerary steps can be specified as Messaging or Orchestration services. Using Messaging steps you can construct an itinerary which does not use any custom send or receive ports or locations – a message can be transformed and sent within the itinerary on-ramp processing.
In ESB Guidance, itinerary on-ramps are implemented by two receive ports – OnRamp.Itinerary and OnRamp.Itinerary.Response (for request and request-response messages), each having two receive locations (for SOAP and WCF adapters). Receive Locations use the ItineraryReceiveXml pipeline which has two custom components. In the Decode stage Microsoft.Practices.ESB.Itinerary.PipelineComponents.Itinerary simply promotes data from the incoming message header to context properties.
In the Disassemble stage PipelineComponents.DispatcherDisassemble does work on any itinerary steps which have a service type of Messaging. The itinerary can specify transforms and/or routings that can be resolved and executed within the pipeline component, without the requirement for maps to be explicitly exposed in a receive port or an orchestration, or for send ports to be configured.
DispatcherDisassemble.Disassemble() looks for services of type Messaging with name Microsoft.Practices.ESB.Services.Transform. For any it finds, it executes the specified map on the message and advances the itinerary to the next step – which could be another messaging transform. Messaging transforms are executed in a loop until there are no more itinerary steps, or the next step is not a messaging transform.
DispatcherDisassemble.GetNext() does work if the current step of the itinerary is a Messaging service named Microsoft.Practices.ESB.Services.Routing. For each resolver specified for the service it resolves the endpoint, sends a copy of the incoming message to the message box and advances the itinerary. When the subscriber receives the message, its CurrentStep has been advanced beyond the Routing step.
If messaging steps are used exclusively in a service (without any orchestrations), the step after Routing must be defined such that the messages published by the Routing step have a subscriber. In the ESB Samples the dynamic send port DynamicOneWayResolution subscribes to messages with an itinerary service name of DynamicTest – in this usage, the port works as a passthrough and publishes the message already configured by the Routing step.
Here's a sample itinerary which works just on messaging steps:
<?xml version="1.0" encoding="utf-8"?>
<Itinerary xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" uuid="" beginTime="" completeTime="" state="Pending" isRequestResponse="false" xmlns="http://schemas.microsoft.biztalk.practices.esb.com/itinerary">
<ServiceInstance uuid="" name="Microsoft.Practices.ESB.Services.Transform" type="Messaging" state="Pending" position="0" isRequestResponse="false" xmlns="" />
<Services xmlns="">
<Service uuid="" beginTime="" completeTime="" name="Microsoft.Practices.ESB.Services.Transform" type="Messaging" state="Pending" isRequestResponse="false" position="0" serviceInstanceId="" />
</Services>
<Services xmlns="">
<Service uuid="" beginTime="" completeTime="" name="Microsoft.Practices.ESB.Services.Routing" type="Messaging" state="Pending" isRequestResponse="false" position="1" serviceInstanceId="" />
</Services>
<Services xmlns="">
<Service uuid="" beginTime="" completeTime="" name="DynamicTest" type="Messaging" state="Pending" isRequestResponse="false" position="2" serviceInstanceId="" />
</Services>
<ResolverGroups xmlns="">
<Resolvers serviceId="Microsoft.Practices.ESB.Services.Transform0"><![CDATA[STATIC:\\TransportLocation=;TargetNamespace=;MessageExchangePattern=;EndpointConfig=;JaxRpcResponse=;TransportType=;Action=;TransformType=ESBSimpleSamples.BizTalk.Maps.GetASCIICode_ExtractFirstChar,ESBSimpleSamples.BizTalk, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a9e52fd7be56a687;]]></Resolvers>
<Resolvers serviceId="Microsoft.Practices.ESB.Services.Routing1"><![CDATA[STATIC:\\TransportLocation=FILE://C:\dt_es_%MessageID%.xml;TargetNamespace=;MessageExchangePattern=;EndpointConfig=;JaxRpcResponse=;TransportType=;Action=;TransformType=;]]><![CDATA[STATIC:\\TransportLocation=FILE://C:\dt2_es_%MessageID%.xml;TargetNamespace=;MessageExchangePattern=;EndpointConfig=;JaxRpcResponse=;TransportType=;Action=;TransformType=;]]></Resolvers>
<Resolvers serviceId="DynamicTest2" />
</ResolverGroups>
</Itinerary>
When a message with this itinerary header hits the on-ramp, the pipeline components do all the work. The first service (position 0 – note, this is also the ServiceInstance position, indicating that the current step of the itinerary is 0) specifies a Messaging transform. It has one resolver, in this case a STATIC reference. For a transform this requires the assembly-qualified map name in the TranformType field:
TransformType=ESBSimpleSamples.BizTalk.Maps.GetASCIICode_ExtractFirstChar,ESBSimpleSamples.BizTalk, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a9e52fd7be56a687;
The Disassemble stage executes the map and publishes the result as a new message, with the same itinerary header but advanced to the next step (ServiceInstance position=1). Service 1 is a routing step with two resolvers. Here they're STATIC, hard-coded FILE drop locations. The GetNext calls result in two messages being published, with their itinerary headers set to position 2, and with OutboundTransportLocation specified from the resolved location.
These messages match the filter condition of the DynamicResolutionOneWay port, so it picks them up and sends them out. We've received, transformed and sent out our message to two recipients, with a just two custom BizTalk artifact – the message schema and the map.
Note: it's worth mentioning where this processing takes place. The on-ramps live in the isolated host, so in the sample above the first two messages (transform and routing) are processed by w3wp.exe. The third and fourth messages (heading for the passthrough port) are picked up by the in-process host, BTSNTSvc.exe.