Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Advanced CORBA Programming wit C++ - M. Henning, S. Vinoski.pdf
Скачиваний:
65
Добавлен:
24.05.2014
Размер:
5 Mб
Скачать

IT-SC book: Advanced CORBA® Programming with C++

Chapter 3. A Minimal CORBA Application

3.1 Chapter Overview

This chapter shows how to build a simple CORBA application consisting of a server that implements a single object and a client that accesses that object. The point of this chapter is to familiarize you with the basic steps required to build a minimal application, and we explain very few details of the source code here. Do not be concerned if something does not seem clear—later chapters provide all the detail.

Section 3.2 shows how to write and compile a simple interface definition, Section 3.2 covers how to write the server, Section 3.4 shows how to write the client, and

Section 3.5 illustrates how to run the complete application.

3.2 Writing and Compiling an IDL Definition

The first step for every CORBA application is to define its interfaces in IDL. For our minimal application, the IDL contains a structure definition and a single interface:

struct TimeOfDay {

// 0

- 23

short

hour;

short

minute;

//

0

-

59

short

second;

//

0

-

59

};

 

 

 

 

 

interface Time { TimeOfDay get_gmt();

};

The Time interface defines an object that delivers the current time. A Time object has only a single operation, get_gmt. Clients invoke this operation to obtain the current time in the Greenwich time zone. The operation returns the current time as a structure of type TimeOfDay, which contains the current hour, minute, and second.

Having written this IDL definition and placed it in a file called time.idl, you must compile it. The CORBA specification standardizes neither how to invoke the IDL compiler nor what the names of the generated files should be, so the example that follows may need some adjustment for your particular ORB. However, the basic idea is the same for all ORBs with a C++ language mapping.

To compile the IDL, you invoke the compiler with the IDL source file name as a command-line argument. Note that for your ORB, the actual command may be something other than idl.[1]

47

IT-SC book: Advanced CORBA® Programming with C++

[1] We assume a UNIX environment and a Bourne or Korn shell whenever we show commands in this book.

$ idl time.idl

Provided there are no errors in the IDL definition, you will find several new files in the current directory. (The names of these files are ORB-dependent, so you may see file names that differ in name and number from the ones shown here.)

time.hh

This is a header file for inclusion in the client source code. It contains C++ type definitions corresponding to the IDL types used in time.idl.

timeC.cc

This file contains C++ stub code to be compiled and linked into the client application. It provides a generated API that the client application can call to communicate with objects defined in time.idl.

timeS.hh

This is a header file for inclusion in the server source code. It contains definitions that allow the application code to implement an up-call interface to the objects defined in time.idl.

timeS.cc

This file contains C++ skeleton code to be compiled and linked into the server application. It provides the run-time support required by the server application, so it can receive operation invocations sent by clients.

3.3 Writing and Compiling a Server

The source code for the entire server takes only a few lines:

#include <time.h> #include <iostream.h> #include "timeS.hh"

class Time_impl : public virtual POA_Time { public:

virtual TimeOfDay get_gmt() throw(CORBA::SystemException);

};

TimeOfDay

Time_impl::

get_gmt() throw(CORBA::SystemException)

{

48

IT-SC book: Advanced CORBA® Programming with C++

time_t time_now = time(0);

struct tm * time_p = gmtime(&time_now);

TimeOfDay tod;

tod.hour = time_p->tm_hour; tod.minute = time_p->tm_min; tod.second = time_p->tm_sec;

return tod;

}

int

main(int argc, char * argv[])

{

try {

// Initialize orb

CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);

//Get reference to Root POA. CORBA::Object_var obj

=orb->resolve_initial_references("RootPOA"); PortableServer::POA_var poa

=PortableServer::POA::_narrow(obj);

//Activate POA manager PortableServer::POAManager_var mgr

=poa->the_POAManager();

mgr->activate();

//Create an object Time_impl time_servant;

//Write its stringified reference to stdout Time_var tm = time_servant._this(); CORBA::String_var str = orb->object_to_string(tm); cout < str < endl;

//Accept requests

orb->run();

}

catch (const CORBA::Exception &) {

cerr < "Uncaught CORBA exception" < endl; return 1;

}

return 0;

}

The server implements one Time object. The timeS.hh header file contains an abstract base class called POA_Time. Its definition looks like this (tidied up a little to get rid of code that is irrelevant to the application):

// In file timeS.hh: class POA_Time :

public virtual PortableServer::ServantBase {

49

IT-SC book: Advanced CORBA® Programming with C++

public:

~POA_Time();

virtual

Time_ptr

_this();

virtual TimeOfDay

get_gmt()

};

throw(CORBA::SystemException) = 0;

 

Note that this class contains a get_gmt pure virtual method. To create an implementation object that clients can call, we must derive a concrete class from POA_Time that provides an implementation for the get_gmt method. This means that the first few lines of our server program look like this:

#include <time.h> #include <iostream.h> #include "timeS.hh"

class Time_impl : public virtual POA_Time { public:

virtual TimeOfDay get_gmt() throw(CORBA::SystemException);

};

Here, we define a class Time_impl that inherits from POA_Time. This class provides a concrete implementation of a Time object that clients actually can communicate with. Our implementation class is very simple. It has only the single method get_gmt (which is not pure virtual because we require a concrete class that can actually be instantiated).

The next step is to implement the get_gmt method of Time_impl. For now, we are ignoring error conditions. If the call to time fails with a return value of -1, get_gmt returns a garbage time value instead of raising an exception. (We discuss how to deal with errors in Chapters 7 and 9.)

TimeOfDay

Time_impl::

get_gmt() throw(CORBA::SystemException)

{

time_t time_now = time(0);

struct tm * time_p = gmtime(&time_now);

TimeOfDay tod;

tod.hour = time_p->tm_hour; tod.minute = time_p->tm_min; tod.second = time_p->tm_sec;

return tod;

}

This completes the object implementation. What remains is to provide a main function for the server. The first few lines are identical for most servers and initialize the serverside ORB run time:

int

50

IT-SC book: Advanced CORBA® Programming with C++

main(int argc, char * argv[])

{

try {

// Initialize orb

CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);

//Get reference to Root POA. CORBA::Object_var obj

=orb->resolve_initial_references("RootPOA"); PortableServer::POA_var poa

=PortableServer::POA::_narrow(obj);

//Activate POA manager PortableServer::POAManager_var mgr

=poa->the_POAManager();

mgr->activate();

Do not be concerned about the details of this code for the moment—we will discuss its precise purpose in later chapters.

The next step is to provide an actual servant for a Time object so that clients can send invocations to it. We do this by creating an instance of the Time_impl servant class:

// Create a Time object Time_impl time_servant;

For the client to be able to access the object, the client requires an object reference. In this simple example, we provide that reference by writing it as a string to stdout. Of course, this is not a distributed solution, but it will suffice for now:

//Write a stringified reference

//for the Time object to stdout Time_var tm = time_servant._this();

CORBA::String_var str = orb->object_to_string(tm); cout < str < endl;

The call to _this creates an object reference for the object, and object_to_string converts that reference into a printable string.

At this point we have a concrete implementation of a Time object whose reference is available to the client. The server is now ready to accept requests, something that it indicates to the ORB run time by calling run:

// Accept requests orb->run();

The run method starts an event loop that waits for incoming requests from clients.

51

IT-SC book: Advanced CORBA® Programming with C++

The remainder of the server source code sets an exception handler that prints an error message if anything goes wrong and terminates main. (The closing curly brace at the start of this code fragment completes the try block we opened at the beginning of main.)

}

catch (const CORBA::Exception &) {

cerr < "Uncaught CORBA exception" < endl; return 1;

}

return 0;

}

This completes the server source code. In this short example, most of the source code is boilerplate that you will find in every server. In a more realistic application, most of the server source code consists of the actual operation implementations.

We are now ready to compile and link the server code. The exact compile and link commands you use depend on your compiler and ORB. For example, include paths differ from vendor to vendor, and you may have to add various preprocessor or compiler options. However, the basic idea is the same for all ORBs: you compile the generated stub file (timeC.cc), the generated skeleton file (timeS.cc), and the server source code you have written, which we assume is in the file myserver.cc. Simple compilation commands could look like this:

$ CC -c -I/opt/myORB/include timeC.cc $ CC -c -I/opt/myORB/include timeS.cc

$ CC -c -I/opt/myORB/include myserver.cc

Assuming that there are no errors, this produces three object files that we can link into an executable. Again, the exact link command you use depends on your C++ compiler and ORB vendor. Also, the name and location of the ORB run-time libraries you link with will differ for each vendor. A simple link command is

$ CC -o myserver timeC.o timeS.o myserver.o \ > -L/opt/myORB/lib -lorb

Here, we assume that the ORB run-time library is called liborb. Assuming that there are no errors, we now have a complete executable we can run from the command line. On start-up, the server prints a reference to its Time object on stdout. The server then waits indefinitely for client requests. (To stop the server, we must kill it by sending it a signal.)

$ ./myserver IOR:000000000000000d49444c3a54696d653a312e300000000000000001000000 00000000f000010100000000066d6572676500060b000000d7030231310c000016 7e0000175d360aed118143582d466163653a20457348795e426e5851664e527333 3d4d7268787b72643b4b4c4e59295a526a4c3a39564628296e4345633637533d6a 2c77245879727c7b6371752b7434567d61383b3422535e514a2b48322e772f354f

52