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+.

COM+ subdivides apartments into contexts

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.

Lightweight Proxies

When COM+ marshals a call between two contexts in the same apartment, it does not need to perform a thread context switch. However, COM+ still puts a proxy and stub in place to intercept the call from the client to the object and perform a service context switch. This switch ensures that the object gets the runtime environment it requires. COM+ uses a new kind of proxy for this marshaling: a lightweight proxy. It is called a lightweight proxy because no expensive thread context switch is needed to marshal calls from the client to the object. The performance hit for a service context switch is a fraction of that incurred when performing a thread context switch. A service context switch can sometimes be as lightweight as simply checking the value of a flag, but usually it involves some pre- and post-call processing to marshal away differences in the runtime environment between the client and the object.

The lightweight proxies are not the standard proxies used for cross-apartment/process/machine calls. Standard proxies are either created using the MIDL compiler or provided by the standard type library marshaler. For a service switch, COM+ generates the lightweight proxies on the fly, at runtime, based on the exact object configuration. A lightweight proxy, like any other proxy, presents the client with the exact same set of interfaces as those found on the actual object. COM+ provides the lightweight proxy with the right interface signatures based on the type library embedded in the component’s DLL.

An example for a lightweight proxy is a proxy that provides calls synchronization to the object. If the object is configured to require synchronization (to prevent access by multiple concurrent threads), but its client does not require synchronization, COM+ puts a lightweight synchronization proxy between the two. Another example is security. If the object is configured to require an access check before accessing it, verifying that the caller was granted access to the object, but its client does not care about security, there will be a lightweight security proxy in between. This proxy makes sure that only authorized callers are allowed access to the object

If the object is in a different context from that of its caller because of incompatibility in just one component service (or if a service always mandates a separate context), there will be just one lightweight proxy between the caller and the object. Therefore, what should COM+ do if the client and the object differ in more than one service? The exact way the lightweight proxies mechanism is implemented is not documented or widely known. However, in this case, COM+ probably does not generate just one lightweight proxy to do multiple service switches, but rather puts in place as many lightweight proxies as needed, one for every service switch. For example, consider an object that implements the interface IMyInterface and is configured to use two COM+ services: Service A and Service B. If the client does not use Service A and Service B, COM+ puts two lightweight proxies in place, as shown in Figure 2-4. The lightweight proxy to Service A only knows how to do a Service A switch, and the lightweight proxy to Service B only knows how to do a Service B switch. Both services support the IMyInterface interface, and would delegate the client call from the first proxy to the second, to the object, and then back again. The net result is that when the client calls into the context where the object resides, the object gets the correct runtime environment it requires to operate. If the client and the object both use Service C, no lightweight proxy to Service C is required. (Stubs have been removed from Figure 2-4 for clarity.)

Lightweight proxies perform service switches

Figure 2-4.  Lightweight proxies perform service switches

Assigning Objects to Contexts

When a client calls CoCreateInstance( ) (New or CreateObject( ), in Visual Basic), asking for a new instance of a configured component (an object), COM+ first constructs the object and then decides which context to place the object in. In COM+ terminology, COM+ decides in which context to activate the object. COM+ bases its decision on two factors: the component’s configuration and the configuration of its creating client. Obviously, it would be best if the object could share a context with the client. Doing so would obliterate the need for COM+ to marshal calls from the client to the object, and thus avoid having to pay even the slight performance penalty of lightweight proxies.

COM+ examines the newly created object’s configuration in the COM+ catalog and compares it with the configuration (or rather, the context attributes) of the creating client. If the client’s context can provide the object with a sufficient runtime environment for its configuration, COM+ places the object in the client’s context.

If, on the other hand, the client’s context cannot provide the object with its required runtime environment, COM+ creates a new context, places the object in it, and puts lightweight proxies between the two contexts. Note that COM+ does not try to find out if another appropriate context for the object in that apartment already exists. The algorithm is simple—the object either shares its creator’s context or gets a new context. Obviously, the precondition for same-context activation is having a compatible threading model between the client and the object. Otherwise, the object is placed in a different apartment, and hence, a different context by definition, since a context belongs to exactly one apartment.

Classic COM components (nonconfigured components) do not rely on COM+ services to operate and do not require lightweight proxies to mediate between their client runtime environment and their own. If a nonconfigured component can share the same apartment as its creating client (compatible threading model), it will also share its context, and the client will get a direct pointer to it, instead of a proxy. However, if the nonconfigured object requires a different apartment, it is placed in a suitable apartment, in what is known as the default context. Each apartment has one default context used for hosting nonconfigured components. The default context is defined mostly for COM+ internal consistency (every object must have a context), and no lightweight proxies are used when objects in other contexts (in the same apartment) access it.

You can sum up the COM+ algorithm for allocating objects to contexts with this rule: a configured component is usually placed in its own context, and a nonconfigured component shares its creator’s context.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset