omniNotify Logo Free High Performance CORBA Notification Service
from AT&T Laboratories
ATT logo omniORB Home
AT&T Research
Home SF Project Features Release
Notes
Download Instal-
lation
Documen-
tation
Patch &
Bug List
More
Info
Keep in
Touch

Back to Documentation Home Page

Common Code used in All Examples

The Sixteen Example Clients that Share Code

For the CosNotification interfaces, a client can:

The result is twelve kinds of clients. We provide 1 example of each:

any_push_consumer.cc, any_push_supplier.cc
any_pull_consumer.cc, any_pull_supplier.cc

struct_push_consumer.cc, struct_push_supplier.cc
struct_pull_consumer.cc, struct_pull_supplier.cc

batch_push_consumer.cc, batch_push_supplier.cc
batch_pull_consumer.cc, batch_pull_supplier.cc

The old-style CosEvents interfaces support push/pull of CORBA::Any events, giving 4 kinds of legacy clients. Again we provide 1 example of each:

legacy_push_consumer.cc, legacy_push_supplier.cc
legacy_pull_consumer.cc, legacy_pull_supplier.cc

All 16 examples are similar in structure. To make it easier for the person writing the examples, common code is grouped into helper files (such as main_program.h) and all the important client classes have been grouped into regular and legacy client cases (sample_clients.h/.cc and legacy_clients.h/.cc).

Although this organization makes it easy to modify all 16 examples without having to edit 16+ files, it makes it harder for a novice to read the code.  Here we provide an overview of the common files.


sample_clients.h & sample_clients.cc

These 2 files contains 12 implementation classes which implement the different CosNotificationclients. These classes can be configured in different ways -- command-line arguments plus some defaults are fed into the static create method to get different behavior. See the comment at the top of sample_clients.h for a description of the configuration params.

Here we give an overview of what these classes do. We go over the details of four of the classes later. (See Developing push-based consumers, Developing pull-based consumers., Developing push-based suppliers., and Developing pull-based suppliers..)

Each class creates a single helper thread when an object of the class is constructed. The helper thread is used as follows:

* For push suppliers, the helper thread runs a push loop that pushes events to its proxy (i.e., pushes to the channel) until the desired # of events is reached. If _millisecs has been set (using the -m option) then after each push the thread waits for the specified number of milliseconds.

* For pull consumers, the helper thread runs a pull loop that calls a pull method on its proxy (i.e., pulls from the channel) until the desired # of events is reached. If _millisecs has been set (using the -m option) then after each pull the thread waits for the specified number of milliseconds.

* For push consumers and pull suppliers, if _millisecs has been set (using the -m option) then the helper thread runs a "ping" loop that invokes a method on its proxy every _millisecs milliseconds -- the result is ignored as the only purpose of the invocation is to test whether the proxy is alive.

In all cases, if the helper thread encounters a communication error while communicating with its proxy, it sets an error flag and its _done flag to 1, which forces the client program to cleanup and exit. (One could have programmed some retries, but our sample code is already complicated enough!!)

Note that for push consumers and pull suppliers the main work (push or pull) occurs in an incoming invocation thread (controlled by omniORB) each time the channel either calls push or try_pull on the client. It is not strictly necessary to have a helper thread that performs a ping loop, however without this thread the client would never discover that a notification channel has crashed, since it would simply wait forever for a push or try_pull call that never arrives.

In contrast, push suppliers and pull consumers will detect channel failure when they actively try to push an event to the channel or pull an event from the channel, thus they do not need to do a ping loop.

Helper functions at the top of sample_clients.cc

get_proxy_consumer : This function is used by each supplier client to obtain an appropriate consumer proxy. (Note that consumers connect to proxy suppliers, while suppliers connect to proxy consumers.) The arguments specify a channel reference, a client type (one of CosNotifyChannelAdmin::ANY_EVENT, CosNotifyChannelAdmin::STRUCTURED_EVENT, or CosNotifyChannelAdmin::SEQUENCE_EVENT), and whether push or pull style is requested. The helper function performs two key calls: channel->new_for_suppliers is used to obtain an admin object, and either admin->obtain_notification_push_consumer or admin->obtain_notification_pull_consumer is used to obtain the appropriate proxy object.

get_proxy_supplier : This function is used by each consumer client to obtain an appropriate supplier proxy. The arguments specify a channel reference, a client type, and whether push or pull style is requested. The helper function performs two key calls: channel->new_for_consumers is used to obtain an admin object, and either admin->obtain_notification_push_supplier or admin->obtain_notification_pull_supplier is used to obtain the appropriate proxy object.

sample_add_filter : The helper takes a channel reference as argument because it must obtain a filter factory from the channel (using channel->default_filter_fatory).  It also takes a reference to an object that supports the CosNotifyFilter::FilterAdmin interface -- all of the CosNotification proxy types support this interface, since one can add filters to both supplier proxies and consumer proxies. It creates a filter from the factory using create_filter, then adds a constraint using add_constraint, and finally adds the filter to the proxy using add_filter.

write_ior_to_file : A filename given as argument may be an empty string, in which case nothing is done, otherwise the proxy's object reference is stringified using orb->object_to_string and then written to the specified file.

 


legacy_clients.h & legacy_clients.cc

Contains 4 implementation classes which implement the different legacy CosEvents clients. These are very similar to the clients in sample_clients, except these cases do not require support for the new CosNotification features such as filtering, offer_change, and subscription_change.


sample_functions.h & sample_functions.cc

The implementation classes can be instantiated with user-specified functions that override the default behavior for event supplying, event consuming, and subscription_change/offer_change handling. sample_functions.h contains the typedefs for these function parameters and declares sample versions of each kind of function. sample_functions.cc has the implementations of for these declarations. (These sample versions that are used by default unless different user-specified functions are specified when constructing one of the 12 client classes.)


parse_cmd_line.h

Contains a helper routine that parses command-line arguments and reports usage information if there is an error.  The command-line options that are supported are described in the Running the Examples section.


get_channel.h

Contains functions that get a CosNotifyChannelAdmin::EventChannel reference by looking up a name in the naming service or by reading an stringified IOR from a file.


main_program.h

This file contains an implementation of the main program. Only a few things differ across the different clients, which is why we can take this approach of putting the common code in main_program.h.

Before including main_program.h, some macros must be defined (described below). The main program initializes the ORB and default POA and then carries out these steps:

(1)  Parse the command line arguments. We use the function from parse_cmd_line.h.

(2)  Obtain a reference to a notification channel. We use 2 get-channel functions from get_channel.h, one that uses the naming service, one that uses a stringified IOR stored in a file.

(3)  Create an instance of a class that implements the appropriate supplier or consumer API. Each program uses a different class from sample_clients.h/.cc. For this step to work, these macros must be defined:

CLIENT_IMPL_CLASS : the name of the class that implements the required consumer or supplier interface

CLIENT_CLASS_VAR : the name of the _var variable type for the interface that the client class implements

CLIENT_NAME : a name for the client

SUPPLY_OR_CONSUME_FN : the name of an appropriate supply or consume function, such as one of the functions in sample_functions.cc

For the legacy client examples, LEGACY_CLIENT must be defined. For the other client examples, LEGACY_CLIENT should not be defined, but this macro must be defined:

CHANGE_FN: the name of an appropriate function for handling offer_change or subscription_change calls, such as one of the functions in sample_functions.cc

(4)  Active the POA manager

(5)  Set things in motion: connect the client to its proxy and have it start pushing/pulling events; wait for the desired # of events. We use the client methods client->connect() and client->wait_done().

 

The 16 main client programs simply define appropriate macros and then include main_program.h. For example, any_push_consumer.cc defines these macros:


#define CLIENT_IMPL_CLASS PushConsumer_i
#define CLIENT_CLASS_VAR CosNotifyComm::PushConsumer_var
#define CLIENT_NAME "any_push_consumer"

#define SUPPLY_OR_CONSUME_FN sample_consume_any_fn
#define CHANGE_FN sample_offer_change_fn
 

In English, the macros have the following effect:

They select class PushConsumer_i, which is a consumer that implements the CosNotifyComm::PushConsumer interface. The name chosen for the consumer is "any_push_consumer". The consume function chosen is "sample_consume_any_fn" which is a function that expects a CORBA::ULong to be stored in CORBA::Any events. The change function chosen, sample_offer_change_fn, simply outputs a description of each offer_change message that is received by the consumer.


Modifying the Code to Suit Your Own Needs

Note that we used external functions to do supplying and consuming so that we could avoid code duplication. The downside is that this approach is not a good object-oriented coding style.

If you copy one of the classes in sample_functions.h/.cc and change it for your own needs, it might make sense to eliminate the function parameters and to implement the supply or consume behavior directly in the class's push or pull methods.

Similarly, you should probably copy the code in main_program.h into a top-level <client_program_name>.cc file and modify it to suit your needs, rather than continuing to use the approach of defining macros prior to including main_program.h.


Back to Documentation Home Page


For comments, feedback, etc, please see the 'Keep in touch' page.