summaryrefslogtreecommitdiff
path: root/TAO/docs/tutorials/Quoter/Event_Service/index.html
blob: 1d5b58407e7442fa506115bc952b1910ec6341ce (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
  <head>
    <title>TAO's COS Event Service</title>
    <!--  -->
  </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/tao_cosnaming -m 1
</PRE>
    <P>The CORBA Event Service
    </P>
<PRE>
$ $TAO_ROOT/orbsvcs/CosEvent_Service/tao_cosevent
</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>