CCM-based Publish/Subscribe CORBA Stock Quoter Service (Assignment 3)

Overview

This assignment will enhance the previous CORBA 2.x publish/subscribe-based implementation of the stock quoter service so it will use CORBA Component Model (CCM) features. In this design, publishers generate events that are transmitted to one or more subscribers, who can then take further action depending on the events they receive and their internal state. The overall flow of information in our latest incarnation of the stock quoter system thus works as follows:

  1. Stock broker clients subscribe with a stock distributor server to receive notification events whenever a stock value of interest to them changes.

  2. The stock distributor server monitors a real-time stock feed database.

  3. Whenever the value of a stock changes, the distributor publishes an event to interested stock brokers.

  4. If stock brokers are interested in learning more details about a stock whose value has changed, they can invoke an operation on the stock distributor to receive more information.
By employing the Publisher/Subscriber pattern in our stock quoter example, we can alleviate the key drawbacks with polling-based request/response designs, i.e., stock brokers only contact the stock distributor server via request/response operations when the value of a stock changes, rather than polling them repeatedly to see if the value has changed.

CORBA IDL 3.x Specification

The CORBA Stock Quoter service will be designed using the Publisher/Subscriber pattern. We define a StockDistributor component that publishes events to indicate that a particular stock's value has changed. This component will monitor the real-time stock database and, when the values of particular stocks change, will push an eventtype containing the stock name to the corresponding event sink of one or more StockBroker components. The StockBroker objects that consume this event will then examine the stock name stored in the event. If they are interested in the stock, they can invoke a request/response operation via an object reference exported by the StockDistributor component to obtain more information about the stock.

Step 1: Defining the Stock Quoter Interfaces using IDL 2.x Types. You will start by defining some interfaces and other types using IDL 2.x features. All of these types will be defined in a module called Stock, i.e.:


// IDL schema definition for Stock Quoter publisher/subscriber service.
module Stock
{
  exception Invalid_Stock {};
  // Feel free to add other exceptions as you see fit.

  // ...
When stock brokers want to learn more information about a particular stock whose value has changed recently, they can invoke the get_stock_info() operation on the following StockQuoter interface within the Stock module:

	interface StockQuoter {
          StockInfo get_stock_info (in string stock_name) raises Invalid_Stock;
	};
This interface returns the following StockInfo struct:

       struct StockInfo {
         string name;
         long high;
         long low;
         long last;
         // ...
       };
As shown above, StockInfo contains information about the high and low trading values of the stock during the trading thus far today, along with the most recent value. It also includes the stock name so that each StockInfo instance is self-identifying and is thus easily trackable and usable in collections.

The stock distributor itself runs as a daemon that can be started and stopped by a system administrator. The following Trigger interface instructs the stock distributor to perform these control operations:


	interface Trigger { 
          void start ();
          void stop ();
	}
When an administrator calls start() the stock distributor begins to monitor the real-time stock database until the stop() operation is called. In your application, you can simply spawn a thread that reads from a simple STL std::map that maps stock names (i.e., "BEAS," "IBM," and "MSFT") to "real-time" stock quotes.

Step 2: Defining the Stock Quoter Components Using IDL 3.x Types. Now that we've illustrated the core IDL 2.x types in our stock quoter system, we'll show how they are combined together using IDL 3.x component types. We start with the eventtype data type that components can use to communicate using CCM's publisher/subscriber event mechanism. Whenever a stock value changes, the stock distributor will publish the following eventtype containing the name of the stock:


        eventtype StockName { 
          // Name of the stock.
          public string name; 
        };
Unlike CORBA Objects (which are passed by reference), instances of CORBA eventtype are always passed by value. Like CORBA structs, they can contain state in the form of fields. Unlike structs, however, they can have user-defined operations and support inheritance.

Now that we've defined our StockName eventtype, we can combine it with the StockQuoter interface defined earlier to create a CCM component called StockBroker:


        component StockBroker
        {
          consumes StockName notifier_in;
          uses StockQuoter quoter_info_in;
        };
The StockBroker component contains two ports that correspond to the two roles it plays. First, it is a subscriber that consumes a StockNameL eventtype called notifier_in that's published by the StockDistributor when the value of a stock changes. Second, it is a user of the StockQuoter interface we defined earlier to provide additional information about a stock. This dependency is indicated explicitly in IDL 3.x via a CCM receptacle called quoter_info_in that indicates the uses relationship on the StockQuoter interface. The StockDistributor object has the following IDL 2.x interface description:

        component StockDistributor supports Trigger 
        {
          publishes StockName notifier_out;
          provides StockQuoter quoter_info_out;
          attribute long notification_rate;
        };
This CCM component supports (i.e., inherits from) the Trigger interface defined earlier, which enables a system administrator application to start() and stop() instances of StockDistributor.

The supports keyword is useful for components like StockDistributor that have a "primary" interface, which alleviates application developers from having to go through extra steps just to access the operations of that interface. If the interface were specified as a facet via the provides keyword instead, applications would have to call an operation to get a reference to the facet, and then invoke the desired operation. Instead, the supports keyword allows administrator applications to invoke the start() and stop() operation directly on the component reference since the Trigger interface is a parent of StockDistributor. StockDistributor also publishes a StockName eventtype called notifier_out that is pushed to the StockBroker subscriber components when a stock value changes. StockDistributor also defines a StockQuoter facet called quoter_info_out.

Finally, in addition to the inherited Trigger operations, system administrators can use the notification_rate attribute to control the rate at which the StockDistributor object checks the stock quote database and pushes changes to StockBroker subscribers. Attributes in are primarily used for configuration, e.g., to define optional behaviors, modality, resource hints, etc. They are represented as a pair of accessor/mutator methods in C++.

Step 3: Use IDL 3.x Features to Manage the Creation of Objects. Instances of the StockBroker and StockDistributor objects can be created at startup time via home factories. In CCM, home is a new IDL keyword that's used to define a factory that manages one type of component. A component instance is managed by one home instance. Since a home has an interface, it's identified via an object reference. By default, a home has standard lifecycle operations, e.g., create(), though which users can define operations with arbitrary parameter lists if the defaults don't suffice. For our stock quoter example we can use the defaults, so our homes are defined as follows:


        home StockBrokerHome manages StockBroker {};
        home StockDistributorHome manages StockDistributor {};
These home factories reduce the bookkeeping that CORBA applications must do to create and manage their objects.

You will use the TAO CORBA IDL compiler to translate these IDL 2.x and 3.x interfaces into stubs and skeletons. The stock distributor application (which you must write) will use the stubs as a proxy to publish StockNames the stock broker application(s). As discussed above, the brokers can call back to the distributor to get more info on the stocks. In addition, you must also write the factory interfaces that set everything in motion.

Step 4: Use CIDL Features to Map Components to Executors. Component developers need to define the structure and state of their components. The CCM approach is to use the composition declaration in the Component Implementation Definition Language CIDL that describes how to connect component definitions (which perform business logic) with home definitions (which are the factories that create components). Since a component can be instantiated by more than one home, a composition designates which home will manage which component.

CCM allows developers to define a single component and have multiple home definitions that manage the same component using IDL 3.x keywords. By defining the composition, the component developer designates the relationship between a home and the component it manages. It is important to note, however, that the composition does not specify component types explicitly, but rather implicitly from the home type within the composition since each home can manage only one type of component. The CIDL for our stock quoter example is placed in a file called stock.cdl, as shown below:


composition session StockDistributor_Impl {
  home executor StockDistributorHome_Exec  {
    implements StockDistributorHome;      
    manages StockDistributor_Exec;  
  }
};

composition session StockBroker_Impl {
  home executor StockBrokerHome_Exec  {
    implements StockBrokerHome;      
    manages StockBroker_Exec;  
  }
};
You'll then need to implement the executors corresponding to the code generated by the CIDL compiler.

Step 5: Implement the Main Programs. You'll need to implement a main() functions for stock distributors and stock brokers that will dynamically create the appropriate instances of the home factories and bootstrap components, obtains and activates the RootPOAs, and then calls CORBA::ORB::run() method to run the application event loops. To make your life easy, applications should write their IORs to a file before it calls the ORB's run() method (later, we'll use more advanced techniques, such as a Naming Service). Client applications should read the name of the file that contains the IOR discussed above. When the client starts up, it will read the contents of this file into a string and use the CORBA::string_to_object() method to convert the string into an object reference. This object reference will then be downcast via _narrow() to an object reference for the various factories operations.

For the purposes of the assignment, you can make the driver programs very simple, i.e., you can read commands from standard input and write replies to standard output. For example, you could type the following commands to the stock distributor


% ./distributor
> rate 1 second
to tell it to generate events at a rate of 1 second. Likewise, you could type the following commands to the stock broker:

% ./broker tango.dre.vanderbilt.edu
> subscribe "MSFT"
Current value of MSFT is $100
Current value of MSFT is $101
...
which will subscribe and print out the value of MSFT stock when it receives it from the publisher.

If anything fails to work properly the programs should simply print out the appropriate exception and exit with a return status of 1. If everything works correctly, the programs should exit with a return status of 0.


Learning and Using CORBA

As usual, we will be using the TAO CORBA Object Request Broker (ORB) implementation. Please see the online help for information on how to setup your TAO development environment on EECS's computing system. If you'd prefer to use TAO on your laptop or home PC you can download it from deuce.doc.wustl.edu/Download.html.


Concluding Remarks

This third CORBA assignment requires more knowledge of CCM and distributed software design than the first two assignments, which just exercised basic skills required to become adept at using CORBA component middleware to developed distributed applications. Subsequent assignments will build on the third assignment, so make sure you get it working correctly.


Back to CS 396 home page.