Тут я опишу, как создать простой 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