CORBA 2.x Publish/Subscribe CORBA Stock Quoter Service (Assignment 2)

Overview

This assignment will enhance a previous CORBA 2.x publish/subscribe-based implementation of the stock quoter service so it will use publish/subscribe features in conjunction with Real-time CORBA features, such as thread pools with lanes, portable priorities, and various priority models. In this design, publishers generate events at designated priorities 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 at a designated priority (e.g., high, medium, or low) 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 at the priority designated by the subscriber (i.e., using the CLIENT_PROPAGATED priority model).

  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. These operations should be handled by a thread in a thread pool with lanes corresponding to the priority of the subscriber (i.e., high, medium, or low).
By employing Real-time CORBA features in our stock quoter example, we can alleviate the key sources of non-determinism and priority inversion, e.g., priorities are respected end-to-end from publisher-to-subscriber and from server-to-client. Likewise, the use of the Publisher/Subscriber pattern alleviates 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 2.x Specification

The CORBA Stock Quoter service will be designed using the Publisher/Subscriber pattern. We define a StockDistributor object that publishes events to indicate that a particular stock's value has changed. This object will monitor the real-time stock database and, when the values of particular stocks change, will push a valuetype containing the stock name to the corresponding event sink of one or more StockBroker objects at a priority designated by the StockBroker when it subscribed with the StockDistributor. 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 object to obtain more information about the stock. This object reference will indicate the appropriate priority at which to run the request in the stock distributor, which will contains a thread pool with lanes corresponding to the priorities at which StockBrokers subscribe.

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 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. Note that this thread will need to set its priority (via RTCurrent::the_priority()) to the appropriate value before making calls to StockBrokers.

Step 2: Defining the Stock Quoter Interfaces Using IDL 2.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 2.x interfaces. We start with the valuetype data type that objects can use to communicate using CCM's publisher/subscriber event mechanism. Whenever a stock value changes, the stock distributor will publish the following valuetype containing the name of the stock:


valuetype StockName { 
  // Name of the stock.
  public string name; 
};
Unlike CORBA Objects (which are passed by reference), instances of CORBA valuetype 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.

You will next write an interface implemented by the stock broker to handle callbacks from the stock distributor.


interface StockNameConsumer 
{
  // Push an event to the consumer.
  void push_StockName (in StockName the_stockname);
};
Now that we've defined our StockName valuetype, we can combine it with the StockQuoter interface defined earlier to create a factory interface called StockBroker:

interface StockBroker
{
  // Factory operation to return StockNameConsumer object reference.
  StockNameConsumer get_consumer_notifier ();

  // Event sink operations to perform connectivity.
  void connect_quoter_info  (in StockQuoter c);
  StockQuoter disconnect_quoter_info ();
  StockQuoter get_connection_quoter_info ();
};
The get_consumer_notifier() operation returns an object reference to the StockNameConsumer IDL 2.x interface shown earlier. When the stock quoter system is initialized, this factory operation will be used to connect the stock distributor publisher with the StockNameConsumer subscriber.

These connect_quoter_info(), disconnect_quoter_info(), and get_connection_quoter_info() operations are also used connect the StockBroker with the StockQuoter object that's provided by the StockDistributor interface describe next.

The StockDistributor object has the following IDL 2.x interface description:


interface StockDistributor : Trigger 
{
  // Event source operations to establish connectivity.
  Cookie subscribe_notifier (in Stock::StockNameConsumer c,
                             in RTCORBA::Priority priority);
  Stock::StockNameConsumer unsubscribe_notifier (in Cookie ck);

  // Factory operation to return StockQuoter object reference.
  StockQuoter provide_quoter_info ();

  attribute long notification_rate;
};
The subscribe_notifier() and unsubscribe_notifier() operations are to subscribe/unsubscribe StockBroker objects that consume the StockName events published by the StockDistributor in accordance with a designated priority. For example, assuming there were stockBroker and stockDistributor object references to the respective StockBroker and StockDistributor objects, these object references could be automatically connected using the following steps:

Stock::StockNameConsumer_var consumer = stockBroker->get_consumer_notifier ();
stockDistributor->subscribe_notifier (consumer.in (), HIGH_PRIORITY);
The provide_quoter_info() factory operation returns object references that StockBroker consumers can use to obtain more information about a particular stock. As before, this factory operation can be used in conjunction with the StockBroker's connect_quoter_info() operation to perform connection plumbing, as follows:

Stock::StockQuoter_var quoter = stockDistributor->provide_quoter_info ();
stockBroker->connect_quoter_info (quoter.in ());
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 2.x Features to Manage the Creation of Objects. Instances of the StockBroker and StockDistributor objects can be created at startup time via factories.


interface StockBrokerHome 
{
  StockBroker create ();
}; 
 
interface StockDistributorHome
{
  StockDistributor create ();
}; 
These 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 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: Implement the Main Program. You'll need to implement a main() functions for stock distributors and stock brokers that will dynamically create the appropriate instances of the factories and bootstrap objects, obtains and activates the appropriate POA (you'll need to use RTPortableServer::POA for both the StockDistributed and StockBroker), 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 (if you'd like to use a Naming Service please feel free to do so). 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" 10
Current value of MSFT is $100
Current value of MSFT is $101
...
which will subscribe for MSFT stock at priority 10 and then print out its value 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 CORBA assignment is harder than assignment 2 since it requires more knowledge of Real-time CORBA. We're using CORBA 2.x for this solution since we know it works with Real-time CORBA. If you can figure out how to make things work with CORBA 3.x and CIAO please feel free!


Back to CS 396 home page.