Documents
Resources
Learning Center
Upload
Plans & pricing Sign in
Sign Out

transact_adapter

VIEWS: 7 PAGES: 52

									Contents
Developing a Transactional BizTalk Adapter Using the Microsoft Base Adapter Classes . 3
 Transactions in Two-Way Adapter Interactions ............................................................... 4
 Transactions, Messages, and Batches ............................................................................ 6
 Base Adapter Classes ..................................................................................................... 8
 Base Adapter Class Hierarchy ........................................................................................ 9
   Primary Classes ......................................................................................................... 10
   Secondary Classes .................................................................................................... 16
 Batch Processing Class Hierarchy ................................................................................ 20
 Transactional Adapter Sample ...................................................................................... 24
   Receive Side of the Adapter ...................................................................................... 25
   Send Side of the Adapter ........................................................................................... 32
 Additional Adapter Design Issues .................................................................................. 35
   The Message Ordering Problem ................................................................................ 35
     Send-side ordering .................................................................................................. 35
     Receive-side ordering ............................................................................................. 38
   The Poison Message Problem ................................................................................... 40
   Termination Management .......................................................................................... 42
 Application of the Base Adapter Classes ...................................................................... 46
   Receive-Side Functionality ......................................................................................... 46
   Send-Side Functionality ............................................................................................. 47
     Non-transactional adapter....................................................................................... 47
     Transactional adapter ............................................................................................. 47
   Registration ................................................................................................................ 47
 Message Processing Requirements .............................................................................. 48
   Transactional Send Operation ................................................................................... 48
   Non-Transactional Send Operation ............................................................................ 49
   Transactional Receive Operation ............................................................................... 49
   Non-Transactional Receive Operation ....................................................................... 50
 Debugging the Transactional Adapter ........................................................................... 50
 Summary ....................................................................................................................... 52
 See Also ......................................................................Error! Bookmark not defined.52
                                                                                         3




Developing a Transactional BizTalk
Adapter Using the Microsoft Base
Adapter Classes
Writing a Microsoft BizTalk Server 2006 adapter can be a daunting task. A significant part
of the initial difficulty involves understanding the flow of messages into and out of an
adapter. This complexity includes which interfaces are involved, the sequence of method
calls, which components are responsible for implementing which interface methods, and
which functionality is implemented by the adapter or by the BizTalk Messaging Engine.

During the development process you decide which of the following primary approaches to
use in writing your adapter:

   You can use the BizTalk Adapter Framework and its public APIs. The Adapter
     Framework provides administrative interfaces (property page browser), tools (the Add
     Adapter Metadata Wizard), development restrictions, and a runtime environment that
     simplifies development. The Adapter Framework also provides a set of standard
     interfaces to ensure that all design-time configuration for any adapter is confined to
     the BizTalk Server Administration console and BizTalk Explorer in Microsoft Visual
     Studio 2005.
   A simpler choice is to use the BizTalk Server SDK Base Adapter classes. The source
     code ships as part of the BizTalk Server 2006 SDK as a sample project from which to
     derive your adapter. Here most of the primary adapter functionality is implemented
     for you, so you need to write less code than you would if you used the Adapter
     Framework. The tradeoff is that you have to do more initial research during the
     design phase to understand which Base Adapter classes to inherit from, which
     classes to use, and which code implements which functionality. After you understand
     what you need to do and what the Base Adapter classes give you for free, you can
     more efficiently develop your adapter.

Understanding how much functionality is provided for you by the Base Adapter classes
and what you are responsible for implementing is a primary motivation behind this
document. This document's intent is to support adapter developers by providing an
explanation of how to implement different functionalities when using these classes. It
should help you to understand which parts of your adapter the Base Adapter classes
implement on your behalf and which parts you need to write.
                                                                                         4


    Note
    This document contains some advanced adapter concepts. For supporting
    information about basic adapter concepts, see the information under "Developing
    Custom Adapters" in BizTalk Server 2006 Help.

To demonstrate development concepts, we mainly refer to the transactional adapter SDK
sample. Occasionally we also refer to the non-transactional file adapter SDK sample to
contrast or expand upon implementation details. You can find both of these samples in
the SDK\Samples\Adapter Development folder. We recommend that you have the
applicable project open in Visual Studio so that you can follow along with the concepts
discussed here.



Transactions in Two-Way Adapter Interactions
One of the main areas of focus for this document is the fundamental concepts of sending
and receiving messages for one-way adapters (send or receive). The more complex two-
way adapter interactions of Solicit-Response and Request-Response are just variations
of these two basic constructs, but they differ in their use of transactions.

You cannot have transacted Request-Response adapters due to a fundamental limitation
of the BizTalk Server architecture. To comprehend why this is not feasible let's examine
the order of events and transactions that occur in a Request-Response message
exchange involving an orchestration:

1. The request message arrives at the receive adapter from an external requestor.
2. The receive adapter translates the data into a BizTalk Server message and submits it
   to the Messaging Engine.

3. In a transactional operation TXN1, the Messaging Engine submits the message into
   the MessageBox database and commits transaction TXN1.

4. The orchestration (or alternatively a send port) with an active subscription to the
   message type gets the message. It processes the message and creates a response
   message, which it submits to the MessageBox database using another transaction,
   TXN2.

5. The adapter receives the response message from BizTalk Server and sends the
   response message to the original external requestor. After successful transmission of
   the response message, the adapter acknowledges its successful processing of that
   message by executing a Delete of the response message against BizTalk Server.
To get past step 3, the transaction surrounding the insert of the request message into the
MessageBox database must be committed. If this transaction is not committed, then step
                                                                                           5


3 cannot complete because the data is not actually in the MessageBox database until the
transaction commits. If step 4 fails and rolls back TXN2, then TXN1 will not roll back
because it has already been committed. This leaves an orphaned request message in the
database without a corresponding response message. You want either both the request
and response messages to be processed, or none of them to be processed. With
separate transactions in this case you cannot guarantee these processing results. Thus
Request-Response adapters cannot be transactional.
On the send side the Solicit-Response interaction is a subtly different story. Logical ports
used in an orchestration are physically implemented as internal queues inside of BizTalk
Server. A message coming into a receive port goes into an internal queue. The
orchestration receives the message from that queue (receive location), processes it, and
writes it out to another queue (send port) by using a transaction. All of these steps can be
done under one transaction. Unlike the Request-Response interaction, which needs two
separate transactions to complete its processing, the Solicit-Response interaction can do
all its processing with one transaction. This permits two-way transactional
communication. You create the transaction in the adapter, make a request and receive a
response against an external system, and then delete the message from BizTalk Server,
all under one transaction. For example, the adapter could execute a stored procedure on
an external database and then execute the SubmitResponse and Delete operations on
BizTalk Server. All this can be done under the same transaction. SubmitResponse and
Delete should always be called from the same batch so they are done under the same
transaction. This is true whether that transaction originates in the send adapter or
whether BizTalk Server creates it behind the scenes.

The following diagram shows a Request-Response interaction on the receive side and a
Solicit-Response interaction on the send side. It also shows the internal BizTalk Server
queues and the interaction of the receive and send adapters with these queues. There is
always a transaction when a message is submitted to, or received from, a queue.
                                                                                           6




   (RR1) Request side of a Request-Response interaction. The transaction used in
     RR1 is for the receive operation where the BizTalk Server message is placed into the
     inbound queue. This transaction is internal and BizTalk Server creates it
     automatically when the message is submitted to the MessageBox database.

   (SR1) Solicit-Response interaction. The transaction used in SR1 is an explicit
     MSDTC transaction created by the adapter. Explicit MSDTC transactions are needed
     to interact with external systems. This transaction is used to acknowledge the
     message receipt from an internal queue, send the message to an external server for
     processing, accept the response from the external server, and submit the response
     back into an internal BizTalk Server queue, all using the same transaction.

   (RR2) Response side of a Request-Response interaction. The new transaction
     used in the Response side of RR2 is the acknowledgment of the message
     transmitted to the external client as a response. The acknowledgment of a message
     from an internal queue in BizTalk Server means to delete the message. Like the
     transaction used in RR1, this transaction is implicitly created by BizTalk Server.

The use of different transactions in RR1 and RR2 shows that, given the existence of the
queues in the current architecture, the Request-Response interaction can never
meaningfully involve an explicit MSDTC transaction or a single implicit transaction.



Transactions, Messages, and Batches
The use of transactions within a BizTalk Server adapter can be somewhat confusing. The
presence of a transaction allows BizTalk Server to guarantee that a message batch is
delivered once and only once to BizTalk Server. This is a positive attribute in a distributed
message processing environment such as BizTalk Server.
                                                                                            7


When using the term "transactional" adapter specifically we are referring to adapters that
explicitly create their own Microsoft Distributed Transaction Coordinator (MSDTC)
transaction and submit it to BizTalk Server. (Throughout this document a "transaction", or
"explicit transaction", refers to an MSDTC transaction.) An explicit transaction is required
by a transactional adapter when it needs to involve an external system's processing
within the scope of the work done by BizTalk Server. A resource manager is a
transactional-aware entity, such as SQL Server, MQSeries, or Message Queuing (also
known as MSMQ), which speaks the OLETX transaction protocol with MSDTC. The
transaction logically connects a BizTalk Server operation and ancillary operations
executed by an external resource manager into one atomic operation. For example, you
may want to both submit a message to BizTalk Server (receive side of your adapter) and
write a part of it into an MSMQ message and send it (send side of your adapter) to a
transactional MSMQ message queue.
A non-transactional adapter does not explicitly create an MSDTC transaction. Rather it
implicitly leaves it up to BizTalk Server to handle that task.

To better understand explicit vs. implicit transactions we need to look at the notion of a
batch. All messages are submitted to BizTalk Server using a batch. Batches are linked to
transactions in that every batch runs in the context of a transaction inside of BizTalk
Server. It is the origin of this transaction that is a legitimate point of confusion for some
developers. The transaction used is either implicitly created by BizTalk Server, or
explicitly created by the adapter. In the implicit case the non-transactional adapter passes
in a NULL transaction object parameter when submitting the batch and BizTalk Server
creates an internal transaction. This private transaction is not accessible to the adapter. It
is committed or aborted by BizTalk Server based upon the success or failure of all its
contained messages being submitted into the MessageBox database. In the explicit
scenario, the transactional adapter creates and passes in the transaction to BizTalk
Server. The transaction remains accessible to the adapter for additional enlistment with
other MSDTC resource managers. The outcome of the transaction (commit or abort) is
now determined by the adapter and its logic, and not by BizTalk Server. The adapter
merely calls DTCConfirmCommit to inform BizTalk Server of its decision.

An example of a non-transactional adapter is the File adapter because none of its
operations are transactional. In contrast, the SQL adapter's operations are transactional
because it requires access to an external transaction object.

A transaction's role differs depending upon whether it is used in a BizTalk Server send or
receive operation. When sending, a transaction is used to ensure the resource manager
successfully receives the message and can guarantee it has been delivered. For receive
operations it is used when submitting incoming messages into BizTalk Server.
                                                                                           8


For performance purposes it is always better to allow the resource manager (SQL Server
in the case of BizTalk Server message submission) to use its own optimized, internal
transactional processing. The overhead of creating and passing around an explicit
MSDTC transaction is significant. Under load with multiple transactional adapter threads
concurrently talking to SQL Server through explicit MSDTC transactions you will see a
drop in throughput as opposed to using implicit transactions. The "D" in MSDTC stands
for "Distributed," meaning the transaction will be shared or distributed across more than
one transactional resource manager during its lifetime. Thus if you are not distributing the
transaction, and are not using an external resource manager, there is no need for an
explicit MSDTC transaction. To use it in that case would just lower performance and
make the adapter code more complex. If the external system supports the MSDTC
OLETX protocol, you should use transactions and let MSDTC do the work for you.



Base Adapter Classes
The less you are required to know about the low-level BizTalk Server interfaces the
simpler it is to develop and maintain an adapter solution. The Base Adapter modules are
source helper classes created with the sole purpose of making it easier for developers to
code a BizTalk adapter. These classes abstract out and wrap the lower-level adapter
functionality. In some cases the code may not meet your needs exactly and thus you
cannot derive your functionality from them as is. In those situations it may be best to
create your own classes by doing a cut-and-paste operation into your class module and
modifying the code as needed. The transactional adapter sample takes this approach in
certain spots.
The BizTalk Server functionality wrapped by these classes lies in native libraries that are
called by using a .NET interop proxy from managed code. Keep in mind during your
development process that management and cleanup of instance memory is an important
issue to understand in mixed-mode scenarios like this.

The Base Adapter classes were used in the development of the native adapters that ship
with BizTalk Server 2006. With the exception of the SubmitDirect sample, all SDK
samples use the Base Adapter classes. When moving from BizTalk Server 2004 to
BizTalk Server 2006, support was added to this class surrounding transactional receive
and transmit operations. This updated version of the Base Adapter classes removed a
layer of indirection for handling batches. This has resulted in less complex code in fewer
lines of execution within batch processing, both transactional and normal.
                                                                                      9


Base Adapter Class Hierarchy
The following figure shows the class structure surrounding the Base Adapter and its
components. For the sake of this discussion the classes are broken into primary and
secondary categories. The primary classes are those with which you do most of your
main adapter work. The secondary classes are more generic in nature and assist with
auxiliary functionality.
                                                                                       10




Primary Classes
These classes are the primary ones you will use in your adapter project. They pertain
mainly to endpoints, batches, and send or receive functionality. As you can see from the
preceding figure, the classes you use and objects you create for a receive adapter
                                                                                          11


(receiver and receiver endpoint) are different from those for a send adapter (transmitter,
transmitter batch, and transmitter endpoint).

The primary classes are as follows:

   Adapter (Adapter.cs). This is an abstract root class for both send and receive
     adapters. Using the abstract modifier at the class level means it cannot be
     instantiated and can only be used as a base class for subclasses. With the exception
     of batch and design-time classes, most of the Base Adapter classes that relate
     directly to the actual receive and send processing of messages are defined as
     abstract.

     Adapter implements IBTTransport to identify the adapter when BizTalk Server
     requests its information (name, version, CLSID, description, and transport type). It
     implements IBTTransportControl for initialization and termination processing for in-
     process adapters. (For more information, see the "Termination Management" section
     later in this document.) The Adapter class also implements the optional
     IPersistPropertyBag interface with which the adapter receives handler configuration
     data from BizTalk Server during initialization.
     In the sample, both Receiver (receive functionality) and AsyncTransmitter (send
     functionality) derive from Adapter.

   Receiver (Receiver.cs). This is an abstract base class for all receive adapters. It
     derives from Adapter and provides a standard implementation of a receive adapter's
     interfaces. It keeps a hash table of active receive endpoints (the ReceiverEndpoint
     class, which is discussed later) that correspond to receive locations. It implements
     IBTTransportConfig to manage adding, updating, and removing endpoints. It
     implements IDisposable to free native resources when the adapter shuts down.

     When your receive adapter derives from Receiver your class is a singleton object.
     That means no matter how many client threads call an API or method to create an
     instance of your object, the result is one and only one shared instance of the same
     object. All the issues that surround object serialization, such as throughput, blocking,
     and synchronization of shared resources, need to be taken into account in your
     adapter development process. The primary reason for using a singleton architecture
     is that, as an optimization, BizTalk Server allows messages for different endpoint
     locations to be submitted in the same batch. By having a single instance of a receive
     adapter its logic can extract the different endpoints from the messages and ensure
     they are sent optimally and correctly to their destinations. The Receiver class acts as
     a class factory for instances of the ReceiverEndPoint class.
                                                                                         12


     In the sample, the TransactionalReceiver class is the receive adapter. This inherits
     from Receiver, and BizTalk Server loads it at startup time when it instantiates all
     receive adapters.

   AsyncTransmitter (AsyncTransmitter.cs). This is the base class for all send
     adapters, deriving from Adapter and implementing IBTBatchTransmitter. The main
     method for this class is GetBatch, which provides an AsyncTransmitterBatch
     (discussed later) message batch to the BizTalk Messaging Engine when it has
     messages to be transmitted by the adapter. AsyncTransmitter acts as a class
     factory for instances of the TransmitReceiverEndpoint class. This class also
     contains endpoint management methods along with termination processing (see the
     "Termination Management" section). It implements IDisposable to free native
     resources when the adapter shuts down. Like the receive adapter, when your send
     adapter derives from AsyncTransmitter it is a singleton object.
     In the sample, the TransactionalTransmitter class is the send adapter. This inherits
     from AsyncTransmitter, and BizTalk Server loads it at run time when it instantiates
     the send adapter. Send adapters use the "lazy creation" paradigm by default for
     performance purposes. A send adapter is instantiated only at the point when
     messages need to be transmitted to endpoints linked to that adapter.

   ReceiverEndpoint (ReceiverEndpoint.cs). You derive your own receiver endpoint
     class from the abstract ReceiverEndpoint class and implement its methods to
     receive messages from a specific receive location. You code the receive operation
     (listening or polling) particular to your adapter in this class.

     The singleton Receiver class uses the ReceiverEndpoint class to track its active
     endpoints. For each active endpoint, Receiver creates an instance of the
     ReceiverEndpoint class and calls its Open method. You implement this method to
     include the receipt of the message and its submission into BizTalk Server. The
     Receiver class holds a reference to an instance of the ReceiverEndpoint class by
     using a Dictionary object indexed on URI. Thus if there are 10 different FTP receive
     locations there will be 10 instances of your derived ReceiverEndpoint class serviced
     by one (FTP) Receiver adapter class. If the administrator updates or deletes a
     receive location, the BizTalk Server Administration console calls the Update or
     Dispose method to carry out the corresponding operation.

     In the sample, TransactionalReceiverEndpoint derives from ReceiverEndpoint.
     This derived class contains all the code related to receiving a transactional message.
     In the sample, a single timer does the polling. You would implement your own
     functionality to receive a message specific to your protocol and architecture.
     Receiving the message (in the sample, pulling data from the Northwind database),
     creating the IBaseMessage object, and submitting it to BizTalk Server is done in the
                                                                                         13


     SubmitBatch method. This is discussed in detail later in the "Receive Side of the
     Adapter" section.

   AsyncTransmitterBatch (AsyncTransmitterBatch.cs). This class is the
     implementation of IBTTransmitterBatch and does not derive from the Batch class
     like all other batch subclasses. Your code subclasses this class and provides an
     instance of this object to the BizTalk Messaging Engine. The Messaging Engine in
     turn uses this object to send messages back to your adapter for transmission. This
     class allows these messages to come automatically into your endpoint class. Be
     careful not to confuse this "outgoing" batch, which the adapter provides for the
     BizTalk Messaging Engine to accept outgoing messages for transmission, with the
     "incoming" batch (Batch), which the transport proxy provides to the adapter on behalf
     of the BizTalk Messaging Engine to accept incoming messages for submission to
     BizTalk Server.

         Note
         Source code comments inside the AsyncTransmitterBatch.cs code module
         mention routing based upon the OutboundTransmitLocation property. This
         is incorrect because that property does not exist. Rather, the
         OutboundLocation member is used to route the messages.

     The Worker method is the high-level call where message transmission begins. You
     can override this method for specific batch processing, such as sorting the batch into
     smaller batches to be processed individually at a more granular level. This method
     creates a TransmitResponseBatch batch and calls ProcessMessage in a loop on
     each endpoint object for each message in the batch. ProcessMessage is an
     abstract method that you override in your endpoint class to execute the transmission
     operation for a single message. In your implementation of the
     AsyncTransmitterEndpointBatch class you call the
     AsyncTransmitterEndpoint.ProcessMessage method once for each endpoint.
     Upon return from this method BizTalk Server deletes the message from its queue if
     this is a one-way send. If ProcessMessage returns a response message, it is
     submitted back into BizTalk Server.

     The following code from the AsyncTransmitterBatch.Worker method shows how to
     use ProcessMessage and take the correct subsequent steps based upon success
     or failure of that call:
     foreach (IBaseMessage message in this.messages)
     {
     AsyncTransmitterEndpoint endpoint = null;
     try
     {
     //Get appropriate endpoint for the message. Should always be non-//null.
                                                                                      14


GetEndPoint copies the message context into a new message //context and
returns the context.OutboundTransportLocation //property.
endpoint = (AsyncTransmitterEndpoint)asyncTransmitter.GetEndpoint(message);

// Ask the endpoint to process the message.
IBaseMessage responseMsg = endpoint.ProcessMessage(message

//Message was sent and processed successfully by the endpoint to tell the
//BizTalk Messaging Engine we are finished with this message.
batch.DeleteMessage(message);
if (responseMsg != null)
{
batch.SubmitResponseMessage(message, responseMsg);
}
}
catch (AdapterException e)
{
HandleException(e, batch, message);
}
catch (Exception e)
{
HandleException(new ErrorTransmitUnexpectedClrException(e.Message), batch,
message);
 }
 finally
 {
//If we have a valid endpoint and we are not going to reuse the //endpoint
remove the AsyncTransmitterEndpoint endpoint.
if (endpoint != null && endpoint.ReuseEndpoint == false)
{
endpoint.Dispose();
endpoint = null;
}
}
}


The transactional adapter does not derive from the AsyncTransmitterBatch class.
Its functionality instead exists within TransactionalAsyncBatch.cs in a modified form.
The developer of the sample felt initially that most of the code in
AsyncTransmitterBatch did not meet the sample's requirements. Introducing
transactions slightly alters the control flow so the default implementation of this class
may not work. As a workaround he implemented the sample's functionality directly
from the IBTTransmitterBatch interface. However, after later revisiting this class he
stated that using AsyncTransmitterBatch would probably have been a better
approach, and that a developer can probably use the AsyncTransmitterBatch class
as is in almost all cases.
                                                                                        15


     Because of the direct implementation of IBTTramsmitterBatch, the
     ProcessMessage method is not used in the transactional adapter sample. This can
     be a common point of confusion for those trying to understand the Base Adapter
     classes. Instead, all the message transmission occurs in the SendMessage method.
     If you would like an example of how to implement ProcessMessage, then
     SendMessage will help you because the actual send work occurs here. Alternatively
     you can refer to the ProcessMessage implementation in the sample file adapter's
     DotNetFileTransmitterEndpoint.cs file.

   AsyncTransmitterEndpoint (AsyncTransmitterEndpoint.cs). This is an abstract
     base class for send adapter endpoints. It contains the three class definitions of
     EndpointParameters, DefaultEndpointParameters, and
     AsyncTransmitterEndpoint. These classes are used directly by the
     AsyncTransmitter and AsyncTransmitterBatch classes, and are typically not used
     by your adapter's derived classes.

     The three endpoint classes are as follows:

        EndpointParameters is a base class that represents the endpoint location
          where the message is to be sent. The URI of this location is stored in the
          OutboundLocation member. When a message is sent to the adapter for
          transmission, by default the adapter uses the OutboundLocation member of
          this class to decide where to send the message. If you do not want your adapter
          to use this value to send a message, then override the
          CreateEndPointParameters method in your derived endpoint class and use
          your custom location property. Additionally there may be times when the
          OutboundLocation alone does not adequately define the endpoint and an
          additional part of the message context might need to be used for disambiguation.

          BizTalk Server enables you to configure multiple send ports using the same URI
          and does not provide for unique values to differentiate the URIs. For example,
          take the case of Enterprise Single Sign-On. (For more information see
          "Enterprise Single Sign-On" in BizTalk Server 2006 Help.) Given an affiliate
          application ID and a corresponding ticket acquired by a given username and
          password, the adapter does a lookup in the SSO database for a particular FTP
          URI folder on a remote system. While the FTP URI remains constant, the ticket
          changes depending upon the user's credentials. This ticket allows further
          refinement of the send URI by possibly returning different folders on the same
          FTP server based upon which credentials are being used to make the call. If you
          logged on to a UNIX machine with different FTP credentials you would see a
          different directory. To return your own additional unique value you can override
          the abstract (string) member SessionKey constuctor to return a differentiating
          string value for an endpoint.
                                                                                         16


        DefaultEndPointParameters is derived from EndPointParameters. In the
          default case, its implementation of the SessionKey member returns the
          OutboundLocation member of the EndPointParameters base class.

        AsyncTransmitterEndpoint is a base class derived from IDisposable. It
          contains two virtual and two abstract methods to open and reuse an
          EndpointParameters endpoint. It also enables you to clean up unmanaged
          resources related to an endpoint's lifetime, and to map the
          OutboundTransportLocation context property of an outgoing message to an
          endpoint location. The adapter derives its own class from the
          AsyncTransmitterBatch class, which uses AsyncTransmitterEndpoint
          endpoints. This is done when creating a new TransmitResponseBatch batch of
          messages that the BizTalk Messaging Engine wants the adapter to send on its
          behalf. As mentioned earlier, the transactional adapter sample does not use the
          AsyncTransmitterBatch class but rather implements the
          TransactionalAsyncBatch class directly from IBTTransmitterBatch.


Secondary Classes
You can use these classes for secondary responsibilities like handling exceptions,
loading schemas, manipulating XML, and initialization/termination.

The secondary classes are as follows:

   AdapterManagementBase (AdapterManagementBase.cs). Use this utility helper
     class for loading schemas to localize resources during design-time adapter
     configuration.

     The design-time user interface for an adapter is implemented by using XSD-based
     property sheet generation. The text displayed in the property sheet dialog box is
     extracted from the "displayname" annotations in the XSD file. The adapter writer
     returns the XSD file as a string to the BizTalk Server Administration console.

     The following is a receive location property sheet dialog box followed by a section of
     code from its associated XSD file, found at <samples path>Adapters
     Usage\TransactionalAdapter\Admin\TransactionalReceiveLocation.XSD. Notice the
     "Connection String" property in the dialog box and how it maps to the "Connection
     String" node in the XSD file with respect to the "The SQL database connection string"
     text.
                                                                                  17




<xs:element name="connectionString" type="xs:string" minOccurs="1"
default="Data Source='.';Initial Catalog='Northwind';Integrated
Security='SSPI';">
<xs:annotation>
<xs:appinfo>
 <baf:designer>
 <baf:displayname _locID="">Connection String</baf:displayname>
 <baf:description _locID="">The SQL database connection
string.</baf:description>
 </baf:designer>
 </xs:appinfo>
 </xs:annotation>
 </xs:element>




The LocalizeSchemaDOM method is a helper function that helps the adapter writer
dynamically alter the XSD schema for the purpose of localization. You can localize
XSD schema elements by storing strings to be localized into a standard resource file
                                                                                    18


with the _locID localization attribute. The _locID attribute is purely a convention the
code uses to accomplish the localization. Without the convention and without this
code it is a tedious process to manipulate all the strings to localize the XSD. The
LocalizeSchemaDOM method uses an XPath query to extract all _locID children
nodes from that resource file into an XMLNodeList. For each node found, the method
finds the localized value from the resource file and dynamically replaces the
InnerText value of that node element with the new localized value. When the loop is
finished all the localizable elements are inserted into the XMLDocument that is
returned. For example, to localize a server's name from English to French use
_locID=servername. In the resource file create the value servername="Le Serva".
This value is returned and used when the localization ID is French and servername is
used in the code.

The following code shows the LocalizeSchemaDOM method:
protected XmlDocument LocalizeSchemaDOM (string schema, ResourceManager
resourceManager)
{
XmlDocument document = new XmlDocument();
document.LoadXml(schema);

XmlNodeList nodes = document.SelectNodes("/descendant::*[@_locID]");
foreach (XmlNode node in nodes)
{
string locID = node.Attributes["_locID"].Value;
node.InnerText = resourceManager.GetString(locID);
}
return document;
}


The following code obtains the schema from the resource file as a string. The
resource string is passed to LocalizeSchemaDOM, which localizes nodes with the
_locID attribute.
string mySchema = GetSchemaFromResource("mySchema");
string myLocalizedSchema = LocalizeSchemaDOM (mySchema, resourceManager);
// where…
protected string GetSchemaFromResource (string name)
{
Assembly assem = this.GetType().Assembly;
Stream stream = assem.GetManifestResourceStream(name);
StreamReader reader = new StreamReader(stream);
string schema = reader.ReadToEnd();
return schema;
}
                                                                                        19


     Note The transactional adapter sample does not use any of the
     LocalizeSchemaDOM functionality.

   AdapterExceptions (AdapterExceptions.cs). This class derives from
     ApplicationException, which throws an exception any time a nonfatal exception
     occurs. It contains numerous utility exception classes that write specific error
     messages in both the BizTalk Server Tracking database and the Application event
     log. For example, the class ErrorTransmitUnexpectedClrException is defined in
     this file as:

     public class ErrorTransmitUnexpectedClrException : AdapterException
     {
     public ErrorTransmitUnexpectedClrException (string message) :
     base(String.Format("An unexpected failure occurred while processing a message.
     The text associated with the exception is \"{0}\".", message)) { }

     protected ErrorTransmitUnexpectedClrException (SerializationInfo info,
     StreamingContext context) : base(info, context) { }
     }




     When an exception occurs within the Worker member function of the
     AsyncTransmitterBatch class, the exception is caught and an instance of the
     ErrorTransmitUnexpectedClrException class is created in its private
     HandleException method, as shown in the following code:
     catch (Exception e)
                               {
                                 HandleException(new
     ErrorTransmitUnexpectedClrException(e.Message), batch, message);
                             }




     In the HandleException implementation the code calls
     IBaseMessage.SetErrorInfo(e) to set the error information object for the current
     thread of execution using the incoming AdapterException object. A call to
     SetErrorInfo means that the error message appears in the BizTalk Server Tracking
     database and in the Application event log. In your code you can use these messages
     as defined in this file or derive your own custom exception class to output the error
     messages using GetErrorInfo.

   ConfigProperties (ConfigProperties.cs). Use this helper class to extract values
     from an XML DOM object.
                                                                                        20


   ControlledTermination (ControlledTermination.cs). This class inherits from
     IDisposable for resource management of unmanaged resources. Use it when
     adapter termination is proposed while multiple concurrent threads have outstanding
     batches still running. The adapter needs to prevent shutdown while the Messaging
     Engine is processing outstanding message batches. This class provides support to
     handle pending messages during termination processing. See the "Termination
     Management" section for more details.



Batch Processing Class Hierarchy
The Base Adapter classes provide support for both receive batches (submitted to BizTalk
Server) and send batches (sent from BizTalk Server to an endpoint). These batches can
be transactional if the situation dictates this requirement. The following figure shows the
class structure surrounding adapter batch processing and the Base Adapter.
                                                                                      21




The classes in the batch processing hierarchy are as follows:

   Batch. The common base class for all batches used in receiving a BizTalk Server
     message. This includes the ReceiveBatch, TxnBatch, and
     TransmitResponseBatch classes, which all inherit directly from Batch.
     AsyncTransmitterBatch is the only batch class that does not derive from Batch
     because it does not receive any messages (and thus is not in the preceding
     diagram). Instead it derives directly from the IBTTransmitterBatch interface because
     it is a transmitter of BizTalk Server messages.
                                                                                          22


     The Batch class implements the IDisposable and the receiver-specific
     IBTBatchCallback interfaces. Its function is to interact with the transport proxy and
     pass in a batch to be processed. All the IBTBatchCallback methods are
     implemented in this class, and most of them are virtual. You cannot override the
     BatchComplete method. It calls multiple virtual methods in the Batch class that you
     may want to override for your custom handling of the batch submission process in
     your derived class.
   ReceiveBatch. Derives directly from Batch and is used for receiving raw message
     data, placing that data into BizTalk Server messages, and submitting those
     messages to the BizTalk Messaging Engine in non-transactional batches. This class
     contains specific methods to deal with success and failure of message submissions.

     Regardless of whether you use transactions in your high-level, derived Base Adapter
     class, submission of a receive batch always uses an internal transaction. When one
     message fails submission the entire batch fails as well. Subsequently a new batch
     must be created for resubmission with the failed ("poison") message(s) removed.

     The innerBatch member (of type ReceiveBatch) is used to resolve errors and
     resubmit a new batch of messages during batch submission processing. It appears
     throughout the code in the ReceiveBatch.cs module. The innerBatch is the "next-
     round" innerBatch batch. If the next submission fails, this object recursively
     continues to remove poison messages and resubmit batches up to n times for a
     batch size of n messages. Resubmitting n times is the absolute worst case where
     every submitted message fails. The innerBatchCount member is a check to prevent
     infinite looping.

   TransmitResponseBatch. Derives directly from Batch and does everything needed
     for sending a non-transactional batch in an asynchronous fashion. If a failure occurs,
     TransmitResponseBatch tries to submit the failed message again (Resubmit
     method). On failure it hands the message to the next transport after the retry count
     has been exceeded (MoveToNextTransport method), or suspends the message if
     the backup transport fails (MoveToSuspendQ method).

   TxnBatch. Derives directly from Batch and overrides many base methods for
     committing or aborting a transaction associated with a message batch. Use this class
     if you are using a batch in the scope of a transaction. When resubmission of a
     message fails it is sent to the backup transport. If that also fails the message is sent
     to the Suspended queue.

   SyncReceiveSubmitBatch. Derives from ReceiveBatch and adds an additional
     option to wait synchronously on a batch result by using an internal event. This class
     is useful for invoking code to handle the ordering of messages.
                                                                                           23


   ReceiveTxnBatch. Contains three classes that all derive from TxnBatch to support
     transactional receive operations: AbortOnAllFailureReceiveTxnBatch,
     AbortOnFailureReceiveTxnBatch, and SingleMessageReceiveTxnBatch.

     These classes are as follows:
        AbortOnFailureReceiveTxnBatch. This is a transactional batch that aborts if
          BizTalk Server fails to accept the messages into the MessageBox database. In
          this case the message data is not persisted to any BizTalk Server database and
          is returned to the adapter. The adapter still owns the data and is responsible for
          handling it. A viable option at that point is for the adapter to send the message
          back to BizTalk Server to place into the Suspended queue. If you are using
          multiple message batches and transactions, this is the main failure mechanism
          you will use in almost all cases.

              Note
              For simplicity, we strongly recommend that you use single-message
              batches when using transactions. Unless you have pressing
              performance requirements, always submit a single-message batch
              through SingleMessageReceiveTxnBatch. For more information, see
              the "Poison Messages" section.

        AbortOnAllFailureReceiveTxnBatch. Like the previous class, this is a
          transactional batch that aborts if BizTalk Server fails to accept the messages into
          the MessageBox database. However, unlike the
          AbortOnFailureReceiveTxnBatch class, BizTalk Server does not return the raw
          data to the adapter with AbortOnAllFailureReceiveTxnBatch. Instead it places
          the messages into the Suspended queue. Do not use this failure method if you
          want to support strict ordering of messages, because ordering semantics dictate
          that no messages in the batch end up in the Suspended queue. To support strict
          ordering, use AbortOnFailureReceiveTxnBatch.

          However, if you are writing an adapter for BizTalk Server 2006, you do not need
          to use the AbortOnFailureReceiveTxnBatch class if you use the new BizTalk
          ServerActionOnFailure context property. This property is available for any
          adapter used with BizTalk Server 2006. Set ActionOnFailure to zero in the
          context property of each messaqe that you do not want BizTalk Server to
          suspend on a processing exception. Failure to set this property allows BizTalk
          Server to fall back to its default behavior of suspending the message on a
          processing exception.

        SingleMessageReceiveTxnBatch. This is a transactional batch that receives a
          single message. If submission to BizTalk Server fails this class suspends that
                                                                                       24


        message within the same transaction. SingleMessageReceiveTxnBatch
        derives from TxnBatch and resides in the ReceiveTxnBatch.cs file. It receives
        the CommittableTransaction transaction object in its constructor, which
        delegates to its TxnBatch base constructor. In the base constructor the
        CommittableTransaction is used in the call to GetDtcTransaction. This call
        takes the transaction and promotes it to an MSDTC transaction, returning an
        IDtcTransaction object instance that represents the distributed transaction. This
        transaction member takes the passed-in pointer to the
        CommittableTransaction. Both of these transactional objects are used in the
        TxnBatch.Done and TxnBatch.EndBatchComplete methods to commit or
        abort the transaction. Following is the constructor for the base TxnBatch class
        that performs these actions:
        public TxnBatch(IBTTransportProxy transportProxy, ControlledTermination
        control, CommittableTransaction transaction, ManualResetEvent
        orderedEvent, bool makeSuccessCall) : base(transportProxy,
        makeSuccessCall)
        {
        this.control = control;
                  this.comTxn = TransactionInterop.GetDtcTransaction(transaction);

                   // the System.Transactions transaction - must be the original
        transaction - only that can be used to commit
                   this.transaction = transaction;
        this.orderedEvent = orderedEvent;
        }




Transactional Adapter Sample
At a high level the processing flow of messages in the transactional adapter sample
occurs as follows:

1. Based upon database configuration values set by the administrator at design time,
   the receive adapter uses a timer to poll the Northwind database at a set interval and
   pull data from the Customers table.

2. This data is transformed into a BizTalk Server message and submitted to the
   MessageBox database using an explicit MSDTC transaction.

3. The send adapter subscribes to the receive port the receive adapter uses and gets
   the message from BizTalk Server.
4. The send adapter uses a transaction to write the message to a SQL Server table
   named "scratch."
                                                                                           25


One of the primary goals of the transactional adapter is to show how easy it is to create
and control transactions by using the System.Transactions class. The next sections
discuss both sides of this adapter as well as the respective flow of data in and out of the
adapter.


Receive Side of the Adapter
One of the last steps in running the transactional adapter sample is to enable the receive
location in the BizTalk Server Administration console. If an adapter's host process is
running when this occurs, this action generates a call to
IBTTransportConfig.AddReceiveEndpoint. It informs the adapter of a new active URI
upon which it should listen for incoming messages. For example, invoking
AddReceiveEndpoint in the file adapter sample leads to calling the
System.IO.DirectorySystem.GetFiles method to get a list of all the files that exist in the
directory endpoint the user specified. These files are then opened one at a time and their
BizTalk Server message payloads are extracted and then put into a formal
IBaseMessage object and passed to BizTalk Server. The transactional adapter sample
connects to the Northwind SQL Server database as each timer interval expires and pulls
out data to put into an IBaseMessage object to submit to BizTalk Server. This is the type
of code you would write in your custom adapter to obtain the message in whatever way
makes sense using your protocol and submit an IBaseMessage object to BizTalk Server.

Let's look in more detail at the transactional adapter's implementation of this functionality.
The Base Adapter's Receiver class functions as a singleton object broker responsible for
lifetime management of endpoints. After it creates the endpoint the Receiver initializes it
and adds it to the list of active endpoints maintained by the Receiver. As endpoints are
modified or deleted the Receiver class respectively calls Update and Dispose on the
endpoint class. In the Receiver class you find the implementations of
IBTTransportConfig (called by the Messaging Engine to manage endpoint additions,
modifications, and deletions) and IBTTransportControl (used for adapter initialization
and termination). In the AddReceiveEndpoint (Receiver.cs) method the Receiver class
creates an endpoint whose endpointType is specified as the last parameter in the
Receiver constructor. In this architecture there is a TransactionalReceiverEndpoint
(derived from the abstract Receiver Base Adapter class) object for each actively
configured endpoint, as shown in the following code:
ReceiverEndpoint endpoint =
(ReceiverEndpoint)Activator.CreateInstance(this.endpointType);




The endpointType member in this sample is set to TransactionalReceiverEndpoint
(last parameter) in the TransactionalReceiver constructor. In the
                                                                                         26


TransactionalReceiverEndpoint class you write your adapter-specific receive
operations, such as monitoring a file folder, monitoring an FTP site, or polling a SQL
Server database for message data as this adapter does.

The following code shows the TransactionalReceiver constructor:
public class TransactionalReceiver : Receiver
{
public TransactionalReceiver() : base(
"Transactional Adapter",
"1.0",
"Fetch remote messages into BizTalk with a transaction",
"txn",
new Guid("12F8CA70-4DE4-44e8-8F78-D947523F8DEF"),
"http://schemas.microsoft.com/BizTalk/2006/txn-properties",
typeof(TransactionalReceiverEndpoint))
{
}
}




To initialize a TransactionalReceiverEndpoint object, override the Open method and
do whatever adapter-specific initializations you need to do before starting the listening
process. The Open method sets the handler properties, which in this case pertain to the
information needed to poll data from a SQL Server database. Additionally the transport
proxy used to interface with the Messaging Engine, the URI where the listening is to
occur, and other key properties are stored in member variables.

The following code is the start of the Open method as described above:
this.properties = new TransactionalReceiveProperties(uri);
//Handler properties
XmlDocument handlerConfigDom =
ConfigProperties.IfExistsExtractConfigDom(handlerPropertyBag);
if (null != handlerConfigDom)
this.properties.HandlerConfiguration(handlerConfigDom);
// Location properties – possibly override some handler properties
XmlDocument locationConfigDom = ConfigProperties.ExtractConfigDom(config);
this.properties.LocationConfiguration(locationConfigDom);

// This is our handle back to the Messaging Engine.
this.transportProxy = transportProxy;
// Used to control if the BizTalk Server Messaging Engine can unload //us.
this.control = control;
this.uri = uri;
this.transportType = transportType;
this.propertyNamespace = propertyNamespace;
this.messageFactory = this.transportProxy.GetMessageFactory();
Start();
                                                                                             27



After initialization is complete, Open calls the Start method to activate the receive
process. In the transactional adapter it means the polling timer is activated. At design
time, the administrator sets the specified unit (U) time interval (T) for polling in the
properties for the receive location. As each occurrence of this time interval elapses, the
timer's TimerTask method is called.

The following code shows the Start method:
private void Start()
{
this.timer = new Timer(new TimerCallback(TimerTask));
  this.timer.Change(0, this.properties.PollingInterval);
  }


The TimerTask method simply calls SubmitBatch, where almost all of the adapter's
receive processing occurs. In SubmitBatch, the Connection String and Command Text
values that were set in the receive location at design time are used to query the
Northwind database, extract data, place that data into an IBaseMessage object, and
submit it to BizTalk Server in the scope of a transaction. Because this method is large
and contains many key points, we will split it up and discuss each point. We will focus
primarily on the database operations (Northwind and MessageBox) and the transactions
that support them.

There is not much to discuss during the initialization process. Later we discuss
termination management in detail, so refer to that section if you feel you need that
information at this point to help your understanding. The MemoryStream variable stream
is the memory in which the BizTalk Server message will eventually reside when it is
submitted to BizTalk Server. Before submission it is used in conjunction with XML DOM
objects to structure the data in memory, as shown in the following code:
public void SubmitBatch()
{
bool needToLeave = false;

CommittableTransaction transaction = null;

try
{
// used to block the Terminate from BizTalk
if (!this.control.Enter())
{
needToLeave = false;
return;
}
needToLeave = true;
                                                                                         28


ManualResetEvent orderedEvent = new ManualResetEvent(false);

string connectionString = this.properties.ConnectionString;
string cmdText = this.properties.CmdText;
string rootElementName = "Root";

MemoryStream stream = new MemoryStream();
bool dataAvailable = false;




Making the interaction with SQL Server transactional is almost trivial thanks to the new
Microsoft .NET Framework 2.0 System.Transactions namespace. This powerful class
allows code to create and participate in a distributed or local transaction with one or more
resource managers. The adapter creates an instance of the CommitableTransaction
class to allow the adapter to explicitly commit (Commit method) or abort (Rollback
method) a transaction. It calls these methods based upon the outcome of the adapter's
transactional code logic, which allows the adapter to uniquely define exactly what
transactional success or failure means.

The TransactionScope class is used to mark an entire block of code as transactional. In
this block we make the initial read from SQL Server and the injection of the message
data into an XML DOM all part of one transaction. This read operation uses the
connectionString and cmdText values set in the receive location at design time. The
connection.Open method opens the connection to SQL Server by using
connectionString. The SqlCommand object takes the cmdText and the connection in
its constructor to use when it makes the call to SQL Server to obtain the data by using
the ExecuteXMLReader method. The SqlCommand object then writes the data to the
stream by using the XMLTextWriter. If no exceptions are thrown during this transactional
interaction with SQL Server and XML manipulations, the TransactionScope.Complete
method is called. This notifies the transaction manager that all of your operations were
successful, the data is consistent and durably stored, and your vote is to commit the
transaction. The transaction manager now knows your operations are successful, and
can decide to commit or abort the transaction based upon the votes of other leaf nodes
and the root of the transaction. Note that committing a transaction does not always mean
the message was successfully submitted. It does mean that BizTalk Server has the data,
regardless of whether it is placed in the MessageBox database or in the Suspended
queue.

In this sample a local transaction is used because only one resource manager—SQL
Server—is involved. Because this TransactionScope object created the transaction, the
commit action occurs with the call to Complete at the end of the "using" block. In a more
complex, real-world, distributed transaction (MSDTC) scenario, other transactional
resource managers (TransactionScope objects) also cast their votes with the
                                                                                        29


transaction manager. The transaction manager commits the transaction only if all the
resource managers vote to commit it. If any resource manager votes not to commit the
transaction, the transaction manager does not commit it. (For more information about
these powerful transaction classes, see "Implementing an Explicit Transaction using
Committable Transaction" and "Introducing System.Transactions in the .NET Framework
2.0" on ">MSDN).

Continuing with the code in the SubmitBatch method, we see the use of
CommitableTransaction and TransactionScope objects. The transaction is passed
implicitly to SQL Server by the "using TransactionScope" block. If all goes well the
transaction is committed with the call to ts.Complete. It is a good idea to make this the
last line of code in the TransactionScope object's "using" block. If you forget to call
ts.Complete, MSDTC will abort the transaction even though SQL Server carried out the
work correctly.
// Create the System.Transactions transaction
transaction = new CommittableTransaction();

// Explicit interop with COM+ - this should not be necessary in future //(on SQL
2005 for example)
using (TransactionScope ts = new TransactionScope(transaction,
TimeSpan.FromHours(1), EnterpriseServicesInteropOption.Full))
{
// The connection is created inside the TransactionScope so the //database will
be included in the transaction
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();

SqlCommand command = new SqlCommand(cmdText, connection);

// The Encoding on the writer should be consistent with the BizTalk //message.
If you change this to UTF8 then you should change the //BizTalk message CharSet to
UTF8.

XmlWriterSettings settings = new XmlWriterSettings();
settings.Encoding = Encoding.Unicode;
settings.Indent = true;
settings.OmitXmlDeclaration = false;

using (XmlWriter writer = XmlTextWriter.Create(stream, settings))
{
writer.WriteStartDocument();

// Add the root element because the reader form the database doesn't
// include the root element for the xml - it's just rows
writer.WriteStartElement(rootElementName);

//   WriteNode is a little opaque checking the size of the underlying //stream is a
                                                                                           30


simple way to see whether any data was retrieved.
writer.Flush();
long beforeSize = stream.Length;

using (XmlReader reader = command.ExecuteXmlReader())
{
while (!reader.EOF)
{
writer.WriteNode(reader, true);
}
}

writer.Flush();
long afterSize = stream.Length;

// we only want to add a new message to BizTalk if the reader actually //returned
anything
   dataAvailable = (afterSize > beforeSize);

   writer.WriteEndElement();
   }
   }

   // An exception will have skipped this next line
   ts.Complete();
   }




At this point the message is in memory as an XML object. The comment in the sample
code module states, "Typically when polling like this you would want to loop checking
dataAvailable and only return to the sleep state when there is no data to feed into
BizTalk". This means that if your polling mechanism is similar to this (like the SQL
adapter), the code typically loops and reads data as long as there is data to be read
(dataAvailable). It returns to a sleep state when there is no longer any data to submit to
BizTalk Server.

For simplicity, this procedure is not shown here. If you choose to explicitly put this thread
to sleep while waiting for data, be careful to avoid starvation of the .NET thread pool.
Sleeping on an increasing number of threads for an extended period of time can cause
thread-starvation. This means that there is work waiting for threads but no threads are
available. Therefore you have to implement logic in your adapter to ensure there are
always threads available to process incoming data as it becomes available. Building in
some sort of custom yield in processing, perhaps with a timer, to check for messages
during the looping is a good idea. Using BeginInvoke to spin off a different thread to
process the work when data becomes visible to the main thread may be an option.
                                                                                              31


Regardless of the protocol and how you receive the incoming raw message data you
probably will read it into a stream. Seek is called to move the pointer to the start of the
stream. A SingleMessageReceiveTxnBatch batch object is created to submit the
stream (message) to BizTalk Server, as shown in the following code:
if (dataAvailable)
{
// Remember to Seek the stream or we won't get the data
stream.Seek(0, SeekOrigin.Begin);

// Note the batch has been given the CommittableTransaction and it //will take
responsibility for Commit
using (Batch batch = new SingleMessageReceiveTxnBatch(this.transportProxy,
this.control, transaction, orderedEvent))
{
batch.SubmitMessage(CreateMessage(stream));
batch.Done();
}
orderedEvent.WaitOne();
}
else
{
// No data so no batch but in a database scenario (like this) we might // still
wish to Commit. Other transactional adapters might choose to // Rollback in this
circumstance.
transaction.Commit();
}

// no exception in Done so we will be getting a BatchComplete which //will do the
necessary Leave
needToLeave = false;
}




Error handling in SubmitBatch is straightforward. If an exception occurs, set the
ErrorInfo for the event log and abort the transaction by calling
CommittableTransaction.Rollback, as shown in the following BatchWorker code:
catch (Exception e)
{
this.transportProxy.SetErrorInfo(e);

 if (transaction != null)
 transaction.Rollback();
 }
 finally
 {
 // if this is true there must have been some exception in or before              // Done
if (needToLeave)
this.control.Leave();
                                                                                      32


}
}




Send Side of the Adapter
The primary send functionality in this sample is found in the TransactionalAsyncBatch
class. This is derived from the IBTTransmitterBatch interface, which provides a batch to
the BizTalk Messaging Engine in which to place outgoing messages for the adapter to
transmit. The BizTalk Messaging Engine calls TransmitMessage for every message it
adds to the batch before calling Done to tell the adapter there are no more messages for
transmission.

If IBTTransport.Done completes correctly (does not result in a BizTalk Server exception
being thrown), then you have successfully started an asynchronous message submission
operation on a BizTalk Messaging Engine thread. In its implementation of this method the
send adapter adds each message to an array and returns control to the BizTalk
Messaging Engine to add the next message to the batch.

The actual transmission of the messages is done in the BatchWorker method.
TransactionalAsyncBatch defines two delegates in the Worker and Batchworker
methods. In .NET a delegate is a means to pass a method pointer as a parameter. By
being defined as a delegate, an object contains the BeginInvoke method, which allows
the delegate (function pointer) to execute on the same main thread as the object. The
delegates are defined as follows:
private WorkerDelegate worker;
private BatchWorkerDelegate batchWorker;




In its constructor, TransactionalAsyncBatch instantiates both a worker and a
batchWorker object using a WorkerDelegate and a BatchWorkerDelegate
respectively. The WorkerDelegate is assigned the method Worker, and the
BatchWorkerDelegate the method BatchWorker, as shown in the following code:
this.worker = new WorkerDelegate(Worker);
this.batchWorker = new BatchWorkerDelegate(BatchWorker);




When the BizTalk Messaging Engine has finished adding all its messages to the batch it
calls the (IBTTransmitter) Done method in TransactionalAsyncBatch. In its
implementation Done calls BeginInvoke on the worker delegate, as shown in the
following code:
                                                                                         33


foreach (DictionaryEntry entry in batches)
{
this.batchWorker.BeginInvoke((string)entry.Key, (ArrayList)entry.Value, null,
null);
}




BeginInvoke is an asynchronous call that returns instantly and allows a new thread to
execute the Worker method. If you want customized batch processing you can override
this method. Worker enters a loop and calls the batchWorker delegate's BeginInvoke
method once for each batch of messages to process the batch. This is an asynchronous
call into BatchWorker that returns immediately. This permits multiple messages in
different batches to be processed concurrently on multiple .NET threads per batch. Each
thread is executing in the BatchWorker code, so this method must be thread-safe.
Common resources and data that is shared across threads, such as global and static
variables, must be protected against concurrent access by using synchronization
primitives.

For each message that it transmits, the adapter creates a new message context and
transaction of type CommitableTransaction, as shown in the following code:
SystemMessageContext context = new SystemMessageContext(message.Context);
CommittableTransaction transaction = null;




The CommitableTransaction class allows you to create an MSDTC transaction that can
be passed to any transactional resource manager to enlist the send operation. For the
actual send operation in the transactional adapter the message is written using a
transaction to a specific SQL Server table. In the real world an installation could develop
a front-end tool to open this table and view the messages for analysis.

Keep in mind that this transactional send to SQL Server is not the same as the
transaction used to initially place the newly received message into the BizTalk Server
MessageBox database. The send operation used here was an arbitrary choice of the
transactional adapter developer. Instead we could have written it in a non-transactional
manner to an Excel spreadsheet or called a .NET Web service. Do not confuse the
transaction used by the receive adapter to initially submit the message into the BizTalk
Server database with this arbitrary transactional operation of outputting the message to a
SQL Server table.

An instance of TransactionalDeleteBatch is created whose sole purpose is to abort or
commit the transaction. It stores the CommittableTransaction object in a member
variable for later use when dictating its outcome. A TransactionScope object is used to
make a transactional call to SendMessage to write the message data to SQL Server. If
                                                                                     34


an error occurs, the CommittableTransaction object is used to inform all resource
managers associated with this transaction to roll back all their work. A new batch is
created and the failed messages that BizTalk Server sent for transmission are submitted
back to BizTalk Server to be reprocessed.
The following code shows this processing flow:
private void BatchWorker (string outboundTransportLocation, ArrayList messages)
{
<code removed>

foreach (IBaseMessage message in messages)
{
//Create a new message contxt for each messsage
SystemMessageContext context = new SystemMessageContext(message.Context);
CommittableTransaction transaction = null;
try
{
//Create a commitable transaction to allow to commit or abort the //transaction
explicitly. Pass the transaction to the //TransactionalDeleteBatch who will assume
responsibility to commit it.
transaction = new CommittableTransaction();
using (Batch batch = new TransactionalDeleteBatch(this.transportProxy,
this.control, transaction))
{
using (TransactionScope ts = new TransactionScope(transaction,
TimeSpan.FromHours(1), EnterpriseServicesInteropOption.Full))
{
//Write the data to the SQL database.
SendMessage(message, properties);
//Commit the transactional scope.
ts.Complete();
}
//A delete is part of the same transaction of the successful send //operation.
batch.DeleteMessage(message);

//If there was a response to submit a message it would have been added //here
using the same batch and same transaction objects as //DeleteMessage.
batch.Done();
}
}
catch (Exception e)
{
// In the scenario we will explicilty Rollback the transaction on //failure.
if (transaction != null)
transaction.Rollback();

//Remember to set the exception on the message itself. This will now //appear in
tracking in additio to the event log.
message.SetErrorInfo(e);
//Any failure needs to be retried. But the change of state back on //BizTalk is
                                                                                        35


outside of the transaction that was used to send the //message. After all the
transactoin will undoudtly get rollback with a //failure. This batch is non
transactional from the adapter's point of //view.
using (TransmitResponseBatch batch = new
TransmitResponseBatch(this.transportProxy, new
TransmitResponseBatch.AllWorkDoneDelegate(AllWorkDone)))
{
batch.Resubmit(message, false, null);
batch.Done();
}
}
}
needToLeave = false;
}
catch (Exception e)
{
this.transportProxy.SetErrorInfo(e);
}
finally
{
if (needToLeave)
this.control.Leave();
  }
  }




Additional Adapter Design Issues
This section discusses a few additional issues to be aware of when designing and coding
your adapter.


The Message Ordering Problem
Message ordering applies to both the send side and the receive side of an adapter.


Send-side ordering
On the send side an adapter can process messages in the order in which it receives
them from the BizTalk Messaging Engine. However, this is an almost useless fact
because the adapter receives many batches concurrently. Multiple adapter worker
threads work to transmit batches to the specific endpoint. Send-side message ordering
significantly slows down the adapter. This process limits message transmission to one
adapter thread, effectively canceling any concurrency.
                                                                                           36


Message ordering on the send side means the BizTalk Messaging Engine waits for the
adapter to call Delete for each message it successfully transfers. After the adapter calls
Delete for a message, the Messaging Engine gives the adapter another message to
transmit. When you configure ordering on the send port, the BizTalk Message Engine
handles the ordering for you.

When configuring a send port there is an Ordered delivery check box. This preserves
the outgoing order of messages as it relates to the order in which they were originally
submitted into the MessageBox database. This submission could be a result of a receive
location or an orchestration initially creating a message. If you bring up the properties for
a send port and click Transport Advanced Options you will see the Ordered delivery
check box. Selecting it tells BizTalk Server to give a send adapter the next sequential
message only after the adapter tells BizTalk Server that the previous message has been
successfully sent. After Ordered delivery is selected, you can select the Stop sending
subsequent messages on current message failure check box. Enabling this option
causes the send port to be suspended when any message fails transmission. Otherwise
on message failure only the failed message is suspended and other messages continue
to be sent.
                                                                                       37



Preserving message ordering through this paradigm is costly in terms of throughput
performance. One reason for this is the overhead of the internal BizTalk Server
sequential message management code that is executed only when this check box is
selected. Adapter performance is slowed because the adapter must wait on the
completion of the asynchronous Done method. (SyncReceiveSubmitBatch helps you
manage this.) To eliminate concurrency, you should run all your message submission
code on a single receive adapter thread. The ordering can also be broken if multiple host
threads access an adapter concurrently. Therefore, if you want to use ordered message
delivery and are using clustering, ensure that your BizTalk Host runs on an active-
passive cluster. Normally BizTalk Server is an active-active cluster with multiple hosts.

For performance reasons, it is a good idea for an adapter writer to expose a property to
toggle message ordering. Within the adapter's logic it checks to see if ordering is
enabled. If so, all batch submission code runs on a single thread as shown in the
following pseudocode:
While (messages to process)
{
If control.enter() then return:
Batch b = new SyncReceiveSubmitBatch()
b.Submit (msg1);
b.Submit (msg2);
b.Submit (msg3);
b.Submit (msg4);
b.Done();//this message is asynch
bool bSuccess = batch.Wait()//Will wait on batch complete to reset manual reset
event
if (!bSuccess)
{
//Process error but make sure not to break ordering.
//configurable "On failure" dictates what to do here.
}
}




Ordered delivery adds some challenges to error handling. For example, suppose the
adapter gets halfway through a batch and a message fails in its submission process. (For
example, in the preceding code, bSuccess returns a failure code from batch.Wait. What
do we do if we want to preserve ordering? In some adapters a failure action is
configurable and that choice is left up to the administrator. For example, for a receive
location configured to use the MSMQ adapter there is the On Failure property (found
under Properties/Configure). If the administrator sets the Ordered Processing property
to true, the On Failure property becomes enabled and offers three options:
                                                                                        38




   Stop prevents the adapter from receiving any more messages.

   Suspend (Non-resumable) moves the message into the Suspended queue and
     marks it as not resumable.

   Suspend (Resumable) moves the message into the Suspended queue and marks it
     as resumable.


Receive-side ordering
The requirement for receive-side ordering is that you must wait for the previous batch to
complete before submitting a new receive batch. This is done by the Messaging Engine
calling back into the adapter's batchComplete method.

A problem could occur in the ordering if the BizTalk Server default error processing is
allowed to execute. Normally BizTalk Server suspends messages and returns a warning
instead of an error code to the adapter. If BizTalk Server chooses not to handle the bad
                                                                                        39


message it returns an error to the adapter. The MSMQ adapter's ActionOnFailure
property can be set from within the adapter's receive location configuration. The adapter
developer should provide a user interface similar to the one for the send port. In the
receive adapter's error-handling block (like the "if (!bSuccess)" block above), the code
examines this property's configured value. Depending upon its setting BizTalk Server
takes appropriate action when a message fails submission. For example, if the MSMQ
adapter sees this value set to 0 it enables logic to tell BizTalk Server not to suspend a
message on a submission failure. This allows the adapter to implement its own handling
to dictate its own policy on message ordering.

Database adapters also need to consider ordering, locking, concurrency, and
serialization. Serialization makes it easier for the stored procedure writer to avoid
deadlock and performance drains due to concurrent locking, yet offers the worst
throughput. A high level of concurrency against a database does not necessarily result in
higher throughput due to potential table and record locking. The earlier discussion about
message ordering on the send side basically came down to stopping the concurrency. A
database adapter developer might use similar techniques to reduce the concurrency.
Typically a database adapter runs queries at set intervals using transactions against the
database to pull data into BizTalk Server messages. After data is successfully transferred
to BizTalk Server the adapter deletes each message (row). In isolation this does not
cause concurrency problems.

Production database adapters are typically written to check if there is more data to read
from the database and put into a BizTalk Server message. These adapters poll for data.
When the adapter finds data, it keeps looping as long as there is more data, and submits
the data to BizTalk Server. If it finds no data the adapter typically goes to sleep for a
small fixed time interval. When the interval expires, the adapter wakes up to see if there
is any new data.

The following pseudocode shows this process:
do
{
do
{
//loop polling for data
} while (more data)

Sleep (a few seconds)

} while (adapter enabled)
                                                                                            40


One potential pitfall in this approach is that this inner loop continuously reads data while it
is available. This can result in starving the .NET thread pool because your loop is
probably executing on a thread-pool thread (for example, from a timer), and does not
release the thread until there is no more data. This could starve other receive locations. It
might be better to execute a single batch of messages and then reschedule your
operation on the .NET thread pool. This way is more fair to other send operations
(potentially other receive locations) sharing the same .NET thread pool.
The following are the main points related to message ordering:

   Errors and how they are handled can corrupt the order of messages. The
     adapter writer should provide a configuration option or default error-handling
     processing to ensure that an error does not corrupt the entire operation. In the worst
     case, the adapter might simply shut itself down to avoid corruption due to a break in
     the ordering.
   The receive side should be single-threaded and truly synchronous in its
     message processing. However, the receive adapter submits messages to the BizTalk
     Messaging Engine in an asynchronous manner. Normally this is not an issue, but
     asynchronous submission does not work with respect to message ordering. It is even
     more complicated in that additional asynchronous batches (innerBatch) may be
     submitted during error processing.
     The solution is to make the batch processing more synchronous in nature. The Base
     Adapter classes do this in three ways. The first technique uses the callback, the
     second uses an event parameter, and the third creates and uses an event that
     enables you to wait on the batch after you have called Done. The transactional
     adapter is inconsistent in how this is done and could lead to confusion.


The Poison Message Problem
Any message in a batch that fails when submitted to BizTalk Server can be called a
"poison" message. In a sense its failure "poisons" the batch of messages because it
prevents the other messages from being submitted as long as it remains part of that
batch. The adapter code must implement logic to deal with this type of message. In a
single-message batch the adapter can suspend the poison message and fail submission
of the batch under the same transaction. In a multi-message batch the entire batch must
be aborted even if only one of its messages is poison.

For an example of a poison message, assume that there are fifty messages to be
submitted. The adapter divides them into five batches of ten messages each. Each batch
is submitted immediately after the previous one has been successfully submitted.
(Submission here means handing the batch to a BizTalk Server worker thread for
                                                                                       41


processing.) The BizTalk Server worker threads process each message by entering it into
the MessageBox database as part of one transactional operation. If any message fails
the submission process, the entire batch submission fails and the transaction is rolled
back. Because the Done call that submits the message batch to the worker thread is an
asynchronous operation, all five batches will most likely be processing concurrently at
some time.

Suppose batches 1, 3, and 5 were processed to completion and their transactions
committed them into the MessageBox database. Unfortunately, batches 2 and 4
contained poison messages and failed submission, yielding an error. The adapter
asynchronously aborts those transactions and the messages from batches 2 and 4
reappear in the adapter's private work queue. If you are using the adapter's batch classes
you do not need to implement BatchComplete to handle this situation. The retry
algorithm shifts back into single-message batch mode processing until the poison
messages have been submitted successfully to BizTalk Server. (Recall that successful
submission means being published into the MessageBox database or placed into the
Suspended queue.) At that point the retry algorithm shifts back to multi-message
processing for the sake of performance. An additional explanation of this scenario is in
"Writing Effective BizTalk Server Adapters"
(http://go.microsoft.com/fwlink/?LinkId=65211)
under "Transactional BatchComplete Errors".

SingleMessageReceiveTxnBatch submits a batch with a single message. If you do not
care about performance this is the best batch class to use. However, if performance is
important, then use the multi-message scheme with either
AbortOnFailureReceiveTxnBatch or AbortOnFailureAllReceiveTxnBatch. Use this
scheme until you get an error, and then switch to the single-message scheme and submit
messages individually until you locate and suspend the poison message. Then you can
return to the multi-message paradigm if you want to. If you use
SingleMessageReceiveTxnBatch, the base class does all the mode-swapping work for
you.

To summarize, use AbortOnFailureReceiveTxnBatch and
SingleMessageReceiveTxnBatch together to asynchronously process message
streams with efficiency while handling poison messages. Both classes implement
BatchComplete and implement proper error handling. If you do not care about maximum
throughput optimization, then submit single-message batches with
SingleMessageReceiveTxnBatch. Because this submits single messages, it effectively
disables batching and thus is slow.

The following pseudocode shows how to use multi-message mode:
Commitable Txm   //not a dtc tx but you promote it on demand in system.transactions
                                                                                         42


DtcTxn = Txn.GetDTCTransaction //promotes since BTS needs a DTC tx
Batch b = new AbortOnFailureTxnBatch(DtcTxn)
Batch.submit (msg1);
Batch.submit (msg2);
Batch.submit (msg3);Batch.Done()




The following code shows how to transmit single messages in a batch:
//not a dtc tx but you promote it on demand in system.transactions Commitable Txn
//promotes since BTS needs a DTC tx)
DtcTxn = Txn.GetDTCTransaction
Batch b = new SingleMessageReceiveTxnBatch(DtcTxn)
Batch.submit (msg);
Batch.Done()


None of the Base Adapter's Batch objects used for receive operations are thread-safe.
This means that if you use the same Batch object from multiple threads (sharing an
object's reference), then any operations on the batch should be serialized. Otherwise,
data corruption can occur. The following pseudocode illustrates this point:
lock(batchObject)
{
batchObject.Submit(msg);
}




The non-transactional ReceiveBatch batch class continues to submit multiple batches
until it is successful. For example, suppose you have a ReceiveBatch with ten messages
and the fourth message fails submission. ReceiveBatch creates a second batch and
includes the first poison message destined for the Suspended queue (it calls
IBTTransportBatch.MoveToSuspendQ on that message instead of
IBTTransportBatch.SubmitMessage). If a failure occurs in the next batch submission, it
continues to call Submit for each remaining good message and MoveToSuspendQ for
each bad message in the batch. Each batch is aborted until it finally has all the poison
messages as calls to MoveToSuspendQ and all the good messages as calls to Submit
in one final batch, which is the only batch that finally commits.


Termination Management
BizTalk Server invokes an in-process adapter's Terminate method when the BizTalk
Server service is shutting down. BizTalk Server termination processing dictates that an
adapter must block processing in its Terminate method until all pending messages in all
its submitted batches have processed to completion. This includes messages either in a
                                                                                               43


batch submitted to the BizTalk Messaging Engine or waiting on a message response
from a client. It is the adapter's responsibility to keep track of every batch thread it has
executing. The BizTalk Messaging Engine calls back into the adapter's
IBTBatchComplete.BatchCallback method once for each batch processed. After the
last callback all its batches have been processed to completion and the adapter can
return from Terminate.

As you can imagine, the adapter synchronization code associated with this process in a
multithreaded environment can be very complex. The Base Adapter does a good job of
managing this for you with the ControlledTermination class. You only need to
understand how to harness this capability.

The ControlledTermination class acts like a standard reader-writer lock. That type of
synchronization-lock primitive has multiple readers but only one thread has exclusive
write access. The reader-writer lock implements this functionality for you. Your
responsibility is to make sure you properly acquire a lock to write data and release it
promptly when you are finished with it.

There is only one global ControlledTermination object per adapter. The constructor for
every Base Adapter batch class takes a controlled termination object. Make sure you
pass a ControlledTermination variable to whatever batch object you are using. The
following code inside the SubmitBatch method (TransactionalReceiveEndpoint.cs)
shows the constructor call to create a SingleMessageReceiveTxnBatch batch. It takes
the ControlledTermination object as the control parameter.
using (Batch batch = new SingleMessageReceiveTxnBatch(this.transportProxy,
this.control, transaction, orderedEvent))




When using the ControlledTermination class your adapter should explicitly call the
Enter method every time it enters a thread. This includes a thread waiting on a callback
from BizTalk Server. Correspondingly, your adapter should explicitly call Leave every
time it exits a thread. The Batch class transparently calls Leave for you automatically if
Done was called without any errors.

ControlledTermination keeps an activity count using a custom synchronization primitive
that coordinates multiple activities. An activity represents a submitted batch of messages
and is tracked by using the module-level global variable activityCount. This value is
incremented each time Enter is called and decremented when Leave is called. If the
Terminate method has been called but the adapter has not shut down, the terminate
member will be true. That prevents any new threads from starting up when they call
Enter. Instead it blocks until all threads currently executing are finished and the
activityCount falls to zero.
                                                                                      44


The following code shows the Enter and Terminate methods and their use of the
activityCount variable to make sure termination does not occur with any outstanding
submitted batches.
// to be called at the start of the activity
// returns false if terminate has been called
public bool Enter ()
{
lock (this)
{
if (true == this.terminate)
{
return false;
}

   this.activityCount++;
   }
   return true;
   }



  // to be called at the end of the activity
  public void Leave ()
  {
  lock (this)
  {
  this.activityCount--;

  // Set the event only if Terminate() is called
 if (this.activityCount == 0 && this.terminate)
 this.e.Set();
 }
 }

// this method blocks waiting for any activity to complete
public void Terminate ()
{
 bool result;
 lock (this)
 {
 this.terminate = true;
 result = (this.activityCount == 0);
 }

 // If activity count was not zero, wait for pending activities
 if (!result)
 {
 this.e.WaitOne();
 }
 }
                                                                                         45



The needToLeave variable is used throughout the batch classes to prevent Terminate
from continuing improperly with outstanding batches that have not been completely
processed. If the value of needToLeave is true, then an error occurred in the processing
of Done. This means that an outstanding Enter was called and Leave will not be called
due to errors. You will have to handle the errors and call Leave manually, typically in the
"finally" block. If the Done call on the batch succeeded, then needToLeave is false and
the Base Adapter class calls Leave for you.

The following code from the ReceiveBatch class shows both the needToLeave and
innerBatch (discussed earlier in the "Batch Processing Class Hierarchy" section)
members:
protected override void EndProcessFailures ()
{
if (this.innerBatch != null && this.innerBatchCount > 0)
{
try
{
this.innerBatch.Done(null);
this.needToLeave = false;
}
catch (Exception e)
{
Trace.WriteLine("ReceiveBatch.EndProcessFailures Exception: {0}", e.Message);

this.innerBatch = null;
}
}
}

protected override void EndBatchComplete ()
{
if (this.needToLeave)
this.control.Leave();

// if there is no pending work and we have been given an event to set // then set
it!
if (this.innerBatch == null)
{
// Theoretically, suspend should never fail unless DB is down/not-    //reachable
or the stream is not seekable. In such cases, there is a //chance of duplicates
but that's safer than deleting messages that are not in the DB.
if (this.ReceiveBatchComplete != null)
this.ReceiveBatchComplete(this.OverallSuccess && !this.suspendFailed);
if (this.orderedEvent != null)
this.orderedEvent.Set();
}
}
                                                                                          46




Application of the Base Adapter Classes
The previous sections discuss how the Base Adapter classes are structured as well as
the roles they play in the transactional adapter sample. This gives a theoretical
understanding of the power of these classes, but does not show how to make the best
use of them. In this section we look at how you apply the classes to your adapter solution
in clearly defined steps.

Regardless of whether your adapter is send or receive, or both, you create a new Class
Library project for the run-time functionality. Add references to the following assemblies:

   Microsoft.BizTalk.Adapter.Framework

   Microsoft.BizTalk.Pipeline

   Microsoft.Samples.BizTalk.Adapter.Common

   Microsoft.BizTalk.GlobalPropertySchemas

   Microsoft.BizTalk.Interop.TransportProxy

   Microsoft.XLANGs.BaseTypes

For adapter design-time management you create another Class Library project, and in
that project you only need references to the first three assemblies listed above. You also
need to create two XSD files for send (transmithandler.xsd and transmitlocation.xsd) and
two for receive (receivehandler.xsd and receivelocation.xsd) design-time configuration.


Receive-Side Functionality
On the receive side you use the Receiver, ReceiverEndpoint, and one of the derived
Batch classes. Perform the following steps:

1. Subclass the Receiver class (refer to the TransactionalReceiver class). In the
   constructor, add the transport type, GUID, property namespace, and your receive
   endpoint. This acts as a class factory for the ReceiverEndpoint class.

2. Subclass the ReceiverEndpoint class to create your own receiver type (refer to the
   TransactionalReceiverEndpoint class). This class listens or polls the external
   system, and when data is available it retrieves that data (optionally within a
   transaction), creates a BizTalk Server message, and creates a batch class.
                                                                                           47


3. Select a batch class depending upon whether you are using transactions or more
   than one message in a batch.


Send-Side Functionality
The AsyncTransmitterBatch class does all the work for a send adapter. Recall that the
transactional sample does not use this class. You can view the use of this class from
within the file adapter sample in its DotNetFileAsyncTransmitterBatch class.


Non-transactional adapter
A non-transactional send adapter is rather straightforward. Perform the following steps:

1. Subclass the AsyncTransmitter class (see the TransactionalTransmitter class in
   the sample) to send a batch to BizTalk Server when it needs to send messages. The
   constructor is the same as the receive side where you add the transport type, GUID,
   property namespace, and transmit endpoint.

2. Subclass the TransmitterEndpoint class and add code to send the message. Again
   the transactional adapter sample does not use this class. Refer to the
   DotNetFileTransmitterEndpoint class in the file adapter sample for an example of
   how to implement this functionality.


Transactional adapter
A transactional send adapter is a bit more complex. Perform the following steps:

1. Just like a non-transactional adapter, subclass the AsyncTransmitter class.

2. Additionally in this class you override the CreateAsyncTransmitterBatch method to
   return a new instance of an AsyncTransmitterBatch object (refer to the
   TransactionalTransmitter class). For this batch object you can either subclass from
   AsyncTransmitterBatch (DotNetFileAsyncTransmitterBatch class in the file
   adapter sample) or implement IBTTransmitterBatch directly
   (TransactionalAsyncBatch class in the transactional adapter).


Registration
After your adapter is compiled it needs to be installed into the registry on the host
computer upon which it will execute. For example, the registry needs to know the path to
your assembly, its alias to display in the properties dialog boxes, the GUIDs of the send
and receive adapters, and so on. You can manually create this file but it will be prone to
errors due to all the nested folders and GUIDs. We recommend that you use the Adapter
                                                                                          48


Registration Wizard that ships with the SDK in the <installation
directory>\SDK\Utilities\AdapterRegistryWizard folder. To create the registration file refer
to the "Adapter Registration Wizard" topic on ">MSDN at
http://go.microsoft.com/fwlink/?LinkId=64298. This wizard outputs a .reg file that you can
import into the registry. This file is described in detail at
http://go.microsoft.com/fwlink/?LinkId=64299. You can view an example of its output at
by looking at the TransactionalAdmin.reg file located at
<samples>\AdaptersDevelopment\TransactionalAdapter\Admin.



Message Processing Requirements
Earlier we examined the high-level class derivations and instantiations that need to occur
for receive and send adapters. Now we look deeper into the interface implementations
and semantics of sending and receiving messages. This encompasses working both with
and without transactions.


Transactional Send Operation
When sending a message the transaction is established with the target resource
manager. In the transactional adapter sample that resource manager is SQL Server
(refer to the BatchWorker and SendMessage methods in TransactionalAsyncBatch).
A transactional send operation proceeds as follows:

1. BizTalk Server sends to the adapter a list of messages to be transmitted in the batch
   the adapter provides. With each call on the batch's
   IBTTransmitterBatch.TransmitMessage method, build a list of messages one at a
   time. After IBTTransmitterBatch.Done is called, sort the list by endpoint, create a
   transaction, and send the messages to the external system (refer to the
   TransactionalAsyncBatch class).

2. The adapter creates a transaction TXN1 and submits the messages to the external
   transactional system (for example, Oracle or SQL Server) using TXN1.

    a. If the transmission succeeds, the adapter creates a batch of message deletions
       and submits them back to BizTalk Server to delete the batch using the same
       transaction (TXN1). The adapter then commits the transaction explicitly from its
       code.

    b. If the transmission fails, the adapter aborts the transaction. It then creates a new,
       non-transactional batch and resubmits the message to BizTalk Server. If
       Resubmit fails, you need to handle the message. Typically you do this by calling
       MoveToNextTransport or MoveToSuspendQ.
                                                                                       49


Message deletions (following a successful one-way send) or sending a response
message (second part of a solicit-response operation) can never be part of the same
transactional batch that was used for Resubmit, MoveToNextTransport, and
MoveToSuspendQ.


Non-Transactional Send Operation
1. BizTalk Server sends to the adapter a list of messages to be transmitted in the batch
   the adapter provides. The adapter may sort the messages by endpoint.

2. The adapter submits the messages to the external system.

    a. If the transmission succeeds, the adapter submits the message back to BizTalk
       Server to delete it using a batch.

    b. If the transmission fails, the adapter resubmits the message to BizTalk Server. If
       using multiple messages, the batch used here is usually part of the same batch
       used to transmit in the previous step.


Transactional Receive Operation
1. Create transaction TXN1 in the receive adapter.

2. Obtain the message from the external system using transaction TXN1.

3. Create a transactional batch. If using a transactional batch use
   SingleMessageReceiveTxnbatch for single-message batches, or
   AbortOnFailureReceiveTxnBatch, which handles multiple messages. With the
   latter you must write code to abort on error, go to single-message mode, and
   eventually go back to batch mode.

4. Submit the message to BizTalk Server using the batch class and TXN1. Call Done
   when the batch is ready to go and loop as you see fit for more data. Realize that
   different systems have different ways of peeking or waiting on data so you will have
   to do what is appropriate for your protocol. For example, POP3 has a login delay, and
   the MSMQ adapter might run out of sockets.

    a. If the transmission to BizTalk Server succeeds, then commit the transaction
       TXN1.
    b. If the transmission to BizTalk Server fails, then:

        If using a multiple-message batch AbortOnFailureReceiveTxnBatch, then abort
        the transaction TXN1.
                                                                                       50


        If using a single-message batch using the SingleMessageReceiveTxnBatch
        class, then call MoveToSuspendQ, and commit the transaction TXN1.


Non-Transactional Receive Operation
1. Obtain the message from the external system using the receive adapter.

2. Create a non-transactional batch. For a non-transactional batch use ReceiveBatch
   and SyncReceiveSubmitBatch if doing message ordering.

3. Submit the message to BizTalk Server using a non-transactional batch.

    a. If the transmission to BizTalk Server fails, then call MoveToSuspendQ to
       suspend the failed message.



Debugging the Transactional Adapter
In this section, we debug an adapter. This process will teach you about the transactional
adapter by walking through the code. It will also show you how to debug your own
adapter during your development process.

To debug the transactional adapter, follow these steps:
1. Configure the transactional adapter with the instructions in BizTalk Server 2006 Help
   under Development\Samples in the SDK\Adapter Samples –
   Development\Transactional Adapter.

    Ensure that the Northwind database exists on your system by using the Microsoft
    SQL Server Management Console. If necessary, install it from
    http://go.microsoft.com/fwlink/?LinkId=64296. You must do this before running the
    SQL query script in "Running the Sample" to install the stored procedure and create
    the table in the Northwind database.

    When you get to the end of the "Running the Sample" section, just for now, do not
    enable the receive location or start the send port. We need to set breakpoints and we
    want to track what the send adapter is doing to SQL Server.

2. Open the BizTalk Server Administration console and stop the
   BizTalkServerApplication host if it is started.

3. Open Visual Studio 2005 and load the transactional adapter's solution file
   (transactionaladapter.sln).
                                                                                           51


4. Open the file TransactionalReceiverEndpoint.cs and find the SubmitBatch method.
   This is where the primary receive processing occurs. Move your cursor to that line
   and press F9 to add a breakpoint.

5. Open the file TransactionalAsyncBatch.cs and find the Batchworker and Send
   methods. This is where the primary send processing occurs. Move your cursor to
   these lines and press F9 to add a breakpoint.

    Note that due to the asynchronous nature of the adapter, the breakpoint execution
    flow may not work as you are accustomed to. Therefore feel free at this point to set
    breakpoints on other key lines of code you have an interest in watching execute. Be
    aware that you may need to disable the breakpoints on the send side to view the
    receive side, and vice versa. Also try to set breakpoints past any of the termination
    and synchronization blocks that have to do with the Enter method and the
    needToLeave variable. Breakpoints set there can cause an endless loop in some
    situations due to timing.

6. Open Microsoft SQL Server Management Studio to monitor the "scratch" table in the
   Northwind database. From step 1 above the Northwind database should already be
   configured correctly with a table named "scratch."

7. Enable the receive location TxnReceiveLocation1 and start the send port
   TxnSendPort1. Make sure to "Start" the send port and not just "Enlist" it. ("Start" both
   begins its processing and enlists it for you in one operation.)

8. Start the BizTalkServerApplication host.

9. In Visual Studio, on the Debug menu, click Attach to Process, select Show
   processes from all users, select BTSNTSvc.exe, and click Attach.

10. As soon as the configured time interval for the receive location is triggered
    breakpoints will be hit and control will jump to Visual Studio. Use F10 to step over
    functions and F11 to step into them.

11. In Microsoft SQL Server Management Studio, select New Query, enter the following,
    then press ENTER:
    Use Northwind

    Select * from Scratch

    This shows you each row that is being written to the "scratch" database table. To
    remove all rows and see it start over, replace the select statement with "Delete
    Scratch".
                                                                                       52


Summary
The Base Adapter classes are one way to develop adapters. With the 2006 release of
BizTalk Server they have been updated to provide transactional support for both send
and receive adapters. Understanding the methods you override or implement and what
functionality the Base Adapter provides for free are keys to a productive adapter
development cycle. Using the Base Adapter is a great way to write adapters after you
understand the execution path and which code (yours or the Base Adapter's) is
responsible for what processing.

The following points summarize the main areas to implement when writing your adapter:

   In a receive adapter, inherit from the Receiver class. Create a ReceiverEndpoint for
     every receive location and write your transport-specific code to receive a BizTalk
     Server message. Choose and instantiate a class derived from Batch to submit the
     messages one at a time or in a batch to BizTalk Server. Use transactional classes as
     applicable.

   In a non-transactional send adapter, inherit from the AsyncTransmitter class.
     Create a TransmitterEndpoint and add code to send the message.

     In a transactional send adapter, the steps are similar but use
     AsyncTransmitterBatch or IBTTransmitterBatch to pass to the BizTalk Messaging
     Engine and get its messages to be sent. Sift through these messages per endpoint
     and transmit them.

								
To top