CORBA / С++: Hello world!

Dec 07, 2006 18:42

Тут я опишу, как создать простой CORBA-объект, написать сервер и клиент, используя статические клиентские стабы и серверные скелетоны.

Задача
Объект-счетчик, который умеет увеличивать и уменьшать счетчик, а так же возвращать его текущее значение.

Решение
Здесь описано, как решить нашу маленькую и тупую задачку с помощью omniORB4.

Шаг 1: описание интерфейса на IDL
Мы описываем интерфейс требуемого объекта на OMG IDL. Выглядит это так (я, как особо ленивый, нарисовал квадратик в UML и сгенерировал IDL автоматически):

/**
* @(#) Count.idl
*/
#ifndef _Count_idl
#define _Count_idl

interface Counter
{
attribute long currValue;
long incCount( );
long decCount( );

};
#endif

Сохраняем этот файл в Count.idl

Шаг 2: генерация клиентского стаба и серверного скелетона
В состав omniORB входит утилита omniidl (компилятор IDL), умеющая генерировать заглушки на основании idl. Чтобы получить от нее желаемые исходники мы вызываем ее следущим образом:
zmij@zmij idl $ omniidl -bcxx -Wbh='.h' -Wbs='SK.cpp' Count.idl
В результате у нас в рабочей директории появляются два новых файла: Count.h и CountSK.cpp. Хедер содержит в себе объявления интерфейсов в грамотном IDL -> C++ переводе. Наш интерфейс выглядит теперь вот так:

class Counter {
public:
// Declarations for this interface type.
typedef Counter_ptr _ptr_type;
typedef Counter_var _var_type;

static _ptr_type _duplicate(_ptr_type);
static _ptr_type _narrow(CORBA::Object_ptr);
static _ptr_type _unchecked_narrow(CORBA::Object_ptr);
static _ptr_type _nil();

static inline void _marshalObjRef(_ptr_type, cdrStream&);

static inline _ptr_type _unmarshalObjRef(cdrStream& s) {
omniObjRef* o = omniObjRef::_unMarshal(_PD_repoId,s);
if (o)
return (_ptr_type) o->_ptrToObjRef(_PD_repoId);
else
return _nil();
}

static _core_attr const char* _PD_repoId;

// Other IDL defined within this scope.

};

Кроме того, хедер содержит объявления всевозможных хелперов, необходимых для правильной работы ORB, POA и прочих механизмов ;) На данный момент они нас не интересуют.
В файле CountSK.cpp находится реализация неинтересных хелперов. Файл нужен, чтобы все собиралось.

Шаг 3: Пишем сервер

Для того, чтобы наш объект заработал, необходимо предоставить его реализацию и сделать его доступным для клиентов ;)
Шаг 3.1: Реализуем интерфейс объекта

Объявляем класс-наследник от абстрактного класса Count (CountSrvImpl.h):

#ifndef __CountSrvImpl_h__
#define __CountSrvImpl_h__

#include "Count.h"

class Counter_i :
public POA_Counter
{
long f_Count;
public:
Counter_i() : POA_Counter(), f_Count(0) {};
virtual ~Counter_i() {};
virtual void currValue( CORBA::Long _v );
virtual CORBA::Long decCount();
virtual CORBA::Long currValue();
virtual CORBA::Long incCount();

};

#endif //__CountSrvImpl_h__

и реализуем его (CountSrvImpl.cpp):

#include "CountSrvImpl.h"
#include

using namespace std;

/*!
\fn Count_i::incCount()
*/
CORBA::Long Counter_i::incCount()
{
cout << "Incrementing counter. Current counter is: " << this->f_Count << endl;
return this->f_Count++;
}

/*!
\fn Count_i::decCount()
*/
CORBA::Long Counter_i::decCount()
{
cout << "Decrementing counter. Current counter is: " << this->f_Count << endl;
return this->f_Count--;
}

/*!
\fn Count_i::currValue
*/
CORBA::Long Counter_i::currValue()
{
return this->f_Count;
}

/*!
\fn Count_i::currValue( CORBA::Long _v )
*/
void Counter_i::currValue( CORBA::Long _v )
{
this->f_Count = _v;
}

Шаг 3.2: пишем main()

//---------------------------------------------------------------------------
int main(int argc, char *argv[]) {
CORBA::ORB_var orb = NULL;
try {
// Инициализируем ORB
orb = CORBA::ORB_init(argc, argv);
cout << "ORB initialized" << endl;

// Получаем ссылку на корневой POA
CORBA::Object_var obj = orb->resolve_initial_references("RootPOA");
PortableServer::POA_var poa = PortableServer::POA::_narrow(obj);

// Создаем экземпляр нашего объекта
Counter_i *cnt = new Counter_i();
PortableServer::ObjectId_var cnt_id = poa->activate_object(cnt);

// Получаем объектную ссылку на наш сервер
Counter_var cntref = cnt->_this();
// и выводим ее в строковом виде в терминал
CORBA::String_var sior( orb->object_to_string(cntref) );
cout << "IOR: " << sior << endl;

// уменьшаем счеткик, пусть теперь POA колупается с менеджментом нашего объекта
cnt->_remove_ref();

// Получаем ссылку на POAManager
PortableServer::POAManager_var pman = poa->the_POAManager();
// и активируем его
pman->activate();

// Мы готовы обрабатывать запросы, банзай.
orb->run();
}
catch(CORBA::SystemException& ex) {
cerr << "Caught CORBA::" << ex._name() << endl;
}
catch(CORBA::Exception& ex) {
cerr << "Caught CORBA::Exception: " << ex._name() << endl;
}
catch(omniORB::fatalException& fe) {
cerr << "Caught omniORB::fatalException:" << endl;
cerr << " file: " << fe.file() << endl;
cerr << " line: " << fe.line() << endl;
cerr << " mesg: " << fe.errmsg() << endl;
}

// Зачищаем ресурсы
if (orb) orb->destroy();
return EXIT_SUCCESS;
}
//---------------------------------------------------------------------------

Компилируем, запускаем - сервер готов.

Шаг 4: пишем клиента

Тут все еще проще (ну в общем так и должно было быть):

#include
#include

#include "Count.h"

using namespace std;

static void inc_cnt(Counter_ptr c) {
long currCnt = c->currValue();
cout << "Current counter value: " << currCnt << endl;
c->incCount();
}

static void dec_cnt(Counter_ptr c) {
long currCnt = c->currValue();
cout << "Current counter value: " << currCnt << endl;
c->decCount();
}

int main(int argc, char *argv[])
{
CORBA::ORB_var orb = NULL;
try {
// Проверяем число аргументов (мы хотим объектную ссылку в аргументе)
if ( argc != 2 ) {
cerr << "usage: cntclnt " << endl;
return 1;
}
// Инициализируем ORB
orb = CORBA::ORB_init( argc, argv );

// Просим ORB строку объектной ссылки перести в юзабельную объектную ссылку
CORBA::Object_var obj = orb->string_to_object( argv[1] );

// Получаем указатель на интерфейс Counter
Counter_var cntref = Counter::_narrow( obj );

// А вдруг херня приключилась?
if ( CORBA::is_nil( cntref ) ) {
cerr << "Can't narrow reference to type Counter (or it was nil)." << endl;
orb->destroy();
return 1;
}

// Дергаем методы по ссылке
for ( int i = 0; i < 10; i++) {
inc_cnt(cntref);
}
}
catch(CORBA::TRANSIENT&) {
cerr << "Caught system exception TRANSIENT -- unable to contact the "
<< "server." << endl;
}
catch(CORBA::SystemException& ex) {
cerr << "Caught a CORBA::" << ex._name() << endl;
}
catch(CORBA::Exception& ex) {
cerr << "Caught CORBA::Exception: " << ex._name() << endl;
}
catch(omniORB::fatalException& fe) {
cerr << "Caught omniORB::fatalException:" << endl;
cerr << " file: " << fe.file() << endl;
cerr << " line: " << fe.line() << endl;
cerr << " mesg: " << fe.errmsg() << endl;
}

// Зачищаем ресурсы
if (orb) orb->destroy();
return EXIT_SUCCESS;
}

Компилируем, запускаем с параметром ссылки (выглядит это жутковато):

zmij@zmij:~/develop/cntclnt/debug/src$ ./cntclnt IOR:010000001000000049444c3a436f756e7465723a312e3000010000000000000064\
000000010102000e0000003139322e3136382e302e31353600beb30e000000fe563a784500001ba1000000000000000200000000000000080\
000000100000000545441010000001c00000001000000010001000100000001000105090101000100000009010100

и смотрим как сервер плюется в свою консоль, а клиент - в свою :)

Ссылку используйте ту, которую ваш сервер плюнул в свою консоль. Та ссылка, которую я привел, давно уже не действует на моей машине ;)

Файлы:

Сервер Внимание! В той версии, что лежит на сервере, требуется COSNamingService. Для того, чтобы сервер заработал, требуется закомментировать вызов функции bindObjectToName.
Клиент
Ссылки по теме:

Обзор CORBA

ништяк, программизм, corba, работа, c++

Previous post Next post
Up