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
|
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>Introduction - A very simple client</title>
<!-- $Id$ -->
</head>
<BODY text = "#000000"
link="#000fff"
vlink="#ff0f0f"
bgcolor="#ffffff">
<h3>Introduction - A very simple client</h3>
<P>We will start with a reasonably simple IDL interface, we want
to build a stock quoter server, some interface to query the
prices of stock. To make a life interesting we will use a
different CORBA object for each Stock, this may sound as an
overkill, but it will motivate a number of elements that are
interesting to learn on the start.
</P>
<H3>Defining the IDL interfaces</H3>
<P>At the very least we want an operation to query the stock
price, as in:
</P>
<PRE>
interface Stock {
double price ();
};
</PRE>
<P>but stocks usually have symbols and full names, so why not add
a couple of attributes to query them:
</P>
<PRE>
interface Stock {
double price ();
readonly attribute string symbol;
readonly attribute string full_name;
};
</PRE>
<P>We also need some interface to gain access to the
<CODE>Stock</CODE> object references from their symbols,
usually we call this kind of interface a <EM>Factory</EM>,
and they look like this:
</P>
<PRE>
interface Stock_Factory {
Stock get_stock (in string stock_symbol);
};
</PRE>
<P>Notice how arguments have a <EM>direction</EM>, they are
qualified as <EM>input only (<CODE>in</CODE>)</EM>,
<EM>output only (<CODE>out</CODE>)</EM>
or <EM>input-output (<CODE>inout</CODE>)</EM> arguments.
There is a problem though, what happens if the stock symbol is
invalid? The CORBA way is to raise an exception:
</P>
<PRE>
exception Invalid_Stock_Symbol {};
</PRE>
<P>and then make the exception part of the
<CODE>Stock_Factory</CODE> interface:
</P>
<PRE>
interface Stock_Factory {
Stock get_stock (in string stock_symbol)
raises (Invalid_Stock_Symbol);
};
</PRE>
<P>Finally we put all this IDL constructs in a module to avoid
namespace pollution to obtain the
<A HREF="../Quoter.idl">Quoter.idl</A> file.
</P>
<H3>The Generated Files</H3>
<P>Let's take a minute to look at the generated code,
you don't need to do this often, in fact you rarely have to do
it at all. But doing it once is educative and can demystify the
role of the IDL compiler.
</P>
<P>To generate the code you must
invoke the IDL compiler, like
this:
</P>
<PRE>
$ $ACE_ROOT/TAO/TAO_IDL/tao_idl Quoter.idl
</PRE>
<P>The complete documentation of the IDL compiler and its options
are included in the
<A HREF="../../../../compiler.html">compiler.html</A>.
naturally this file is included in the distribution.
</P>
<P>TAO generates 9 files for each IDL file,
the <CODE>QuoterC.h</CODE>, <CODE>QuoterC.i</CODE>
and <CODE>QuoterC.cpp</CODE> contain the client-side interfaces,
notice that the inline functions are on a separate file so you
can optionally compile them out-of-line for smaller code.
Pure clients only need to link the object file
generated from <CODE>QuoterC.cpp</CODE>.
</P>
<P>
Similarly the <CODE>QuoterS.h</CODE>, <CODE>QuoterS.i</CODE>
and <CODE>QuoterS.cpp</CODE> contain the server-side
<EM>skeletons</EM>. Servers must link the object files generated
from <CODE>QuoterS.cpp</CODE> <STRONG>and</STRONG> <CODE>QuoterC.cpp</CODE>.
</P>
<P>
Finally <CODE>QuoterS_T.h</CODE>, <CODE>QuoterS_T.i</CODE>
and <CODE>QuoterS_T.cpp</CODE> contain the <EM>TIE</EM> classes,
this are the standard (after the CORBA 2.2 spec) skeletons based
on composition instead of inheritance.
They are on a separate file only because some compilers cannot
handle mixed template and non-template code in the same source
file, you <STRONG>do not</STRONG> need to compile these files in any
platform.
However, the files are required to compile
<CODE>QuoterS.cpp</CODE>.
Also notice that if your platform does not support namespaces
then you may be unable to use the TIE approach for some IDL
interfaces.
</P>
<P>All the extensions and suffixes discussed above can be modified
using options in the IDL compiler, check the documentation for
more details. Notice though that you should use consistent
extensions across your project, otherwise you may have problems
with some <CODE>#include</CODE> directives in your IDL source.
</P>
<H3>Building a simple client</H3>
<P>With our simple IDL interface ready we want to start with a
simple client. The first thing to do in any CORBA client or
server is initialize the ORB:
</P>
<PRE>
int main (int argc, char* argv[])
{
try {
// First initialize the ORB, that will remove some arguments...
CORBA::ORB_var orb =
CORBA::ORB_init (argc, argv,
"" /* the ORB name, it can be anything! */);
</PRE>
<P>IDL supports variable-length types whose sizes are not known
at compile time. Hence they must be dynamically allocated at run
time. <CODE>_var</CODE> types relieve us of the explicit memory
management of the variable-length types and thus hide the
differences between fixed and variable-length structured types.
</P>
<P>Since the ORB initialization can fail, and in fact, any CORBA
operation can raise a <CODE>CORBA::SystemException</CODE> we use
a <CODE>try/catch</CODE> block to check for any failures.
Needless to say this is very naive, some failures can be
temporary and we should have a better way to recover from
errors, but this is enough for our example.
In consequence, at the end of <CODE>main()</CODE> we catch all
kinds of CORBA exceptions:
</P>
<PRE>
}
catch (CORBA::Exception &ex) {
std::cerr << "CORBA exception raised!" << std::endl;
}
return 0;
}
</PRE>
<P>We must not forget that the ORB is a resource that must be
released by the application, until CORBA 2.3 there was no
standard way to do this. TAO has adopted the new specification,
so our client should really look like this:
</P>
<PRE>
int main (int argc, char* argv[])
{
try {
// First initialize the ORB, that will remove some arguments...
CORBA::ORB_var orb =
CORBA::ORB_init (argc, argv,
"" /* the ORB name, it can be anything! */);
// the real code goes here!
orb->destroy ();
}
catch (CORBA::Exception &ex) {
std::cerr << "CORBA exception raised!" << std::endl;
}
return 0;
}
</PRE>
<P>Just a few words about the ORB name, the spec requires the ORB
to return the same ORB pointer if the same ORB id is used in
<CODE>CORBA::init</CODE>,
the implementation is free to return the same pointer even if
different ids are used.
Usually this is not a problem as most applications instantiate a
single ORB. TAO is one of the few CORBA implementations that
actually support multiple ORB pointers, this can be important
for real-time applications where each ORB executes at different
priority.
</P>
<P>Now that we have the ORB pointer we can start bootstrapping the
application, normally we would use the Naming Service,
Trading Service or the Interoperable Naming Service to locate
the stock factory, but for simplicity let us use just an IOR
string passed in the first argument.
</P>
<P>The easiest way is to use the first argument to get the string,
and then use <CODE>string_to_object()</CODE> to convert it into an
object reference:
</P>
<PRE>
CORBA::Object_var factory_object =
orb->string_to_object (argv[1]);
Quoter::Stock_Factory_var factory =
Quoter::Stock_Factory::_narrow (factory_object.in ());
</PRE>
<P>The <CODE>_narrow()</CODE> tests is used to test if a reference
is of the specified type. If the reference is of the specified
type, it returns a non-nil reference. Else it returns a nil.
</P>
<P>There are a few interesting things about TAO, first it supports
the <CODE>file:</CODE> scheme for object references, so the first
argument could be <CODE>file://a_file_name</CODE>, in that case
the ORB will open the file named <CODE>"a_file_name"</CODE> and
read the IOR from that file.
TAO also supports the <CODE>corbaloc:</CODE> scheme, for example
<CODE>corbaloc:iiop:1.1@ace.cs.wustl.edu:12345/Stock_Factory</CODE>.
So using a string can be a very powerful bootstrapping protocol.
</P>
<P>Before we go any further, at this point we are using interfaces
generated by the IDL compiler, so we must include the correct
header file!
</P>
<PRE>
#include "QuoterC.h"
</PRE>
notice that this is all you need to include, the IDL compiler
generates code that includes all the required internal header
files.
When you use TAO services don't forget to include their
corresponding header file too.
</P>
<P>Another interesting TAO feature is the support for
<CODE>_unchecked_narrow()</CODE>,
this is part of the CORBA Messaging specification and
essentially performs the same work as <CODE>_narrow()</CODE>,
but it does not check the types remotely,
if you have compile time knowledge that ensures the correctness
of the narrow operation, it is more efficient to use the
unchecked version.
</P>
<P>Now we can use the rest of the arguments to obtain the stock
objects:
</P>
<PRE>
for (int i = 2; i != argc; ++i) {
try {
// Get the stock object
Quoter::Stock_var stock =
factory->get_stock (argv[i]);
</PRE>
<H3>Exercise 1</H3>
<P>Complete the client implementation, it should be easy at this
point, but it will give you a chance to setup your environment
and become familiar with the basics of building a TAO
application.
</P>
<P>You don't need to do everything,
the <A HREF="../Quoter.idl">Quoter.idl</A> file and
a <A HREF="Makefile">Makefile</A> are provided, just
implement the <CODE>client.cpp</CODE> file.
Cut & paste the ORB initialization code, and anything you find
useful from above, you can even cheat and look at the solution,
but it is going to be some really boring 30 minutes if you do!
</P>
<H4>Solution</H4>
<P>Look at the
<A HREF="client.cpp">client.cpp</A> file, it should
not be much different from yours. Count the number of lines in
your client, the idl file and the <CODE>QuoterC.*</CODE> files, do
you want to write all that code ever again?
</P>
<H3>Testing</H3>
<P>To test this application we need a server working, this is a
good excuse to go to the next
<A HREF="../Server/index.html">lesson</A> in the tutorial.
</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: Wed Nov 15 16:19:04 PST 2000
<!-- hhmts end -->
</body>
</html>
|