1 <!DOCTYPE HTML PUBLIC
"-//IETF//DTD HTML//EN">
4 <title>TAO's COS Event Service
</title>
13 <h3>TAO's COS Event Service
</h3>
15 <P>To poll the values of stocks constantly just to
16 check if they have changed is not an efficient or scalable
18 We want to be informed when the price changes so we can take
20 We could devise our own call back mechanism, but this kind of
21 task is easier to achieve using the CORBA Event Service.
24 <H3>Defining the Event Type
</H3>
26 <P>We need to define an IDL
<CODE>struct
</CODE> that will carry
28 Of course we want to include the stock price, its symbol and
29 maybe its full name in the event:
38 <P>We also extend the
<CODE>Stock
</CODE> interface so we can
42 interface Modify_Stock : Stock {
43 void set_price (in double new_price);
47 <H3>Getting the Price Changes
</H3>
49 <H3>Connecting as a consumer
</H3>
51 <P>Connecting as a consumer is a similar process, but we will use
52 the more traditional inheritance based approach instead of TIE.
53 First let us define the consumer object:
56 class Stock_Consumer : public POA_CosEventComm::PushConsumer {
60 void push (const CORBA::Any& data);
61 void disconnect_push_consumer void);
67 The
<CODE>disconnect_push_consumer()
</CODE> method is invoked by
68 the Events Service when it is disconnecting,
69 for example, because it was shut down before the Consumer got a
70 chance to disconnect itself.
71 The
<CODE>push()
</CODE> method is invoked by the Events Service
72 whenever some event is sent by a supplier.
73 Let's take a look at this method.
74 First we need to extract the event data from the any:
78 Stock_Consumer::push (const CORBA::Any& data)
80 const Quoter::Event *event =
0;
81 if ((data
>>= event) ==
0)
82 return; // Invalid event
84 <P>Notice that the extraction can fail: anys can store all IDL
85 data types, and only at extraction time are the types checked.
86 Also notice that we use a pointer to the event;
87 the CORBA rules are that variable sized structures,
88 i.e., structures that contain elements of variable size,
90 are extracted by reference.
91 We do
<STRONG>not
</STRONG> need to manage this memory,
92 the ORB will destroy it for us.
93 Now we can print out the new stock price:
96 std::cout <<
"The new price for one stock in \""
97 << event->full_name.in ()
98 << "\
" (" << event-
>symbol.in ()
99 <<
") is " << event-
>price << std::endl;
103 <P>Going back to our example,
104 when the event channel disconnects we receive a callback, too.
105 At that point we want to forget about the original connection:
109 Stock_Consumer::disconnect_push_consumer void)
111 this-
>supplier_proxy_ = CosEventChannelAdmin::ProxyPushSupplier::_nil ();
114 <P>But why do we need to have a connection to the event channel in
115 the first place? All we want is to receive events. The
116 connection to the event channel will let you disconnect
117 gracefully, so the event channel does not have to maintain
118 resources for old consumers.
120 we could implement a method such as:
124 Stock_Consumer::disconnect ()
126 // Do not receive any more events...
127 this-
>supplier_proxy_-
>disconnect_push_supplier ();
131 <H4>How to connect to the event channel
</H4>
133 <P>Connecting to the event channel is a
3 step process.
134 First we obtain a factory used by all the consumers that want to
136 Next we obtain a supplier proxy, so we can report when
137 we do not want any more events.
138 Finally we connect to the proxy to start receiving events.
140 <P>We will assume that we are using the naming service or
141 something similar to obtain a reference to the event service:
144 CORBA::Object_var tmp = naming_context-
>resolve (name);
145 CosEventChannelAdmin::EventChannel_var event_channel =
146 CosEventChannelAdmin::EventChannel::_narrow (tmp);
148 <P>Now we use the event channel to obtain the factory used for
149 consumer connections:
152 CosEventChannelAdmin::ConsumerAdmin_var consumer_admin =
153 event_channel-
>for_consumers ();
155 <P>And use the factory to obtain a proxy:
159 Stock_Consumer::connect (CosEventChannelAdmin::ConsumerAdmin_ptr consumer_admin)
161 this-
>supplier_proxy_ =
162 consumer_admin-
>obtain_push_supplier ();
164 <P>And finally we connect:
167 CosEventComm::PushConsumer_var myself = this-
>_this ();
168 this-
>supplier_proxy_-
>connect_push_consumer (myself.in ());
172 <H3>Notifying the Price Changes
</H3>
174 <P>We will now examine how the suppliers generate events.
175 Let us look at an implementation of the
176 <CODE>Modify_Stock
</CODE> interface:
179 class Quoter_Modify_Stock_i : public POA_Quoter::Modify_Stock {
181 Quoter_Modify_Stock_i (const char *symbol,
182 const char *full_name,
183 CORBA::Double price);
185 void set_price (CORBA::Double new_price);
190 CosEventChannelAdmin::ProxyPushConsumer_var consumer_proxy_;
193 <P>Notice how we use the IDL structure to maintain the data. This
194 is just to make the code a little shorter.
195 The
<CODE>consumer_proxy_
</CODE> object is just like
196 the
<CODE>supplier_proxy_
</CODE> discussed above,
197 except that we also use it to send the events.
198 The start of the
<CODE>set_price()
</CODE> method will look like
203 Quoter_Stock_i::set_price (CORBA::Double new_price)
205 this-
>data_.price = new_price;
207 <P>Next we prepare the event. The COS Events Service uses a CORBA
208 any to send all the data, like this:
212 event <<= this-
>data_;
214 <P>Finally we send the event to the consumer:
217 this-
>consumer_proxy_-
>push (event);
221 <H3>Connecting to the Event Service as a Supplier
</H3>
223 <P>Sending the event was easy. Connecting to the Event Channel
224 as a supplier is very similar to the connection as a consumer.
225 We will need a
<CODE>CosEventComm::PushSupplier
</CODE> object.
226 This is a good application of the TIE objects:
229 class Quoter_Stock_i : public POA_Quoter::Modify_Stock {
231 // some details removed...
233 void disconnect_push_supplier (void);
236 POA_CosEventComm::PushSupplier_tie < Quoter_Stock_i
> supplier_personality_;
239 <P>The
<CODE>PushSupplier_tie
</CODE> is a template generated by
240 the IDL compiler. It implements the
241 <CODE>CosEventComm::PushSupplier
</CODE> interface,
242 but it actually just forwards all the calls to its single
244 For example, in this case the
245 <CODE>disconnect_push_supplier
</CODE> call is implemented as:
248 template
<class T
> void
249 POA_CosEventComm::PushSupplier_tie < T
>::disconnect_push_supplier ()
251 this-
>ptr_-
>disconnect_push_supplier ();
254 <P>The
<CODE>ptr_
</CODE> field is actually a pointer to the
256 so we don't have to implement a separate class just to receive a
257 disconnect callback, we can use the same
258 <CODE>Modify_Stock_i
</CODE> class to do it.
261 <P>Going back to the connection code, first we gain access to the
262 Event Service, for example using the naming service:
265 CORBA::Object_var tmp = naming_context-
>resolve (name);
266 CosEventChannelAdmin::EventChannel_var event_channel =
267 CosEventChannelAdmin::EventChannel::_narrow (tmp);
269 <P>Now we use the event channel to obtain the factory used for
270 supplier connections:
273 CosEventChannelAdmin::SupplierAdmin_var supplier_admin =
274 event_channel-
>for_suppliers ();
276 <P>And use the factory to obtain a proxy:
279 this-
>consumer_proxy_ =
280 supplier_admin-
>obtain_push_consumer ();
282 <P>And finally we use our supplier personality to connect to the
286 CosEventComm::PushSupplier_var supplier =
287 this-
>supplier_personality_._this ();
288 this-
>consumer_proxy_-
>connect_push_supplier (supplier);
291 <P>Finally we implement the disconnect callback:
295 Quoter_Stock_i::disconnect_push_supplier (void)
297 // Forget about the consumer. It is not there anymore
298 this-
>consumer_proxy_ =
299 CosEventChannelAdmin::ProxyPushConsumer::_nil ();
305 <P>Implement a consumer that receives the price update events.
308 <A HREF=
"Stock_Consumer.h">header file
</A>
311 <A HREF=
"client.cpp">client.cpp
</A>.
312 And other support files
313 <A HREF=
"Quoter.idl">Quoter.idl
</A>,
314 <A HREF=
"GNUMakefile">Makefile
</A>,
315 <A HREF=
"Stock_i.h">Stock_i.h
</A>,
316 <A HREF=
"Stock_i.cpp">Stock_i.cpp
</A>,
317 <A HREF=
"Stock_Factory_i.h">Stock_Factory_i.h
</A>,
318 <A HREF=
"Stock_Factory_i.cpp">Stock_Factory_i.cpp
</A>,
319 and
<A HREF=
"server.cpp">server.cpp
</A>.
324 <P>Compare your solution with
325 <A HREF=
"Stock_Consumer.cpp">Stock_Consumer.cpp
</A>.
330 <P>To test your changes you need to run four programs,
331 first TAO's Naming Service:
333 $ $TAO_ROOT/orbsvcs/Naming_Service/tao_cosnaming -m
1
335 <P>The CORBA Event Service
338 $ $TAO_ROOT/orbsvcs/CosEvent_Service/tao_cosevent
341 <P>Now you can run your client:
345 <P>and finally the server:
348 $ server AAAA MSFT RHAT < stock_list.txt
351 <A HREF=
"stock_list.txt">stock_list.txt file
</A>.
356 <P>Run the same configuration as above,
357 but this time run multiple clients and servers:
362 $ server AAAA BBBB < stock_list1.txt
363 $ server ZZZZ YYYY < stock_list2.txt
365 <P>Do the clients receive all the events from both servers? What
366 if you don't want to receive all the events, for example,
367 because you are only interested in certain stocks?
370 <A HREF=
"stock_list.txt">stock_list1.txt
</A>
372 <A HREF=
"stock_list.txt">stock_list2.txt
</A>
377 <address><a href=
"mailto:coryan@cs.wustl.edu">Carlos O'Ryan
</a></address>
378 <!-- Created: Sat Nov 27 15:47:01 CST 1999 -->
380 Last modified: Sun Apr
1 13:
59:
59 PDT
2001