NewsC


Using Microsoft Transaction Server with VFP

 

          By Rick Strahl
          http://www.west-wind.com/

 

          Last update: November 5, 1999

 

 

Microsoft has been on course to integrate into the Enterprise for a few years now and there's been hard push at the company to provide tools to make the move to scalable server based applications as easy as possible for developers. One of the main components that figures heavily in Microsoft's WinDNA (Windows interNet Architecture) plans is Microsoft Transaction Server. This tool defies easy classifications as it provides a host of useful and powerful functionalities that have traditionally been very difficult to program. This very diversity is one of the reasons MTS is often misunderstood and difficult to get a handle on. In this article I'll address several of the key features and what they mean to VFP developers.

 

This document covers the following topics:

         

 

¨        An overview of Microsoft Transaction Servers main features

¨        How MTS works

¨        The impact of Transaction Server in server based applications

¨        How to create VFP COM components for use in MTS

¨        Specific discussions of several technologies in MTS:

¨        Just In Time Activation

¨        Distributed Transaction Management

¨        Role based security

¨        An analysis of what these features actually mean for VFP applications

 

 

So what's the big idea?

So, you've been building applications that use COM. You have a few server applications that are humming away but in the back of your mind you're thinking: "I don't really know what to do when the time comes to scale this application both on the local machine and across the network." The focus of Microsoft Transaction Server is in helping making this process easier by providing an abstraction layer that handles many of these details for you. You've probably heard about 3 and n-tier programming, which should contain middle tier servers. One of the tasks of a middle tier component is to be able to abstract the scalability issues – MTS can fill this place rather nicely by hosting your components.

 

MTS accomplishes most of its functionality through a concept called packages (to be renamed COM+ applications in Windows 2000). Packages are host containers for COM components. To create a COM component that runs in MTS you create the component and then add it to one of these packages. A package is a grouping of components that share the package's attributes. The package provides a common context to all components that reside within it. The context allows MTS to manage the components inside, rather than allowing your application to access them directly. It does so through an abstraction layer that relies on COM marshalling and remapping registry entries of your components to itself. When a component is invoked by a client (using CREATEOBJECT() or the CoCreateInstance() API just like you always do), the MTS package is activated instead and the MTS package then in turn creates your COM object and passes any calls to methods and properties forward to the actual component running within the package context.

 

If you're thinking this will slow down the performance of your COM components, you're right. MTS packages are typically implemented Out Of Process, which requires all the overhead of an EXE server, plus additional overhead for the package to call your component. However, some of the overhead is gained back through some caching that occurs inside of the MTS package as it has the ability to manage the components running inside of it.

 

The good news is that MTS can potentially provide a number of serious scalability improvements like pool management and resource sharing. The bad news is that MTS in its current version doesn't do much of this yet. What it does provide though, through this abstraction layer is functionality that's very easy to access and implement from within your applications. Some of this functionality has been traditionally very hard to implement – MTS can make this task almost easy.

What does MTS provide?

Let's look at what functionality you can gain by running inside of a Transaction Server package. Not an easy task as even a passing discussion of all of the functionality would cover a full book. MTS appears to have so much seemingly unrelated functionality wrapped up into a single product. However, all of this functionality relies heavily on the packaging mechanism just discussed and so it makes sense to provide it all within the scope of MTS.

 

MTS wraps core functionality that has to do with building scalable applications and implements functionality that has been mainly implemented for large mainframe systems. It provides functionality in a large variety of areas starting with scalability, distributed transaction management for SQL, a simple security model, COM object packaging, server based context management and adminstration and monitoring features. Let's look at a few if these in detail.

 

¨        Scalability and Resource management of COM objects—Just In Time Activation (JITA)
This feature is probably the most important one that developers are looking for as a feature from MTS. COM objects loaded through MTS can be automatically unloaded and reloaded. MTS caches the VFP runtime, so load and reload is relatively fast. MTS may also cache COM references of your object, so if objects are loaded in quick cycles your servers are never unloaded/reloaded even though your client code releases references. MTS handles all of this through its own abstraction layer, saving resources on the server. But there is a cost: COM operation through MTS can be considerably slower than calling COM objects directly depending on how you implement MTS functionality in your components.

¨        Distributed Transaction Management
This is where MTS gets its name. MTS allows database-independent management of transactions spanning multiple data sources and multiple COM objects. Essentially MTS can wrap operations into its own transactional monitor, which works through the Distributed Transaction Controller (DTC), causing any ODBC/OleDB database operations to be monitored. Components can call transactional functions to complete or abort operations, at which time MTS—rather than the application—causes the data sources to commit or revert. Data sources must be DTC compliant, which means SQL Server or Oracle at this time. (This may also apply to other data sources with third-party drivers.) The Visual FoxPro ODBC driver is not DTC compliant so you can't have VFP data participate in distributed transactions, but you can of course use VFP data in your COM objects as long as they don't require DTC transactions.
The key feature of this technology is that transactions have been abstracted to the COM layer. Rather than coding transactional SQL syntax, you can use code-based logic to deal with transactions. The transactions are SQL back-end independent (as long as the back end is DTC capable). Transactions can also span multiple databases and even across servers running different SQL back ends, such as SQL Server and Oracle. A single transaction could wrap data access that writes to both! If you've written code for this type of transaction before you know how difficult this task is and MTS now makes this a breeze.

¨        Role-based security
MTS implements a new security model that allows you to configure roles for a component. The roles are configured at the component level and mapped to NT users. Your code can then query for the roles under which the component is running, rather than using the much more complex NT Security model and functions to figure out user identity. Roles are also useful for deployment because they can be packaged with the component and can be automatically installed on other machines if the necessary accounts exist. Roles are similar to NT groups, but they are applied specifically to a package and span all of the components that live in the package.

¨        Packaging technology
MTS includes a packaging mechanism that allows taking a package (an MTS word for an application or group of objects), which might contain multiple COM objects, and packing it up into a distribution file. The distribution file contains all binary files (DLLs, type libraries and support files) as well as all information about the registry and the security settings of the component. This package can be installed on another machine for instant uptime.

¨        Remoting Support
In Process DLL COM objects can not be natively remoted to another machine. DCOM requires out of process servers in order to run. With MTS package mechanism the package can be activated across a network connection which in turn allows your In Process objects to be run over the network. An additional benefit with this functionality is the fact that a client is never directly accessing the component on the other end. If a DCOM connection dies it tends to orphan the running server on the server machine. With MTS the package can catch connection problems and automatically release and possibly even reload the server after a connection has been lost and re-established from the client side.

¨        Context management
MTS includes a static data store object similar to ASP's Application object to allow components to store data for state-keeping and inter-component communication.

 

Multithreaded COM is finally here with Service Pack 3

I've written a number of articles in this magazine that pointed out the serious issues related to the fact that the Visual FoxPro runtime was unable to handle multiple simultaneous method calls. Almost any IIS article I had to postscript with a scalability warning. This meant that ASP pages and MTS requests had to wait for another method call to finish before they could be processed resulting in very limited load capability of Visual FoxPro COM objects in a multithreaded environment such as IIS.

 

Well, no more! With Service Pack 3 of Visual Studio, Microsoft has totally revamped the VFP runtime with a new multi-threaded version in a new seperately available file called vfp6t.dll. This new runtime has moved all of VFP's global data into thread local storage that provides the data abstraction needed to run COM objects on different threads totally independent of each other. Since each instance gets its own local data there's no interference between a 'global' instance of VFP as there was (and still is with the 'standard runtime') with previous versions. The result is that VFP finally is capable of making simultaneous method calls from multi-threaded clients.

 

To build components for the new runtime there's a new command BUILD MTDLL, which builds a DLL that links with the new vfp6t.dll file. A new project build option is also available which accomplishes the same task:

 

 

If you're using Visual FoxPro with Active Server Pages, MTS or any other multi-threaded client you'll want to upgrade to SP3 as soon as possible.

 

Keep in mind though that although multi-threading is crucially important for scaling applications, that it's not a silver bullet that will solve all your problems. In fact, you'll possibly face other more serious issues like possible server overloading due to too many requests processing simultaneously and overloading the resources available on your server. Multi-threading can also cause operations to slow down as NT's thread management and simultaneous operation of requests can significantly affect performance. Multi-threading rarely provides better performance, rather it provides better responsiveness where the overall request times may increase, but more people may receive their results in a timely manner. Weigh these things carefully and know your bottlenecks so you don't get surprised by an overloaded server.

 

Do you need Microsoft Transaction Server?

Before you decide to use MTS you should ask yourself whether you really need its functionality. If you don't need any of its specialty features like role-based security or multi-phase commit of database transactions, there might be no good reason to bother with it. Although scalability is one of the often touted features of MTS, the current release of MTS does not provide much in this area at this time. Even when running the new multi-threaded VFP runtime, MTS is not much more than a wrapper around your COM object. This out-of-process COM object wrapper in turn acts as a proxy and hosts all access to your component via passthrough. All object access occurs through an out-of-process COM call, and there's the additional overhead of passing the data through two interface layers. The result is that running components inside MTS tends to be slower than running them as plain in-process COM objects, without gaining any real scalability benefits at least at this time.

 

Figure 1.1 –MTS hosts your components inside of its own out of process package container. The proxy passes through all object calls to your actual component. This layer of abstraction allows MTS to manage your components for you.

 

Here's how it works. MTS hosts your COM objects inside a package, which is an out-of-process component you can see on the Processes tab of the Task Manager running as Mtx.exe. Each package uses its own Mtx.exe host to encapsulate its application COM objects. When an MTS component is installed into MTS, the ClassIds and ProgIds are mapped to the package, which in turn knows how to pass on any interface calls directly to your actual COM components residing inside the package. The end result is that a client application never has a direct connection to your COM object, but always has a proxy reference to the MTS package, which passes through all interface calls.

 

Through some registry mapping, an instance of this object is instantiated when you issue CREATEOBJECT. The package then creates your object and calls the methods in typical COM passthrough fashion using proxy and stub architecture that is also used for cross process and DCOM marshalling of interface access calls. Any method calls made to the component hit the package container's proxy object first, and then are passed down to your internal COM object. Figure 1.1 shows the context object that sits between the client and your component – this context object provides the abstraction layer that MTS uses to provide its features to your COM components. Figure 1.2 shows how objects are called from an ASP page which is a typical example of a multi-threaded client calling your COM components. Note that if multiple simultaneous requests are made on your component, multiple instances start up and run inside the MTS package. How these object references to this package object is provided is up to MTS – it could be a brand new object, or a recycled reference. If you're using the new multi-threaded runtime available with Visual Studio SP3, you'll be able to get simultaneous method calls with the original version of VFP 6.0.

The standard runtime calls to your component will block, essentially allowing only a single method call to occur at a time in a given package. If you use ASP with or without MTS make sure you download and install the new SP3 runtime to get maximum functionality from your VFP COM components (see sidebar).

 

Figure 1.2 – When running from a multi-threaded client like IIS MTS allows multiple VFP COM objects to run simultaneously as long as Visual Studio SP3 is installed. Each client request (ASP page hit here) instantiates a new instance of the COM object inside of the MTS package. Note that the package does not run inside of the calling process (IIS).

 

The MTS package provides component services I mentioned earlier and isolates your component from the client process. (Hey, that's not really a feature since you can do that with out-of-process components on your own—but that's marketing for you.)

 

Out-of-process packages – known as Server Packages -  are the norm, but MTS also supports in-process packages which are called Library Packages. These work the same as out-of-process packages but run inside the client process. However, I've seen problems with this approach, and it seems Microsoft provided this feature more for its own system services (like IIS virtual applications) than anything else. Running components in in-process packages has resulted in many weird and unexplainable crashes and lockups in my tests, where the out-of-process packages of the same exact configuration work fine. Your mileage might vary. In-process packages are slightly faster, but they also void the process-isolation features that protect the client process from a crashing component in your package. You might not be able to restart in-process packages, and they often require the calling process to shut down before refreshing or updating the components. I recommend to stick to Server Packages.

The state of the stateless

In order to use MTS and gain scalability benefits you have to think about building stateless components. Stateless objects are objects that don't rely on property values to maintain their "state" or context between method calls. These objects make no assumptions about any values for themselves or other "global" aspects of the system. They reestablish state themselves when they start up, by using parameters or values stored in a database or establishing state from scratch. The typical steps for a stateless object are:

 

1.      Receive a method call

2.      Establish state

3.      Run application logic

4.      Release state

5.      Return results to the client

 

The important thing here is that each method call cannot rely on property values being set a certain way when the method is entered. When a server is stateless the server has the ability to recycle or unload the object without the client noticing that underlying server reference is different. And this is exactly what MTS can do behind the scenes for you without you having to write any code to make it happen. This and many other tasks are the responsibility of the package. In theory, this is how MTS can abstract many scalability issues for you, so object management code does not need to be written by a client application. Your job is to create the object and forget about it – MTS will manage the component and makes sure that it's available when you need it. That's the theory anyway, but currently this functionality is very limited and as we'll see in a minute, also very inefficient. This may change in the future though.

 

Regardless, if you're going to build an effective component for use with MTS, it should be stateless. For MTS this means that when a method call completes it should be able to release the object and hand you a brand-new copy on the next request. In order for this to work, your component cannot rely on non-default property settings used by multiple consecutive method calls.

 

If the object is stateless, MTS can take the existing object reference and recycle it to pass on to another client. That's the theory, anyway, but in reality MTS destroys your object after each call to release state (which releases state for the object – more on this in a second) and then totally re-creates it when you make another method call, rather than simply caching existing references. MTS does not provide object pooling yet – this is a feature that likely will be provided in later versions of MTS. The current behavior provides a safe environment for legacy (non-MTS) components by always guaranteeing startup state to a stateless component that is not the optimal way to cache objects. Pooling on the other hand would simply keep an instance of an object alive and reuse that existing instance. Pooling is much more efficient, but potentially problematic for legacy objects that don't know about state and MTS context objects.

 

To manage state MTS introduces a concept called Context. The context object is essentially a reference to the MTS package, which maintains the state of the COM objects inside of it. A COM component can retrieve a reference to this context object with CREATEOBJECT("MTXAS.AppServer.1"). From the object the current package's context can be retrieved with the GetObjectContext() method. This reference lets you control the status of state by committing transactions using the SetComplete() or SetAbort() methods of the context object. When you call the latter methods MTS lets the object go stateless – it actually releases the object reference completely causing the COM object to unload.

 

Although objects should be stateless, you can also keep state in objects simply by not calling SetComplete() or SetAbort() at the end of a method call. If you do this, the object reference behaves like a typical COM object that is stateful – all the property values and internal object state remain intact between method calls until you finally call SetComplete or SetAbort. This is great for legacy servers that weren't designed for stateless operation to start with. However, stateful servers don't scale well. While servers have state MTS can't manage them. This means they tie up the resources they use and also could hang because of a lost connection or other failure from the client. Because the object isn't managed MTS can't trap these failures as it could with a stateless object.

VFP MTS Basics

To demonstrate how MTS works, I'll build a small functional sample that calls a simulated long-running method called SlowHit(). SlowHit() doesn't do anything other than wait for a specified number of seconds passed in as a parameter to simulate multiple simultaneously running requests on this server. To start, create the basic server code:

 

DEFINE CLASS MTSDemo AS Custom OLEPUBLIC

 

oMTS = .NULL.

hFile = 0

 

FUNCTION Init

  THIS.WriteOutput("Init Fired")

  THIS.oMTS = CREATEOBJECT("MTXAS.AppServer.1")

ENDFUNC

 

FUNCTION Destroy

  THIS.WriteOutput("Destroy Fired")

  IF THIS.hFile > 0

    FCLOSE(THIS.hFile)

  ENDIF

ENDFUNC

 

************************************************************************

* MTSDemo :: SlowHit

*********************************

***  Function: Allows you to simulate a long request. Pass number of

***            seconds. Use MTS transactions

************************************************************************

FUNCTION SlowHit

LPARAMETER lnSecs

LOCAL loMTS, loContext, x

 

THIS.Writeoutput("SlowHit Fired" + TRANSFORM(lnSecs))

loContext = THIS.oMTS.GetObjectContext()

 

lnSecs = IIF(EMPTY(lnSecs), 0, lnSecs)

 

DECLARE Sleep IN Win32API INTEGER

FOR x = 1 TO lnSecs

  FOR x = 1 TO lnSecs

     Sleep(1000)

  ENDFOR

ENDFOR

 

DECLARE INTEGER GetCurrentThreadId IN Win32API

 

IF !ISNULL(loContext)

   loContext.SetComplete()

ENDIF

 

RETURN GetCurrentThreadId()

ENDFUNC

 

 

FUNCTION WriteOutput

LPARAMETER lcString

IF THIS.hFile = 0

   Declare INTEGER GetCurrentThreadId IN Win32API

   THIS.hFile = FCREATE(Sys(2023) + "\MTSDemo"  +SYS(2015) + ".txt")

ENDIF

IF THIS.hFile # -1

  FWRITE(THIS.hFile, lcString + CHR(13) + CHR(10))

ENDIF

 

ENDFUNC

 

ENDDEFINE

 

A few optional pieces in this code deal with logging information to disk using the WriteOutput method —I'll discuss these later. The key feature of a COM object built for use in MTS is the retrieval of an object reference to the MTXAS.AppServer.1 object, which is the MTS base object. To retrieve the current package context for the currently active request, use its GetObjectContext() method and store it to a variable:

 

THIS.oMTS = CREATEOBJECT("MTXAS.AppServer.1")

loContext = THIS.oMTS.GetObjectContext()

 

I stuck the CreateObject() call into the Init event of the class so I don't have to retype the code that has to run in each call method. As you'll see in a minute, the Init of your server fires every time a method call is made to your object, so this is no more efficient than directly calling the same code in each method. However, it saves some typing and centralizes the code.

 

The only thing left to do in your server method code is to call either SetComplete() or SetAbort()—it doesn't matter which one you call, since this component isn't participating in any SQL transactions. To tell MTS that it's okay to release resources on the object, use:

 

IF !ISNULL(loContext)

   loContext.SetComplete()

ENDIF

 

Now create a project and add a PRG file (or VCX if you choose to go that route) named MTSDemos. Now compile this server into a COM object:

 

BUILD MTDLL MTSDemos FROM MTSDemos

 

If you don't have the new multi-threaded runtime included with SP3 (see sidebar), just use BUILD DLL instead—but realize your server will block simultaneous client requests – no concurrent access on that object will be possible.

 

Before dumping the server into MTS, use the following code to test it:

 

o = CREATEOBJECT("MTSDemos.MTSDemo")

? o.SlowHit(5)

 

This should run the server, wait for five seconds and then spit out a thread ID number. If that works, move this component into MTS by following these steps:

 

1.      Start the Microsoft Management Console.

2.      In the tree view, select Microsoft Transaction Server, then My Computer, then Packages Installed.

3.      Click the New icon on the toolbar and create an empty package named MTS Book Samples (the name doesn't matter really, but it matches the image below).

4.      Select the new package, press the right mouse key, and click Properties.

5.      Click the Identity tab and notice that the default is set to Interactive User, which is the currently logged-in account. If your component runs prior to login, the SYSTEM account will be used. Here you can also change the user account to a specific user account. Note: This setting determines the user context and the rights for what resources your component can access, regardless of the client. Save and exit this dialog.

6.      Go down one more level to Components and click the New icon.

7.      Click Install New Components and select MTSSserver.dll which we just created above. If you aren't using the new multi-threaded runtime, also add the MTSDemos.tlb file. Click OK.

8.      In the component's Properties, set its Transaction options. If you don't use distributed transactions through DTC, set the "Does not support Transactions" option, which will result in slightly faster performance  than the other options. Use "Supports Transactions" if you have a mixed-request load. Typically you'll want to set this to "Supports Transactions" The figure below  shows how the installed component should look inside MTS.

 

Figure 1.3 – The MTS Explorer allows you to manage packages and the components inside of them. Components are added to packages and you can configure both the package and each individual component. Note that you can see active object status in the Explorer to give you a quick view of activity. This is very useful in understanding on how MTS works.

 

Once the component is registered with MTS, nothing changes in the way the client calls the object, so re-run the previous code but bump the timeout to a higher number so we can see things happening:

 

o = CREATEOBJECT("MTSDemos.MTSDemo")

? o.SlowHit(15)

 

Run this code from VFP and then switch back to the Transaction Server Manager. You'll notice that the icon for your object is now spinning, which means it is active and "in call" or in context. Click the detail view and you'll see one active object and one in-call object while your server continues to wait for completion. Once the method completes and the code executes the SetComplete() method call on the Context object, the in-call counter goes down by one, leaving you with one active object reference and no objects that are in call. This status display is quite useful when figuring out how MTS works.

 

If you create another object in your VFP application like this:

 

o2 = CREATEOBJECT("MTSDemos.MTSDemo")

 

the active object count will go up by one. Because the Visual FoxPro client is single threaded, you can't run two simultaneous methods from the single VFP client. But you can simulate the process by firing up another copy of VFP, loading instances from there, and running several SlowHit calls simultaneously in each open copy of VFP. These, too, will show up as active instances and stick around. If you now run a SlowHit in each instance of Visual FoxPro, MTS will show two active in-call objects. This demonstrates that the MTS package is indeed 'global' to the system rather than the application that you're running.

 

A more typical MTS example, however, is being called from a multi-threaded client like IIS and Active Server Pages. You can simulate the same scenario by creating a pair of ASP pages, using Server.CreateObject() to create instances of your servers, and then running them simultaneously—you'll see them show up as in-call objects while processing. In short, all your object instantiations run through MTS regardless of which process (IIS, VFP) created the object.

Understanding Just In Time Activation (JITA)

Notice that if you create an object from the command window, it will immediately show as activated. Once you make the SlowHit method call and it completes, the object is no longer shown as activated! Yet, if you go back and re-run slow hit on your existing object reference, it works just fine. What's going on here?

 

That's Just In Time Activation in action. The above code creates an object, but doesn't call SetComplete() immediately after creating the object in the Init. Until you do, the object is assumed to have state and cannot be released by MTS. As soon as you call SetComplete(), MTS is free to unload your object. The VFP client app, however, doesn't know that the object was unloaded and assumes it is still active with the app still holding a reference. But the actual reference is to the MTX proxy, rather than the real object, which the package has unloaded. When you make the next interface call on the object, MTS re-creates the actual object and services the request from this newly created instance.

 

As you can see, this requires your object to be stateless. If state was maintained between method calls, all properties would clear, and only the startup properties would be provided. If you have to keep state from one method call to another, be sure not to call SetComplete() or SetAbort() in the client code, in which case the object will not be unloaded by MTS. The object stays loaded and dedicated to your client, showing as activated but not in call while between method calls.

 

Let's look a little closer at what's happening behind the scenes here. To do so, we need to use the WriteOutput() method I mentioned earlier. When called, WriteOutput()sends output to a temporary file; the purpose of this exercise is to see how MTS loads objects. Run the following example code against the object hosted in MTS:

 

o = CREATEOBJECT("MTSDemos.MTSDemo")

? o.SlowHit(1)

WAIT WINDOW

? o.SlowHit(2)

WAIT WINDOW

? o.SlowHit(3)

 

The Write() method creates a new file with a SYS(2015)-generated tag to keep the files separate. It does so once per object instance creation by using the hFile file handle at the object level. For hFile to be valid, it has to maintain state—otherwise a new file will be created. After running these four lines of code, you'll find that three temporary files were created in your TEMP directory. Each file contains the following information:

 

Init Fired

SlowHit Fired3

Destroy Fired

 

This demonstrates clearly that MTS does not actually cache object references, but rather re-creates your object from scratch on each method call that calls SetComplete or SetAbort! This means Init and fire on each method call that uses SetComplete and SetAbort. In addition to being stateless, your object also has to be lightweight in terms of how it instantiates and destroys, in order to be efficient. If you have lots of Init code or many properties that are initialized when the object is created, you'll incur a significant amount of overhead on each call to the MTS-controlled object.

 

Remember, Just In Time Activation unloads objects after the method call completes and a call to SetComplete() or SetAbort() is made in the method.

 

Recompiling an MTS Component – a few extra steps

Now, go ahead and remove the call to SetComplete() from the Slowhit method and recompile the server. Note, that if you make the change and recompile the server you'll get a 'File Access Denied' error. This is because MTS is actually holding a reference to your object even though the reference count has gone to 0. To unload the object bring up the package in the Management Console and right click, then choose Shut Down. This function unloads all objects that are loaded inside of the package. Now recompile your component. When you recompile a component it is built from VFP as a pure COM component and the connection to the MTS package is lost at that point. If you were to instantiate your object at this point you'd find that the object is no longer running in MTS. To reattach the component to the package, go the Management console, select the package, then the components folder and right click. Choose Refresh from the shortcut menu, which rereads the package components into the registry.

 

Ok, your object is now ready to test again, this time without committing the MTS transaction. Create the MTSDemos object, then call the SlowHit() method 3 times in a row and release the object. Your WriteOutput() method will create the following output for the three method calls above:

 

Init Fired

SlowHit Fired1

SlowHit Fired2

SlowHit Fired3

Destroy Fired

 

As you can see without the committed transaction, the object is instantiated and remains loaded for each of the method calls until you actually release the object. This is very similar to the way an object behaves when it's not running through MTS – in other words it's stateful.

 

What does this mean?

I consider this unloading/reloading when using MTS transactions to be extremely inefficient behavior, even for lightweight objects.

 

Consider a typical Web page that uses an ASP COM component written in VFP. In typical scenarios you'll call several methods from a single page. Let's assume for a moment that you make three method calls on your server for the ASP page. If you followed the MTS model properly and created transactable methods that SetComplete or SetAbort to release state, your COM object will load and unload 3 times for this single ASP page! Compare with a single creation when you don't run the component through MTS.

 

Rather than re-creating objects on each hit, these object references should be pooled and reused, especially if objects are truly stateless. But that's not how MTS currently works, so you'll have to put up with the extra overhead it imposes.

 

Unfortunately, this is the model that Microsoft recommends for MTS methods implemented by components where each operation (ie. self-contained method) should be transacted. A more efficient yet less elegant approach might be to control transaction management from the client layer of the application by making the method calls required for a request and then calling SetComplete/SetAbort as needed when everything has been completed. ASP supports MTS transactions internally, or you can simply add methods to your COM components that wrap the Context object SetAbort() and SetComplete() methods so they can be accessed externally. The problem with this approach is that now the client application has to have specific knowledge about how the component works in order to make these calls – it diminishes the usefulness of a middle tier server significantly.

 

When does JITA make sense

Frankly, JITA doesn't make a lot of sense for Web applications that come from a multi-threaded client. The reason is that in this scenario ASP or other middleware Web software manages the objects efficiently on its own. For example, ASP loads servers at the top of the page then releases them at the end. In this respect servers are stateless in their own right without ever dealing with MTS. If you now add MTS into the ASP mix, you simply add overhead to that equation by adding the Out Of Process call, plus all of the loading unloading required on multiple method calls on the same object. The same applies to tools like Web Connection and FoxISAPI which in turn use their own very efficient pool managers that actually pool server instances without unloading. These managers are much more efficient than MTS and also more efficient than ASP because there's no overhead for reloading any instances on each Web hit. Servers must still be stateless because the state may vary between requests, but in this case no assumption about the state of the server is made on each hit – it's reestablished on each hit.

 

Object pooling is something that is currently missing from MTS 2.0 shipping on NT 4. Object pooling is planned for MTS 5.0 but it's unclear at this time exactly what types of COM objects will be able to run in the MTS object pool.

 

So, where does JITA make sense? Consider servers that have large numbers of connected clients that need to hold connections to servers for long periods of time. A typical example, is an application server that's called from client workstations. In this scenario hundreds of users may be connected at any time. The application creates an object reference to a server once then holds on to it for a long period of time most likely over a network DCOM connection. If the components are MTS compatible and can release state, objects will actually unload on the server, while the client still perceives an active connection to the server. MTS has unloaded the object, but as soon as the client requests another method or property access MTS automatically reloads the object – the client never knows that the object wasn't loaded all of the idle time in between. So, instead of running hundreds or thousands of instances of the COM object, on a few are probably active at any point in time. Furthermore, because the object is loaded and managed on the server the performance of accessing that object is much better than a reconnect via pure DCOM over the network would incur. This is because the connection with MTS proxy remains, and only the MTS package reloads the object! This near invokation can provide significant performance gains especially when objects would otherwise have to be reloaded.

 

Other scenarios include, using ASP Sessions. If you have to build stateful ASP applications that must manage users through a site you can assign a COM reference to an ASP session. Without MTS you may have 1000s of simultaneous objects running but with MTS the objects load only when an actual method call is made. This is not a very scalable setup even with MTS, but compared to not using MTS at all there's a difference between running several hundred objects with MTS vs. just a handful without.

 

Look carefully at whether JITA makes sense – in most typical Web application scenarios it does not. For large network COM applications with many clients it may be a huge boon.

MTS Transactions

One of the handiest features of MTS is its distributed SQL transaction support through application/component code rather than through database transactions. The bottom line to this feature is that you get much easier transaction support that also works across multiple databases and even entirely different database products – say SQL Server and Oracle. In other words, you can create a transaction that writes data in both of these databases and control it with a single code module or even span it across multiple COM object calls. MTS wraps the Distributed Transaction Monitor, which works against OleDB/ODBC datasources. If you've ever needed to code two-phase commit code yourself with stored procedures you know how difficult this can be as you have to actually program the DTC directly via API calls or special SQL commands. Using MTS transactions makes this process relatively painless using the same SetComplete/SetAbort method calls to commit or abort transactions.

 

For VFP transactions I suggest you implement a base class that implements the three basic methods that are required for handling MTS transactions. It may not always possible to implement a base class from your business objects which may already be inheriting from other classes – in that case you can add these methods in the appropriate level of the class hierarchy:

 

¨        GetObjectContext()
Retrieves an MTS context object which indicates that the object wants to become stateful. If you want to be part of a SQL transaction you also have to call EnableCommit which is automated by calling this method with a .t. parameter. This method returns an object reference to the MTS context object.

 

¨        SetComplete()
Indicates that the object is to go stateless again indicating success. If EnableCommit was called and transactions were active the SQL transactions are committed. This method simply wraps the MTS SetComplete method, but also adds a NULL check so this doesn't have to be done in any client calling code.

 

¨        SetAbort()
Same as SetComplete except that any SQL transactions are aborted. Understand that SetAbort's behavior in a non-transaction environment (ie. where no SQL transactions are involved) really is no different than SetComplete().

 

The class code looks like this:

 

*************************************************************

DEFINE CLASS MTSBASE AS Relation

*************************************************************

***  Function: Wrapper class for MTS transaction objects

***            Suggest you implement this code even on

***            objects that can't inherit from this.

*************************************************************

 

*** Custom Properties

oContext = .NULL.

oMTS = .NULL.

cErrorMsg = ""

 

************************************************************************

* MTSDemo :: GetObjectContext

*********************************

***  Function: Gets a reference to MTS Context objects

***    Assume: Sets the THIS.oContext property

***      Pass: llTransaction  -  If this object uses real SQL

***                              transactions set this flag to .t.

***    Return: Context Object or .NULL. on failure

************************************************************************

FUNCTION GetObjectContext

LPARAMETERS llSQLTransaction

 

THIS.oMTS = CREATEOBJECT("MTXAS.AppServer.1")

IF ISNULL(THIS.oMTS)

   RETURN .NULL.

ENDIF

  

THIS.oContext = THIS.oMTS.GetObjectContext()

IF ISNULL(THIS.oContext)

   RETURN .NULL.

ENDIF  

 

IF llTransaction

   THIS.oContext.EnableCommit()

ENDIF

 

RETURN THIS.oContext  

ENDFUNC

* MTSDemo :: GetObjectContext

 

 

************************************************************************

* MTSDemo :: SetComplete

*********************************

***  Function: Wrapper around oObjectContext.SetComplete to handle

***            NULLS.

************************************************************************

FUNCTION SetComplete

 

IF !ISNULL(THIS.oContext)

    THIS.oContext.SetComplete()

ENDIF 

 

ENDFUNC

* MTSDemo :: SetComplete

 

************************************************************************

* MTSDemo :: SetAbort

*********************************

***  Function: Wrapper around oObjectContext.SetComplete to handle

***            NULLS.

************************************************************************

FUNCTION SetAbort

 

IF !ISNULL(THIS.oContext)

    THIS.oContext.SetAbort()

ENDIF 

 

ENDFUNC

* MTSDemo :: SetAbort

 

ENDDEFINE

 

To demonstrate how Transactions work against database updates let's look at a simple if not very useful example. To keep things simple I'll use the SQL Server Pubs database with a single table Authors. The following code runs two SQL UPDATE statements, which can either be committed or updated depending on the parameters passed into the method. While this example uses a single database and table realize that you could write code that does the same thing with two completely separate databases (ie one update to a SQL Server table, one to an Oracle table) with exactly the same results:

 

DEFINE CLASS MTSDemo AS MTSBase OLEPUBLIC

 

************************************************************************

* MTSDemo :: ModiAuthor

*********************************

***  Function: Demonstrates that you can modify any DTC database

***            and abort all changes without using transactions.

***            This simple example writes two changes and either

***            writes them or aborts them based on the llFail flag.

***            Real Uses will involve multiple tables or even

***            multiple databases (SQL and Oracle)

***    Assume: Doesn't work against Fox Data

***      Pass: lcID     -    Pubs Author ID (optional)

***            lcPhone  -    Phone number to set to

***            llFail   -    If .T. Abort the Transaction otherwise

***                          write

***    Return: .T. or .F.

************************************************************************

FUNCTION TransactAbortDemo

LPARAMETER lcID, lcPhone, llFail

 

lcID=IIF(EMPTY(lcID),"213-46-8915",lcID)

lcPhone=IIF(EMPTY(lcPhone),"123",lcPhone)

 

*** Get Object Context with Transactions

THIS.GetObjectContext(.T.)

 

*** wwSQL is a SQL Passthrough wrapper class

oSQL = CREATE("wwSQL")

IF !oSQL.Connect("Pubs")

   THIS.cErrorMsg = oSQL.cErrorMsg

   RETURN THIS.SetTransaction(.T.)

ENDIF  

 

lnResult = oSQL.Execute("Update authors "+ ;

                        " set phone = '" + lcPhone + "' " + ;

                        " where au_id = '" + lcId + "' " )

IF lnResult < 1

   THIS.cErrorMsg = oSQL.cErrorMsg

   RETURN THIS.SetTransaction(.T.)

ENDIF                          

 

lcID = "172-32-1176"  && Just a valid record in the Authors table

lnResult = oSQL.Execute("Update authors " + ;

                        " Set phone = '" + lcPhone + "'" + ;

                        " where au_id = '" + lcId + "' " )

IF lnResult < 1

   THIS.cErrorMsg = oSQL.cErrorMsg

   RETURN THIS.SetTransaction(.T.)

ENDIF                          

 

*** Success

IF llFail

  THIS.SetAbort()

  RETURN .F.

ELSE

  THIS.SetComplete()

  RETURN .T.

ENDIF

 

ENDFUNC

 

ENDDEFINE

 

To run this code make sure that you build the server and then add it into MTS as a component. This example runs against the SQL Server Pubs database, so you need to also make sure that you have a DSN called Pubs set up in the ODBC manager. Since we're using SQL Server here I suggest you bring up the SQL Server Enterprise manager and run the following query in its query tool to see the data:

 

SELECT * FROM Authors

 

This will allow you to see the data as it changes (or doesn't change on aborted transactions). In VFP you can now run this sample with:

 

o=create("MTSDemos.MTSDemo")

? o.TransactAbortDemo(,"999",.F.)

? o.cErrorMsg

 

The first parameter is the name of a Social Security number you want to modify. The second is the value of the phone number to set to and the third is a flag that determines whether you want to force the transaction to fail on purpose (simulating a problem).

 

If you run this with the flag left to .F. or no parameter passed the two updates in the example go through and the changes are applied to the database. Do this first to validate that your SQL syntax is working correctly. Then change the value for the phone number above to something else from the "999" and set the flag to .T. This time around you'll find that the data in the table does not change because the transaction was aborted!

 

So what? This would be easy enough to do with regular SQL code, right? Well, look at the code again! Realize that in order to abort both SQL commands all I have to do is call SetAbort(). There are no SQL transactions involved at all – no special BEGIN TRANSACTION/COMMIT/REVERT code to deal with in Transact SQL of a stored procedure or SQLExecute() call. Furthermore, remember this is an overly simple example. This very same code will work against multiple databases, for which it would be impossible to write Transact SQL spanning two databases especially if they are SQL Server and Oracle for example.

 

This functionality lets you write code at the COM component level in your middle tier or business logic layers of the application rather than being forced to write them at the SQL Script level. Writing the code at the higher level provides much better abstraction since the transaction logic is also database backend independent. Moreover, the DTC code automatically handles the updates against totally different backends, which is extremely difficult to program otherwise.

 

It's also possible to spread logic over multiple COM objects by using the Context.CreateInstance() method to create new components that run within the same transaction context as the currently executing transaction. This allows for unlimited nesting of transaction logic. If at any point SetAbort() is called all the data is rolled back.

 

If you use SQL transactions you can also monitor the status of the Distributed Transaction Monitor right inside of IIS:

 

Figure 1.4 – MTS allows monitoring of SQL transactions that are occurring in the system. Note that this form does not report non-SQL transaction of methods that simply use SetComplete/SetAbort – MTSStats.gif

MTS security

There are two kinds of security to set up with MTS: package security as configured by the identity of the package and role-based security for actual security-checking from within the code. The package identity determines which user account is impersonated when the components in the package run. In other words, the package has access to all resources that the specified user has access to. All the components running within the package automatically inherit these rights and this user account. This is a boon especially for applications that run in IIS. As you may know IIS always uses IUSR_Machinename as the anonymous user account which COM objects loaded from IIS automatically inherit. This can be a problem because this account has no rights and there's no way to change this user account! By using MTS you can override the security which changes impersonation as soon as its accessed by the COM client. This is very similar in the way that DCOMCNFG allows configuration of a EXE COM component's user identity – in fact that's exactly what MTS does behind the scenes for the package wrapper which is simply an EXE COM server.

 

When you set up a user id you can choose either Interactive User, which is a good choice for standalone applications and even most Web applications, or a specific user account.

 

One of the nicest features of MTS is its ability to check for security via roles, independent of a user's login ID. MTS allows you to map users to an MTS role and lets you easily check for this role through code. The user in question will be the client that is calling your application, typically the Interactive User in standalone applications, or the IUSR_ account when calling from a Web application. Client applications can check who is accessing them by configuring roles for specific users and then checking for the role with the IsCallerInRole method:

 

IF loContext.IsCallerInRole("BookReader")

   THIS.WriteOutput("Caller is in role")

ENDIF  

 

To add roles, use the management controls to add a role and then add users to the role as needed. The figure below shows the setup of BookReader users.

 

Figure 1.5 – Each packagage can be configured to include specific roles for users. Roles are useful for grouping users into groups that can be easily checked from within an MTS component. Rights can be set for each role which then applies to all users in a role.

 

 

Role-based security comes in very handy when you need to check user identities closely inside a component. For Web applications, most requests come in as generic users on the IUSR_ account, but through Authentication (basic Authentication or NT Passthrough security it's possible to pass user account information down to components to be checked by roles.

 

Summing up

Whew. That was a lot of info and still I haven't touched on all of the functionality that MTS provides. MTS is fairly strategic to Microsoft both in terms of marketing (currently this appears to be the primary reason of the MTS push) as well as the scalability push that will be provided by new features in Windows 2000. In Windows 2000 MTS will help take advantage of many of the advanced scalability features without having to understand and know every detail of their implementation behind the scenes. This is a typical middle tier implementation feature and MTS fits this bill nicely. It remains to be seen how well Microsoft will succeed at this ambitious goal set for the release of Windows 2000 – much of this is unclear even now that Beta 3 of Win2000 is under way…

 

However, if you're looking for big scalability gains from MTS today, you'll be very disappointed. MTS can provide enhancements in some scalability scenarios especially those that need to deal with lots of simultaneously and continuously connected clients, but this is not a common scenario especially in the Web environment which is so popular today for large scale applications. MTS also doesn't currently provide any features for scaling beyond a single machine which is a mystery to me…

 

Regardless, I think MTS is important enough to understand now. MTS will be very tightly integrated with the Windows 2000 COM+ system and the line between 'plain' COM and MTS COM objects will start to blur considerably as MTS features will be merged directly into the base COM system.

 

Keep in mind that MTS is also useful beyond scalability. The distributed transaction support is one feature that is priceless in terms of ease of implementation as is the packaging and role-based security implementation.

 

Weigh the benefits carefully of function versus performance carefully. This is much more important for server applications than others where performance is a crucial issue for success or failure of an application.

 

 

Rick Strahl is an independent developer in Maui, Hawaii. His company West Wind Technologies specializes in Internet application development and tools focused on Internet Information Server, ISAPI, C++ and Visual FoxPro. Rick is author of West Wind Web Connection, a powerful Web application framework for Visual FoxPro, West Wind HTML Help Builder, co-author of Visual WebBuilder, a Microsoft Most Valuable Professional, and a frequent contributor to FoxPro magazines and books. His new book "Internet Applications with Visual FoxPro 6.0", was published April 1999 by Hentzenwerke Publishing. You can contact Rick at: http://www.west-wind.com/.

NewsC