Loosely Coupled Events With COM+
COM+ introduces a new way to architect and extend applications: The COM+ Event Service. This service is extremely flexible and much easier to handle and maintain than all other COM based event models we've seen so far. They are especially useful for business events that can now be published throughout the system without losing any control over business rules.
Events are widely used throughout development languages and environments. The developer drops a button on a form and adds code to the click method. This code essentially reacts to the click event and performs whatever action the programmer desires.
Similar mechanisms have also been available in COM. ADO is a good example since ADO record sets can query data in an asynchronous manner. Whenever the query is completed, an event is fired, notifying the client that the requested data is now available. Other examples of COM based events are ActiveX controls. Similar to the button example above, ActiveX controls may simply be dropped on a form and code can be added to event code snippets.
In both COM examples, the communication between the COM object/ ActiveX control and the client/ host are accomplished in similar fashion. The COM object (or ActiveX control... you get the idea) and the COM handler object must both be instantiated. In many cases, this happens transparently for the developer. Visual Basic allows the creation of COM objects "with events" and ActiveX controls live inside an ActiveX container object (which is mostly invisible to the developer) that always handles events. The same is true in Visual FoxPro, at least for ActiveX controls. COM objects do not natively support events in Visual FoxPro. However, Microsoft shipped an add-on called the "VFPCOM Utility" that allows the manual creation of a COM event handler object. A handler object has all the code that reacts to events in methods that are named identically to the handled event. The event handler object then needs to be bound (connected) manually to the COM object that fires those events.
This might seem like a lot of extra work, and in fact, represents tasks most development environments take care of for the programmer. But it also demonstrates how COM based events work. Generally, there is always one object that raises the events, and another one that reacts to them. Both objects need to be instantiated and tied together, this step is usually referred to as "event binding". Depending on the language used, event binding may occur at compile time (Visual Basic) or at runtime (Visual FoxPro). Either way, the developer has to take care of the link either in source code or through compiler settings. This is referred to as "tightly coupled events", a technology mostly used for technical issues such as reacting to a mouse click or waiting for data to be retrieved.
However, loosely coupled events are different!
In a loosely coupled event model, we also have an object that raises events (called the "publisher") and one or more objects that react to those events (referred to as the "subscriber"). The major difference is that the two objects are not compiled together, nor are they linked in source code. In fact, the subscriber doesn't even have to be running in memory when the publisher raises an event. All of this is taken care of by the operating system and COM+. To bind a subscriber to an event publisher, the administrator registers the subscriber using the MMC (Microsoft Management Console). Alternatively, this step can be taken care of by the setup program (and I assume this would be the normal case).
These kinds of events make a lot of sense in business situations. For this reason I also like to call these events "business events". Here's an example: Imagine you are the owner of a company that's large enough to employ a full time accountant. However, you would still like to stay in control and be notified whenever the accountant writes a check for more than $5000. Assuming that your accounting package raises COM+ events, you can simply write a mall subscriber component that sends automated email. COM+ even allows the component to be set up in a way so it only receives the event if the check amount is greater than $5000.
How It All Works
Figure 1 provides an overview of the COM+ Event Service Model. There basically is an Event Publisher, which can be any COM enabled application. This Publisher invokes a COM object that's registered with COM+ as an Event Class. This Event Class doesn't do a whole lot. In fact it does nothing at all. It only defines the events that can be raised. These events are simply methods that are invoked by the Publisher.
Figure 1: The COM+ Event Service Model. An external Event Publisher notified an event class registered in COM+. The Event Service then uses configured subscriptions to invoke event subscribers.
At this point, COM+ takes over. It checks all the subscriptions set up by the administrator (see below). These subscriptions guide COM+ to Event Subscriber objects. Best of all, these objects don't even have to be running in memory! COM+ instantiates them for us as they are needed. This is very important for scalability, as the subscribers are only instantiated when they are used.
Most of this is relatively trivial to set up. In fact, there is very little programming involved. The actual linking part itself is a purely administrative task. This means that a COM+ Events based application can be distributed in a very flexible manner.
So let's have a look at what it takes to program this scenario. As an example, I'm going to develop a scenario that just occurred here 30 minutes ago, when my better half asked my to carry out the garbage. I'm choosing this scenario to demonstrate that COM+ Events are much more business related than any other events models.
Our First Events Class...
We start out with the Event Class. This is the class we will register with COM+ and that will live inside the COM+ Event Service as shown in Figure 1. We will create this class in Visual Basic. To do so, I'm creating a new ActiveX DLL project and name the main project "Wife". (For those of you who are not familiar with Visual Basic, simply select the project node in the Project Explorer and change the Name property in the Properties Window.)
When creating a new ActiveX DLL project, Visual Basic also creates a class. I rename this class to "Garbage". So far, so good. We are now ready to define all the events this class can raise. We simply do this by adding methods to our class. In our example, we only need one method with one parameter. Here's the code required for that:
|Public Sub TakeOutGarbage(ByVal What As String)End Sub|
Note that all parameters are explicitly passed by value. Also, make sure you don't forget to set the method "Public".
OK, this is all it takes to create a COM+ Event Class. There is no code in the methods. This is how we define the „Interface". We leave it to the event subscriber to implement the behavior for this interface (just as it is always up to the men to carry out the garbage...).
We now need to compile this class into a COM Component (choose the "Make ..." item from the File menu) and register it with COM+. The registration is done using the "Component Service" tool that can be found in the "Administrative Tools" folder of your Windows 2000 start menu. This launches the MMC (Microsoft Management Console) with the Component Services Snap In (Image 1). This tool is useful for a number of different things. At this point, we are only interested in the "Component Services" item. Drill down until you find the "COM+ Applications" node.
Figure 2: The Component Service MMC snap in allows for easy configuration of all COM+ Components. This image shows our completed COM+ Events Example.
For our example, we are going to create a new "Application" (an "application" in the COM+ sense is simply a set of COM+ components). To do so, we right click on the COM+ Applications node and select "New/Component". The wizard now guides us through the steps of setting up a new COM+ application. We can accept all the provided defaults. We only specify the application name ("Household" in the current example).
The next step is to actually register the COM+ Events Class we just created. We can do this by right clicking on the "Components" folder in our new application and selecting "New/Component". For people who are familiar with MTS or COM+, this was all business as usual. However, we now take a slightly different route. In the wizard, we choose to install a new event class (Image 2) and not a new or existing COM component. This is important in order to make the event service realize that it has to take over. In the next step of the wizard, we select the component we just created, (wife.dll) and proceed on to register the event class.
Figure 3: The COM Component install Wizard offers a special feature to install Event Classes
We now have the "wife" in place and she already knows how to give instructions to carry out the garbage. However, there is nobody there who listens. Speaking out of my own experience, this isn't good. So it's time we find her a husband...
...And Our First Subscriber
Thankfully, finding her a husband in the COM+ world is much easier than in the real world. We can simply program one in Visual Basic. We create a new ActiveX DLL project and name the main project "husband" and the actual class "garbageman" (as you can see, naming doesn't really matter here...).
In this class, we now need to "implement" the interface defined in the "wife" class. This sounds confusing at first, but it really only means that we will create a class that has the same methods as the class that defines the interface ("wife" in our example) and we will actually add code to these methods (the actual "implementation"). However, COM+ isn't very trusting as far as interfaces go, so we have to officially declare that our interface implementation will be compatible to the interface definition. We do this with the "INTERFACE" keyword. Here is that code:
Before we add this line of code to our class, we need to add the Wife component to the references of our project. We can do this through the "References" menu item of the "Project" menu.
OK, we now are ready to implement the interface. In our example, this is going to be relatively trivial and straightforward. We simply create a method named "TakeOutGarbage" just like in the Wife class. This time that class doesn't have to be visible to the outside world, so we declare it "private". Also, we specifically state that this is the implementation of the TakeOutGarbage method defined in the "Garbage" interface of the Wife class. We do this by setting the name of the interface before the method name, separated by an underscore. Here's the entire code that goes into our class:
|Private Sub Garbage_TakeOutGarbate(ByVal What As String)|
As you can see, my implementation is very simple and consists only of a message box. This is something I would not do in a real implementation since I would not want any interface to be invoked here. But keep in mind that this is only an example and for that it does the trick.
OK. We are now ready to compile our husband into a DLL. Once this is done, we add the component to our COM+ application in a similar fashion to how we added the wife class before. However, this time we do not add an event class, but just a plain component.
So we now have a husband and (which is really amazing to me), he is actually compatible with the wife. So all that's left to do is get the wedding over with and move on in life. The COM+ equivalent thereof is a "subscription". In other words, the husband subscribes to all the events, the wife fires. To set this up, expand the Husband item in the Component Service Manager and select New/ Subscription from the right click menu (Image 3).
Figure 4: Subscriptions are set up directly on the Subscriber object. One Subscriber can subscribe to multiple Event Classes.
This launches the Subscription Wizard that guides you through a couple of simple steps. First of all, we select the class/ interface we want to subscribe to. In our case, this is the "_Garbage" interface that belongs to the Wife object (which we don't see in this wizard). When we move to the next step, the system searches the registry and it might appear as if it was hung for a moment, but it really isn't. In fact, when you move the mouse cursor over the (disabled) listbox, it will actually change its appearance to the hourglass. Once this process is done, the wizard displays the class that defines the selected interface (Wife.Garbage in our example). We select that class and proceed.
Now, all that's left is to specify a name for the subscription (I called it "marriage") and to enable the component right away.
Voila! We have newlyweds, where all the details of the contract are handled by COM+. In fact, the whole relationship is rather relaxed and the wife so far doesn't even know what happened to her (I'm convinced that whoever at Microsoft was in charge of this technology must have been male...).
We are all set to handle and subscribe to events now. All that's missing at this point is an event publisher that actually makes the wife fire an event (imagine my cat just tripped over the garbage can...). In the COM+ world, this can be any COM enabled client. In this example, I'm using Visual FoxPro's command window, which allows to us to directly enter the following commands without creating a program of any kind. Here's the first command that instantiates the wife object:
|Wife = CreateObject("Wife.Garbage")|
This created the object and we can visually verify that through the Component Service Manager where the little ball representing the wife object starts spinning. Note that the event subscriber is not instantiated at this point since it has not received any event notifications yet.
We can now go ahead and publish events. This means, we essentially fire methods on that object, like so:
Immediately, the message box implemented in the husband object will fire and display "Kitchen" (which is the parameter we passed in). Also, when you look in the Component Service Manager, you will see that both balls are instantiated and spinning now.
Click the OK button in the message box to complete that task, and you will see that the reference to the husband object is released immediately even though the wife object is still in memory.
At this point, the wife assumes her request is taken care of. Little does she know he only replied with a mumbled "yeah-yeah" but didn't actually get away from the TV.
That is the very nature of COM+ events. The events fired by the events class are published to the system where any number of subscribers could react to that event. However, that number of subscribers could very well be zero (all wives know what I'm talking about). In fact, the wife doesn't even get a chance to verify whether there are subscribers, and if they have taken care of the desired task, other than checking the trash can again.
This means that the system is great for one of the following two scenarios:
- We know we have a reliable subscriber because we created and configured it ourselves and there is no need to directly check up on it. An example would be a reclamation sent by a customer that is put into a database. The system will then publish an event that states that a complaint was filed and we hope that some subscriber takes care of it. We also keep checking our basket (database) of complaints and re-issue the event until it is taken care of.
- We already handled all tasks internally, but publish the event anyway so other components can make use of certain business events and perform additional tasks. In this scenario, we would take care of the complaint ourselves, but we publish the event, so any other component (such as an email component) gets a chance to react to it and perform additional action.
At this point, our husband component is subscribing to all events the wife may fire,and this may very much be the desired behavior, at least from the wife's point of view. I think I'm pretty good with these kinds of things, and the garbage has been taken out from the kitchen every time I was asked to do so (thankfully, we have a maid...). The point is not that I am a lazy husband, but that a subscriber can also act as a publisher and can publish events to other subscribers. However, there is one thing I'm not particularly excited about: cleaning the litter box. I refuse to subscribe to that event. The COM+ equivalent to this is "Event Filters".
Right click on the subscription you created and select "Properties". In the "Options" page, you will find a setting for a "Filter Criteria" (Figure 5). Here you can specify filters based on the passed parameters. In our example, the filter criteria looks like so:
You can now publish the event again as we did before, and the husband object will react every time, unless the passed parameter is "Litter Box".
Figure 5: Subscribed events can be filtered.
This is a very important feature for scalability and flexibility (and for a happy marriage...). Typical uses would be stock updates where a subscriber only reacts to events that have to do with stock that is in a certain portfolio. Or maybe our complaints subscriber only reacts to complaints concerning orders of significance based on the order amount. So every order that is returned that had an order amount of more than $100,000 is directly reported to the CEO by email, but we don't bother her with smaller issues. Obviously, this can be set up in a very flexible manner without changing source code or adding configuration options to your system.
Events can also be filtered based on the COM+ component that fired the event. This is useful in scenarios where one subscriber subscribes to events fired by a number of different components (that all use the same interface).
COM+ Events are one of the most exciting new features in Windows DNA 2000. It offers great new ways to architect applications and make not only business data, but also business events available throughout the system. This creates much more flexible and productive work environments. We will revisit COM+ Events in several other articles in upcoming issues of Code Magazine. In the next issue, we will talk about Queued Components, which form a great team with COM+ Events.