R. L. Parker
If you're developing enterprise applications, Microsoft Transaction Server (MTS) will play an increasingly important role in your life. This article summarizes some of the features of MTS and their impact on your VB applications.
During the past few years, the concepts have outpaced the tools for enterprise- and mission-critical application development. For example, business objects that live in the middle tier allow us to create and maintain encapsulated business logic, and so-called "thin clients" provide us with more options for deployment -- to mention two.
However, as we became convinced of the merits of this architecture and tried to implement it, we discovered that we were lacking some tools and infrastructure. For example, we needed to implement object pools on the server, and we needed to implement sophisticated security mechanisms to support different user populations. MTS narrows the gap between concept and reality -- it's a tool that provides some of the infrastructure our business objects need.
Project requirementsLet's suppose that our job is to implement a Company Directory application. The directory will include an entry for each person in the company. In addition, it will show a value for each possible way to contact a given person: a telephone number, an e-mail address, and so forth. Most users will be allowed only to browse the directory information, but administrators will be allowed to edit it.
Of course, we'll need a database to support our application. It can be modeled very simply (see Figure 1).
MTS is an object brokerMTS is a service that runs on NT and hosts "packages," where a package is defined as consisting of one or more "components" and one or more "roles." For our purposes, we can think of a component as a business object. Each business object is a COM object (read VB5 project built as an ActiveX DLL) with at least one public multi-use class (or "interface," in MTS terminology). A "role" can be thought of as a set of users who are granted permission to some collection of functionality provided by the business object. So, MTS hosts our business objects on a server and provides some run-time utilities to them.
I think MTS was misnamed. Yes, it supports sophisticated transaction management (I'll cover that topic later). But on a day-to-day basis, I think there are a couple of other things that are more important to the average VB application developer. The first is object management. I actually think of MTS as an object request broker. What does that mean? Consider the following fragment of VB code from our Company Directory client application:
Public moCM As DirectoryManager.clsDirectoryManager
Set moCM = New DirectoryManager.clsDirectoryManager
…
If the Registry has been correctly configured, the new instance named moCM will be created in the MTS runtime environment on the business server, rather than locally on the client workstation. MTS even provides a utility called "Export Package" that automates Registry configuration on client machines.
In the old days (before MTS), if you wanted to run a business object on a remote server, you would have implemented it as an ActiveX EXE and configured it via RACMAN or DCOMCNFG. You would have had to make a decision about the public class's instancing property -- you could make it a multi-use server or a single-use server. It was a difficult decision to make, because if you made it a multi-use server, all clients would share a single instance of the business object; of course, performance might suffer. On the other hand, if you made it a single-use server, resource usage on the server might become a problem, because these business objects tend to use memory in massive quantities. Neither approach scaled very well. The solution, in many early n-tier applications, was to implement a homegrown "pool manager." At runtime, the application asked the pool manager for an object instead of instantiating it directly. The pool manager decided whether enough resources were available to grant the request.
Figure 1. A logical model that supports the Company Directory application
Now, things are different. MTS expects a business object to be a multi-use public class in an ActiveX DLL (which, of course, you can build with VB5). Each instance of the object runs in process with the MTS run-time environment. Where's the scalability advantage? If your code gives it permission to do so, MTS "deactivates" objects when they're not in use. What does "deactivation" mean? It means, essentially, that the instance is deallocated. This is completely transparent to the client code -- but it means that dozens or hundreds of clients can be supported with the same resources that could support only a handful of clients otherwise. I like to think of this as the best of both options that we used to have. Resource usage is minimized while simultaneously increasing the practical number of users your application can support.
You might have wondered, "If my object is deallocated, what happens to its state -- the value of module-level variables, for example?" That's the catch: Your business objects must be "stateless" to be MTS-friendly. This idea is one that might take some getting used to. Let's look at statelessness in a little more detail. Many early n-tier applications were designed like this:
Public Function LoadCustomers() As Long '# of custs
Public Function GetCustomerDetails(lCustomerNum as _
Long) …
Public Sub UpdateCustomer(lCustomerNum, …)
From this fragment of a business layer interface, it's easy to see that this business object itself is maintaining a collection of customer objects, which the application layer then navigates through, probably displaying customer details to the user.
The collection of customer objects and the pointer to the "current" customer are "state" that the business object maintains on behalf of its client (the application). In the MTS environment, the design considerations are different. Instead of thinking of the business object as a "class," it should be thought of as a "service." The application state is maintained by objects that live in the application layer, instead of by objects that live in the business layer. This makes it possible to write stateless business objects that are MTS-friendly.
Now that we have stateless business objects, how do we tell MTS that it's okay to deactivate our business objects? Listing 1 contains the implementation of one of the methods in our Company Directory business object. As its name suggests, it updates individual information in the company directory.
Listing 1. Code to update information in the Company Directory.
Public Sub UpdateIndividual(ByVal lIndividualID As _
Long, Optional sNewFirstName As String = "", _
Optional sNewLastName As String = "")
On Error GoTo UpdateIndividualErr
Dim sQuery As String
Dim oDataLayer As clsData
Dim oCtx As ObjectContext
Dim vResults As Long
Set oCtx = GetObjectContext()
Set oDataLayer = New clsData
sQuery = "UPDATE Individuals SET"
sQuery = sQuery & " sFirstName = '" & _
sNewFirstName & "'"
sQuery = sQuery & ", sLastName = '" & _
sNewLastName & "'"
sQuery = sQuery & " WHERE IndividualID = " & _
CStr(lIndividualID)
oDataLayer.UpdateData sQuery
If Not oCtx Is Nothing Then 'running under MTS
GetObjectContext.SetComplete 'tell MTS we're done
End If
Exit Sub
UpdateIndividualErr:
If Not oCtx Is Nothing Then 'running under MTS
GetObjectContext.SetAbort 'tell MTS we're unhappy
End If
Err.Raise Err.Number, Err.Source & " " & _
"clsDirectoryEntryManager.UpdateIndividual", _
Err.Description
End Sub
The lines to notice for now are:
GetObjectContext.SetComplete
GetObjectContext.SetAbort
Communicating with MTS through the ObjectContext object, you tell MTS with either of these method calls that your work is done and your object can be deactivated (you'll see these calls again when I talk about transactions).
MTS is a security managerI'm very excited about MTS's ability to manage security for an application. For example, recall that, in our Company Directory application, two different user populations are supported: "editors" (see Figure 2) and "browsers" (see Figure 3).
Figure 2. The Company Directory application as seen by an"editor."
Figure 3. The Company Directory application as seen by an"browser."
To support that kind of security requirement, the application needs to be able to ask the business object the following question: "Given the user's network ID, what permissions does the user have?" Then the application can use the answer to correctly configure the application.
Without MTS, you need to add the following pieces in order to support the security requirement:
With MTS, you can eliminate the first two items. MTS provides the security infrastructure so you don't have to build it yourself. The MTS security infrastructure is based on "roles." A role can be thought of -- from the application's point of view -- as a collection of permitted functionality. The roles should be determined during the analysis phase. They should be documented in the requirements document, along with the screen variations that users in different roles will see when the application is run. After the business object is installed in MTS, the defined roles are added to the package and to the appropriate components. The DirectoryManager package and its roles are shown in Figure 4.
MTS provides two ways to use the role information: "declarative" security and "programmatic" security. Declarative security is configured completely from the Microsoft Management Console -- no code is required, but it's a gross level of control. Any user in a role (directly or indirectly through an NT group) that's in the "role membership" list of a component can instantiate and use the component. If a component has more than one interface (that is, more than one public class), the roles can be added to the role membership lists of the appropriate interface instead for finer control. Still, unless you structure your business objects so those roles are mapped one-to-one to business objects (which doesn't sound like a good idea), you're going to need an even finer level of control. Programmatic security gives you all the control you need. The MTS run-time environment exposes an object called ObjectContext that provides several methods that your business object code can use to interact with MTS. For our present purposes, the interesting method is IsCallerInRole. This method takes a string argument (the Role name) and returns a Boolean result. If the security is enabled on the component, and the "base client" is in the role, IsCallerInRole returns True; otherwise, it returns False. Okay, so now we can implement the "browsers" and "editors" functionality. We could do it this way in the business object:
Figure 4. The DirectoryManager package and its roles. Notice that it runs inside the NT hosted Microsoft Management Console (MMC)
Public Sub UpdateIndividual(…)
Dim oCtx As ObjectContext
Set oCtx = GetObjectContext() 'from MTS runtime env
…
If Not oCtx.IsCallerInRole("Editors") Then
'user doesn't have permission!
Err.Raise …
End If
' if you got this far, the user passed the
' permission test, so do the edit
…
End Sub
This is what I call the reactive approach -- it meets the requirements by preventing unauthorized users from editing any information in the Company Directory. We need it in order to make sure that our business object is bulletproof. However, I prefer a proactive approach -- so let's add a method to our business object:
Public Function UserCanEdit() as Boolean
Dim oCtx As ObjectContext
Set oCtx = GetObjectContext()
UserCanEdit = oCtx.IsCallerInRole("Editors")
…
End Function
Now the application can configure the form by calling this method in the business layer. Unauthorized users can't even attempt to edit the data.
MTS is a transaction managerOkay, MTS is a transaction manager -- so what about transactions? Looking again at Listing 1, you'll see that we call either SetComplete or SetAbort every time the UpdateIndividual method is invoked on our business object. If there are no errors, we'll tell MTS that everything is okay, and it can commit any transaction(s) that were caused by the execution of our method. On the other hand, if we call SetAbort, it means that any transactions should be rolled back. MTS handles all the transaction details for us -- even across physical data sources.
Web clientsImagine this: We're just wrapping up our Company Directory project. We've implemented the business object and installed it in MTS. We've finished the VB client and have it running on the desktop. Just then, the boss walks in and says, "Oh, by the way, top brass wants the Company Directory to run on our intranet, and they want it by the end of the month." Then he adds, "They want it to run on our public site, too, but they only want the phone numbers of the salesmen on that page." Yikes!
But we did good analysis and design. And we were smart enough to create our functionality in a business object and run it under MTS. So the only work left to do is to create the Active Server Page (see Figure 5). We reply with a smile, "Any bonus for delivering it by the end of the day?"
Conclusion
MTS provides us with some crucial infrastructure for building scalable applications. It's easy to code for, easy to install and administer, and it makes life better all around. What are you waiting for?
Download sample code here.R. L. Parker is a Microsoft Certified Solution Developer and senior technical lead at DB Basics, Inc. in Raleigh, NC, who specializes in mentoring and custom development of mission-critical database applications, including data-driven Web sites. rlp@dbbasics.com.
Figure 5. ASP code underlying this page is included in the subscriber Download file.