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.
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.
When a pooled object is configured to use JITA, the semantics of the maximum pool size value actually sets the total number of objects that COM+ is forced to create to serve active client calls, not the total number of connections to clients. The number of connections (the number of clients holding references to proxies) may be a much larger number because many clients may not be engaged in calling a method on objects.
Configuring a COM+ component to be a singleton is an interesting example of what you can do when combining JITA with object pooling. A singleton is a component with only one instance. All clients share the same singleton—the clients are often not even aware that there is just one instance of the class.[2] For example, suppose you have a configured component used to control a single resource, such as a hardware device or a communication port. To make sure that all clients get the same object, you can configure your component to use JITA and object pooling, with minimum and maximum pool sizes set to one. Having a pool size of exactly one ensures that at any given moment, exactly one object (a singleton) is associated with a resource. Using JITA ensures that once the object has finished servicing one client, it can serve another, even if the current client has not released its reference to it. The singleton is also the only case of a JITA object that can maintain full state between method calls, since you can be certain that the same object is called to serve all clients. However, before you start using a singleton, make sure that its disadvantages (a single point of failure, a performance hot spot, a bottleneck, and an inability to scale to large number or clients) are not relevant in your design and that it is a valid modeling of an entity in your application domain.
[2] See Design Patterns—Elements of Reusable Object-Oriented Software, by Gamma, et al. (Addison Wesley, 1995), p. 127.