Introduction
Today I want to talk to you about sending Acknowledgements from BizTalk which can be achieved with and without using Orchestration.
Background
Whenever we are using the Request Response Receive adapter we need to send acknowledgements to the source application on receiving the message in BizTalk. The scenarios are listed below:
- 2 way WCF-SAP Adapter
- 2 way MLLP Adapter
- 2 way WCF SQL Adapter
In some cases the messages just needs an acknowledgement to ensure the message has reached BizTalk and it is time to send the next message and in a few other cases all the messages being sent are waiting for an acknowledgement to ensure the batch of messages in the queue can be deleted.
In a few of the cases the acknowledgements are expected to the static response while in other cases they need some data, based on the received message, to be plugged in the response. There are many other scenarios but just wanted to concentrate on a very few ones here.
Using the code
How to create an Acknowledgement and send it?
With Orchestration:
- Receive Message from Request-Response port with Port bindings: ‘Specify later’
- Create the response (irrespective of whether the response is static or based on the request message) in the Construct Message Shape
- Send Response message through Request-Response port as depicted in the figure below
This is the simplest approach but the overhead in this approach uses the XLang engine for acknowledgement.
With pipeline:
- The code for the Pipeline component is responsible for promoting the properties required for Ack and also to send the Ack as below:
namespace PipelineComponents.Ack
{
using System;
using System.IO;
using System.Text;
using System.Drawing;
using System.Resources;
using System.Reflection;
using System.Diagnostics;
using System.Collections;
using System.ComponentModel;
using Microsoft.BizTalk.Message.Interop;
using Microsoft.BizTalk.Component.Interop;
using Microsoft.BizTalk.Component;
using Microsoft.BizTalk.Messaging;
using Microsoft.BizTalk.Streaming;
[ComponentCategory(CategoryTypes.CATID_PipelineComponent)]
[System.Runtime.InteropServices.Guid("bbcf1287-f4fb-41f8-97b2-b329e5153658")]
[ComponentCategory(CategoryTypes.CATID_Any)]
public class Ack : Microsoft.BizTalk.Component.Interop.IComponent,
IBaseComponent, IPersistPropertyBag, IComponentUI
{
private System.Resources.ResourceManager resourceManager =
new System.Resources.ResourceManager(
"PipelineComponents.Ack.Ack", Assembly.GetExecutingAssembly());
private bool _IsEnabled;
public bool IsEnabled
{
get
{
return _IsEnabled;
}
set
{
_IsEnabled = value;
}
}
private bool _IsStaticResponse;
public bool IsStaticResponse
{
get
{
return _IsStaticResponse;
}
set
{
_IsStaticResponse = value;
}
}
private bool _IsReceive;
public bool IsReceive
{
get
{
return _IsReceive;
}
set
{
_IsReceive = value;
}
}
private string _StaticResponse;
public string StaticResponse
{
get
{
return _StaticResponse;
}
set
{
_StaticResponse = value;
}
}
#region IBaseComponent members
[Browsable(false)]
public string Name
{
get
{
return "Ack";
}
}
[Browsable(false)]
public string Version
{
get
{
return "1.0";
}
}
[Browsable(false)]
public string Description
{
get
{
return "For Acknowledgment";
}
}
#endregion
#region IPersistPropertyBag members
public void GetClassID(out System.Guid classid)
{
classid = new System.Guid("bbcf1287-f4fb-41f8-97b2-b329e5153658");
}
public void InitNew()
{
}
public virtual void Load(Microsoft.BizTalk.Component.Interop.IPropertyBag pb, int errlog)
{
object val = null;
val = this.ReadPropertyBag(pb, "IsEnabled");
if ((val != null))
{
this._IsEnabled = ((bool)(val));
}
val = this.ReadPropertyBag(pb, "IsStaticResponse");
if ((val != null))
{
this._IsStaticResponse = ((bool)(val));
}
val = this.ReadPropertyBag(pb, "IsReceive");
if ((val != null))
{
this._IsReceive = ((bool)(val));
}
val = this.ReadPropertyBag(pb, "StaticResponse");
if ((val != null))
{
this._StaticResponse = ((string)(val));
}
}
public virtual void Save(Microsoft.BizTalk.Component.Interop.IPropertyBag pb,
bool fClearDirty, bool fSaveAllProperties)
{
this.WritePropertyBag(pb, "IsEnabled", this.IsEnabled);
this.WritePropertyBag(pb, "IsStaticResponse", this.IsStaticResponse);
this.WritePropertyBag(pb, "IsReceive", this.IsReceive);
this.WritePropertyBag(pb, "StaticResponse", this.StaticResponse);
}
#region utility functionality
private object ReadPropertyBag(Microsoft.BizTalk.Component.Interop.IPropertyBag pb, string propName)
{
object val = null;
try
{
pb.Read(propName, out val, 0);
}
catch (System.ArgumentException )
{
return val;
}
catch (System.Exception e)
{
throw new System.ApplicationException(e.Message);
}
return val;
}
private void WritePropertyBag(Microsoft.BizTalk.Component.Interop.IPropertyBag pb, string propName, object val)
{
try
{
pb.Write(propName, ref val);
}
catch (System.Exception e)
{
throw new System.ApplicationException(e.Message);
}
}
#endregion
#endregion
#region IComponentUI members
[Browsable(false)]
public IntPtr Icon
{
get
{
return ((System.Drawing.Bitmap)(this.resourceManager.GetObject("COMPONENTICON",
System.Globalization.CultureInfo.InvariantCulture))).GetHicon();
}
}
public System.Collections.IEnumerator Validate(object obj)
{
return null;
}
#endregion
#region IComponent members
public Microsoft.BizTalk.Message.Interop.IBaseMessage Execute(
Microsoft.BizTalk.Component.Interop.IPipelineContext pc,
Microsoft.BizTalk.Message.Interop.IBaseMessage inmsg)
{
if (IsEnabled)
{
int bufferSize = 0x280;
int thresholdSize = 0x100000;
IBaseMessageContext messageContext = inmsg.Context;
IBaseMessagePart bodyPart = inmsg.BodyPart;
string msgout = "";
Stream inboundStream = bodyPart.GetOriginalDataStream();
VirtualStream virtualStream = new VirtualStream(bufferSize, thresholdSize);
ReadOnlySeekableStream readOnlySeekableStream =
new ReadOnlySeekableStream(inboundStream, virtualStream, bufferSize);
if (IsReceive)
{
#region ReceiveLogic
string sysproperty_namespace = "<a href="http:
".com/BizTalk/2003/system-properties">http:
"schemas.microsoft.com/BizTalk/2003/system-properties</a>";
try
{
string EpmRRCorrelationToken = String.Empty;
bool RouteDirectToTP = true;
object objEpmToken = messageContext.Read("EpmRRCorrelationToken", sysproperty_namespace);
if (objEpmToken != null)
EpmRRCorrelationToken = (string)objEpmToken;
else
EpmRRCorrelationToken = System.Guid.NewGuid().ToString();
messageContext.Promote("EpmRRCorrelationToken", sysproperty_namespace, EpmRRCorrelationToken);
messageContext.Promote("RouteDirectToTP", sysproperty_namespace, RouteDirectToTP);
readOnlySeekableStream.Position = 0;
bodyPart.Data = readOnlySeekableStream;
readOnlySeekableStream.Position = 0;
bodyPart.Data = readOnlySeekableStream;
}
catch (Exception ex)
{
throw (ex);
}
#endregion
}
else
{
#region SendLogic
if (IsStaticResponse)
msgout = StaticResponse;
else
{
msgout = StaticResponse;
}
#endregion
}
VirtualStream outStream = new VirtualStream();
StreamWriter sw = new StreamWriter(outStream, Encoding.Default);
sw.Write(msgout);
sw.Flush();
outStream.Seek(0, SeekOrigin.Begin);
inmsg.BodyPart.Data = outStream;
pc.ResourceTracker.AddResource(outStream);
}
return inmsg;
}
#endregion
}
}
- Build and Deploy this Pipeline Component
- Add this component in any stage of the pipeline
- The Configuration for the Receive pipeline will look something like this
The Configuration for the Send pipeline will look something like this:
How this works?
- If you look at the subscription you will observe that the Request-Response Receive location has a subscription for the response message and the following screenshot depicts the details of the subscription.
- Hence, promote the following two properties of the Request message in the Receive Pipeline of the Request-Response Receive Location
http://schemas.microsoft.com/BizTalk/2003/system-properties.EpmRRCorrelationToken
and
http://schemas.microsoft.com/BizTalk/2003/system-properties.RouteDirectToTP
- The moment the Request message hits the message box the request message will be picked up by the Subscription waiting on the response message.
- Now if the Acknowledgement is expected to be static then hardcode the Message in the Send Pipeline else write the business logic for creating the response message with the Pipeline component.
For static response, this will work amazing with no performance overhead and in case you want to create the acknowledge based on the request message or other parameters then add the logic for creating the acknowledgement in the section where you will find the below comment in the above code:
Happy coding!!!
Points of Interest
Acknowledgements without Orchestration is possible and is possible without any overhead and is very performance friendly.