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.
Back to CS 396 home page.