diff options
Diffstat (limited to 'trunk/TAO/docs/tutorials/Quoter/Event_Service/index.html')
-rw-r--r-- | trunk/TAO/docs/tutorials/Quoter/Event_Service/index.html | 408 |
1 files changed, 408 insertions, 0 deletions
diff --git a/trunk/TAO/docs/tutorials/Quoter/Event_Service/index.html b/trunk/TAO/docs/tutorials/Quoter/Event_Service/index.html new file mode 100644 index 00000000000..8b60f5ec91c --- /dev/null +++ b/trunk/TAO/docs/tutorials/Quoter/Event_Service/index.html @@ -0,0 +1,408 @@ +<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> +<html> + <head> + <title>TAO's COS Event Service</title> + <!-- $Id$ --> + </head> + + <BODY text = "#000000" + link="#000fff" + vlink="#ff0f0f" + bgcolor="#ffffff"> + + <h3>TAO's COS Event Service</h3> + + <P>To poll the values of stocks constantly just to + check if they have changed is not an efficient or scalable + solution. + We want to be informed when the price changes so we can take + appropriate action. + We could devise our own call back mechanism, but this kind of + task is easier to achieve using the CORBA Event Service. + </P> + + <H3>Defining the Event Type</H3> + + <P>We need to define an IDL <CODE>struct</CODE> that will carry + our event data. + Of course we want to include the stock price, its symbol and + maybe its full name in the event: + </P> +<PRE> + struct Event { + double price; + string symbol; + string full_name; + }; +</PRE> + <P>We also extend the <CODE>Stock</CODE> interface so we can + modify the value: + </P> +<PRE> + interface Modify_Stock : Stock { + void set_price (in double new_price); + }; +</PRE> + + <H3>Getting the Price Changes</H3> + + <H3>Connecting as a consumer</H3> + + <P>Connecting as a consumer is a similar process, but we will use + the more traditional inheritance based approach instead of TIE. + First let us define the consumer object: + </P> +<PRE> +class Stock_Consumer : public POA_CosEventComm::PushConsumer { +public: + Stock_Consumer (); + + void push (const CORBA::Any& data TAO_ENV_ARG_DECL); + void disconnect_push_consumer (TAO_ENV_SINGLE_ARG_DECL); + + // details omitted +}; +</PRE> + <P>Let us ignore the strange <CODE>TAO_ENV_ARG_DECL</CODE> + arguments and concentrate on the implementation. + The <CODE>disconnect_push_consumer()</CODE> method is invoked by + the Events Service when it is disconnecting, + for example, because it was shut down before the Consumer got a + chance to disconnect itself. + The <CODE>push()</CODE> method is invoked by the Events Service + whenever some event is sent by a supplier. + Let's take a look at this method. + First we need to extract the event data from the any: + </P> +<PRE> +void +Stock_Consumer::push (const CORBA::Any& data + TAO_ENV_ARG_DECL_NOT_USED) +{ + Quoter::Event *event; + if ((data >>= event) == 0) + return; // Invalid event +</PRE> + <P>Notice that the extraction can fail: anys can store all IDL + data types, and only at extraction time are the types checked. + Also notice that we use a pointer to the event; + the CORBA rules are that variable sized structures, + i.e., structures that contain elements of variable size, + such as strings, + are extracted by reference. + We do <STRONG>not</STRONG> need to manage this memory, + the ORB will destroy it for us. + Now we can print out the new stock price: + </P> +<PRE> + std::cout << "The new price for one stock in \"" + << event->full_name.in () + << "\" (" << event->symbol.in () + << ") is " << event->price << std::endl; +} +</PRE> + + <P>So what was the <CODE>TAO_ENV_ARG_DECL</CODE> about? After + all it was not used. TAO services must support platforms + without support for native C++ exceptions. On those platforms + the CORBA specification defines an <EM>alternate mapping</EM> + where all the operations use an extra argument to return any + exceptions. + Notice that the TAO IDL compiler supports both mappings. We have + been using the standard one so far! It is only if your C++ + compiler does not support exceptions that you need to use + the alternative mapping. + </P> + <P>Notice that on platforms with native C++ exception support we + can safely ignore the extra argument, + and use the usual mechanisms to catch and throw exceptions. + For more information on how to use the alternative mapping and + how to write portable code for both environments check + <A HREF="../../../../../docs/exceptions.html">Using + ACE try macros for CORBA programming</A>. + </P> + + <P>Going back to our example, + when the event channel disconnects we receive a callback, too. + At that point we want to forget about the original connection: + </P> +<PRE> +void +Stock_Consumer::disconnect_push_consumer (TAO_ENV_SINGLE_ARG_DECL_NOT_USED) +{ + this->supplier_proxy_ = CosEventChannelAdmin::ProxyPushSupplier::_nil (); +} +</PRE> + <P>But why do we need to have a connection to the event channel in + the first place? All we want is to receive events. The + connection to the event channel will let you disconnect + gracefully, so the event channel does not have to maintain + resources for old consumers. + For example, + we could implement a method such as: + </P> +<PRE> +void +Stock_Consumer::disconnect () +{ + // Do not receive any more events... + this->supplier_proxy_->disconnect_push_supplier (); +} +</PRE> + + <H4>How to connect to the event channel</H4> + + <P>Connecting to the event channel is a 3 step process. + First we obtain a factory used by all the consumers that want to + connect. + Next we obtain a supplier proxy, so we can report when + we do not want any more events. + Finally we connect to the proxy to start receiving events. + </P> + <P>We will assume that we are using the naming service or + something similar to obtain a reference to the event service: + </P> +<PRE> + CORBA::Object_var tmp = naming_context->resolve (name); + CosEventChannelAdmin::EventChannel_var event_channel = + CosEventChannelAdmin::EventChannel::_narrow (tmp); +</PRE> + <P>Now we use the event channel to obtain the factory used for + consumer connections: + </P> +<PRE> + CosEventChannelAdmin::ConsumerAdmin_var consumer_admin = + event_channel->for_consumers (); +</PRE> + <P>And use the factory to obtain a proxy: + </P> +<PRE> +void +Stock_Consumer::connect (CosEventChannelAdmin::ConsumerAdmin_ptr consumer_admin) +{ + this->supplier_proxy_ = + consumer_admin->obtain_push_supplier (); +</PRE> + <P>And finally we connect: + </P> +<PRE> + CosEventComm::PushConsumer_var myself = this->_this (); + this->supplier_proxy_->connect_push_consumer (myself.in ()); +} +</PRE> + + <H3>Notifying the Price Changes</H3> + + <P>We will now examine how the suppliers generate events. + Let us look at an implementation of the + <CODE>Modify_Stock</CODE> interface: + </P> +<PRE> +class Quoter_Modify_Stock_i : public POA_Quoter::Modify_Stock { +public: + Quoter_Modify_Stock_i (const char *symbol, + const char *full_name, + CORBA::Double price); + + void set_price (CORBA::Double new_price) + throw (CORBA::SystemException); + +private: + Quoter::Event data_; + + CosEventChannelAdmin::ProxyPushConsumer_var consumer_proxy_; +}; +</PRE> + <P>Notice how we use the IDL structure to maintain the data. This + is just to make the code a little shorter. + The <CODE>consumer_proxy_</CODE> object is just like + the <CODE>supplier_proxy_</CODE> discussed above, + except that we also use it to send the events. + The start of the <CODE>set_price()</CODE> method will look like + this: + </P> +<PRE> +void +Quoter_Stock_i::set_price (CORBA::Double new_price) + throw (CORBA::SystemException) +{ + this->data_.price = new_price; +</PRE> + <P>Next we prepare the event. The COS Events Service uses a CORBA + any to send all the data, like this: + </P> +<PRE> + CORBA::Any event; + event <<= this->data_; +</PRE> + <P>Finally we send the event to the consumer: + </P> +<PRE> + this->consumer_proxy_->push (event); +} +</PRE> + + <H3>Connecting to the Event Service as a Supplier</H3> + + <P>Sending the event was easy. Connecting to the Event Channel + as a supplier is very similar to the connection as a consumer. + We will need a <CODE>CosEventComm::PushSupplier</CODE> object. + This is a good application of the TIE objects: + </P> +<PRE> +class Quoter_Stock_i : public POA_Quoter::Modify_Stock { +public: + // some details removed... + + void disconnect_push_supplier (TAO_ENV_SINGLE_ARG_DECL_NOT_USED); + +private: + POA_CosEventComm::PushSupplier_tie < Quoter_Stock_i > supplier_personality_; +}; +</PRE> + <P>The <CODE>PushSupplier_tie</CODE> is a template generated by + the IDL compiler. It implements the + <CODE>CosEventComm::PushSupplier</CODE> interface, + but it actually just forwards all the calls to its single + template argument. + For example, in this case the + <CODE>disconnect_push_supplier</CODE> call is implemented as: + </P> +<PRE> +template<class T> void +POA_CosEventComm::PushSupplier_tie < T >::disconnect_push_supplier () +{ + this->ptr_->disconnect_push_supplier (); +} +</PRE> + <P>The <CODE>ptr_</CODE> field is actually a pointer to the + template argument, + so we don't have to implement a separate class just to receive a + disconnect callback, we can use the same + <CODE>Modify_Stock_i</CODE> class to do it. + </P> + + <P>Going back to the connection code, first we gain access to the + Event Service, for example using the naming service: + </P> +<PRE> + CORBA::Object_var tmp = naming_context->resolve (name); + CosEventChannelAdmin::EventChannel_var event_channel = + CosEventChannelAdmin::EventChannel::_narrow (tmp); +</PRE> + <P>Now we use the event channel to obtain the factory used for + supplier connections: + </P> +<PRE> + CosEventChannelAdmin::SupplierAdmin_var supplier_admin = + event_channel->for_suppliers (); +</PRE> + <P>And use the factory to obtain a proxy: + </P> +<PRE> + this->consumer_proxy_ = + supplier_admin->obtain_push_consumer (); +</PRE> + <P>And finally we use our supplier personality to connect to the + consumer proxy: + </P> +<PRE> + CosEventComm::PushSupplier_var supplier = + this->supplier_personality_._this (); + this->consumer_proxy_->connect_push_supplier (supplier); +</PRE> + + <P>Finally we implement the disconnect callback: + </P> +<PRE> +void +Quoter_Stock_i::disconnect_push_supplier (TAO_ENV_SINGLE_ARG_DECL_NOT_USED) + throw (CORBA::SystemException) +{ + // Forget about the consumer. It is not there anymore + this->consumer_proxy_ = + CosEventChannelAdmin::ProxyPushConsumer::_nil (); +} +</PRE> + + <H3>Exercise 1</H3> + + <P>Implement a consumer that receives the price update events. + </P> + <P>The + <A HREF="Stock_Consumer.h">header file</A> + is already provided, + along with a sample + <A HREF="client.cpp">client.cpp</A>. + And other support files + <A HREF="Quoter.idl">Quoter.idl</A>, + <A HREF="GNUMakefile">Makefile</A>, + <A HREF="Stock_i.h">Stock_i.h</A>, + <A HREF="Stock_i.cpp">Stock_i.cpp</A>, + <A HREF="Stock_Factory_i.h">Stock_Factory_i.h</A>, + <A HREF="Stock_Factory_i.cpp">Stock_Factory_i.cpp</A>, + and <A HREF="server.cpp">server.cpp</A>. + </P> + + <H4>Solution</H4> + + <P>Compare your solution with + <A HREF="Stock_Consumer.cpp">Stock_Consumer.cpp</A>. + </P> + + <H4>Testing</H4> + + <P>To test your changes you need to run four programs, + first TAO's Naming Service: +<PRE> +$ $TAO_ROOT/orbsvcs/Naming_Service/Naming_Service -m 1 +</PRE> + <P>The CORBA Event Service + </P> +<PRE> +$ $TAO_ROOT/orbsvcs/CosEvent_Service/CosEvent_Service +</PRE> + + <P>Now you can run your client: +<PRE> +$ client +</PRE> + <P>and finally the server: + </P> +<PRE> +$ server AAAA MSFT RHAT < stock_list.txt +</PRE> + <P>Here is the + <A HREF="stock_list.txt">stock_list.txt file</A>. + </P> + + <H3>Exercise 2</H3> + + <P>Run the same configuration as above, + but this time run multiple clients and servers: + </P> +<PRE> +$ client +$ client +$ server AAAA BBBB < stock_list1.txt +$ server ZZZZ YYYY < stock_list2.txt +</PRE> + <P>Do the clients receive all the events from both servers? What + if you don't want to receive all the events, for example, + because you are only interested in certain stocks? + </P> + <P>Here are the + <A HREF="stock_list.txt">stock_list1.txt</A> + and + <A HREF="stock_list.txt">stock_list2.txt</A> + files. + </P> + + <hr> + <address><a href="mailto:coryan@cs.wustl.edu">Carlos O'Ryan</a></address> +<!-- Created: Sat Nov 27 15:47:01 CST 1999 --> +<!-- hhmts start --> +Last modified: Sun Apr 1 13:59:59 PDT 2001 +<!-- hhmts end --> + </body> +</html> |