Merge pull request #2309 from mitza-oci/warnings
[ACE_TAO.git] / TAO / docs / tutorials / Quoter / Event_Service / index.html
bloba482a62dcafc63168f70b6e923f487ca77645159
1 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
2 <html>
3 <head>
4 <title>TAO's COS Event Service</title>
5 <!-- -->
6 </head>
8 <BODY text = "#000000"
9 link="#000fff"
10 vlink="#ff0f0f"
11 bgcolor="#ffffff">
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
17 solution.
18 We want to be informed when the price changes so we can take
19 appropriate action.
20 We could devise our own call back mechanism, but this kind of
21 task is easier to achieve using the CORBA Event Service.
22 </P>
24 <H3>Defining the Event Type</H3>
26 <P>We need to define an IDL <CODE>struct</CODE> that will carry
27 our event data.
28 Of course we want to include the stock price, its symbol and
29 maybe its full name in the event:
30 </P>
31 <PRE>
32 struct Event {
33 double price;
34 string symbol;
35 string full_name;
37 </PRE>
38 <P>We also extend the <CODE>Stock</CODE> interface so we can
39 modify the value:
40 </P>
41 <PRE>
42 interface Modify_Stock : Stock {
43 void set_price (in double new_price);
45 </PRE>
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:
54 </P>
55 <PRE>
56 class Stock_Consumer : public POA_CosEventComm::PushConsumer {
57 public:
58 Stock_Consumer ();
60 void push (const CORBA::Any& data);
61 void disconnect_push_consumer void);
63 // details omitted
65 </PRE>
66 <P>
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:
75 </P>
76 <PRE>
77 void
78 Stock_Consumer::push (const CORBA::Any& data)
80 const Quoter::Event *event = 0;
81 if ((data >>= event) == 0)
82 return; // Invalid event
83 </PRE>
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,
89 such as strings,
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:
94 </P>
95 <PRE>
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;
101 </PRE>
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:
106 </P>
107 <PRE>
108 void
109 Stock_Consumer::disconnect_push_consumer void)
111 this->supplier_proxy_ = CosEventChannelAdmin::ProxyPushSupplier::_nil ();
113 </PRE>
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.
119 For example,
120 we could implement a method such as:
121 </P>
122 <PRE>
123 void
124 Stock_Consumer::disconnect ()
126 // Do not receive any more events...
127 this->supplier_proxy_->disconnect_push_supplier ();
129 </PRE>
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
135 connect.
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.
139 </P>
140 <P>We will assume that we are using the naming service or
141 something similar to obtain a reference to the event service:
142 </P>
143 <PRE>
144 CORBA::Object_var tmp = naming_context->resolve (name);
145 CosEventChannelAdmin::EventChannel_var event_channel =
146 CosEventChannelAdmin::EventChannel::_narrow (tmp);
147 </PRE>
148 <P>Now we use the event channel to obtain the factory used for
149 consumer connections:
150 </P>
151 <PRE>
152 CosEventChannelAdmin::ConsumerAdmin_var consumer_admin =
153 event_channel->for_consumers ();
154 </PRE>
155 <P>And use the factory to obtain a proxy:
156 </P>
157 <PRE>
158 void
159 Stock_Consumer::connect (CosEventChannelAdmin::ConsumerAdmin_ptr consumer_admin)
161 this->supplier_proxy_ =
162 consumer_admin->obtain_push_supplier ();
163 </PRE>
164 <P>And finally we connect:
165 </P>
166 <PRE>
167 CosEventComm::PushConsumer_var myself = this->_this ();
168 this->supplier_proxy_->connect_push_consumer (myself.in ());
170 </PRE>
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:
177 </P>
178 <PRE>
179 class Quoter_Modify_Stock_i : public POA_Quoter::Modify_Stock {
180 public:
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);
187 private:
188 Quoter::Event data_;
190 CosEventChannelAdmin::ProxyPushConsumer_var consumer_proxy_;
192 </PRE>
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
199 this:
200 </P>
201 <PRE>
202 void
203 Quoter_Stock_i::set_price (CORBA::Double new_price)
205 this->data_.price = new_price;
206 </PRE>
207 <P>Next we prepare the event. The COS Events Service uses a CORBA
208 any to send all the data, like this:
209 </P>
210 <PRE>
211 CORBA::Any event;
212 event <<= this->data_;
213 </PRE>
214 <P>Finally we send the event to the consumer:
215 </P>
216 <PRE>
217 this->consumer_proxy_->push (event);
219 </PRE>
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:
227 </P>
228 <PRE>
229 class Quoter_Stock_i : public POA_Quoter::Modify_Stock {
230 public:
231 // some details removed...
233 void disconnect_push_supplier (void);
235 private:
236 POA_CosEventComm::PushSupplier_tie < Quoter_Stock_i > supplier_personality_;
238 </PRE>
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
243 template argument.
244 For example, in this case the
245 <CODE>disconnect_push_supplier</CODE> call is implemented as:
246 </P>
247 <PRE>
248 template<class T> void
249 POA_CosEventComm::PushSupplier_tie < T >::disconnect_push_supplier ()
251 this->ptr_->disconnect_push_supplier ();
253 </PRE>
254 <P>The <CODE>ptr_</CODE> field is actually a pointer to the
255 template argument,
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.
259 </P>
261 <P>Going back to the connection code, first we gain access to the
262 Event Service, for example using the naming service:
263 </P>
264 <PRE>
265 CORBA::Object_var tmp = naming_context->resolve (name);
266 CosEventChannelAdmin::EventChannel_var event_channel =
267 CosEventChannelAdmin::EventChannel::_narrow (tmp);
268 </PRE>
269 <P>Now we use the event channel to obtain the factory used for
270 supplier connections:
271 </P>
272 <PRE>
273 CosEventChannelAdmin::SupplierAdmin_var supplier_admin =
274 event_channel->for_suppliers ();
275 </PRE>
276 <P>And use the factory to obtain a proxy:
277 </P>
278 <PRE>
279 this->consumer_proxy_ =
280 supplier_admin->obtain_push_consumer ();
281 </PRE>
282 <P>And finally we use our supplier personality to connect to the
283 consumer proxy:
284 </P>
285 <PRE>
286 CosEventComm::PushSupplier_var supplier =
287 this->supplier_personality_._this ();
288 this->consumer_proxy_->connect_push_supplier (supplier);
289 </PRE>
291 <P>Finally we implement the disconnect callback:
292 </P>
293 <PRE>
294 void
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 ();
301 </PRE>
303 <H3>Exercise 1</H3>
305 <P>Implement a consumer that receives the price update events.
306 </P>
307 <P>The
308 <A HREF="Stock_Consumer.h">header file</A>
309 is already provided,
310 along with a sample
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>.
320 </P>
322 <H4>Solution</H4>
324 <P>Compare your solution with
325 <A HREF="Stock_Consumer.cpp">Stock_Consumer.cpp</A>.
326 </P>
328 <H4>Testing</H4>
330 <P>To test your changes you need to run four programs,
331 first TAO's Naming Service:
332 <PRE>
333 $ $TAO_ROOT/orbsvcs/Naming_Service/tao_cosnaming -m 1
334 </PRE>
335 <P>The CORBA Event Service
336 </P>
337 <PRE>
338 $ $TAO_ROOT/orbsvcs/CosEvent_Service/tao_cosevent
339 </PRE>
341 <P>Now you can run your client:
342 <PRE>
343 $ client
344 </PRE>
345 <P>and finally the server:
346 </P>
347 <PRE>
348 $ server AAAA MSFT RHAT < stock_list.txt
349 </PRE>
350 <P>Here is the
351 <A HREF="stock_list.txt">stock_list.txt file</A>.
352 </P>
354 <H3>Exercise 2</H3>
356 <P>Run the same configuration as above,
357 but this time run multiple clients and servers:
358 </P>
359 <PRE>
360 $ client
361 $ client
362 $ server AAAA BBBB < stock_list1.txt
363 $ server ZZZZ YYYY < stock_list2.txt
364 </PRE>
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?
368 </P>
369 <P>Here are the
370 <A HREF="stock_list.txt">stock_list1.txt</A>
372 <A HREF="stock_list.txt">stock_list2.txt</A>
373 files.
374 </P>
376 <hr>
377 <address><a href="mailto:coryan@cs.wustl.edu">Carlos O'Ryan</a></address>
378 <!-- Created: Sat Nov 27 15:47:01 CST 1999 -->
379 <!-- hhmts start -->
380 Last modified: Sun Apr 1 13:59:59 PDT 2001
381 <!-- hhmts end -->
382 </body>
383 </html>