summaryrefslogtreecommitdiff
path: root/ACE/TAO/docs/tutorials/Quoter/Event_Service/index.html
diff options
context:
space:
mode:
Diffstat (limited to 'ACE/TAO/docs/tutorials/Quoter/Event_Service/index.html')
-rw-r--r--ACE/TAO/docs/tutorials/Quoter/Event_Service/index.html383
1 files changed, 383 insertions, 0 deletions
diff --git a/ACE/TAO/docs/tutorials/Quoter/Event_Service/index.html b/ACE/TAO/docs/tutorials/Quoter/Event_Service/index.html
new file mode 100644
index 00000000000..6a27dd4e750
--- /dev/null
+++ b/ACE/TAO/docs/tutorials/Quoter/Event_Service/index.html
@@ -0,0 +1,383 @@
+<!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);
+ void disconnect_push_consumer void);
+
+ // details omitted
+};
+</PRE>
+ <P>
+ 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)
+{
+ 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>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 void)
+{
+ 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);
+
+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)
+{
+ 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 (void);
+
+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 (void)
+{
+ // 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>