asp tutorials, asp.net tutorials, sample code, and Microsoft news from 15Seconds
Data Access  |   Troubleshooting  |   Security  |   Performance  |   ADSI  |   Upload  |   Email  |   Control Building  |   Component Building  |   Forms  |   XML  |   Web Services  |   ASP.NET  |   .NET Features  |   .NET 2.0  |   App Development  |   App Architecture  |   IIS  |   Wireless
 
Pioneering Active Server
 Power Search





Active News
15 Seconds Weekly Newsletter
• Complete Coverage
• Site Updates
• Upcoming Features

More Free Newsletters
Reference
News
Articles
Archive
Writers
Code Samples
Components
Tools
FAQ
Feedback
Books
Links
DL Archives
Community
Messageboard
List Servers
Mailing List
WebHosts
Consultants
Tech Jobs
15 Seconds
Home
Site Map
Press
Legal
Privacy Policy
internet.commerce














internet.com
IT
Developer
Internet News
Small Business
Personal Technology

Search internet.com
Advertise
Corporate Info
Newsletters
Tech Jobs
E-mail Offers

HardwareCentral
Compare products, prices, and stores at Hardware Central!

Introducing Transaction Server
By Alex Homer
Rating: 4.5 out of 5
Rate this article


  • email this article to a colleague
  • suggest an article

    Introduction


    Now that we've examined the core concepts of DNA and seen the kinds of components that we can use within its architecture, we can move on to look at where Microsoft Transaction Server (MTS) comes into the picture. Unfortunately, Microsoft momentarily lost their legendary knack of giving their products really useful and meaningful names when they christened this little baby. Yes, MTS does have something to do with 'transactions', but in fact it does a lot more useful things as well.

    In this chapter we'll start off by looking at what MTS is, and why it's suddenly become one of the hottest new technologies around at the moment. We'll also discuss what transactions are, and you'll see why the name Transaction Server is a misnomer for this exciting new technology.

    Then, once we've got to grips with the theory of MTS, we'll move on to look at how we use it in our applications. We'll show you just how easy it is to take advantage of its services with existing components, and we'll also be adding MTS integration to the simple WCCFinance component we developed in the previous chapter. To start with, let's look at the background and basic principles of MTS.

    What is Transaction Server?

    MTS is an integral part of Windows NT, and is installed by default as part of the operating system in NT5. It is aservice in much the same way as Internet Information Server or the File and Print services that we now take for granted. In other words, it is part of the system that is available in the background whenever one of our applications requires it.

    Control and configuration of MTS is via either a snap-in to the Microsoft Management Console, or through the HTML administration pages that are included with MTS. This is very similar to the interface provided for Internet Information Server 4, and gives an integrated management function that is useful when building and setting up distributed applications.

     

    At the time of writing, MTS was available as an add-on to Windows NT4 in the form of the NT4 Option Pack, or as part of the Windows NT4 Enterprise Edition. The Option Pack can be installed over the top of Windows NT4, requiring Service Pack 3 and Internet Explorer 4.01. Both are included in the Option Pack and are installed automatically.

    What Does Transaction Server Do?

    To understand what MTS is and what it does, we need to first make one very important point clear. This software should really have been named Microsoft Component Server, not Microsoft Transaction Server. MTS is all about managing the way applications use components, and not just about managing transactions. Yes, transactions are a big part of many applications we write and MTS can help to manage these—but MTS also provides a very useful service for applications that don’t use transactions at all.

    We're already tossing about words like 'transactions'—which are short on real meaning at the moment. To be able to define MTS accurately, we first need to understand what goes on inside it in the most basic way. That's what we'll do in this section, and you won't see anything more about transactions until later in the chapter.

    We'll start by looking at the way we traditionally use components within our applications. This will help you to understand how and why MTS can make the whole process more efficient.

    Using Components Without MTS

    When we use a component in an ASP page, for example, we have to create it before we can use it. The sequence of events is:

     

    Instantiate the component to create an instance of it

    Initialize the component instance, so that it is ready for use

    Use the component instance within our page

    Destroy the component instance after use

    For example, in the previous chapter, we created and used an instance of the WCCFinance component like this:

    
    
    
    

    ...

    Set objFinance = Server.CreateObject("WCCFinance.PaymentTerms")

    objFinance.TotalPrice = Request.Form("txtTotalPrice")

    objFinance.InterestRate = Request.Form("txtInterestRate")

    objFinance.MonthlyPayment = Request.Form("txtMonthlyPayment")

    intResult = objFinance.GetNumberPayments

    'rest of page

    ...

    The Problem - Holding Component Instances

    Although we didn’t destroy the component explicitly in our page, it is implicitly destroyed as soon as the last reference to it is lost—i.e. when our page finishes executing, the results have been sent to the client, and the ASP code is removed from memory. Because we held the reference until our page was complete, the component was retained in memory. Even while it's not being used, it is taking up resources on the server.

    In a multi-user environment, where there are high demands on server resources, it makes sense to destroy each instance of a component as quickly as possible. We could have improved the behavior of our page by adding a line that destroys it directly after the call to the method that provides the result:

    
    
    

    Set objFinance = Server.CreateObject("WCCFinance.PaymentTerms")

    objFinance.TotalPrice = Request.Form("txtTotalPrice")

    objFinance.InterestRate = Request.Form("txtInterestRate")

    objFinance.MonthlyPayment = Request.Form("txtMonthlyPayment")

    intResult = objFinance.GetNumberPayments

    Set objFinance = Nothing

    'rest of page

    However, if we decide that we need to use the component again later in the page, we will then have to create a new instance. We'll also have to set all the properties again, because the new instance will contain the default values. In this case we often tend to hang on to our object instead of explicitly destroying it, because it is far slower and much less efficient to keep recreating it.

    It's also possible to hold on to component instances throughout the life of an application or user session in ASP, by defining it in global.asa. This is fine if it is used very regularly, but forces it to stay in memory for the life of the application or session.

    Holding Database Connections

    While holding on to component instances is bad enough, there is an even worse scenario. Most applications require access to a data store of some kind, usually a database such as Oracle, SQL Server, Sybase, etc. To provide database access in a component, we generally create a connection to the appropriate database through an OLE-DB or ODBC driver.

    Database connections are expensive things to hang onto. There are a limited number available in any system, and they also take time and server resources both to create and maintain. Holding onto one in your ASP page is fine until you get a lot of users. At this point the database driver will begin to refuse connections, or the server will run out of resources. On the other hand, creating them over and over again slows the application down. Take, for instance, this pseudo code:

    get a database connection

    read some records

    process some user-input values

    process the record values

    store the updated records

    read some more records

    calculate values for new records

    create and store the new records

    close and free the database connection

    All the time this code is executing it holds onto the database connection, preventing other pages from using it. What we should really be doing is:

    get a database connection

    read some records

    close and free the database connection

    process some user-input values

    process the record values

    get a database connection

    store the updated records

    read some more records

    close and free the database connection

    calculate values for new records

    get a database connection

    create and store the new records

    close and free the database connection

    Now, the connection is available to other users while we are doing some other processing in our code. The problem is that the page will perform far more slowly. To help in this situation, many database drivers implement connection pooling to minimize the delays.

    Database Connection Pooling

    Rather than have to create a new database connection each time a user requests one, many database (or data store) drivers, such as the ODBC 3.0 or later driver for SQL Server, provide connection pooling. After a connection has been created, used by an application, and then freed when the application has finished with it, the driver holds the connection in memory in a pool.

    When another application requests a connection the driver software searches the pool of available unused connections and, if it finds one that matches the requirements, supplies this instead of creating a new one. This provides much faster response. However, applications still have to acquire, use, and then free the connections, and this is still a time-consuming business.

     

    Note that connection pooling is not enabled by default in IIS 3, as it is in IIS4. In IIS 3 it can be enabled via a Registry setting—check out the IIS documentation for more details.

    Providing Persistent Context for Components

    MTS gets round the problems of component creation and destruction by providing a pool of component instances in much the same way as the data store drivers can provide a pool of available connections. However, it does this in a much more intelligent and comprehensive way.

    MTS can provide a component to an application on demand, and allow other applications to use the component when the first one is just hanging on to the component instance, but not actually using it. It does this by fooling the application into thinking it still holds a reference to the component, when in fact MTS has spirited it away while the application wasn't looking and given it to someone else.

    If the application suddenly wants to use the component that it thinks it's holding a reference to, MTS rushes around the pool of available instance and steals one from another application. Only if there are no available instances does it create a new one, and hand it over to the first application.

    This rather risky-sounding technique is called persistent context, and it works because MTS provides a substitute 'dummy' context to the application, which thinks this is a real component. What it's actually got, of course, is just an empty shell...

    How MTS Provides Persistent Context

    To see how MTS provides persistent context, take a look at this following series of events. Imagine we have three clients that at some point require access to a component that is on the server, and which already provides instances to clients. In the following diagram (stage one) only clients A and B are using instances of the component. Client C has not yet created an instance of the component:

     

    What's happened is that MTS has intercepted the calls from A and B that create the component instances (in our code earlier this was CreateObject), and instead created virtual context objects and returned references to these, rather than references to the component instances themselves. By reading the interface of the component it can (as we saw in the previous chapter) tell what functions the component provides, and thereby impersonate this interface in the context object.

     

    A context object is not actually a physical object in itself. In COM terms, MTS creates a context and class factory wrapper for the component, and links this to a class factory object. However, the way it is actually implemented internally is not important here.

    Now (at stage two), client A has finished using the component, but has held on to the reference to it—in other words it hasn’t destroyed it by setting the object variable it received when it created the instance to Nothing. What it's actually holding on to, of course, is the context object and not the component. MTS reclaims the component instance and returns it to the pool of available instances. At the same time, client C has requested an instance of the component, so MTS creates the context object for client C and links it to the instance of the component that it just took away from client A:

    But, in stage three, client A wants to use the component again, maybe by calling another method within it. Unfortunately client C is still using it, but client B has temporarily finished with its instance of the component so MTS can grab this one and pass it to client A—by pointing A's context object to this instance. Notice that client A will use this component instance without being able to tell that it's a different instance to the one it used last time:

     

    So, MTS can provide a seamless context for each client application for the life of the page or application process. The clients don’t realize that the objects they are referencing are being constantly shuffled around behind the scenes. Like a good magic show, it's all smoke and mirrors.

    State and the Component Context

    The big question is, of course, how does MTS know when an application has finished using a component instance? Obviously when the application tries to destroy the object either explicitly (with " SIZE=1>Set objectvariable " SIZE=1>= " SIZE=1>Nothing) or implicitly when the page or process is complete, MTS knows that the object can be returned to the pool and the application's context object is then destroyed. But this is only half the story.

    We looked at how an application may hold on to an instance of a component right through its lifetime. However, it may not actually be using the component all the time, and this is just the point where MTS needs to be able to reclaim the object and let other applications or processes use it. This is done by a couple of special lines of code added to the component.

    When a component has finished a task, it calls the SetComplete or SetAbort methods of its context object, which effectively tell MTS that the component has finished the task and that it no longer requires its internal state to be maintained—i.e. all the data it was holding has either been returned to the client or stored permanently on disk. At this point MTS knows that it can reclaim the component and use it again elsewhere. When the original application comes back to use the component again, MTS can provide a fresh copy of it with default values for all the properties, etc.

    This last point is vitally important to bear in mind. MTS can only reuse a component when the client application no longer expects the state to be the same as before. In other words, property settings and other information stored inside the component will probably not be the same as the last time it used the component. The application must reset any values that it needs by, for example, setting all the properties again.

    It's important to realize that it is the design of a component that dictates whether it requires its internal state to be maintained in order to function properly. Essentially, an object (in the traditional sense of the word) is a collection of data and methods that act upon the data. Most traditional client/server components follow a stateful paradigm, in which an object retains its data and methods in memory. Such components are created early and kept around for the entire duration of processing—and only released when the process completes.

    Component Caching and Pooling

    Originally it was suggested that MTS would provide pooling of component instances, as hinted at in the section above. In fact in the current release—and probably for the foreseeable future—this is not actually the case. The CanBePooled method that MTS implements for each installed component has no effect at present.

    The non-implementation of object pooling arises because all components currently used in MTS run under the Apartment threading model. However, if you mark the objects as Both threaded (only C++ or J++ support this, and not VB) then these object will be able to be pooled when (and if) MTS does support this feature in the future.

    MTS will cache the component itself, but it destroys instances of it when they are freed by the application that uses them; in other words when the application ends (or the ASP page is complete), or when the application or ASP code releases it's reference to the component explicitly. This may, for example, be the result of executing the line Set objectvariable = Nothing.

    However, MTS can efficiently create new instances of the component on demand from the cached object, so the lack of component instance pooling is not generally a disadvantage. In the cached state, a component is said to be deactivated. By default MTS maintains a component object in this state for three minutes after the last time it was referenced by an application, though this delay can be changed. The process of caching components like this is often referred to as Just-In-Time (JIT) Activation.

    Transactions Within MTS

    MTS evolved as a Transaction Processing (TP) system to provide on Windows NT the same kinds of features as available in CICS®, Tuxedo®, etc. on other platforms. These are purely designed for creating stable transactional environments for data sources—predominantly relational databases like IBM's DB/2, Oracle, Informix and Sybase. Microsoft Transaction Server already offers support for SQL Server and Oracle, and will be extended to cover the other mainline database systems in the future.

    Distributed Transactions

    The first thing to consider is why we might want to extend transactions to MTS, instead of using the traditional database-implemented transactions we discussed earlier. The most obvious is that transactions within a database system can only encompass that database, although they can—of course—span different tables within it.

    In the real world, we often need to carry out transacted operations that span different databases, which may be on different servers and even at different sites. Think back to the Wrox Car Co application where, as you saw earlier, we need to update both a local database on the showroom server and a remote database at the head office. Both updates must fall within the same transaction to make any sense. MTS can do this for us (almost) automatically.

    Resource Managers and the DTC

    When MTS (or SQL Server) is installed, a separate service is added to Windows NT called the Distributed Transaction Coordinator (DTC). This runs as a service under Windows NT, and is used to coordinate transactions that span separate data stores or resources. To work, it requires each data store to implement its own proprietary Resource Manager (RM). SQL Server and most major databases already include these, though at present not all are compatible with the Windows NT DTC. They are generally part of the driver software—perhaps OLE-DB but more often ODBC at the moment.

    The common protocol for communication between the DTC and the resource managers is , developed by the X/Open group. MTS supports this, and also it's own protocol called OLE Transactions (currently only available for SQL Server). Notice, however, that we talk about 'data stores' rather than databases—one of the aims of Microsoft's Universal Data Access initiative is to provide seamless access to all kinds of data. OLE-DB drivers are becoming available for of Microsoft's own applications, such as the Active Directory Service and Exchange message stores.

    To understand how a distributed transaction works, look at the following diagram. When a component requests access to a data store, the data store driver (usually OLE-DB or ODBC) checks with the appropriate MTS context object to see if a transaction is required. If it is it informs the DTC, and then contacts the resource manager for the data store and tells it to start a transaction within that data store—in other words it automatically starts an integral 'database' transaction for this operation. Then the component (in this case via ADO) can work with the data on this device through the driver software; adding, updating and deleting records as required:

     

    In the simplest case, where the component only accesses this data store and no other, it can call the SetComplete (or SetAbort) method once all the actions on the data are complete. This tells the DTC that the transaction is complete, and (under XA protocol) it instructs the driver software to commit or abort the transaction as appropriate. If the system is using OLE Transactions, the DTC can communicate directly with the resource manager to commit or abort the transaction instead of getting the driver to do it.

    Now consider the case where we have two data stores to update within one transaction. In this case, the DTC and driver software start the database transaction in the first data store via the resource manager, as before. Then, once the component opens a connection to the second data store, the DTC and driver software contact this resource manager and tell it to start a new internal transaction for the updates that follow. Now both data stores are holding open transactions with the updates:

     

    Once the component calls the SetComplete or SetAbort method the DTC contacts (via the driver software if XA is being used) both resource managers and instructs them to commit or abort their current transaction. In this way, the transaction within MTS has been expanded to include the remote data stores as well. Either all the updates on all the data stores will succeed, or all will be rolled back.

    Controlling MTS Transactions Explicitly

    The context object that is created when a client application references a component in MTS provides information to MTS, the client, and other software such as the data source driver and the DTC. This information includes the 'transaction' setting for the component, which depends both on the properties set when it was installed in MTS and the code in the application—such as an ASP page that uses the component.

    As well as calling the SetComplete and SetAbort methods, a component can exert control over how transactions work. For example it can call the DisableCommit method to prevent the DTC allowing a SetComplete or SetAbort method to commit open data store transactions on all resource managers until further notice. When it’s ready to commit or abort the transaction, it can call the EnableCommit method.

    Creating Objects Within Transactions

    Often, as it executes, a component needs to create instances of other objects with which to work. For example, an order handling component may need to create instances of a 'customer' object to get information about the customer that is placing the order. In VB, there are two main ways to create an instance of another object, New and CreateObject. MTS adds another method, CreateInstance. We’ll look at the differences in the next section, where we examine all the methods that MTS provides in more detail.

    Using Components With MTS

    Having seen in outline what MTS does, we'll now look in more detail at how we adapt components to work within it, and then how we adapt our applications to use the components once they are installed there. Later in this section of the chapter we'll convert our simple WCCFinance component to take advantage of MTS. First, however, we'll examine the theory of designing, building and using components with MTS in more depth.

    Component Design Principles For MTS

    We don’t actually have to do anything to a component to use it in MTS, other than install it in a Package—as you'll see in a while. The component will benefit from the fact that MTS will run it in its own memory space separate from the Web server, and MTS will also control activation and deactivate to make it available to applications more quickly.

    However, to feel the real benefits of MTS, we need to adapt our components and applications by adding MTS-specific code to them. This is easy enough to do, involving generally only a few lines. There are some rules that we need to abide by to get the best performance from MTS, and there are also two different MTS interface objects that we can work with.

    Getting the Best Performance From MTS

    The three main points to remember when you create applications or components for use in MTS are:

     

    Acquire a pointer or reference to any components that you will use as early as possible. In other words, create the object and store the reference to it throughout the life of the application, or until you are sure that it will not be required again. In Visual Basic, this means using the CreateObject statement (the New keyword should not be used to create instances of components that reside in MTS, as you'll see shortly). Because all you are holding is a reference to a context object, and not an instance of a real component, this is low on resource use. As you saw earlier, existing instances of the component will be dynamically shared between the applications or components that require it.

     

    Acquire any data store (i.e. database) connections that you need as late as possible, and free them as soon as you have finished with them and as often as possible in between. In most cases, data connections are pooled so that all applications can share them. This works best if applications only hold on to a connection for the minimum period possible. Also try and use the same connection information (such as username and password) where possible, because connections with different values for these cannot be shared, meaning there is less chance of them being reused from the pool. We'll be looking at usernames and passwords in detail in Chapter 7.

     

    Call the SetComplete method inside a component or application as often as possible to allow MTS to recover the component instance and use it with another application. The component reference you are holding on to remains valid, but the component instance is not held in memory waiting to be used.

    Using MTS Interface Objects In A Component

    There are two main COM interface objects available in MTS that we can code to, and between them they allow us to control how our applications and components behave within the MTS environment.

    The ObjectContext Interface Object

    The most useful interface the MTS provides for our components is that of the context object that is allocated to our application for this component. Every context object exposes the COM interface IObjectContext. There is a global MTS method GetObjectContext() that returns a reference (or interface pointer) to the context object. In a Visual Basic component, we normally use this as follows:

    Dim objOContext As ObjectContext

    Set objOContext = GetObjectContext()

    Then we can use the methods of the context object to:

     

    Commit or abort the current transaction

    Enable and disable the current transaction from being committed

    Check out the security clearance of users of the component

    We'll be looking at the whole subject of security in a later chapter. For the meantime, we'll concentrate on how we use the other methods within our components. The SetComplete and SetAbort methods, as we've seen, are used to indicate to MTS whether our component is happy with the outcome of its operations, for example:

    objOContext.SetComplete

    tells MTS that we have completed all the operations we intended, and that we are happy that everything went well. As far as this component is concerned, the complete transaction can be committed. If something went wrong, however, we call:

    objOContext.SetAbort

    This tells MTS that we aren't happy with the outcome of the operations we performed, and that it is to abort the entire transaction and roll back all the changes made by all the other components within this transaction.

    If we are embarking on a series of operations, which at certain points could leave data in an indeterminate state, we can tell MTS that the current transaction is not to be committed under any circumstances until we're ready. To do this we with:

    objOContext.DisableCommit

    Then, when we're ready we can call:

    objOContext.EnableCommit

    to indicate that MTS can commit the transaction, but should not release and reuse this object (if we called SetComplete, MTS would destroy our object instance).

    Whether the transaction is committed will depend, of course, on whether all the components within the transaction (including ours) have signaled that they are also happy to commit as well.

    Creating Component Instances

    The context object also provides the CreateInstance method, which we can use to create dependent instances of other objects, for use by our component. This doesn't stop us using the New or CreateObject methods, but it's important to understand the effects of each one as far as MTS is concerned.

    The New keyword in VB is used to create a new instance of a class as an object, for use within the current application. When used in a component within MTS, it creates a Private instance of an object that MTS knows nothing about. It won't have it's own context object, and won't be included within the current transaction. In other words, it has to look after itself with no help from MTS.

    The CreateObject method is almost exactly the opposite. When used in a component running within MTS, this creates a new instance of an object that MTS will treat as separate from the current component instance—and it will get it's own context object. However, this will not contain any information from the context of the component that created the new instance, so it will run outside the current transaction.

    The CreateInstance method of the context object provides a solution to these two problems. It creates a new instance of the referenced component and provides it with a new context object. However, it also copies the transaction information from the context of the object that created it into the new context—thus making it part of the current transaction:

    objOContext.CreateInstance("MyClasses.Customer")

    The ObjectControl Interface Object

    The second COM interface, named IObjectControl, is that of the component class factory, as stored in MTS when the component is installed. This is useful for carrying out tasks during activation and deactivation of the component. In Visual Basic, we implement this interface within our component using the Implements keyword:

    Implements ObjectControl

    Our ObjectControl interface must provide three methods, Activate, Deactivate and CanBePooled. Together they provide a way for us to control how our object interacts with MTS. The Activate method is called when an instance of our component is created—either for the first time or from the cached object pool if it has been deactivated after use. The Deactivate method is called just before it is returned to the cache after use. The CanBePooled method is used to tell MTS whether the component instance can be pooled or not; though remember that instance pooling is not currently supported—only a limited form of component caching is implemented at present.

    Whether we implement it is entirely optional, but if we do need to carry out any processing when the component is activated or deactivated, we have to implement it so that MTS will provide the Activate and Deactivate events.

    To see how we can use the ObjectControl interface, look at the following code. It acquires a reference to the context object as soon as the component instance is activated, and tells MTS that it will be OK to pool this instance if Microsoft ever gets round to implementing support for it:

    
    
    

    Implements ObjectControl

    Private objOContext 'global variable to hold the object context

    Private Function ObjectControl_Activate()

    'get the object context as soon as instance is activated

    Set objOContext = GetObjectContext()

    End Function

    Private Function ObjectControl_Deactivate()

    'release the object context

    Set objOContext = Nothing

    End Function

    Private Function ObjectControl_CanBePooled()

    ObjectControl_CanBePooled = True

    End Function

    Of course, we can do any other initialization and clean-up we need to in the Activate and Deactivate methods. If the object needs to maintain state for any reason (though it generally shouldn't) we can save and reload values here. We can also use them to create and destroy instances of any other objects we need to use.

    
    

    Implements ObjectControl

    Private objOContext 'global variable to hold the object context

    Private objCustomer 'global variable to hold customer object

    Private Function ObjectControl_Activate()

    'get the object context as soon as instance is activated

    Set objOContext = GetObjectContext()

    Set objCustomer = objOContext.CreateInstance("MyClasses.Customer")

    objCustomer.LoadValues 'custom routine to load state from disk

    End Function

    Private Function ObjectControl_Deactivate()

    'release the object context

    objCustomer.SaveValues 'custom routine to save state to disk

    Set objCustomer = Nothing

    Set objOContext = Nothing

    End Function

    Starting a Transaction

    The example component we've been using so far, and which we'll continue to use in this chapter, is a stand-alone server component. It doesn't require a transaction, because only this component is used in the application. We're really only using MTS to get the benefit of its component instance management features. However, this isn't usually the case—we'll often have two or more components in use and these need to be part of a transaction.

    Component Transaction Support Options

    MTS allocates a Transaction Support property to each component installed within it. This property has four possible settings, and defines how the component will behave within MTS when activated. The options are:

    Requires a transaction

    The component will run within an existing transaction if one already exists. If not MTS will start a new one.

    Requires a new transaction

    MTS will start a new transaction each time an instance of the component is activated.

    Supports transactions

    The component will run within an existing transaction if one exists. If not, it will run without a transaction.

    Does not support transactions

    The component will always run outside any existing transactions.

    So, we could set the Transaction Support property for one component (let's call it A) to 'Requires a new transaction' and the other (B) to 'Requires a transaction' (or even 'Supports transactions'). The only thing now is that we would have to be sure to always instantiate them in the right order. If we instantiate A after B, it won’t be part of the same transaction as B—it will create its own new one. And if we had set B to just 'Supports transactions', we wouldn't get a transaction for this one at all unless the application had already started one for a different component.

    On top of this, we also reduce the opportunities for reusing components if we have to run them in a particular order. We can’t use two components that have the 'Requires a new transaction' setting inside one transaction. What we need to do in this case is set all the components that actually achieve a task to 'Requires a transaction' or 'Supports transactions', so that we can freely use them together in any combination.

    Using A Parent Component To Control A Transaction

    The usual way to get round this problem of the Transaction Setting property is to have a 'parent' component that creates instances of the other components and manages them. In the case of the Wrox Car Co application, which you'll be seeing a lot more of in the following chapter, placing an order involves two components. One handles local database updates, and the other handles remote database updates:

     

    The parent component is responsible for initiating a transaction that includes the other two components. For this reason, the parent component has its Transaction Setting property set to 'Requires a new transaction' while the other two components, which do the real work, have their Transaction Setting properties set to 'Requires a transaction'.

    SetComplete And SetAbort In A Parent Component

    The 'parent' component is part of the same transaction as any components that it creates using the CreateInstance method of the ObjectContext object. (Remember that this is not the case with New or CreateObject). Therefore it can control the outcome of the transaction using the SetAbort and SetComplete methods in just the same way as the components it creates can.

    This isn’t usual, however, because the components that do the work should indicate success or failure to MTS by calling SetComplete or SetAbort as appropriate. The only circumstances that the parent component might use these methods are if it has to cancel the transaction on command for some reason. However, it can use the DisableCommit and EnableCommit methods to control when MTS can make the transaction changes permanent, as we saw earlier. This might be useful in a situation where the data could be in an inconsistent state during certain parts of the process.

    Nested Transactions

    We've seen how MTS automatically enlists components into an existing transaction when they are created with the CreateInstance method of the parent's ObjectContext object. However, this only works if the component either supports transactions or requires a transaction. If it requires a new transaction, it cannot be enlisted into an existing one.

    In this case, MTS starts a new nested transaction for this component and any other components it enlists (unless they too require a new transaction). It is then up to this component to indicate to MTS whether it wants to commit or abort the original transaction (by calling SetComplete or SetAbort) based on the results of the nested transaction.

    For example, the following diagram shows four components A, B, C and D, together with their transaction support property. RT means that this component requires (an existing) transaction or supports transactions, while RNT means that the component requires a new transaction:

     

    In this case a single automatic transaction encompasses all the components, and will only complete if all four components agree to commit. However if component C was marked as requiring a new transaction, as in the next diagram, a nested transaction is created. Now component C can still commit the transaction (by calling SetComplete) even if component D failed—if this was appropriate.

     

    Using ASP To Start A Transaction

    We generally think of components as being compiled DLLs created in languages like C++, J++, Delphi or Visual Basic. However Active Server Pages scripts can also be considered as server-side components, and ASP is an ideal tool for initiating and controlling a transaction within MTS. We can take advantage of the ObjectContext interface in Active Server Pages just as we can in other components.

    In Active Server Pages (under IIS 4) we use a different technique to reference the MTS context object. We add a line to the start of the ASP page that indicates how we want to use components within MTS in this page:

    <%@ Transaction = value %>

    Where value can be:

    Required

    Indicates that MTS should either use the current transaction for this page, or start a new transaction if one is not already available.

    Requires_New

    Indicates that MTS should start a new transaction even if there is an existing one.

    Supported

    Indicates that MTS should use the current transaction if one exists, but not start a new one.

    Not_Supported

    Indicates that this page is not to be included in a transaction.

    These values are, of course, similar to the Transaction Property settings we listed earlier for compiled components.

    Note that ASP only allows one '@' line in a page. If you already use this to set the default script language for the page, you can add the Transaction statement to the same line:

    <%@ Language = VBScript Transaction = Required %>

    Using The ObjectContext Events in ASP

    MTS also provides our ASP scripts with events. This gives us an easy way to discover the outcome of a single component's execution, or the result of a multi-component transaction. Remember that components can initiate processes that occur concurrently as part of a complete transaction. So, simply calling the methods of each component within a transaction may not provide confirmation that they all completed properly unless you design the components specifically to do this.

    All we have to do is create an event handler for the OnTransactionCommit() and OnTransactionAbort() events that MTS provides:

    Sub OnTransactionCommit()

    'code for when transaction completed OK

    End

    Sub OnTransactionAbort()

    'code for when transaction failed

    End

    We'll see how these can be used when we adapt and use the WCCFinance sample component in MTS in the next section of this chapter.

    Adapting Our Simple Finance Component

    The context object that MTS provides for each component instance has the two methods we've mentioned already, SetComplete and SetAbort. In this part of the chapter we'll see how we can use these within the simple WCCFinance component that we created in the previous chapter. Then we'll install the component into Transaction Server and get our first glimpse of MTS in action.

    Modifying The Component Code

    As we suggested earlier, the changes required to a component so that it can take advantage of MTS vary from 'none at all' to 'loads of work'. It really comes down to whether the component was originally designed to maintain state (which isn't what we really want) or not. Our sample component has only a single method, and three write-only properties. Generally, using properties in a component creates state, because the code that uses it will usually expect the values to remain the same after they've been set. This isn't, as we've seen, always the case within MTS.

    So it's better to use methods in your components that accept parameters, and provide the values for the parameters when you call the method each time. That way the component can become stateless, and operate more efficiently within MTS. However, we're going to live with properties in this example. You'll see how we use parameters instead of properties in the other components of the Wrox Car Co application.

    Changes To The Component Interface

    One particular point to remember is the one we mentioned earlier about changing the interface of your component as you adapt it for use with MTS. To get the best performance you should call the SetComplete or SetAbort methods as often as possible within the component, However, each time one of these method calls is made, MTS assumes that the component's state (internal values) can be disposed of. Therefore simply adding either of these calls to a component's methods will change the way the component behaves.

    For example, if our application sets some property values inside the component and then calls a method in that component which contains SetComplete or SetAbort, MTS will reclaim the component instance and the property values are lost. If our application then comes back to the component again—through the context object that it thinks is a real component—and reads one of these values, it will get the wrong result. In this case, the application is treating the object as if it were still holding its state, whereas the SetComplete and SetAbort methods tell MTS that it is stateless at that point.

    So even though we haven't changed any of the names or parameters of the component's members, or the way they work under the hood, we have changed the interface definition. Calling a method in it, or reading the value of a property, could well produce a different (and unexpected) result. To get round this, you may want to change the interface definition more obviously—by changing the names of the members for example. In C++ or other languages that permit it, you may prefer to implement another separate interface instead.

    Adding the MTS Code To The WCCFinance Component

    The changes we need to make to our code are minimal, and only affect the GetNumberPayments method. We're not implementing the ObjectControl interface, because we have no procesing requirements for Activate or Deactivate. Instead, we get a reference to the context object as we enter the method. Here are the changes to the code:

    Public Function GetNumberPayments() As Integer

    'get reference to the context object

    Dim objOContext As ObjectContext

    Set objOContext = GetObjectContext()

    'rest of function code goes here

    ...

    The next part of the original code calculated the number of payments, and placed the result in the variable intNumberMonths. If the monthly payment was insufficient to pay the interest and reduce the balance, it set intNumberMonths to zero. The function also returned zero if there was an error. We'll do the same in the new version, but before we end execution of the method code we'll call either SetComplete or SetAbort as appropriate:

    ...

    GetNumberPayments = intNumberMonths

    'complete or abort the MTS context

    If intNumberMonths > 0 Then

    objOContext.SetComplete

    Else

    objOContext.SetAbort

    End If

    Exit Function

    ...

    To complete the method code we add a call to SetAbort in the error handler, so that an error will prevent a transaction from taking place:

    ...

    GNP_Error:

    GetNumberPayments = 0 'indicates an error

    objOContext.SetAbort 'abort the MTS context

    Exit Function

    End Function

    And that's it. We just recompile the component and copy it to the server. You will have to stop and restart the server before you can replace an existing component if you installed the sample from the previous chapter.

    Installing Components Into MTS

    Once the component is on the server and properly registered we can add it to MTS. The process is referred to as installing it in MTS because it permanently changes the way the component is referenced within Windows (until you delete it from MTS again).

    Microsoft Management Console and MTS

    The main administration for MTS is the Transaction Server Explorer. In Windows NT Server this is a snap-in to the Microsoft Management Console (MMC), while in Windows 95 it is a separate executable file. MMC provides a one-stop-shop for working with several services at once, such as Internet Information Server and Index Server as well as MTS. If you don't see the Microsoft Transaction Server entry in MMC after installing MTS, you can add it using the Console menu.

    The following screen shot shows Transaction Server Explorer in the MMC, together with some of the default components that are installed from the NT4 Option Pack. Notice that there is an entry for each computer, because MMC can be configured to administer remote servers as well as the local one. For each computer there is a set of packages that are installed within MTS on that machine. A package can contain one or more components, and is simply a way of setting up and managing them all together. Each package has a set of roles. Roles are part of the security mechanism implemented within MTS, and we'll be looking at this in more detail in Chapter 8:

    Creating A New Package For WCCFinance

    Creating a new package is as easy as right-clicking the Packages Installed entry and selecting New then Component. The Package Wizard opens, with a choice of two ways of creating a package:

    Like most other tasks that you can perform with the MMC, you also can do this from the drop-down list marked Action. In Windows 95, Transaction Server Explorer does not support right-click menus so the standard menu bar is used instead.

    We want an empty package rather than a pre-built one (components and their package can be exported, and then installed as pre-built packages package on other machines). The next screen (not shown) just allows us to enter a name for the package. The third screen is where we set up the identity for the package, i.e. which user account it uses to access other services on the machine. We'll use the default of the current user:

     

    While we are running the components from a Web browser with anonymous access, the current user will be IIS. However there are often situations where this is not the case, and so selecting Interactive User is the best plan unless you can be sure that you can validate the user directly. You'll find a detailed discussion of users and identities in Chapter 8.

    Finally, clicking Finish creates the new package, which we named WCCFinanceTest, and we're ready to add our component to it.

    Installing the WCCFinance Component

    To add our component to the new empty package we right-click the Components entry and select New | Component (or use the Action menu):

    This starts the Component Wizard, and the first screen allows us to install a new (unregistered) component or import a previously registered one. We've already registered our WCCFinance component, so we choose the second option:

     

    Importing previously registered components has the minor disadvantages in that you can’t set the properties for any individual interfaces within that component. In this case you should un-register it and install it as a new component.

    Clicking Next means coffee-time unless you have a fast machine. The Wizard trawls through the Registry building a list of available components. If you turn on the Details checkbox, you can get useful details about the components that are available. In the screen shot, we've found our WCCFinance component in the list:

     

    When you install a component in MTS, the registry entries for it are changed. MTS swaps its own class ID for your component's class ID, so that references to the component are redirected to MTS with a parameter added that identifies which package and component was referenced. For this reason, you won't find any components that are already installed in MTS in the list.

    Selecting the component from the list and clicking Finish places it in the current package:

    You can also install components by dragging them from Explorer into the right-hand pane of MMC when the appropriate package's Components entry is selected.

    The final step is to set the MTS properties for the component, by right-clicking it and selecting Properties. The General tab provides useful information about the component, and allows us to enter a description for it. However, the most important property is the TransactionSupport entry. We need 'Supports transactions' for our component:

     

    We discussed the options available earlier in the chapter If a component has been designed to be used either within a multi-component transaction or by itself, we'll usually choose 'Supports transactions'. However, if it is designed to be the parent of several components, and it always initiates a transaction, we would probably choose 'Requires a new transaction'.

    Why Packages?

    Rather than install all our components as separate items in MTS, we must, as you've seen, create packages and install them within a package. It's a similar concept to storing disk files in separate sub-directories, rather than all in the root directory of your disk.

    Each package should contain a set of components that between them perform related tasks within an application. Packages allow us to encapsulate components for installation as a group either locally or remotely. They also provide a way to allocate properties and security permissions to all the components within a package in one go, in the same way as we can allocate security permissions to users within a group in Windows NT. In a later chapter of this book, we'll look at the security properties in more detail. For the time being, we'll see what other features packages provide.

    Setting The Activation Timeout

    When we discussed how MTS deactivates and caches components after use, we said that the default was to hold the component in memory for three minutes. This behavior can be changed in the Activation page of the Properties dialog for each package separately:

    The setting you choose depends on the way the components in that package will be used in general the default gives reasonable performance under average conditions. The shut down delay is the time-out period in minutes (between 0 and 1440) before the component will be removed from memory after use.

     

    In Transaction Server Explorer there are also two permission-related check boxes: Disable Deletion, which prevents users from deleting the package from the Explorer without first clearing the box; and Disable Changes, which prevents changes to the package’s attributes and contained components unless the check box is clear. In the MMC, these features are handled by Windows own native security features.

    Setting The Activation Location

    The Activation page of the Properties dialog controls where the component instances will be created and executed. In general, you will execute them in the MTS environment in other words in a 'dedicated server process'. If you want to execute them within the memory space of the 'creator' process, you can choose this option. It provides higher performance if there are multiple calls from the creator application to the component, but means that a failure of the component can crash the creator process as well.

     

    If you are using roles to control access permissions to your components, you should always choose the default Server Package option. In general, we will use this setting for all our components.

    Modifying The ASP Code

    Our component is now nestling warm and safe inside MTS, and we can start to use it in an application. We're only going to show you one example here, using an updated version of the Active Server Pages file that we used in the previous chapter with the un-transacted component.

    So, what do we need to do that's different? In the component, we added code that will call the MTS SetComplete method if everything goes OK in the calculation, and SetAbort if not. However this will only happen if the component is running under a transaction. Because we set its Transaction Support property to 'Supports transactions', it will use an existing one but not start a new one if there isn't one running already.

    This means that if we want to benefit from the transactional features of our component we have to start a transaction running before we activate it. Of course, we could have set the Transaction Support property to 'Requires a transaction' but that would mean that we could never run it without one.

    In fact, as you saw when we looked at ways of starting a transaction earlier in the chapter, we can effectively override the Transaction Support property setting in our ASP script using the Transaction statement at the head of the page. This only affects the current page, and hence the instances of components that we create within the page. Once we start a transaction, all the instances of the components we create will join in this transaction unless they have a TransactionSupport property of 'Requires a new transaction' or 'Does not supports Transactions'.

    So we just have to add the statement Transaction = Required to the head of our ASP page to make sure that a transaction is running for this component instance here we've added it to the existing LANGUAGE statement:

    <%@ LANGUAGE = VBScript Transaction = Required %>

    ...

    Creating The Component Instance

    Now we can create our component instance, and set the properties as we did in the previous chapter's example. As soon as we access the component for the first time, i.e. to set the TotalPrice property, MTS creates a context object and activates and holds onto the component for us. It won't be deactivated until SetComplete or SetAbort< is called within the component's code, so we can continue setting the property values with no fear of it disappearing.

    The final line of this section of code calls the GetNumberPayments method of our component:

    
    
    

    ...

    <%

    Set objFinance = Server.CreateObject("WCCFinance.PaymentTerms")

    objFinance.TotalPrice = Request.Form("txtTotalPrice")

    objFinance.InterestRate = Request.Form("txtInterestRate")

    objFinance.MonthlyPayment = Request.Form("txtMonthlyPayment")

    intResult = objFinance.GetNumberPayments

    %>

    ...

    Into The Black Hole

    Because we are using a transaction, we don't know what's going on now until MTS tells us. Although we can guess that when our method call returns the task will be complete, we can't always guarantee this in a more general case. We really only know that it will happen here because we're using a single component that we built ourselves so we know how it works.

    In the real world, we may not know what's going on in the component, and of course there could well be several different ones used in the page anyway. It's a bit like tossing the values into a black hole then waiting to see if anything comes out. The upshot of all this is that the only thing we can reliably do here is tell the user that something's happening:

    ...

    <BODY>

    Your inquiry is being processed, please wait...<P>

    ...

    Responding To The MTS Transaction Events

    Thankfully you have a good deal more chance of something coming back from your component than you do with a black hole (even of you have only limited experience of creating components yourself). This is what we're depending on, but we need to know when MTS actually does return the result. We do this by handling the two MTS context object events OnTransactionCommit and OnTransactionAbort:

    
    
    

    ...

    <%

    Sub OnTransactionCommit()

    strResult = "<P>A total price of <B>$" & Request.Form("txtTotalPrice") _

    & "</B> at a monthly interest rate of <B>" _

    & Request.Form("txtInterestRate") & "%</B>, and paying <B>$" _

    & Request.Form("txtMonthlyPayment") & "</B> per month, " _

    & "will require <B>" & intResult & "</B> payments.</P>"

    Response.Write strResult

    End Sub

    Sub OnTransactionAbort()

    strResult = "<P>Sorry, the calculation could not be completed. " _

    & "Either the monthly payment amount you entered " _

    & "is not sufficient to pay off the loan, " _

    & "or another error occurred.</P>"

    Response.Write strResult

    End Sub

    %>

    </BODY>

    </HTML>

    And here's the result. Isn't it wonderful when a plan comes together like this?

     

    Note that aborting a transaction does not rollback changes to any ASP session-level variables. The OnTransactionAbort event handler should be used to reset the session variables if this is appropriate in your application.

    Summary

    In this chapter we've introduced you to Microsoft Transaction Server, and used a simple component and ASP script to demonstrate it in action. At first, MTS seems a complex concept to grasp, but much of it operates transparently behind the scenes. We will, however, uncover some more of the details in later chapters.

    MTS is a service running under Windows NT Server that caches, allocates, activates and deactivates instances of components that are used by your applications. This can improve response times and maximize resource availability, providing superior performance to distributed applications particularly when they are built along the DNA guidelines. In this respect, it is comparable to what are often referred to as Object Request Broker (ORB) services available on other platforms.

    But MTS can do more than this. It provides the services of a Transaction Processor as well, allowing components to be linked into transactions that must all succeed or all be rolled back. This feature makes building reliable and robust data management applications much simpler, especially in distributed applications where the data may reside on several different and remote servers.

    About the Author

    Alex Homer is a freelance developer and writer, currently sending most of his time working with Wrox Press Limited in the UK. Alex's own company, Stonebroom Software, has been running - on and off and with a couple of name changes - for some fifteen years while he held down a 'day-job' as well. He started writing system utilities in assembler (machine code) which were sold through a small distributor in Blackpool, the UK's 'silicon beach' of home computing in the early eighties. Since then, development has moved through Pascal in MS-DOS, Delphi and Visual Basic in Windows, and now to the Internet in general. Having worked in the Glass and Glazing industry for some years, Alex also develops vertical market software aimed at making the specifier's and estimator's job easier. They still think Windows are things you lean out of.

  • Rate This Article
    Not HelpfulMost Helpful
    1 2 3 4 5
    Mailing List
    Want to receive email when the next article is published? Just Click Here to sign up.

    Support the Active Server Industry