Using Microsoft Transaction Server 2.0 with COM Transaction Integrator 1.0

Angela Mills

January 1998

Contents

Introduction
What Is COM Transaction Integrator
How COMTI Extends Microsoft Transaction Server
Creating a COMTI Component
Configuring COMTI and Microsoft Transaction Server
Using a COMTI Component
An Example Scenario Using COMTI
References

Abstract

This document describes how the Microsoft® COM Transaction Integrator extends Microsoft Transaction Server into the mainframe transaction-processing world. It is a technical document that explains how to configure and use these products and the processes that take place "under the hood."

Introduction

This document explains how the COM Transaction Integrator (COMTI) version 1.0 for Customer Information Control Systems (CICS) and Information Management Systems (IMS) extends Microsoft Transaction Server (MTS) version 2.0 into the mainframe-based, transaction-processing world. Shipping with Microsoft SNA Server version 4.0, COMTI enables application developers to easily create COM components that can expose and extend CICS and IMS transaction programs. Once created, these components can be called from many development and productivity tools such as Microsoft Visual Basic®, Active Server Pages (ASP), and even Microsoft Word, enabling mainframe data and business logic to become more available and flexible.

This is a technical document that assumes a degree of knowledge about Component Object Model (COM) and Microsoft Transaction Server. (For more information on these technologies, see the References section at the end of this document.) Explanations are provided for many of the mainframe terms, so little prior knowledge of this environment is required. This document explains how COMTI integrates with and extends the functionality of Microsoft Transaction Server. Several key areas are covered.

Definitions

The COMTI documentation uses several terms that may have different meanings to different people, or may be new to readers from a nonmainframe background. Brief descriptions of some of the more important terms are given below. For additional information on terminology, as well as general COM/MTS information, see the References section at the end of this document.

Transaction

A transaction is an atomic unit of work: either all the actions in a transaction are committed or none of them are. In the MTS environment, a transaction is coordinated by the Distributed Transaction Coordinator (DTC).

Transaction Program (TP)

A transaction program is any CICS or IMS program that uses Advanced Program-to-Program Communication (APPC) to communicate with another TP on a peer-to-peer basis.

Transaction coordinator

A transaction manager that coordinates transactions which span multiple resource managers. Work can be committed as an atomic transaction even if it spans multiple resource managers, potentially on separate computers.

CICS

The IBM Customer Information Control System (CICS) is a transaction manager that supports distributed online transaction processing. It uses APPC/LU6.2 to communicate with other systems.

The high availability and scalability of CICS come from its architecture. CICS is divided into separate regions or address spaces, which can execute multiple threads. A region owns resources (which include users, communications, and TPs), and entries for each resource are held in a table corresponding to the resource type.

IMS

Information Management System (IMS) is a system designed by IBM for data processing. It comprises two elements—the IMS Database Manager (IMS DB) and the IMS Transaction Manager (IMS TM). IMS TM is a message-based, transaction-processing system that can access data in either IMS DB or DB2 databases. Clients place messages in a queue and IMS executes the applications or TPs specified by these messages. This application will access the database and return results back to IMS TM.

COMMAREA

The Communications Area (COMMAREA) is a term used in the mainframe world to refer to an area of memory that is accessible to different programs. It is similar to a data structure that contains both input parameters and returned data.

APPC and LU 6.2

LU (logical unit) 6.2 is an IBM-architected protocol that guarantees compatibility between programs communicating with one another across a network or on the same system. Advanced Program-to-Program Communication (APPC) is an API supported by SNA Server that provides access to the LU in a well-known and standard way.

For a TP system or program to communicate directly with another TP system using APPC, the two programs must first establish an LU6.2 conversation with each other. LU6.2 is the de facto standard for distributed transaction processing in the mainframe environment and is used by both CICS and IMS subsystems.

What is COM Transaction Integrator?

COM Transaction Integrator (COMTI) enables Microsoft Transaction Server to execute applications running under CICS or IMS on an IBM MVS system. COMTI ships with SNA Server version 4.0 and comprises three main elements:

Figure 1. COM Transaction Integrator

The COMTI Component Builder is a GUI-based stand-alone tool. It allows a developer to automatically create a type library from the COBOL declarations of an existing CICS or IMS application, or vice versa. The type library describes the properties and methods of the COMTI component.

The COMTI run-time proxy provides the Automation server for components created by Component Builder. When methods of a COMTI component are invoked, the proxy translates them into messages that can be sent to and understood by the mainframe application. When the mainframe application returns data to the caller, the proxy translates it into a form the COMTI component can interpret.

Elements of a COMTI Transactional Application

Each of the elements in Figure 1 plays a specific role:

How COMTI Extends Microsoft Transaction Server

COMTI Run-Time Activity

Figure 2 shows what happens at run time when a COMTI method is invoked. The diagram illustrates a method called CEDRBANK, which is invoked on an object called Cedar.Bank. Note that all this complexity is completely hidden from the programmer. Each step is explained in the list following Figure 2.

Figure 2. COMTI run-time activity

  1. The Automation client creates an object.

  2. Find out what the CLSID of Cedar.Bank is.

  3. Request the class factory for the Cedar.Bank class.

  4. Request the class factory from TAGEN.DLL.

  5. Create a "generic COMTI class factory" for the class.

  6. Initialize the generic COMTI class factory for the class.

  7. Get the type library information for the class.

  8. Create a type library reader for the class.

  9. The Automation client requests a new instance of the class.

  10. Call the IClassFactory::CreateInstance() method.

  11. The class factory creates a generic COMTI object.

  12. Initialize the  new generic COMTI object.

  13. The Automation client calls a method on the new object.

  14. Get the dispatch ID for the method.

  15. Look up the dispatch ID in the type library.

  16. Invoke the method.

  17. Ask for a state machine to implement the method call with.

  18. Create (or get from a pool) a state machine.

  19. Ask the state machine to execute the method.

Suppose the Automation client was a Visual Basic application. It creates an instance of a COMTI component class. The programmatic ID (PROGID) of the class is Cedar.Bank. To create the object instance, the Class ID (CLSID) is obtained by calling the COM library function CLSIDFromProgID. The class factory can then be obtained from the CLSID by calling CoGetClassObject(). Now things start to get interesting for COMTI.

The CoGetClassObject() function is called with the CLSID for Cedar.Bank as a parameter. The function examines the registry entries for that class and determines that the class (or at least the class factory) is implemented in TAGEN.DLL. It loads that DLL and calls its DllGetClassObject() function, passing the CLSID as a parameter. The DllGetClassObject() function then passes the call to the class factory object for the specified CLSID. Remember that Component Builder does not generate an implementation of the component, just a type library that describes the component. When TAGEN.DLL is asked to create a class factory for a class created by Component Builder, it just creates a generic COMTI class factory.

The generic class factory is then passed the Type Library ID for the component. Effectively it's saying, "make one of these when your CreateInstance() method is called." The new generic class factory reads the type library into memory and completes its initialization. The IClassFactory interface on this new class factory object is returned to the Automation client.

Within the Typelib Cache, CreateLib is a single-instance object (one for all COMTI components) that is a factory for the ReadLib objects. There is a ReadLib object for every COMTI class (not each instance).

The IClassFactory::CreateInstance() method is called to create an instance of the object. The class factory obtains a generic COMTI object. (Note that the generic COMTI objects are pooled). Like the generic COMTI class factory, the generic COMTI object is also given the information from the type library created by Component Builder. It uses this information to implement the object described by the type library.

The object is now ready for use. An IDispatch interface is returned to the Visual Basic application, which can finally call the methods of this interface in the usual way.

It is important to understand that the generic COMTI object is simple and does not know how to do anything specific. The actual work is done by a "state machine" that interprets the type library and makes calls over the LU6.2 connection to execute the appropriate CICS or IMS TP. Like the CreateLib object, the SMDisp is a single-instance factory object for state-machine objects. State machines are pooled and exist for every COMTI remote environment.

COMTI Transaction Support: The State Machine and DTC

Figure 3. Transaction support in COMTI

  1. The Automation client calls an Automation method via IDispatch::Invoke().

  2. The generic object requests a new state machine from the state-machine dispenser.

  3. The dispenser creates a new state machine and associates it with the LU6.2 transport for the remote environment.

  4. The state machine dispenser then enlists the new state machine with the DTC.

  5. The ICedarSM interface is returned to the generic object.

The generic object performs several different tasks. It implements IDispatch for the client and processes method invocations. It also conforms to the MTS programming model and calls SetComplete or SetAbort, as appropriate.

The state machine dispenser is responsible for allocating a connected (and possibly enlisted) state machine that will be pooled according to the target remote environment.

The state machine's primary role is to serve the resource dispenser (ISMCedar), the generic object, and the DTC. It handles access to the conversation and transport objects and performs all parameter marshaling.

The transport object supports both transactional two-phase commit and nontransactional operations. It exposes a generic interface that is used by the COMTI run time to communicate over the LU 6.2 connection.

State Transitions

Figure 4. COMTI state machine

Nontransactional operation

When the COMTI component has its transactional property set to "Does not support transactions" or "Supports transactions" but is not participating in a transaction context, COMTI does not enlist with the DTC. The method is invoked, the connection made, and the work done, which either succeeds or fails, and the method returns. There may be code within the TP itself that provides transactional behavior, but this is completely outside the scope of COMTI. The upper-right section of Figure 4 shows the state transitions for nontransactional operations.

Transactional Operation

The lower-left section of Figure 4 shows the state transitions for a transactional operation in COMTI. When a COMTI component has its transaction property set to "Requires a new transaction," "Requires a transaction," or "Supports a transaction" and is called within a transaction context, COMTI will enlist with the DTC to provide two-phase commit support.

The state machine is typically driven from the Idle state to the Waiting state by a COMTI generic object, and from Waiting back to Idle (through either commit or abort states) by the DTC.

COMTI does not currently support the presumed abort optimization, and in a failure situation DTC will make a decision on whether to abort or commit the transaction based upon the log entries on recovery. In MTS, it is not possible to configure time outs on a per-method or even per-component basis. All transactions will time-out and are rolled back when the globally set MTS time-out period is reached.

Creating a COMTI Component

Creating a Type Library with Component Builder

The COMTI Component Builder is effectively a parser that translates a COBOL copybook from a mainframe TP into IDL and then uses MIDL to compile this IDL into a COM type library. The COBOL wizard assists with the creation of both methods and recordsets. (Recordsets are used to access arrays of COBOL tables.)

The typical stages involved in creating a COMTI component are:

  1. Create a new, empty component.

  2. Specify the type library and interface names.

  3. Import the COBOL declarations.

  4. Specify the mainframe TP.

For example, the following COBOL code would be used to create a component with a single method called CEDRBANK that would take two input parameters, the account name and number, and return a single parameter, the account balance.

IDENTIFICATION DIVISION.
PROGRAM-ID. CEDRBANK.

ENVIRONMENT DIVISION.

DATA DIVISION.

WORKING-STORAGE SECTION.

LINKAGE SECTION.

01 DFHCOMMAREA.
   02  CEDARBANK-COMMAREA.
       03  NAME                   PIC X(30).
       03  ACCNUM                 PIC 9(6).
       03  ACCBAL                 PIC S9(7)V9(2) COMP-3.

PROCEDURE DIVISION.

    MOVE    777.12 TO ACCBAL.

    EXEC CICS RETURN
            END-EXEC.

The procedure for creating type libraries using Component Builder is fully documented in the COMTI online documentation.

Type Libraries and Mainframe TPs

It is important to understand the relationship between the source code for the mainframe TP and the resulting type library. The IDL for the type library generated by Component Builder for the COBOL function above is shown in the following code.

[
   uuid(EEB4EBF1-9A63-11D0-AB6F-00AA00C1479E),
   version(1.0),
   helpstring("CedarBank sample application")
]
library Cedar
{
   importlib("STDOLE2.TLB");

   [
      uuid(EEB4EBF2-9A63-11D0-AB6F-00AA00C1479E),
      version(1.1)
   ]
   dispinterface _Bank
   {
   properties:
   methods:
      long cedrbank(
         [in, out] BSTR* name, 
         [in, out] BSTR* ACCNUM, 
         [in, out] CURRENCY* ACCBAL);

In fact, this IDL has been simplified for the purposes of illustration. Component Builder inserts many custom attribute tags, which describe, for example, fixed-length fields and whether the parameters are actually input or output.

The component, Cedar, has a single dispatch interface called _Bank. It has no properties (other than the standard property NewRecordset handles defining the shape of a recordset for the caller) and a single method, cedrbank. The library and interface names were provided to Component Builder before the COBOL was imported. By default, the method name is the same as the COBOL PROGID. The three parameters to the method—NAME, ACCOUNT, and ACCBAL—reflect the contents of the COMMAREA.

Data type conversion

In the previous example, Component Builder has made the following type conversions from COBOL to Automation data types shown in Table 1.

Table 1. Type Conversions

Parameter COBOL Type Automation Type
NAME PIC X(30) String
ACCNUM PIC 9(6) String
ACCBAL PIC S9(7)V9(2) COMP-3 Currency

Complete lists of data-type mappings both from COBOL to Automation and from Automation to COBOL are provided in the COMTI online documentation.

Arrays and recordsets

When importing COBOL into Component Builder, it will automatically create recordsets when a fixed- or variable-length table (occurs clause) is encountered. The table must conform to recordset rules—it cannot contain nested tables, which cannot be flattened, and it cannot contain arrays. (In a future version of COMTI, this default behavior will change and these will be treated as arrays of user-defined types). The following code is an example of a COBOL declaration that would require a recordset as its return value in the COMTI component.

01 CUSTOMER-DATA.
    05 CUSTOMER-NUMBER                 PIC 9(9).

    05 INVOICES OCCURS 50 TIMES.                         
       10 INVOICE-NUMBER               PIC 9(9).
       10 INVOICE-DATE                 PIC 9(7) COMP-3.
       10 INVOICE-AMOUNT               PIC S9(13)V9(2) COMP-3.
       10 INVOICE-DESCRIPTION           PIC X(40).
    05 LAST-NAME                       PIC X(20).
    05 FIRST-NAME                      PIC X(20).

The resulting method would be:

GetInvoices(lCustomerNumber As Long, strLastName As String,
 strFirstName As String) As Object

The following code example shows how a Visual Basic application might call this method.

Dim objInvoices As ADODB.Recordset
Dim lCustomerNumber As Long
Dim iRow As Integer
Dim iCol As Integer
Dim strLastName As String
Dim strFirstName As String
Dim Data As Variant

Set objCustomer = CreateObject("Customer.Invoicing.1")
lCustomerNumber = CLng(txtCustomerNumber)
Set objInvoices = objCustomer.GetInvoices( _
    lCustomerNumber, _
    strLastName, strFirstName)

Data = objInvoices.GetRows
grdInvoices.Rows = UBound(Data, 2) + 1
grdInvoices.Cols = UBound(Data, 1) + 1
For iRow = 0 To UBound(Data, 2)
    grdInvoices.Row = iRow
    For iCol = 0 To UBound(Data, 1)
        grdInvoices.Col = iCol
        grdInvoices.Text = Data(iCol, iRow)
    Next iCol
Next iRow

Configuring COMTI and Microsoft Transaction Server

The COMTI Remote Environment

The COMTI remote environment is an abstraction layer that hides the connectivity details from the component. It is simply the glue that tells COMTI what connection to use to access which mainframe TP (CICS or IMS region) for a given component. A remote environment contains three pieces of information, which are configured within SNA Server:

Setting Transaction Properties on a COMTI Component

When a COMTI component is created within Component Builder, its default transaction property should be set to one of the four MTS types:

The appropriate transaction property will depend greatly upon the type and behavior of the mainframe TP being used. Table 2 is a summary of which transaction properties are supported by which type of mainframe TP.

Table 2. Transaction Property Support

Property CICS-LINK CICS IMS
Requires a transaction Yes Yes* No
Requires a new transaction Yes Yes* No
Supports transactions Yes Yes* No
Does not support transactions Yes Yes Yes

* CICS applications can participate in MTS transactions only if they have been written to include transactional support.

Configuring Security

To run applications on the mainframe, COMTI must be capable of passing security credentials to the mainframe. There are a number of options for configuring security on a COMTI component that can be configured by the Administrator. In the case of explicit security, however, changes must be made to the application at design time. Security options are set in MTS as properties of a package.

User-level security

If user-level security is enabled, the COMTI run-time proxy provides the client identity to SNA Server before allocating the LU6.2 conversation. SNA Server can then use the client's identity (the identity of the client that invoked the MTS component and resulted in the invocation of the COMTI component) to map to a mainframe credential (using the single sign-on capability of SNA Server) to authenticate itself with the mainframe.

Package-level security

When using package-level security, the COMTI run-time proxy uses the single sign-on support in SNA Server to authenticate itself to the host using the Windows NT security identity of the MTS package. The package containing the COMTI component must be configured in MTS to run under a Windows NT account that maps to valid mainframe credentials. This option is the most efficient security model, taking full advantage of MTS roles and integrating with both the Windows NT and mainframe security environments. This method should be used wherever possible.

Explicit security

An administrator can allow COMTI to override the specified User ID and password and provide its own. Users can then be expressly queried for their credentials and different rights granted on both MTS and the mainframe TP. This has to be done via a callback method in the application, and the decision to use this form of security must be made at design time. An example of how to do this is given in the Cedar Bank demo on the SNA Server CD.

Although SNA Server and a third-party product make it possible to synchronize Windows NT and mainframe user accounts and passwords, this remains a complex and inflexible option.

Using a COMTI Component

Simple Usage

The COMTI component can be called directly or placed in a wrapper component. Either can be called from any Automation client in the usual way. An example of how the component from the section, Creating a COMTI Component, can be called is shown in the following code.

Dim objBank As Object  
Dim curRetBalance As Currency
Dim rv As Long

This same code could be used from any application that supports Microsoft Visual Basic for Applications or Visual Basic Scripting Edition (VBScript). For example, the same component could be used to place data into a Microsoft Excel spreadsheet, a Microsoft Word document, or a Web page with just a few lines of very simple code.

Wrapper Components

It is necessary to pass all input and output parameters when calling a COMTI method. This can become extremely tedious, especially when (as is the case with many mainframe TPs) there are a lot of parameters. It can be worth creating a "wrapper component" that exposes a simplified set of methods that do not require all parameters to be passed.

For example, the cedrbank method might have a wrapper with two methods—SetAccountDetails (which would take the account name and number) and GetBalance (which would return the account balance for the currently set account). The advantages of a wrapper component become more apparent in complex examples where many output parameters are being returned, such as account balance and recent account activity.

The wrapper should create the COMTI component using the CreateInstance method of the MTS object context to ensure that any transaction context is passed from the wrapper to the COMTI component.

Extending COMTI Components

With COMTI and MTS, developers can extend transactions from the Windows NT Server environment to the mainframe. Microsoft Windows®–based applications using MTS can include CICS applications in MTS coordinated transactions. The example scenario presented at the end of this document shows how this can be done.

Issues and Limitations

Many of the issues and limitations of COMTI come from restrictions imposed by the Windows NT or mainframe programming environments. Some of the more common issues encountered are discussed below, but the list is by no means exhaustive.

Issues for transactional support

COMTI version 1.0 does not support transactions for IMS. A future version of COMTI will provide transactional support for versions of IMS that themselves support transactions (version 6.0 and later).

When a COMTI component is to be used within a transactional MTS application, the mainframe TP cannot contain explicit SYNCPOINT commands. In such an application, the DTC must have control over when an operation will commit or abort, but the explicit SYNCPOINT command would prevent this. The mainframe TP may form an atomic unit of work, but it may also, unknown to itself, form only part of a transaction. Only the DTC can know whether all work in the transaction can be committed, so only the DTC can decide to instruct COMTI to perform an SYNCPOINT. This is not an issue for components that do not form part of an MTS transaction.

Automation limitations

Because COMTI components are called through an Automation interface, the current limitations of Automation apply to all COMTI components. The major issue here is the data types that can be supported.

Language limitations

Although you can write applications which call COMTI components in many different languages, bear in mind that they will have different limitations that may affect how you create your COMTI component. For example, some languages limit the number of parameters on a method call, which may require the component to return a recordset instead of separate parameters.

An Example Scenario Using COMTI

The Friendship Insurance sample is shipped with SNA Server 4.0. The top-level component in this sample is called ProcessClaim. This component uses other components to make changes to both the DB2 and Microsoft SQL Server™ databases as a single MTS transaction. In the example, a ProcessClaim object is created by an Active Server Page (ASP). The code for this ASP is in the Friendship Insurance tutorial directory under \WEBSTE\DEFAULT.ASP. The line of VBScript code that creates the ProcessClaim component reads:

Set myobj = Server.CreateObject("PrcClaim.ProcessClaim")

Because the ProcessClaim component is installed under MTS with the "Requires a new transaction" option set, the call to CreateObject automatically starts a transaction under the DTC. The current object context is enlisted with this new transaction.

Next the VBScript code calls myobj.ProcessClaim. A simplified and annotated version of the method code is shown below. The method takes several parameters describing an insurance claim, for example, the customer's social security number, date the incident occurred, date of birth, and amount of claim. The method allocates claim numbers and adds claim details to both the DB2 and SQL Server databases.

Public Function ProcessClaim( _
    lngCustSSN As Long, dtmDateOfService As String, _
    dtmDateOfBirth As String, szIncident As Long, _
    szProvider As String, curAmount As Double,
    szComment As String) As Long

First, the current object context interface is retrieved by a call to GetObjectContext. (This function is implemented by MTS. It is a global function exported by the MTS DLL.) This object context can be used to create other objects within the same transaction context.

Dim ctxObject As ObjectContext
Set ctxObject = GetObjectContext()

The first task is the assignment of a claim number. This number is assigned by a TP executed on a mainframe. CLAIMKEY is a COMTI component created from existing CICS TP COBOL declarations using Component Builder. An instance of the CLAIMKEY component is created with the current object context, so it becomes associated with the transaction started when the ProcessClaim object was created. The call to ctxObject.CreateInstance() corresponds to steps 1 through 12 in Figure 2, earlier in this document: creation of a generic COMTI object, and associating it with the type library for the CLAIMKEY component.

Dim objCK As Object, lRet As Long
Set objCK = ctxObject.CreateInstance("CLAIMSRV1.CLAIMKEY.1")

The GetNewClaimKey method of the component is used to obtain a new claim number. This corresponds to steps 13 through 19 of Figure 2, and steps 1 to 5 of Figure 3: creation of a state machine, enlistment of the state machine with the DTC, and invocation of the mainframe TP that implements the method.

lRet = objCK.GetNewClaimKey(lngClaimNo)

After the method call returns, the state machine is left in the Waiting state.

If the previous operation was successful, a claim summary record is then inserted into a SQL Server table. This insertion is done using another MTS component which, again, is created with the same object context to allow the transaction outcome to be controlled.

If lRet >= 0 Then
    Dim objICS As Object
    Set objICS = ctxObject.CreateInstance("ClaimsSrv.InsertClaimSummary")
    lRet = objICS.InsertClaimSummary( _
        lngClaimNo, dtmDateOfService, _
        szStatus, lngCustSSN, szProvider)
End If

If the previous operations were successful, a claim record is inserted into a DB2 table. This is done by another COMTI component, INSERTCLAIM, again created from existing CICS TP COBOL declarations. Again, an instance of the component is created with the same context. This means the creation of another COMTI generic object, but this time it is associated with the type library describing the INSERTCLAIM component. When the InsertClaim method is called, this generic object obtains another state machine, enlists it with the DTC, and executes the mainframe TP, leaving this state machine also in the Waiting state.

If lRet >= 0 Then
    Dim objIC As Object
    Set objIC = ctxObject.CreateInstance("CLAIMSRV2.INSERTCLAIM.1")
    lRet = objIC.InsertClaim( _
        lngClaimNo, dtmDateOfService, _
        dtmDateOfBirth, curAmount, _
        szComment, lngCustSSN, _
        szIncCode, szProvider)
End If

Finally, if everything worked, SetComplete is called. If something failed, SetAbort is called.

If lRet >= 0 Then
    ctxObject.SetComplete
Else
    ctxObject.SetAbort
End If

If SetComplete is called, the DTC goes through the commit process for each item of work enlisted for the transaction. For the state machines, this means moving through the Preparing, Prepared, and Committing states and then back to Idle.

If SetAbort is called, the DTC goes through the abort process for each item of work enlisted for the transaction. The state machines move through the Aborting state and then back to Idle.

Figure 5. Friendship Insurance SetComplete and SetAbort scenarios

References

David Chappell. "How Microsoft Transaction Server Changes the COM Programming Model," Microsoft Systems Journal, January 1998.
www.microsoft.com/msj/0198/mtscom/mtscomtop.htm

Dale Rogerson. Inside COM. Microsoft Press®, 1997.

Don Box. Essential COM. Addison Wesley Longman, Inc., 1998.

Steven Gray and Rick Levano. Microsoft Transaction Server 2.0. SAMS Publishing, 1997.

Philip Bernstein and Eric Newcomer. Principles of Transaction Processing. Morgan Kaufmann, 1997.

For general information

www.microsoft.com/com/

http://www.microsoft.com/ntserver/appservice/default.asp

www.microsoft.com/sna/default.asp

http://mspress.microsoft.com/