BUY THIS BOOK
Add to Cart

Print Book $44.99


Safari Books Online

What is this?

Add to UK Cart

Print Book £28.50

What is this?

Looking to Reprint this content?


COM & .NET Component Services
COM & .NET Component Services By Juval Löwy
September 2001
Pages: 384

Cover | Table of Contents | Colophon


Table of Contents

Chapter 1: COM+ Component Services
By now, most developers of large-scale enterprise applications are convinced of the benefits of component-oriented development. They have discovered that by breaking a large system down into smaller units, they can write code that is easier to reuse on other projects, easier to distribute across multiple computers, and easier to maintain. As long as these components adhere to a binary standard that defines how they communicate with one another, they can be invoked as needed at runtime and discarded when they have finished their work. This type of application is also particularly suited to the Web, where clients request services of remote applications and then, once satisfied, move on to other tasks.
For nearly a decade, the Microsoft Component Object Model (COM) has been the standard for components that run on Windows machines, including Windows 9x and Me clients and Windows NT and 2000 servers. The COM model is well documented by the Microsoft Component Object Model Specification. Tools such as Visual C++ and Visual Basic make it easy to create COM components, and scores of books, training classes, and articles are available to teach programmers how to use them. Many features of the Windows operating system are now implemented as COM components, and many companies have invested heavily in COM-based systems of their own.
In July 2000, Microsoft announced a radically new component model as part of its .NET development platform, suddenly calling into question the viability of existing COM applications. .NET components bear little resemblance to legacy COM components and are not backwards compatible. They can be made to interoperate with COM components but do not do so naturally.
When it comes to the services and tools programmers use to build enterprise-scale .NET applications, however, one facility continues to provide the necessary runtime infrastructure and services: COM+ component services. These services have been available on Windows 2000 since its release, but they will gain greater importance in the months ahead. As it turns out, they offer a bridge between traditional COM and .NET applications, making your understanding and mastery of them as important now as it has ever been.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
COM+ Component Services
Components need runtime services to work. The original COM runtime supported components located on the same machine, typically a desktop PC. As the focus of Windows development shifted from standalone PCs to networked systems, Microsoft found it necessary to add additional services (see The Evolution of COM+ Services). First, they added support for distributed applications, or applications whose components are located on more than one machine (sometimes referred to as "COM on a wire"). Later, Microsoft added new services to support enterprise applications, whose complexity and scale placed new demands on the resources of a system and required an entirely new level of support. These trends were only exacerbated by the move to web-based applications aimed at huge numbers of customers connected over the public Internet.
Collectively, the services that support COM and .NET component-based applications are known as the COM+ component services, or simply as COM+.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
The Component Services Explorer
COM+ components and applications are managed through the Component Services Explorer (formerly known as the COM+ Explorer).The Component Services Explorer is a Microsoft Management Console snap-in and is available on every Windows 2000 machine.
To fire up the Component Services Explorer, go to the Start menu and select Settings Control Panel. When the Control Panel window appears, select the Administrative Tools directory and then select the Component Services application.
The first thing you should do after locating the Component Services Explorer is create a shortcut to it on your desktop. As a developer, you need easy access to the Component Services Explorer, your main gateway into COM+ (see Figure 1-1). You can use the Component Services Explorer to create and configure COM+ applications, import and configure COM or .NET components, export and deploy your applications, and administer your local machine. You can even administer COM+ on other machines on the network, provided you have administrative privileges on those machines.
A COM+ application is a logical group of COM+ components. Components usually share an application if they depend on one another to accomplish their tasks and when all the components require the same application level configuration, as with security or activation policy. Components in the same application are often developed by the same team, and are meant to be deployed together.
You can see all the COM+ applications installed on your machine by opening the Component Services Explorer and expanding the Computers folder in the Tree window: Computers My Computer COM+ Applications. Every icon in the COM+ Applications folder represents a COM+ application. Each COM+ application contains COM+ components. Components must be explicitly imported into the Component Services Explorer to take advantage of COM+ services.
The Component Services Explorer offers a hierarchical approach to managing COM+ services and configurations: a computer contains applications,
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Hello COM+
The best way to become acquainted with the Component Services Explorer and basic COM+ terminology is to do a trivial example. This section walks you through the COM+ equivalent of the canonical "Hello World" program. You will build a COM+ application containing a COM component that displays a message box saying "Hello COM+".
When developing your "Hello COM+" application, follow these steps:
  1. Create a classic COM component. All COM+ components start their life as classic COM components, developed with such tools as ATL, MFC, or Visual Basic 6.0.
  2. Create a new COM+ application to host the component.
  3. Add the component to the application.
  4. Write a client and test the component.
The rest of this chapter uses this "Hello COM+" example to demonstrate various COM+ features and capabilities. The example is also available as part of the source files provided with this book (see the Preface for information on how to access these files).
We will use ATL 7.0 to generate a classic COM component, although you can also do it in Visual Basic 6.0 with almost the same ease. Start a new ATL project in Visual Studio.NET and name it Hello. For simplicity, do not use Attributed project (deselect Attributed in the ATL Project Wizard under Application Settings). Also, do not select COM+ 1.0 support. This selection adds a few interfaces explained in subsequent chapters that are not relevant to this example. Bring up the Add Class dialog ATL and select the Simple ATL Object item. This step should bring up the ATL Simple Object Wizard dialog (see Figure 1-2). Type the following entries, in order:
  1. In the Short Name field, enter Message.
  2. In the CoClass field, enter Hello.
Your completed dialog should look like Figure 1-2. There is no need to access the Options selection in the dialog (just use the defaults). Click OK when you're done.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
COM+ Configured Components
COM+ allows you to import only in-proc (DLL) components. You cannot import COM components that reside in a local server (EXE); COM+ lets you configure the activation type of your application, server, or library. In the case of a library, the client simply loads the original DLL into its process and uses the component. If you configure the application to be a server application, COM+ promotes your original DLL to become a local server by hosting it in a surrogate process of its own. However, COM+ cannot make a library application out of a COM local server. In addition, many COM+ services require explicit process-level administration that the local server's code simply does not contain.
Once an in-proc component is imported to COM+, it is called a configured component to emphasize the fact that much component functionality and behavior is actually configured and administered outside the component. A classic COM component (be it in-proc or local) that has not been imported into COM+ is called a nonconfigured component. Configured and nonconfigured components can interact freely and call each other's interfaces. The configured component must reside on a Windows 2000 machine, but the client of a configured component can reside on any Windows-family machine, such as Windows NT, Windows Me, or Windows 9x.
Configuration lets you control the way your application, component, interface, or method behaves under COM+. The COM+ development paradigm lets COM+ manage as much of the nonbusiness logic plumbing as possible by declaring what services you want to use. Doing so lets you focus on the domain problem you are trying to solve and add business value instead of plumbing code to your product.
Your configured component's interfaces can be dual, dispatch, or custom interfaces. If you use automation-compliant interfaces, you do not need to provide COM+ with a proxy/stub DLL (see COM Interface Types for more information).
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Applications, DLLs, and Components
COM+ applications are logical packaging units; DLLs, however, are physical packaging units. There is no correlation between logical and physical packaging. The only requirement is that a configured component must belong to exactly one COM+ application; it cannot belong to more than one, and it must belong to at least one to take advantage of COM+ component services. As demonstrated in Figure 1-8, a COM+ application can host components from one or multiple DLLs (Application 2 has components from two DLLs). It is also possible that not all the components in a DLL are hosted in COM+ applications (such as component E), and one DLL can contribute components to multiple COM+ applications (DLL 1 contributes components to Application 1 and Application 2).
Figure 1-8: COM+ applications and DLLs
The separation of physical from logical packaging gives you great flexibility in designing your application's layout. All the components in the same COM+ application share the same application-level configuration settings, regardless of their underlying DLL packaging. However, I recommend that you avoid installing components from the same DLL into more than one application, such as components B and C in Figure 1-8. The reason is that components in the same application are assumed to operate tightly together and trust each other. On the other hand, nothing is assumed about components from different applications. By placing components from the same DLL into multiple applications, you may introduce needless security checks. You might also introduce cross-process marshaling overhead, if those components need one another to operate, which is probably why they were put in the same DLL in the first place. The COM+ Component Install Wizard also does not handle components from the same DLL in multiple applications very well. When you use the wizard to add components from a DLL to an application, the wizard tries to add all components in the DLL to the application. If some of the components are already part of other applications, the wizard will treat this situation as an error since it will think you are trying to include a component in more than one application.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Configuring COM+ Applications
The primary benefit of using COM+ is that you can configure a component or the application containing it without changing any code on the object or the client side. This advantage enables you to focus your object code on its intended purpose, relying on the various services COM+ provides instead of having to develop them yourself. This section shows you how to configure some of the application-level options for the Hello COM+ program you created.
As mentioned previously, the application activation type (a server or a library application) is a configurable application-level attribute called activation. You can configure the application's activation type in the application's properties page, under the Activation tab (see Figure 1-9).
Figure 1-9: Application Activation tab
Changing the application type has significant implications for most COM+ services. The application type is a design-time decision that should consider the security needs of your components, the calling patterns of your clients, fault isolation (a server application gets its own process), and specific COM+ services requirements. Throughout the book, a particular service configuration that is related to the activation type is pointed out explicitly. However, even without knowing much about COM+, you can use the following rule to decide on your activation type: prefer server type applications, unless you absolutely need to run in the client process for performance reasons. Library applications have some limitations in using COM+ services (such as security and queued component support), and they cannot be accessed from another machine.
If the original COM components resided in a DLL, how does COM+ achieve different activation modes for the configured components? When the application is configured as a library, the client loads the DLL directly into its process. When the application is configured as a server application, COM+ creates a surrogate process for it, called
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Debugging COM+ Applications
Debugging a COM+ application, be it a library or a server application, is not much different from debugging an in-proc COM object or a local server. A library application has the clear advantage of allowing you to step through your code directly from the test client, since a library and a server application share the same process. A server application always runs in a different process than your test client and, therefore, in a different debug session (a different instance of Visual Studio is attached to that process). When debugging the business logic part of your application, you may find it useful to debug it as a library application, even if the design calls for a server application. When debugging a library application, you may also need to point Visual Studio to the exact location of the component's DLLs. This step is required so you can set breakpoints in the component's code.
When debugging a component in a server application, you can step into the component's code from the test client side in two ways. First, you can start the client project in the debugger, break at a line where you call a method on a component in the server application, and simply step into it (F11 in Visual C++ or F8 in Visual Basic). This process launches a new instance of the debugger and attaches it to the running dllhost containing your component. You can then step through your component's code. Second, you can attach a debugger to a server application by configuring it to launch in a debugger. On the server application properties page, under the Advanced tab, there is the Debugging properties group. If you check the Launch in debugger checkbox (see Figure 1-12), when the first request for creating an object from that application comes in, COM+ launches the application in a Visual C++ debugger session. You may use this option often to track bugs in the constructors of components or bugs that do not happen in the scope of a client call. COM+ is able to attach the debugger to the application using a command-line option for Visual Studio. When you launch the debugger with an executable filename as a parameter, the debugger starts a debug session and creates the specified process (in COM+'s case, always dllhost). COM+ also specifies the server application ID as a command line parameter for dllhost:
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Deploying COM+ Applications
Once you have tested your COM+ application and configured all the COM+ services to your liking, you need to install your application on a customer/client machine. The Component Services Explorer can generate a special file that captures all your application components and settings. This file is a Windows Installer (MSI) file, identified by the .msi file extension. Clicking on an MSI file launches the Windows Installer and installs the application with all its COM+ configuration parameters. There is a one-to-one relationship between an application and an MSI file. Thus, if you have multiple applications in your product, you must generate one MSI file for each application.
To generate the MSI file, right-click on your application icon in the Component Services Explorer and select Export from the pop-up context menu. This action should bring up the Application Export Wizard. Click Next to go to the second wizard screen, where you are requested to enter the name and location for the application export file to be created (see Figure 1-13). Next, you should decide how to export the application: as a Server application or as an Application proxy (see Figure 1-13). Click Next and then click Finish on the next Wizard screen.
Figure 1-13: Exporting a COM+ application
The names Server application and Application proxy are confusing. A "Server application" export is relevant for both library and server applications. It means that the application will include in the MSI file the COM objects themselves, their settings, and their proxy/stub DLLs (if required), and will install all on the server machine.
An "Application proxy" export installs on the client machine only the type information in the MSI it creates (as well as the proxy/stub DLLs, if required). The generated file does not have to include the components themselves (unless the type information is embedded in the components, in which case the components are only used as containers and are not registered). You can use a proxy installation when you want to enable remote access from a client machine to the machine where the application actually resides. A proxy export is available only for a COM+ server application, not for a library application.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Summary
In this chapter, you created a trivial example COM component and implemented it in a DLL. You used it as an in-proc server or as a local server and even controlled its life cycle and idle time management by configuring the component (actually its containing application) differently. All this was achieved without changing a single line of code on the object or the client side. This achievement reflects the power of COM+: it enables you to focus on your product and domain problems at hand, while declaratively taking advantage of available services. The rest of this book discusses these services thoroughly, including their interactions and pitfalls, and provides tips and tricks for how to apply them productively.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 2: COM+ Context
COM+ provides services to components by intercepting the calls the client makes to component interfaces. The idea of providing services through an interception mechanism is not a COM+ innovation. As you will see, classic COM also provides component services via interception. What is new is the length to which COM+ takes the idea. This chapter starts by describing the way classic COM uses marshaling to provide its services and to encapsulate the runtime requirements of its objects. Next, the chapter introduces you to the COM+ context—the innermost execution scope of an object. COM+ call interception occurs at context boundaries. Generally, you need not be concerned with contexts at all. They are transparent to you, whether you develop a client or a component. However, the COM+ context is a good model for explaining the way COM+ services are implemented. This book clearly outlines the few cases when you should interact with the contexts directly. Interaction with the contexts occurs mostly when dealing with COM+ instance management and transactions, but also when dealing with some security issues.
One of the core principles of classic COM is location transparency. Location transparency allows the client code to be independent of the actual object's location. Nothing in the client's code pertains to where the object executes, although the client can insist on a specific location as well. A client CoCreates its objects and COM instantiates them in the client's process, in another process on the client's machine, or on another machine altogether. COM decides where the objects will execute based on a few Registry values. Those values are maintained outside the object code. A change in those values can cause the same object to be activated in a different location. The same client code handles all cases of object location. You can say that COM completely encapsulates the object location. A key idea in object-oriented and component-oriented programming is encapsulation, or information hiding. Encapsulation promotes the design of more maintainable and extensible systems. By ignoring the object location, the client code is decoupled further from the object. The client code does not need to be modified if the object location changes. COM encapsulates the object location by introducing a proxy and stub between the object and its client. The client then interacts with the object directly or through a proxy, and COM marshals the call from the client to the object's true location, if it needs to (all three cases are shown in Figure 2-1). The important observation here is that the client code is not required to make assumptions about the location of its called objects or to make explicit calls across processes (using named pipes, for instance) or across machines (using sockets).
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Encapsulation via Marshaling in COM
One of the core principles of classic COM is location transparency. Location transparency allows the client code to be independent of the actual object's location. Nothing in the client's code pertains to where the object executes, although the client can insist on a specific location as well. A client CoCreates its objects and COM instantiates them in the client's process, in another process on the client's machine, or on another machine altogether. COM decides where the objects will execute based on a few Registry values. Those values are maintained outside the object code. A change in those values can cause the same object to be activated in a different location. The same client code handles all cases of object location. You can say that COM completely encapsulates the object location. A key idea in object-oriented and component-oriented programming is encapsulation, or information hiding. Encapsulation promotes the design of more maintainable and extensible systems. By ignoring the object location, the client code is decoupled further from the object. The client code does not need to be modified if the object location changes. COM encapsulates the object location by introducing a proxy and stub between the object and its client. The client then interacts with the object directly or through a proxy, and COM marshals the call from the client to the object's true location, if it needs to (all three cases are shown in Figure 2-1). The important observation here is that the client code is not required to make assumptions about the location of its called objects or to make explicit calls across processes (using named pipes, for instance) or across machines (using sockets).
Figure 2-1: Classic COM completely encapsulates the object location from the client by introducing a proxy/stub between them
To provide location transparency, COM proxies are polymorphic with the object; they support exactly the same set of interfaces as the real object, so the client cannot tell the difference between the proxy and the real object.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Encapsulation via Interception in COM+
COM+ provides its component services via interception. You can configure your component to take advantage of services, and COM+ puts a proxy and stub between the component and its client, if the client and the component instance are incompatible with any one of the services. It also puts a proxy and stub between them if a service requires interception, regardless of the way the client and the object are configured. The exact object configuration is completely encapsulated by the proxy and stub and the call interception. Nothing in the client code couples it to the object configuration. This development is a major step toward ultimate encapsulation, in which the component contains almost nothing but business logic and in which the way it uses component services such as transactions, security, events, and activation is hidden from the client. Similarly, the component does not care about its client configuration, as the two do not need to interact with each other about the way they use the services.
Because an object can have the same threading model as its creating client while differing in other service configuration, apartments can no longer be the innermost execution scope of an object. Instead, COM+ subdivides apartments, so each object can be placed in a correct runtime environment appropriate to its needs and intercept all calls to the object. The subdivision of an apartment into units of objects that share the same configuration is called a context. Each apartment has one or more contexts, and a given context belongs to exactly one apartment. A context can host multiple objects, and each object belongs to exactly one context. Figure 2-3 shows an example of how processes and apartments can be broken down into contexts under COM+.
Figure 2-3: COM+ subdivides apartments into contexts
Because a COM+ object must belong to exactly one context, every apartment has at least one context and potentially many more. There is no limitation to the number of contexts an apartment can host. All calls in and out of a context must be marshaled via a proxy and stub so that COM+ can intercept the calls and provide configured services. This idea is similar to the classic COM requirement that all cross-apartment calls be marshaled so that COM can enforce threading model configurations. Objects in the same context can have direct pointers to one another, because they are configured to use the same set of services in a way that allows same-context activation, and hence, direct access. Mediating between objects in the same context is not necessary.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
The Context Object
COM+ represents each context by an object called the context object. Every context has exactly one context object. Objects can obtain a pointer to their context object by calling CoGetObjectContext( ) (see Figure 2-5). All objects in the same context get the same context object.
CoGetObjectContext( ) is defined as:
Figure 2-5: By calling CoGetObjectContext( ), objects can get a pointer to their context's context object
HRESULT CoGetObjectContext(REFIID riid, void** ppInterface);
The context object supports a few interfaces, so the first parameter of CoGetObjectContext( ) is always an IID that specifies which interface to retrieve. Two of the context object's interfaces, IObjectContext and IObjectContextActivity, are legacy interfaces from MTS and are provided primarily for backward compatibility with MTS components running under COM+. The other two interfaces, IContextState and IObjectContextInfo , are specific to COM+. Throughout this book, all chapters use these two interfaces, rather than the legacy MTS interfaces.
The IContextState interface controls object deactivation (discussed in Chapter 3) and transaction voting (discussed in Chapter 4) by manipulating state bits in the context object.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
The Call Object
In addition to providing a context object to represent the context of an object, COM+ creates a transient object called the call object each time that object is called. The transient call object represents the current call in progress. Objects can access their call object by calling CoGetCallContext( ) (see Figure 2-6). The CoGetCallContext( ) signature is defined as:
HRESULT CoGetCallContext(REFIID riid, void** ppInterface);
The call object only exists as long as a call from a client is in progress, and it is destroyed by COM+ after the called method returns. You should not cache a pointer to the call object as a member variable of your object because that pointer will be invalid once the method that saved it returns. Furthermore, if your object is doing work in the background—that is, no method call from the client is currently in progress—it will not have access to a call object. If you try to access a call object while a call is not in progress, CoGetCallContext( ) will fail and return the error code RPC_E_CALL_COMPLETE . You can, however, still access the context object, which exists as long as the context exists, and whose pointer can be cached by the objects associated with it.
The call object exposes two interfaces used to obtain information about the call security settings. These interfaces, discussed in Chapter 7, are ISecurityCallContext and IServerSecurity .
Figure 2-6: When a method call is in progress, a COM+ object has access to the call object
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Cross-Context Manual Marshaling
Cross-context call interception via marshaling is how COM+ provides its component services to your object. A client in a different context cannot access your object directly, even if it has a direct raw pointer to it. Intercepting the call and performing the right service switches requires a proxy and a stub in between. Otherwise, the object executes in the client context, possibly in an ill-suited runtime environment. If the client gets the pointer to your object in one of the following ways:
  • CoCreating the object
  • Querying an object the client already has for additional interfaces
  • Receiving the pointer as a method parameter on a COM interface
Then COM+ will, under the hood, put interceptors (proxys and stubs) in place, to make sure all calls into the object are marshaled. If the client does anything else to obtain the interface pointer, such as retrieve it from a global variable or a static member variable shared among all clients, you have to marshal the pointer manually yourself. Dealing with pooled objects is another situation requiring manual marshaling, as you will see in the next chapter.
Classic COM requires that all cross-apartment calls be marshaled, even when the call is in the same process, to ensure threading model compatibility. The classic COM mechanisms for manually marshaling interface pointers across apartment boundaries have been made context-aware. They are what you should use to marshal interface pointers manually across context boundaries with COM+.
Generally, these mechanisms rely on the CoMarshalInterface( ) and CoUnmarshalInterface( ) functions. When you need to manually marshal an interface pointer from Context A to Context B, you would serialize the interface pointer into a stream in Context A using CoMarshalInterface( ), and get it out of the stream using CoUnmarshalInterface( ) in Context B. This sequence would manually set up proxies in Context B for accessing the object. You can also use the
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Summary
This chapter introduced the COM+ context concept: a mechanism for providing component services. By intercepting client calls and performing additional processing, COM+ can ensure that the object has just the runtime environment it requires.
As stated at the beginning of this chapter, you usually do not need to interact with COM context or be aware that they exist. But understanding this abstract concept helps demystify the way COM+ services operate. Context and call interception is an extensible mechanism. As time goes by, new services can be added this way without affecting existing applications. When a client creates instances of your old component in the new environment, COM+ silently does its context compatibility in the background, and your existing component never knows that new services are available.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 3: COM+ Instance Management
A few years ago, the dominant programming model and design pattern was the client/server model. COM and DCOM were predominant component technologies, and all was well. Then came the Internet revolution. Almost overnight, a new paradigm emerged—the multitier architecture . Scalability is perhaps the single most important driving force behind the move from classic two-tier client/server to multitier applications. Today, being able to handle a very large number of clients is necessary for survival. The classic two-tier model simply does not scale well from a few dozen clients to tens of thousands of clients hammering on your system at peak load. The two-tier model of dedicating one server object per client quickly causes critical resources to dwindle under such loads. Allocating resources such as a database connection, a system handle, or a worker thread to each client is unrealistic. The middle tier was introduced precisely because you could no longer map client objects directly to your data processing objects. The middle tier allows pooling of resources, such as database connections, hardware objects, or communication ports. The middle tier also allows you to activate your objects just when they are required and release them as soon as possible.
COM+ provides you with two elegant and user-friendly instance management services that you can use to build scalability into your system design from day one: object pooling and Just-in-Time Activation (JITA).
This chapter first defines the problems you face when designing a modern distributed system; it then explains COM+ strategies for managing objects that compose it.
A distributed system, by its very nature, implies that its clients are not on the same machine as the objects providing the services. In every distributed system, there are typically two kinds of clients: rich clients and Internet clients . The rich client typically shares the same local area network, called the Intranet. (A rich client can also be called an
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Client Types
A distributed system, by its very nature, implies that its clients are not on the same machine as the objects providing the services. In every distributed system, there are typically two kinds of clients: rich clients and Internet clients . The rich client typically shares the same local area network, called the Intranet. (A rich client can also be called an intranet client .) In most cases, no firewalls between the rich client and the application exist, so the rich client can invoke binary calls on components in the application. The Internet client connects to your application typically by using a web browser, but more of the other options, such as hand-held PDAs and cellular phones, are possible as well. The Internet client is located outside of your local area network and can reside anywhere on the Internet. In most cases, a firewall exists between the Internet client and your application.
Most applications have a mixture of rich and Internet clients. Some systems had only rich clients until they were opened to the Internet. Other systems were designed primarily for the Internet, but had to support rich clients—perhaps for application management, back-office operations, or other specific needs. In any case, when you design an application, you should plan to support both kinds of clients. The two kinds differ not only in the way they connect to your application, but also in their pattern of interaction with it. Your design should be able to scale up to both kinds of clients and compensate for their differences. COM+ instance management services were developed to answer precisely that challenge.
A rich client's interaction with the server objects of a distributed application resembles that of the classic client/server application. The client connects to the server machine using a network protocol such as TCP/IP. Because the Intranet is considered a secure environment, it usually contains no firewalls and the client can connect directly to your server objects in the middle tier using DCOM (see Figure 3-1). The calling pattern to your application is as follows: create an object, use it, and eventually release it. The rich client usually presents to the user a
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Instance Management and Scaling
Being smart about the way you allocate your objects to clients is the key to scalability in a modern distributed system. Simple algorithms can be used to govern when and how expensive objects that have access to scarce resources will actually service a client request. In distributed-systems terminology, these algorithms and heuristics are called instance management. COM+ refers to instance management as activation .
COM+ provides every configured component with access to ready-made instance management services. Every COM+ component has on its properties page an Activation tab that lets you control the way objects are created and accessed (see Figure 3-3). You can use COM+'s two instance management services, object pooling and JITA, individually, or combine them in a very powerful way. Neither technique is a COM+ innovation. What is new about COM+ is the ease with which you can take advantage of the service. That ease allows you to focus your development efforts on the domain problem at hand, not on the writing of instance management plumbing.
Figure 3-3: The COM+ component's Activation tab
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Object Pooling
The idea behind object pooling is just as the name implies: COM+ can maintain a pool of objects that are already created and ready to serve clients. The pool is created per object type; different objects types have separate pools. You can configure each component type pool by setting the pool parameters on the component's properties Activation tab (as shown in Figure 3-3). With object pooling, for each object in the pool, you pay the cost of creating the object only once and reuse it with many clients. The same object instance is recycled repeatedly for as long as the containing application runs. The object's constructor and destructor are each called only once. Object pooling is an instance management technique designed to deal with the interaction pattern of Internet clients—numerous clients creating objects for every request, not holding references on the objects, but releasing their object references as soon as the request processing is done. Object pooling is useful when instantiating the object is costly or when you need to pool access to scant resources. Object pooling is most appropriate when the object initialization is generic enough to not require client-specific parameters. When using object pooling, you should always strive to perform in the object's constructor as much as possible of the time-consuming work that is the same for all clients, such as acquiring connections (OLEDB, ADO, ODBC), running initialization scripts, initializing external devices, creating file handles, and fetching initialization data from files or across a network. Avoid using object pooling if constructing a new object is not a time-consuming operation because the use of a pool requires a fixed overhead for pool management every time the client creates or releases an object.
Any COM+ application, whether a server or a library application, can host object pools. In the case of a server application, the scope of the pool is the machine. If you install proxies to that application on other machines, the scope of the pool can be the local network. In contrast, if the application is a library application, then a pool of objects is created for each client process that loads the library application. As a result, two clients in different processes will end up using two distinct pools. If you would like to have just one pool of objects, configure your application to be a server application.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Just-in-Time Activation
Object pooling is a great instance management service, but what should you do when you deal with rich clients who can hold onto object references for long periods of time? It is one thing if the rich clients make intensive use of the object, but as you saw earlier, they actually maintain the reference on the object to improve performance on their side, and may actually call methods on the object for only a fraction of that time. From the object's perspective, it must still hold onto its resources because a call may come through at any moment. Object pooling is of little benefit, since it saves you the cost of creating the object, not the cost of maintaining it while tied up with a client. Clearly, another tactic is required to handle greedy Intranet clients.
COM+ provides another instance management technique called Just-in-Time Activation (JITA) that allows you to dedicate an object per client only while a call is in progress. JITA is most useful when instantiating the object is not a costly operation compared with the expensive or scarce resources the object holds onto. It is especially useful if the object holds onto them for long periods.
JITA intercepts the call from the client to the object, activates the object just when the client issues a method call, and then destroys the object as soon as the method returns. As a result, the client must never have a direct reference to the object. As explained in Chapter 2, if the client is in a different context than the object, the client actually holds a pointer to a proxy and the proxy interacts with a stub. The COM+ proxy and stub perform the JITA interception, and together they constitute a single logical entity. Let's call this entity the interceptor. To guarantee that there is always an interceptor between the client and the object, component instances configured to use JITA are always placed in their own context, regardless of potential compatibility with their creator. Figure 3-6 shows how this interception works:
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Combining JITA with Object Pooling
The two instance management techniques provided by COM+ are not mutually exclusive. JITA and object pooling can be combined in a very powerful way. Using both object pooling and JITA on the same component is useful in situations when object initialization is both generic (not client specific) and expensive. Thus, using just JITA would not make sense; when you have no control over the length of time, the object's client keeps its reference to the object, so you would realize marginal gain from object pooling. When you configure your object to use both, instead of creating and releasing the object on each method call, COM+ grabs an object from the pool and returns the object to the pool after the method completes its execution. The JITA aspects are still maintained because the object instance will be torn away from its client. The pool will also be used on every method call, not just on CoCreate and Release calls from the client. Implementing IObjectControl is optional, but I strongly recommend it. As always, a call to IObjectControl::Activate( ) marks entry to a context, and a call to IObjectControl::Deactivate( ) marks an exit. COM+ calls IObjectControl::CanBePooled( ) after every Deactivate( ), letting the object decide whether it wants to be recycled or destroyed. This life cycle is shown in Figure 3-9. When you configure your component to support both JITA and object pooling, COM+ deactivates the object every time the done bit is set and returns it to the pool instead of releasing it. New method calls are served by recycled objects from the pool, not with new instances.
Figure 3-9: The life cycle of a component using JITA and object pooling
Objects now can maintain state between calls because they are not destroyed, but rather returned to the pool. The truth is, when you use JITA and object pooling together, your object still cannot maintain a client-specific state between invocations; Once the object is back in the pool, it could very well be retrieved to serve a different client than the previous one. A JITA object can maintain just the generic part of the state and benefit from going through that initialization only once.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Object Constructor String
COM+ allows you to pass a construction parameter to new instances of your component. This instance activation service has nothing to do with application scalability, JITA, or object pooling, and is nothing more than a neat service.
On your component's Properties page, there is a properties group named "Object construction" on the Activation tab. Once you enable this service (by checking the "Enable object construction" checkbox), you can specify a string in free form. Every instance of your component has access to this one string (you cannot specify a string per instance). Because calls to CoCreateInstance( ) or CreateObject( ) do not accept initialization parameters, you have to work to gain access to the constructor string.
The first thing you need to do (besides enable the service) is have your component implement an interface called IObjectConstruct , defined as:
interface IObjectConstruct : IUnknown
{
   HRESULT Construct([in]IDispatch* pConstructionObj);
};
If you enable object construction but do not implement the interface, all client attempts to create a new instance of your component will fail, showing the error code E_NOINTERFACE . They will fail because COM+ will refuse to hand over to the client an object that could not be initialized properly. IObjectConstruct has only one method, Construct( ) , which COM+ uses to pass in a pointer to another interface called IObjectConstructString , defined as:
interface IObjectConstructString : IDispatch
{
   [id(1),propget] HRESULT ConstructString([out, retval] BSTR* pVal);
};
COM+ calls your object's implementation of IObjectConstruct::Construct( ) to deliver the string only once, immediately after calling the object constructor. Note that COM+ passes the construction string to your object before the call to IObjectControl::Activate( ), since the initialization parameter should provide generic, rather than context-specific, information.
Example 3-2 shows how to use the constructor string object passed into
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
COM+ Instance Management Pitfalls
COM+ instance management and object activation have a few minor pitfalls and limitations you should be aware of to make the best use of what COM+ has to offer. This section also discusses a feature of the Component Services Explorer that will help you profile your application and keep track of your object instances.
Under classic COM, a process hosting COM objects would be left running as long as clients with active references to objects are in that process. Once the last client releases its reference on the last object in that process, COM would shut down the hosting process. This policy clearly conflicts with COM+ object pooling—the idea is to keep objects alive, even if they do not serve any clients. COM+ allows you to configure your server application's idle time management on the Advanced tab of the application's properties page (see Figure 3-10). The Advanced tab has a properties group called Server Process Shutdown. If your application contains pools of objects, you can leave the hosting process running when the application is idle—that is, when the application is not servicing clients and all objects are in the pool. However, your objects continue to occupy resources as long as the process is running, and if the client activation requests are few and far between, this may not be a good tradeoff.
Alternatively, you can specify how long you want to keep the application idle by providing any number between 0 and 999 minutes. You should decide on the exact value based on your clients' calling pattern and optimize the overall activation overhead and resource consumption. For example, if you expect the interval between clients' activations of pooled objects to be 10 minutes, you should configure the application to be left idle at least that long, plus a certain safety factor (20 percent for example). In this case, you would set the idle timeout to 12 minutes. If you set the timeout to 0, you will get the classic COM behavior. Setting the timeout to 0 is especially useful during debugging because as long as the application is running, you cannot rebuild the component DLL; you cannot rebuild it because the application process has that DLL loaded and locked. Usually, when you discover a defect during a debug session, you should fix it, rebuild the component, and retest. By setting the timeout to 0, you can rebuild immediately. By default, after creating a new COM+ application, the application is configured to shut down after 3 minutes of idle time.
Additional content appearing in this section has been removed.
Purchase this book now or read it online at Safari to get the whole thing!
Chapter 4: COM+ Transactions
Content preview·Buy PDF of this chapter|