Chapter 9. COM+ Event Service

In a component-oriented program, an object provides services to clients by letting clients invoke methods on the object’s interfaces. But what if a client (or more than one client) wants to be notified about an event that occurs on the object side? Traditionally, the client implements a callback interface called a sink interface . To notify the client of an occurring event, the object calls a method on the sink interface. Each method on a sink interface corresponds to a type of event fired by the object.

This model raises a few questions: How does the object access the sink interfaces? How do clients find out which sink interfaces the object fires events on? How do the clients unsubscribe from event notification?

As you will see shortly, the COM+ events service is an exciting new service that evolved to address the classic problems of event notification and reception. COM+ events are also known as Loosely Coupled Events (LCE), because they provide an effective way of decoupling components. They put the logic for publishing and subscribing to events outside the scope of the component. Besides significantly improving on the classic COM model for handling events, LCE takes full advantage of such COM+ services as transactions, queuing, and security. Managing event publishing and subscription can be done both declaratively via the Component Services Explorer and programmatically using the COM+ Catalog.

To fully appreciate the elegance of the COM+ events service, you should first understand the drawbacks of the way classic COM handles events.

Classic COM Events

In classic COM, when a client wants to receive events from an object, the client has to pass the object an interface pointer to a client implementation of the sink interface. This operation is called advising the object of the sink. Advising takes place by either using a standard mechanism (connection points ) or a custom one very similar in nature. These mechanisms have changed little since the early days of OLE 2.0 and COM.

If you use connection points, the object has to implement an interface called IConnectionPointContainer (see Figure 9-1). The client uses the connection point container interface to find out whether the object supports firing events on a specified sink interface IID. Think of it like a kind of reverse QueryInterface( ) call: the client queries the object for its ability to use an interface implemented by the client.

Establishing a connection point usually follows a pattern similar to this one:

  1. The client queries an existing object interface for IConnectionPointContainer.

  2. The client uses IConnectionPointContainer to find out whether the object supports firing events on a specified sink interface. If it does, the object returns to the client an object-side implementation of another interface called IConnectionPoint .

  3. The client uses IConnectionPoint to advise the object of the client-side sink interface.

  4. The object has to maintain a list of sinks that have subscribed. It adds the new sink to the list and returns to the client a cookie identifying the connection. Note that the object manages the subscription list.

  5. The object uses the sink interface to notify the client(s) about events.

  6. When the client wants to stop receiving events and break the connection, it calls IConnectionPoint::Unadvise( ), passing in the cookie that identifies the connection.

Classic COM managed events using connection points

Figure 9-1.  Classic COM managed events using connection points

Establishing the connection requires expensive round trips, potentially across the network. The client must repeat this cumbersome sequence for every sink interface on which it wants to receive events and for every object from which it wants to receive events. Using connection points, there is no way for the client to subscribe to a subset of events the object can fire. The client has no way of filtering events that are fired (Notify me about the event only if...); as a result, a COM designer often opts for the use of a custom mechanism (instead of the generic connection points) that allows subscription to a subset of events. Needless to say, this solution introduces coupling between the object and its clients regarding the specific interaction.

Connection point clients must also have a way to access a server instance (the object) to advise it of the sink. Usually the clients know the server CLSID, get the object from another client, or go through some initialization protocol. That, in turn, also introduces coupling between clients and objects and coupling between individual clients.

On the object side, the object has to manage a list of sinks. This code has almost nothing to do with the domain problem the object is designed to solve. Properly managing the list of sinks requires marshaling sink pointers to a worker thread manually to actually perform event firing. That extra code introduces bugs, testing time, and development overhead. To make matters worse, the same code for managing connections is repeated in many servers.

With this model, the object and the clients have coupled lifetimes—the server usually AddRefs the sinks and the clients have to be running to receive events. There is no way for a client to say to COM “If any object fires this particular event, then please create an instance of me and let me handle it.”

There is no easy way to do disconnected work—that is, the object fires the event from an offline machine and the event is subsequently delivered to clients once the machine is brought online. The reverse is also not possible—having a client running on an offline machine and receiving events fired while the connection was down.

Setting up connections has to be done programmatically. There is no administrative way to set up connections.

The events, like any other COM call, are synchronous. The object is blocked while the client handles an event. Other clients are not notified until the current client returns control back to the object. Well-behaved clients avoid lengthy processing of the events (by perhaps delegating to a client-side worker thread), but there is no way of forcing clients to behave nicely or to fire the events on multiple threads without writing a lot of complex code.

There is no safe way to mix transactions and events. Suppose an event fires, but then the transaction the object took part in is subsequently aborted. How can the object notify the clients to roll back?

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

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