Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Professional C++ [eng].pdf
Скачиваний:
284
Добавлен:
16.08.2013
Размер:
11.09 Mб
Скачать

Chapter 24

CORBA

The Common Object Request Broker Architecture, or CORBA, is a standardized language-independent and platform-independent architecture for defining, implementing, and using distributed objects. The main goal of CORBA is to provide a programming environment that hides all the details of serialization and remote procedure calls discussed in the previous section. CORBA also supports location transparency: you can write code that uses objects without knowing whether those objects are really local or remote.

The CORBA architecture itself is not an implementation, and actually includes several standards. The two most important standards are the Interface Definition Language (IDL), which defines the syntax for writing distributed object definitions, and the Internet Inter-ORB Protocol (IIOP) for making remote method invocations. Additionally, CORBA defines many optional accompanying services, including a name service, an event service, a time service, and numerous others.

There are a number of open-source implementations of the CORBA standards available for use at no cost. The examples in this chapter use the “omniORB” framework, which is available at http://omniorb.sourceforge.net/.

Using CORBA requires several steps, including defining your object interfaces, “compiling” the interfaces to generate the networking and serialization code, defining the class method implementation, writing a server process, and writing clients. This section examines each of those steps in the context of developing an extremely simple distributed database, in which clients can access a database server that resides in a different process or even on a different node. The discussion here barely scratches the surface of this powerful, but complicated, architecture. If you are interested in using it for your distributed object framework, you should consult some of the references in Appendix B.

Interface Definition Language

CORBA does an excellent job of separating object interfaces from their implementations. You write a distributed CORBA class by first defining its interface in the Interface Definition Language (IDL). This language looks a lot like C++, but isn’t identical. In fact, the IDL is implementation language independent.

You could theoretically write an implementation for the class in C++ and a client that uses it in Java.

Writing the Interface

In this step, you specify the prototypes for methods that the object implements. However, unlike in C++ class definitions, you don’t show member variables or other implementation details.

For example, suppose that you want your simple distributed database to store key/value records where the key and value are both strings. Here is the IDL file for a database that supports two methods:

// database.idl

interface database {

void addRecord(in string key, in string record); string lookupRecord(in string key);

};

The word “in” before the parameters specifies value parameters instead of reference parameters.

702

Exploring Distributed Objects

Generating Stubs and Skeletons

After writing your object interface, you compile the IDL file with an IDL compiler, which generates the remote procedure call and networking layers for you. There are IDL compilers available for a variety of languages, including Java, Python, C, and, of course, C++. This step generates two sets of files: the stubs and the skeletons.

Stubs

As described in the previous section on RPC, the stubs are the client side of the object methods, which hide the networking and serialization code required to make the actual remote call to another machine. The omniORB IDL compiler puts stub code from the IDL file name.idl in the header file name.hh and the source file nameSK.cc. Here is a small sample of the stub code in database.hh, which is generated from the database.idl file:

//This file is generated by omniidl (C++ backend)- omniORB_4_0. Do not edit.

//<There’s a lot more code than we show here.>

class _objref_database :

public virtual CORBA::Object, public virtual omniObjRef

{

public:

void addRecord(const char* key, const char* record); char* lookupRecord(const char* key);

inline _objref_database() { _PR_setobj(0); } // nil _objref_database(omniIOR*, omniIdentity*);

protected:

virtual ~_objref_database();

private:

virtual void* _ptrToObjRef(const char*);

_objref_database(const _objref_database&); _objref_database& operator = (const _objref_database&); // not implemented

friend class database;

};

Here is one of the method implementations from databaseSK.cc:

//This file is generated by omniidl (C++ backend)- omniORB_4_0. Do not edit.

//<There’s a lot more code than we show here.>

void _objref_database::addRecord(const char* key, const char* record)

{

_0RL_cd_D115D31DB8E47435_00000000 _call_desc(_0RL_lcfn_D115D31DB8E47435_10000\ 000, “addRecord”, 10);

703

Chapter 24

_call_desc.arg_0 = key; _call_desc.arg_1 = record;

_invoke(_call_desc);

}

Don’t worry about understanding this code! We just want to give you an example of the work that goes on “behind the scenes.”

Skeletons

The skeletons are the basis for your class implementation and are usually abstract base classes generated from the IDL interface. omniORB places the skeletons in the same database.hh and databaseSK.cc files in which it puts the stub code. Here is some of the skeleton code from database.hh:

class _impl_database : public virtual omniServant

{

public:

virtual ~_impl_database();

virtual void addRecord(const char* key, const char* record) = 0; virtual char* lookupRecord(const char* key) = 0;

public: // Really protected, workaround for xlC virtual _CORBA_Boolean _dispatch(omniCallHandle&);

private:

virtual void* _ptrToInterface(const char*); virtual const char* _mostDerivedRepoId();

};

class POA_database :

public virtual _impl_database,

public virtual PortableServer::ServantBase

{

public:

virtual ~POA_database();

inline ::database_ptr _this() {

return (::database_ptr) _do_this(::database::_PD_repoId);

}

};

Note that an in string parameter in the IDL file is translated as a const char* in the generated C++ code. POA stands for Portable Object Adapter, a CORBA component that manages object references on the server side.

Implementing the Class

Now that you’ve defined your interface and generated the stubs and skeletons, the next step is to write a class that provides actual implementations of the methods in the IDL file. You write this class by

704

Exploring Distributed Objects

subclassing the abstract skeleton class and filling in the data members and method implementations. You don’t need to worry about serialization or networking code when you implement the methods. You just write them as if they were normal methods. The skeleton code handles all the gory RPC details for you. Here is a definition of the DatabaseServer class based on the previous omniORB skeleton code:

// DatabaseServer.h #include “database.hh” #include <map> #include <string>

class DatabaseServer : public POA_database,

public PortableServer::RefCountServantBase

{

public:

DatabaseServer();

virtual ~DatabaseServer();

virtual void addRecord(const char* key, const char* record); virtual char* lookupRecord(const char* key);

protected:

std::map<std::string, std::string> mDb;

};

Note that this class subclasses the previous POA_database skeleton abstract class, as well as a reference counting mix-in class supplied by the framework. It adds a protected map data member for storing the key/value pairs.

Here are the method implementations:

#include “DatabaseServer.h” using namespace std;

DatabaseServer::DatabaseServer()

{

}

DatabaseServer::~DatabaseServer()

{

}

void DatabaseServer::addRecord(const char* key, const char* record)

{

mDb[key] = record;

}

char* DatabaseServer::lookupRecord(const char* key)

{

return (CORBA::string_dup(mDb[key].c_str()));

}

The only tricky thing about these implementations is to remember to copy the string you return from lookupRecord() using the CORBA::string_dup() method.

705