get_quote()
operation on a Stock::Quoter interface implemented by a
remote server. While this ``polling'' approach is fairly common, it
can also saturate the server and the network by making many requests,
even if the value of the stock hasn't changed! One way to avoid the problems with request/response polling is to employ a variant of the Publisher/Subscriber architectural pattern from POSA1. In this pattern, 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:
valuetype containing the stock name to the corresponding
event sink of one or more StockBroker objects. 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.
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 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);
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. 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 ());
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 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.
Back to CS 396 home page.