The example described in this appendix is founded on a well-known application: A "Hello World!" program presented here in a special client-server version.
Many books on programming start with this tiny demo program. In introductory C++ books you'll probably find the following piece of code in the very first chapter:
// C++ #include <iostream.h> int main(int, char*[]) { cout << "Hello World!" << endl; return 0; }
These applications simply print "Hello World!" to standard output and that is exactly what this appendix is about: Printing "Hello World!" with a CORBA-based client-server application. In other words, we will develop a client program that invokes a hello operation on an object in a server program. The server responds by printing "Hello World!" on its standard output.
How do we write a CORBA-based "Hello World!" application? The first step is to create a file containing our IDL definitions. Since our sample application isn't a complicated one, the IDL code needed for this example is simple:
1 // IDL 2 3 interface Hello 4 { 5 void hello(); 6 };
3 | An interface with the name Hello is defined. An IDL interface is conceptually equivalent to a pure abstract class in C++, or to an interface in Java. |
5 | The only operation defined is hello, which neither takes any parameters nor returns any value. |
The next step is to translate the IDL code to C++ using the IDL-to-C++ compiler. Save the IDL code shown above to a file called Hello.idl. Now translate the code to C++ using the following command:
idl Hello.idl
This command will create the files Hello.h, Hello.cpp, Hello_skel.h and Hello_skel.cpp.
To implement the server, we need to define an implementation class for the Hello interface. To do this, we create a class Hello_impl that is derived from the "skeleton" class Hello_skel, defined in the file Hello_skel.h. The definition for Hello_impl looks like this:
1 // C++ 2 3 #include <Hello_skel.h> 4 5 class Hello_impl : public Hello_skel 6 { 7 public: 8 9 virtual void hello(); 10 };
3 | Since our implementation class derives from the skeleton class Hello_skel, we must include the file Hello_skel.h. |
5 | Here we define Hello_impl as a class derived from Hello_skel. |
9 | Our implementation class must implement all operations from the IDL interface. In this case, this is just the operation hello. |
The implementation for Hello_impl looks as follows:
1 // C++ 2 3 #include <OB/CORBA.h> 4 #include <Hello_impl.h> 5 6 void 7 Hello_impl::hello() 8 { 9 cout << "Hello World!" << endl; 10 }
3 | We must include OB/CORBA.h, which contains definitions for the standard CORBA classes, as well as for other useful things. |
4 | We must also include the Hello_impl class definition, contained in the header file Hello_impl.h. |
6-10 | The hello function simply prints "Hello World!" on standard output. |
Save the class definition of Hello_impl in the file Hello_impl.h and the implementation of Hello_impl in the file Hello_impl.cpp.
Now we need to write the server's main program, which looks like this:
1 // C++ 2 3 #include <OB/CORBA.h> 4 #include <Hello_impl.h> 5 6 #include <fstream.h> 7 8 int 9 main(int argc, char* argv[], char*[]) 10 { 11 CORBA_ORB_var orb = CORBA_ORB_init(argc, argv); 12 CORBA_BOA_var boa = orb -> BOA_init(argc, argv); 13 14 Hello_var p = new Hello_impl; 15 16 CORBA_String_var s = orb -> object_to_string(p); 17 const char* refFile = "Hello.ref"; 18 ofstream out(refFile); 19 out << s << endl; 20 out.close(); 21 22 boa -> impl_is_ready(CORBA_ImplementationDef::_nil()); 23 }
3-6 | Several header files are included. Of these, OB/CORBA.h provides the standard CORBA definitions, and Hello_impl.h contains the definition of the Hello_impl class. |
11,13 | The first thing a CORBA program has to do is to initialize the ORB and the BOA. This is done by CORBA_ORB_init and BOA_init. Both operations expect the parameters with which the program was started. These parameters may or may not be used by the ORB and BOA, depending on the CORBA implementation. ORBACUS recognizes certain options that will be explained later. |
14 | An instance of Hello_impl is created. Hello_var, like all _var types, is a "smart" pointer, i.e., p will release the object created by new Hello_impl automatically when p goes out of scope. |
16-20 | The client must be able to access the implementation object. This can be done by saving a "stringified" object reference to a file which can be read by the client and converted back to the actual object reference. The operation object_to_string() converts a CORBA object reference into its string representation. |
22 | Finally, in order to react to incoming requests, the server must tell BOA that is ready to process requests from clients. This is done by calling impl_is_ready. Since ORBACUS does not use the CORBA_ImplementationDef argument, CORBA_ImplementationDef::_nil() can be used as a dummy argument. |
Save this to a file with the name Server.cpp.
Writing the client requires less work than writing the server, since the client, in this example, only consists of the main function. In several respects the client's main is similar to the server's main function:
1 // C++ 2 3 #include <OB/CORBA.h> 4 #include <Hello.h> 5 6 #include <fstream.h> 7 8 int 9 main(int argc, char* argv[], char*[]) 10 { 11 CORBA_ORB_var orb = CORBA_ORB_init(argc, argv); 12 13 const char* refFile = "Hello.ref"; 14 ifstream in(refFile); 15 char s[1000]; 16 in >> s; 17 CORBA_Object_var obj = orb -> string_to_object(s); 18 Hello_var hello = Hello::_narrow(obj); 20 21 hello -> hello(); 22 }
4 | In contrast to the server, the client does not need to include Hello_impl.h. Only the generated file Hello.h is needed. |
11 | Like the server's implementation of main, the client's main starts with the initialization of the ORB. It's not necessary to initialize the BOA, because the BOA is only needed by server applications. |
13-17 | The "stringified" object reference written by the server is read and converted to a CORBA_Object object reference. |
19 | The _narrow operation generates a Hello object reference from the CORBA_Object object reference. |
21 | Finally, the hello operation on the hello object reference is invoked, causing the server to print "Hello World!". Save this into the file Client.cpp. |
Both the client and the server must be linked with the compiled Hello.cpp, which has the name Hello.o. The compiled Hello_skel.cpp and Hello_impl.cpp are only needed by the server. A Makefile is given in order to compile the example.
Our "Hello World!" application consists of two parts: the client program
and the server program. The first program to be started is the server, because
it must create the file Hello.ref that the client needs in order to connect to
the server. As soon as the server is running, you can start the client. If all
goes well, the "Hello World!" message will appear on the screen.