Creating custom itinerary services

In this section, we will see how to create our own custom itinerary services, both on their messaging and orchestration flavors.

Custom messaging itinerary service

As we mentioned before, the messaging services are implemented as specific classes that will be invoked by the ESB Dispatcher pipeline components.

In order to implement one of those, we need to create a class that implements the IMessagingService interface. This interface defines two properties (Name and SupportsDisassemble) and two methods (Execute and ShouldAdvanceStep) that have to be implemented:

  • Name: It is the name of the service that will be used to reference the service in the itinerary. It will be the one that will identify the service once registered in the ESB configuration file.
  • SupportsDisassemble: It specifies if the component supports disassemble and the execution of multiple resolvers.
  • Execute: It is the method where the processing of the message will take place.
  • ShouldAdvanceStep: It instructs the Dispatcher to advance the itinerary to the next step or not once the component has finalized its processing.

We will now show the implementation of the Execute method for an itinerary service that will perform some compression logic on the body of the message if the resolver used by the service says so:

publicMicrosoft.BizTalk.Message.Interop.IBaseMessage Execute(Microsoft.BizTalk.Component.Interop.IPipelineContext context, Microsoft.BizTalk.Message.Interop.IBaseMessage msg, string resolverString, IItineraryStep step)
        {
if (context == null)
thrownewArgumentNullException("context");
if (msg == null)
thrownewArgumentNullException("msg");
if (string.IsNullOrEmpty(resolverString))
thrownewArgumentException(Properties.Resources.ArgumentStringRequired, "resolverString");

ResolverInfo info = ResolverMgr.GetResolverInfo(ResolutionType.Endpoint, resolverString);
if (!info.Success)
thrownewException(Properties.Resources.ResolverStringInvalid, resolverString);


//Resolve if the message is meant to be compressed  
Dictionary<string, string> resolverDictionary = ResolverMgr.Resolve(info, msg, context);


if (!string.IsNullOrEmpty(resolverDictionary["Acme.RequiresCompression"]))
            {

IBaseMessagePart bodyPart = msg.BodyPart;
string tmpString = "";
if (bodyPart != null)
                {
try
                    {
                        System.IO.StreamReader sr = newSystem.IO.StreamReader(bodyPart.Data);

tmpString = sr.ReadToEnd();

tmpString = Acme.CompressContent(tmpString);

                        System.IO.MemoryStream strm = newSystem.IO.MemoryStream(ASCIIEncoding.Default.GetBytes(tmpString));
                        strm.Position = 0;
                        bodyPart.Data = strm;
context.ResourceTracker.AddResource(strm);
                    }
catch (System.Exception ex)
                    {
throw ex;

                    }
                }
            }
return msg;
        }

Our resolver could be any kind of resolver that returns the RequiresCompression items as part of its resolution result. For example, it could be a business rule that depending on certain properties of the message decides if the message is to be compressed or not.

Once our custom itinerary service is compiled, we will need to deploy it to our BizTalk environment:

  • We will deploy it into the GAC of each of the BizTalk servers that compose our environment.
  • In the ESB.Config file (located in the ESB Toolkit installation path), we will add a new itineraryService entry on the ItineraryServices section, being its attributes:
    • ID: A guide for the service.
    • Name: The name returned by the Name property of the component implemented.
    • Type: The fully qualified name of the class that implements the service.
    • Scope: It must be Messaging, as this is a messaging service.
    • Stage: The stages of an itinerary where the service can run. It can be OnRampReceive, OnRampSend, OffRampSend, OffRampReceive, AllSend, AllReceive, or All.

The following screenshot is an example of the ESB.Config file:

Custom messaging itinerary service

Custom orchestration itinerary service

Now we will create the same compression service that we created in the previous section, but in the orchestration itinerary service flavor.

The message processing lifecycle of this type of services is pretty much the same as in the messaging services (receive, process, mark step as complete, and send) but with slight differences. Let's start designing our orchestration, and we will highlight those differences as we move on.

Receiving the message

In the messaging services, the message is received by the Dispatcher pipeline component as the message flows through the pipeline, but the orchestration services receive them from the BizTalk message box, and so it'll need to subscribe to the messages they are meant to process. The orchestration will subscribe to those messages that match the following context properties filter:

  • The Microsoft.Practices.ESB.Itinerary.Schemas.ServiceName property has a value that is the name of the service implemented by the orchestration.
  • The Microsoft.Practices.ESB.Itinerary.Schemas.ServiceState property value is Pending.
  • The Microsoft.Practices.ESB.Itinerary.Schemas.ServiceType property value is Orchestration.

We will drop our port shape into the orchestration design surface. The port name, port type name, and operation names should follow the naming conventions established in your solution for this type of artifact (and should be consistent across the different itinerary services you implement). We will use a direct port binding, as we will receive the messages by means of the specific filters just mentioned.

The port can be either one way or request/response (depending on our service design). The only differences if we use a request/response are:

  • Our itinerary service will need to be defined as request/response in our itineraries (and so the itinerary should be request/response as well).
  • When we get to the point to deliver the response message, we will need to initialize one extra correlation set to initialize the context properties that will make the message to be routed back directly to the request/response port that initially received the message.

We will now create the inbound and outbound messages for our orchestration. The type of messages sent and received by our orchestration itinerary services will be always of System.Xml.XmlDocument type.

Once we have the port and the messages created, we will add the corresponding receive shape. The receive shape will receive the inbound message that we just created. Finally, we will set up the receive shape filter as described previously, and connect it to the receive operation of the receive port.

Note

Obviously, the Activate property of the receive shape will need to be set to true.

Receiving the message

Processing the message

The processing of the message within the orchestration has three principal steps: retrieve the current itinerary state, executing the actual service process, and advancing the itinerary to the next step.

Retrieving itinerary state

This process will give us access to the current state of the itinerary (stored in the message context) the message is flowing through and to the current itinerary step itself.

The most important information that we will be able to retrieve from the itinerary step are the resolvers that are configured for that step in the itinerary, in order to execute them (if necessary) for our later processing.

Firstly, we will create the variables that we need to store the information to be retrieved from the itinerary:

  • itinerary: It is the variable of Microsoft.Practices.ESB.Itinerary.SerializableItineraryWrapper type that will be assigned the itinerary metadata.
  • itineraryStep: It is the variable of Microsoft.Practices.ESB.Itinerary.SerializableItineraryStepWrapper type that will be assigned the current itinerary step information.
  • resolverDictionary: It is the variable of Microsoft.Practices.ESB.Resolver.ResolverDictionary type that will store the results from the resolution.

Once we have our variables defined, we will drop an expression shape into the design surface, as you can see in the following image. In that expression shape, we will retrieve the itinerary metadata, the resolvers for the current itinerary step, and then, we will execute the resolution.

//Instantiate the itinerary and itineraryStep classes that will be used to hold the itinerary the message is going through and the current itinerary step
itinerary = new Microsoft.Practices.ESB.Itinerary.SerializableItineraryWrapper();
itineraryStep = new Microsoft.Practices.ESB.Itinerary.SerializableItineraryStepWrapper();

itinerary.Itinerary = Microsoft.Practices.ESB.Itinerary.ItineraryOMFactory.Create(InboundMessage);
itineraryStep.ItineraryStep = itinerary.Itinerary.GetItineraryStep(InboundMessage);

//Execute the resolver this service is meant to use to resolve the information that might drive the execution of the service
resolverDictionary = Microsoft.Practices.ESB.Resolver.ResolverMgr.Resolve(OutboundMessage, itineraryStep.ItineraryStep.ResolverCollection[0]);

System.Diagnostics.Trace.WriteLine("ServiceName: " + itineraryStep.ItineraryStep.ServiceName);
System.Diagnostics.Trace.WriteLine("ServiceType: " + System.Convert.ToString(itineraryStep.ItineraryStep.ServiceType));
Retrieving itinerary state

Actual processing

Next, we will add a decision shape. Depending on the resolution result that tells us to execute the compression or not, we will construct the outbound message with the compressed inbound message, or with the unmodified inbound message.

We will use the following sentence in the Yes branch of the decision shape:

!System.String.IsNullOrEmpty(resolverDictionary.Item("Acme.RequiresCompression"))
Actual processing

Completing the itinerary step

In order to let the message be processed by downstream services, we need to mark the current itinerary step as completed. Otherwise, when the message is published back into the message box, our orchestration will continue picking up and processing the message in an infinite loop.

We just need an additional expression shape to execute the corresponding itinerary helper classes that will do the magic.

System.Diagnostics.Trace.WriteLine("        BEGIN - Advance Itinerary");

// Call the Itinerary helper to advance to the next step
itinerary.Itinerary.Advance(OutboundMessage, itineraryStep.ItineraryStep);
itinerary.Itinerary.Write(OutboundMessage);

System.Diagnostics.Trace.WriteLine("        FINISH - Advance Itinerary");
Completing the itinerary step

Sending the message

Finally, we will send the outbound message on its way back to the message box for further processing.

We will create a direct binding send port and the corresponding send shape. Additionally, we will create a correlation set and its corresponding correlation set type, which will be initialized in the send shape to promote the context properties needed for itinerary processing. We will name the correlation set type as itineraryAdvance and the correlation properties will be:

  • Microsoft.Practices.ESB.Itinerary.Schemas.IsRequestResponse
  • Microsoft.Practices.ESB.Itinerary.Schemas.ServiceName
  • Microsoft.Practices.ESB.Itinerary.Schemas.ServiceState
  • Microsoft.Practices.ESB.Itinerary.Schemas.ServiceType

In case our service is a request/response one, we will need an additional correlation set and correlation set type to promote the context properties that will make the message routed to the request/response port that originally received the initial message. The correlation set type name will be itineraryRequestResponse and the correlation properties will be:

  • BTS.CorrelationToken
  • BTS.EpmRRCorrelationToken
  • BTS.IsRequestResponse
  • BTS.ReqRespTransmitPipelineID
  • BTS.RouteDirectToTP
Sending the message

Registering the itinerary service

Before we can use the service in our itineraries, we need to register it on the ESB like we did with the messaging service. The only difference is that the service Scope property value will be Orchestration.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.9.148