Продолжается подписка на наши издания! Вы не забыли подписаться?

Динамическое взаимодействие клиентов и серверов

Александр Цимбал
Код к статье: ftp.k-press.ru/pub/cs/2000/4/dyn_int.zip (24 KB)

Основным способом, используемым для создания CORBA-приложений, является так называемый «статический» подход – основой является набор IDL-объявлений, на базе которого компилятор с языка IDL генерирует все необходимое для взаимодействия программы и CORBA (совокупность сгенерированных классов, функций, типов и пр. на стороне клиента часто называют «стабом»(stub), а не стороне сервера – «скелетоном»). Это означает, что полученный код уже «настроен» на использование конкретных (определенных разработчиком) типов данных, наиболее подходящих и удобных для решения данной конкретной задачи.

Совершенно очевидно, что такой подход не может быть использован при создании средств скорее системного, чем прикладного, уровня – например, универсальных мониторов, отслеживающих поведение CORBA-приложений или мостов, обеспечивающих взаимодействие CORBA с другими технологиями. Кроме того, иногда приходится создавать CORBA-объекты в ходе выполнения программы. Например, вы хотите создать объектное представление данных, хранящихся в одной или нескольких записях реляционной базы данных. Скорее всего, такой объект должен быть создан динамически – ясно, что крайне трудно (если вообще возможно) предусмотреть заранее все необходимые IDL-типы.

Итак, под «динамическим» взаимодействие понимается способ организации удаленных вызовов, при котором либо клиент, либо сервер, либо они оба не используют сгенерированных на основы IDL-деклараций стабов и скелетонов.

Не надо думать, что знание этого механизма совсем не нужно прикладному программисту. Спецификация CORBA и ее конкретные реализации, например, INPRISE VisiBroker, предусматривают, например, возможность использования так называемых «интерсепторов». Интерсепторы представляют из себя обычные callback-функции, т.е. такие функции, которые написаны программистом, но вызываются в определенных ситуацией компонентами самого middleware – например, ORB’ом. Интерсепторы очень удобны во многих ситуациях – например, при определении интенсивности взаимодействия клиентов и серверов, фильтрации посылаемых сообщений на уровне ORB и т.п. Для работы с интерсепторами нужно понимать принципы динамического взаимодействия в CORBA.

CORBA содержит две подсистемы, обеспечивающие работу в динамическом режиме. Подсистема на стороне клиента называется DII – Dynamic Invocation Interface («Интерфейс Динамических Вызовов»), на стороне сервера – DSI (Dynamic Skeleton Interface). Они работают независимо друг от друга. Это означает, что как статический, так и DII-клиент может взаимодействовать как со статическим, так и с DSI-сервером, причем ни клиент, ни сервер не знают и не должны знать о том, какой именно механизм используется на противоположной стороне.

При использовании DII и DSI часто приходиться обращаться к внешним источникам информации – например, к репозитариям интерфейсов.

Какая информация необходима для использования DII/DSI

Первое, что абсолютно необходимо для динамического взаимодействия клиентов и серверов – это знание RepositoryID интерфейса, методы которого будут использоваться. Ясно, что перед вызовом методов должны быть тем или иным образом получена объектная ссылка.

При использовании стандартных средств CORBA (явная передача объектной ссылки через файл, базу данных или электронную почту, использование Naming Service, Trading Service и др.) репозитарный идентификатор интерфейса может присутствовать неявно, но он, конечно же, известен. Как вы увидите дальше, при изучении DSI, иногда RepositoryID должен быть указан явно (подход, когда это необходимо, сейчас признан устаревшим, но он еще используется довольно широко).

В добавление к стандартным средствам CORBA, в VisiBroker предусмотрен нестандартный метод ORB::bind(), который позволяет получить объектную ссылку (тип CORBA::Object). Метод CORBA::ORB::bind() очень похож на все другие методы bind() в VisiBroker. На C++ его объявление выглядит так:

virtual CORBA_Object_ptr bind (const char *rep_id, 
   const char *object_name = (const char*)NULL,
   const char *host_name = (const char*)NULL,
   const CORBA_BindOptions *opt=(CORBA_BindOptions*)NULL);

virtual CORBA_Object_ptr bind (const char* logical_type_id,
   const char *poa_name, 
   const CORBA_OctetSequence& oid,
   const char *host_name = (const char*)NULL,
   const CORBA_BindOptions *opt=(CORBA_BindOptions*)NULL);

Первым его (и обязательным, в отличие от методов bind() в других классах) аргументом является RepositoryID интерфейса, который должен реализовать объект, на который нужно получить объектную ссылку.

После того, как вами получена объектная ссылка, при написании клиентского приложения нужно знать имена и аргументы реализованных на стороне сервера методов. Аналогично, при создании динамического сервера, в общем случае нужно быть к готовым к приходу тех или иных клиентских запросов.

any и TypeCode

Итак, мы пытаемся работать в условиях, когда в создаваемой программе имена методов и характеристики их аргументов (имена, типы, атрибуты и текущие значения) являются переменными в терминах используемого языка программирования – в нашем случае – языка C++. Следовательно, необходимо обеспечить передачу, например, аргументов, в некотором обобщенном виде. Такой способ должен позволять передать «самодокументированные» структуры, которые содержат внутри себя всю необходимую информацию. В CORBA для этого используются несколько интерфейсов (т.е. типов данных). Здесь мы подробнее рассмотрим два из них – any и TypeCode. Они тесно связаны друг с другом. Любой объект типа any состоит из двух частей: первая хранит информацию о типе хранящегося в объекте any значения, вторая – само это значение. Первая часть представляет собой объект типа TypeCode.

Для того, чтобы у читателя создалось более полное представление о типах any и typeCode, будет рассказано и о том, каким образом они используются на уровне IDL-деклараций и как с ними работает компилятор idl2cpp.

any

Поскольку C++ поддерживает концепцию универсальных указателей (void*) и обеспечивает гибкое управление динамической памятью, то при отображении any на C++ просто создается класс CORBA::Any, содержащий в качестве своих private-данных указатель на данное, его признак типа и вспомогательную информацию (например, является ли any владельцем своих данных):

class CORBA_Any : private VISResource
   {
   public:
      CORBA_Any();
      CORBA_Any(const CORBA_Any&);
      CORBA_Any(CORBA_TypeCode_ptr tc, void *value, CORBA::Boolean 
                     release=0UL);
      ~CORBA_Any();
      ...
      CORBA_Any& operator=(const CORBA_Any& );
      ...
      CORBA_TypeCode_ptr type() const;
      const void *value() const;
      static CORBA_Any_ptr _nil() { return (CORBA_Any_ptr)NULL; }
   
      friend CORBA::Long compare(const CORBA_Any&, const CORBA_Any&);
      friend CORBA::ULong hash(const CORBA_Any&);
   ...
   private:
      CORBA_TypeCode_ptr _tc;    // признак типа
      CORBA::Octet _flags;       // признак владения
      CORBA::Long _len;          // занимаемая длина
      void * _vbuf;              // указатель на данное
      CORBA::ULong _hash_value;
      VISAnyValue _value;
   ...
   };

Этот класс содержит очень много методов, большинство из которых обеспечивают запись и извлечение данных в any и из any, соответственно. Такие методы предусмотрены для всех стандартных типов. Для типов, созданных разработчиком (в виде IDL-деклараций) генерация двух методов - вставки и чтения - выполняется компилятором idl2cpp отдельно для каждого такого типа. Текст описания класса CORBA::Any для VisiBroker вы можете найти в файле ...\Visibroker\Include\any.h.

В нем созданы несколько вспомогательных типов, которые решают проблему различения, например, типов CORBA::Boolean и CORBA::Octet, при использовании компиляторов разной степени совместимости со стандартом ANSI, например,

struct from_boolean 
{
   from_boolean(CORBA::Boolean b) : val(b) {}
   CORBA::Boolean val;
};
struct from_octet 
{
   from_octet(CORBA::Octet b) : val(b) {}
   CORBA::Octet val;
};
...
void operator<<=(from_boolean);
void operator<<=(from_octet);
...
struct to_boolean 
{
   to_boolean(CORBA::Boolean& b) : ref(b) {}
   CORBA::Boolean& ref;
};
struct to_octet 
{
   to_octet(CORBA::Octet& b) : ref(b) {}
   CORBA::Octet& ref;
};
...
CORBA::Boolean operator>>=(to_boolean) const;
CORBA::Boolean operator>>=(to_octet) const;

В случаях, когда конфликт CORBA-синонимов типов C++ невозможен, методы выглядят очень просто:

void operator<<=(CORBA::Short);
void operator<<=(CORBA::Long);
void operator<<=(CORBA::Float);
void operator<<=(CORBA::Double);
void operator<<=(const CORBA_Any&);
void operator<<=(const char *);
...
CORBA::Boolean operator>>=(CORBA::Short&) const;
CORBA::Boolean operator>>=(CORBA::Long&) const;
CORBA::Boolean operator>>=(CORBA::Float&) const;
CORBA::Boolean operator>>=(CORBA::Double&) const;
CORBA::Boolean operator>>=(CORBA_Any&) const;
CORBA::Boolean operator>>=(char *&) const;
...

Обратите внимание, что методы извлечения данных возвращают признак типа bool, который имеет значение true, если операция завершилась успешно, или false в противном случае.

Поведение any меняется в зависимости от типа находящегося в нем данного, поэтому имеет смысл рассмотреть работу с разными группами типов отдельно.

any и string/wstring

Работа со string и wstring практически идентична, поэтому рассмотрим правила использования строк на примере типа string.

struct from_string 
{
   from_string(const char *s, CORBA::ULong b, CORBA::Boolean no_copy=0UL) 
               : val((char*)s), bound(b), nocopy(no_copy) {}
   from_string(char *s, CORBA::ULong b, CORBA::Boolean no_copy=0UL) 
               : val(s), bound(b), nocopy(no_copy) {}
   char *val;
   CORBA::ULong bound;
   CORBA::Boolean nocopy;
};

struct to_string 
{
   to_string(const char *&s, CORBA::ULong b) : val(s), bound(b) {}
   const char *&val;
   CORBA::ULong bound;
};
...
void operator<<=(const char *);
CORBA::Boolean operator>>=(char *&) const;

void operator<<=(from_string);
CORBA::Boolean operator>>=(to_string) const;

Формы записи

any <<= “String”;

и

any <<= CORBA::Any::from_string (“String”, 0);

эквивалентны.

Первое, на что нужно обратить внимание, это то, что при выполнении записи строки в any программист может явно указать, нужно ли в any создавать динамическую копию строки или достаточно записать в any только указатель на строку. В первом случае при уничтожении объекта (или операции присваивания) автоматически удаляется содержащаяся в any копия, во втором - вызывается операция CORBA::string_free() для указателя, т.е. в любом случае any является владельцем своих данных. Вот несколько очевидных следствий из этого правила:

{
   CORBA::Any_var any;
   any <<= "String";  // создание копии строки и сохранение ее в any
}   // уничтожение any и копии строки

{
   CORBA::Any_var any;
   char* str = CORBA::string_dup ("String");
   any <<= CORBA::Any::from_string (str, 0, true); // запись только указателя
}  // CORBA::string_free();

{
   CORBA::Any_var any;
   any <<= CORBA::string_dup ("String");
}  // утечка памяти

Следующее важное обстоятельство: строка, извлекаемая из any, по-прежнему находится под управлением any, поэтому не надо ни удалять такую строку, ни даже изменять ее (спецификация требует, что переносимая программа должна трактовать такую строку как константу):

char* str;
any >>= str;

CORBA::string_free(str);    // ошибка
str[0] = 'S';        // непереносимый код

Правильный вариант:

CORBA::String_var copy (const_cast<const char*>(str));
copy[0] = 'S';

any и структуры, объединения и последовательности

Операции >>= и <<= генерируются для таких типов данных на основе их IDL-деклараций при установке соответствующего ключа компилятора idl2cpp. Главной особенностью этих операций является то, что могут создаваться две отдельные операции записи таких типов данных в any: одна из них приводит к копированию самого данного, другая - к записи только указателя. Спецификация OMG ясно требует наличия операции копирования; наличие второй формы обязательным не является (по крайней мере, текст спецификации можно трактовать именно так). К сожалению, VisiBroker использует только режим с созданием настоящей копии:

IDL:
struct MySruct { long l; };
C++:
CORBA::Any any;

  MyStruct ms;
  ms.l = 1;
  any <<= ms;

  MyStruct* pms = new MyStruct;
  pms->l = 2;
  any <<= pms; // ошибка в VisiBroker

Для многих других реализаций CORBA (и, может быть, даже для последующих версий VisiBroker) возможно использовать обе формы операции. Их поведение аналогично поведению операций работы со строками, только для строк режим копирования определяется значением последнего аргумента конструктора класса from_string.

Так же, как и при работе со строками, никогда не следует считывать данные из any в объект _var-класса:

MyStruct_var msv;
any >>= msv;

Причина неизбежного появления ошибки совершенно ясна: и any, и msv сейчас отвечают за освобождение динамической памяти, в которой находится объект типа MyStruct.

any и массивы

Для массивов генерируется специальный класс, специально предназначенный для организации взаимодействия с any - класс, имя которого заканчивается на _forany. Конструктор этого класса имеет аргумент типа bool (имя этого аргумента - nocopy), который определяет, нужно ли выполнять копирование или просто необходимо сохранить в any указатель (разумеется, со взятием управления памятью на себя). Нетрудно заметить, что такое поведение совершенно аналогично поведению класса from_string. В следующем примере выполняется копирование данных:

MyArray_slice* mas = MyArray_alloc();
...
MyArray_forany ma (mas); // MyArray_forany ma (mas, false);
a <<= ma;

А вот так можно избежать копирования:

a <<= MyArray_forany (mas, true); // или a <<= MyArray_forany (mas, 1);

Для чтения массива из any необходимо сначала создать переменную типа MyArray_forany. Для этого типа переопределена операция [], так что можете рассматривать объект этого типа как обычный массив:

MyArray_forany buffer;
any >>= buffer;
if (buffer [2] = buffer[3])
...

any и объектные ссылки

Применительно к взаимодействию any и объектных ссылок справедливы все замечания, касающиеся использования структур, объединений и последовательностей. VisiBroker предусматривает только одну форму операции <<=, а именно, с использованием типов _var и _ptr. В этом случае выполняется копирование объектной ссылки, т.е. вызывается операция _duplicate(). Спецификация говорит о возможности генерации операции <<=, для которой в качестве второго операнда используется адрес объектной ссылки, т.е. указатель на объект _var- или _ptr-типа.

TCKind

TCKind просто представляет из себя перечисление, в котором для каждого типа данных IDL (включая тип any) сопоставлен свой элемент):

module  CORBA
{
   ...
   enum TCKind
   {
      tk_null,     tk_void,      tk_short,    tk_long,  
      tk_ushort,   tk_ulong,     tk_float,    tk_double,
      tk_boolean,  tk_char,      tk_octet,    tk_any,
      tk_TypeCode, tk_Principal, tk_objref,   tk_struct,
      tk_union,    tk_enum,      tk_string,   tk_sequence,
      tk_array,    tk_alias,     tk_except,   tk_longlong,
      tk_ulonglong,tk_longdouble,tk_wchar,    tk_wstring,
      tk_fixed,    tk_value,     tk_value_box,tk_native,
      tk_abstract_interface
   };
   ...
};

TCKind, как перечисление IDL, безо всяких изменений отображается в перечисление (enum) C++. Обычно в СORBA-программах те или иные конкретные значения признака типа используются примерно так:

CORBA::Ant any;
...
TypeCode_var type_code = any->type();
switch (type_code->kind())    // iieo?aiea cia?aiey oeia TCKind
{
case CORBA::tk_short :
   ...
   break;
   
case CORBA::tk_long :
   ...
}

TypeCode

TypeCode представляет собой довольно интересный IDL-тип. C одной стороны, правила его отображения с IDL на конкретный язык программирования могут отличаться от стандартных правил, т.е. TypeCode является псевдо-типом IDL, объявленным с помощью PIDL. С другой стороны, TypeCode можно передавать как аргумент удаленных методов, что нехарактерно для псевдо-типов. Не забывайте также, что TypeCode является IDL-интерфейсом, т.е. это объектная ссылка.

Основными методами интерфейса TypeCode являются следующие:

kind() – возвращает TCKind для TypeCode.

equal() – сравнивает, является ли объект типа TypeCode, для которого вызывается этот метод, «эквивалентным» аргументу этого метода. Метод equal() в CORBA 2.3 возвращает значение TRUE тогда и только тогда, когда сравниваемые объекты типа TypeCode эквивалентны во всех отношениях, т.е. для них применимы одни и те же операции интерфейса TypeCode, и все они дают для обоих объектов одинаковые результаты.

equivalent() – выполняет те же действия, что и equal(), но при этом он поступает более «интеллектуальным» образом. В частности, он игнорирует наличие алиасов типов (т.е. использование синонимов типов, создаваемых с помощью typedef).

get_compact_typecode() – получает новый объект TypeCode на базе исходного путем удаления всех необязательных описателей структуры типа вида name и member_name. Алиасы остаются нетронутыми.

id() – возвращает репозитарный идентификатор (Repository ID).

В чем же заключаются особенности отображения TypeCode по сравнению с «нормальными» интерфейсами CORBA? Одной из таких важных особенностей является возврат для методов name(), id(), member_name() и других C++-типа const char*, а не char*:

IDL:
RepositoryId id () raises (BadKind);
Identifier name () raises (BadKind);
C++:
class CORBA_TypeCode
{
public:
   ...
   virtual const char* id() const;
   virtual const char *name() const; 
   ...
};

Это, в частности, означает, что управление возвращаемыми строками берет на себя сам TypeCode, и вам не следует пытаться вызывать CORBA::string_free(). Вряд ли это удобно, но что поделаешь.

Поскольку TypeCode часто используются при анализе типов используемых данных, возникает вопрос об универсальности их использования – и для стандартных данных, и для типов, созданных программистом. На первый взгляд может показаться, что, например, для сравнения типов объектов вполне достаточно наличия объектов типа TCKind, т.е. вместо сравнения TypeCode лучше сравнивать их TCKind’ы:

TypeCode_ptr  type_code1 = ...
TypeCode_ptr  type_code2 = ...
...
// непереносимый код, так не сравнивают объектные ссылки!
if (type_code1 == type_code2)
...
// Правильно:
if (type_code1->kind() == type_code2->kind())
...

Это будет хорошо работать, но только до тех пор, пока вы не захотите сравнить типы двух массивов или структур. Их TCKind будет одинаков – tk_array или tk_struct, соответственно, но два типа могут иметь разные типы своих элементов, число этих элементов или полей. Поэтому необходимо сравнивать все-таки сами объекты типа TypeCode.

Это сопряжено с определенными трудностями. Вообще, сравнение на равенство (или неравенство) двух объектных ссылок в CORBA – задача трудно решаемая. В общем случае получить достоверный однозначный ответ крайне трудно, если не сказать невозможно. Но в данном случае, к счастью, никаких проблем не возникает. Для сравнения TypeCode вы можете использовать вполне однозначные функции TypeCode::equal() и TypeCode::equivalent().

if(type_code1->equal (type_code2) )...

или

if(type_code1->equivalent (type_code2) )...

Теперь можно задаться следующим вопросом: где взять конкретные значения TypeCode – и для стандартных IDL-типов, и для IDL-типов, определенных пользователем?

Значения стандартных TypeCode для IDL-типов, не требующих наличия дополнительной информации, помимо TCKind, созданы как константы в классе CORBA. Вот список этих констант (все они имеют тип const CORBA::TypeCode_ptr):

_tc_null      _tc_void       _tc_short  _tc_long
_tc_ushort    _tc_ulong      _tc_float  _tc_double
_tc_boolean   _tc_char       _tc_octet  _tc_any  
_tc_TypeCode  _tc_Object     _tc_string _tc_longlong
_tc_ulonglong _tc_longdouble _tc_wchar  _tc_wstring

Для остальных типов данных соответствующие _tc_-константы генерируются непосредственно компилятором idl2cpp.

IDL:
typedef sequence<long, 20> LongFixSeq;
C++:
extern CORBA::TypeCode_ptr _tc_LongFixSeq;
extern CORBA::TypeCode_ptr _tc_LongFixSeq_get();

Это, конечно, прекрасно, но мы говорим о динамическом способе взаимодействия и, следовательно, IDL-декларации просто отсутствуют. В этом случае нужные объекты типа TypeCode можно создать, используя в качестве фабрики сам ORB:

CORBA::TypeCode_ptr alias_tc =
orb->create_alias_tc ("LOCAL:MyRepositoryID", "SIAlias", _tc_SimpleInterface);

Описание метаданных каждого типа данных и список функций для создания объектов типа TypeCode вместе со списками их аргументов приведены в документе 99-10-11.pdf.

dynAny

Итак, мы теперь знаем, что объекты типа any состоят из описания типа (объект TypeCode) и собственно значения. Если и TypeCode, и тип значения, которое нужно поместить в объект any, известно на этапе компиляции, то не возникает никаких трудностей. Вы определяете с помощью IDL свой собственный тип данных (обычно это структура, объединение, последовательность, массив или тип-значение), а компилятор с IDL на выбранный язык программирования сгенерирует для этого типа операции помещения его в объект any и извлечения из него. Для стандартных типов данных - short, string и др. - такие стандартные операции являются частью мэппинга IDL на конкретный язык программирования.

Проблемы возможны в том случае, если код типа (TypeCode) и/или собственно данные, которые нужно поместить в объект типа any, неизвестны на этапе компиляции, т.е. для них нет IDL-декларации и, следовательно, нет сгенерированной операции помещения таких данных в any.

Как динамически создавать коды типов с помощью ORB, было уже показано. Возможно и динамическое - без IDL - создание данных произвольных типов и помещение их в объект типа any. Такие операции предоставляют совместно интерфейсы DynAny и DynAnyFactory.

module DynamicAny 
{
   interface DynAny 
   {
      exception InvalidValue {};
      exception TypeMismatch {};
      
      // Методы, определяющие операции копирования, уничтожения, сравнения и 
      // преобразования типов от any к DynAny и обратно
      
      CORBA::TypeCode type();
      
      void assign(in DynAny dyn_any) raises(TypeMismatch);
      void from_any(in any value) raises(TypeMismatch, InvalidValue);
      any to_any();
      boolean equal(in DynAny dyn_any);
      void destroy();
      DynAny copy();
      
      // Методы записи данных базовых типов, объектных ссылок, 
      // any и DynAny в объект DynAny
      
      void insert_boolean(in boolean value) raises(TypeMismatch, InvalidValue);
      void insert_octet(in octet value) raises(TypeMismatch, InvalidValue);
      void insert_char(in char value) raises(TypeMismatch, InvalidValue);
      void insert_short(in short value) raises(TypeMismatch, InvalidValue);
      ... 
      // Методы чтения данных базовых типов, объектных ссылок, any и DynAny
      // из объекта DynAny
         
      boolean get_boolean() raises(TypeMismatch, InvalidValue);
      octet get_octet() raises(TypeMismatch, InvalidValue);
      char get_char() raises(TypeMismatch, InvalidValue);
      short get_short() raises(TypeMismatch, InvalidValue);
      ...
   };
   
   // Интерфейсы, обеспечивающие работу с? сконструированными типами данных 
   // и перечислениями
   
   interface DynFixed : DynAny 
   {
      string get_value();
      boolean set_value(in string val) raises(TypeMismatch, InvalidValue);
   };
   
   interface DynEnum : DynAny 
   {
      string get_as_string();
      void set_as_string(in string value) 
         raises(InvalidValue);
      unsigned long get_as_ulong();
      void set_as_ulong(in unsigned long value) 
         raises(InvalidValue);
   };
   ...
   interface DynStruct : DynAny { ... };
   interface DynUnion : DynAny { ... };
   interface DynSequence : DynAny { ... };
   interface DynArray : DynAny { ... };
   interface DynValue : DynAny { ... };
   
   // Интерфейс для создания объектов типа DynAny
   
   interface DynAnyFactory 
   {
      exception InconsistentTypeCode {};
      
      DynAny create_dyn_any(in any value) raises(InconsistentTypeCode);
      DynAny create_dyn_any_from_type_code(in 
         CORBA::TypeCode type) raises(InconsistentTypeCode);
   };
   ...
};

Способ получения интерфейса DynAnyFactory и простые примеры работы с DynAny будут рассмотрены ниже.

Более подробное рассмотрение этой темы выходит за рамки статьи. Исчерпывающее описание можно найти в документе 99-07-13.pdf на сайте www.omg.org.

Некоторые IDL-типы,
используемые при работе с DII/DSI

Практически все рассматриваемые в этой главе IDL-объекты являются псевдо-объектами, и на практике гораздо важнее знать, во что превращается данный набор IDL-объявлений при работе с C++ или Java, чем помнить само IDL-объявление. Вследствие этого, мы рассмотрим и IDL-декларации, и их отображения на C++.

NamedValue

Структура NamedValue используется для задания аргументов удаленных методов в динамическом режиме. В принципе информация, которая хранится в такой структуре, соответствует информации, известной компилятору С++, Java или других языков при использовании статических стабов. Тип NamedValue предназначен для использования исключительно как элемент NVList.

module CORBA
{
...
   typedef unsigned long Flags;
   typedef string Identifier;
   const Flags ARG_IN = 1;
   const Flags ARG_OUT = 2;
   const Flags ARG_INOUT = 3;
   const Flags CTX_RESTRICT_SCOPE = 15;
   
   struct NamedValue {       // PIDL
      Identifier name;     // имя аргумента
      any argument;     // значение аргумента
      Flags arg_modes;    // признак вида аргумента
   };
};
Отображение на C++

Согласно спецификации OMG, С++-аналог IDL-типа NamedValue выглядит так:

class NamedValue
{
public:
   const char *name() const;
   Any *value() const;
   Flags flags() const;
};
Отображение для VisiBroker for C++

Реализации Visigenic/INPRISE, в общем, следует спецификации OMG, но при этом добавляет несколько полезных методов (далее мы будем приводить отображение для VisiBroker). Текст находится в файле ...vbroker\include\nameval.h. Ниже приведены наиболее важные фрагменты:

class CORBA_NamedValue : private VISResource
{
private:
   CORBA_NamedValue();
   ~CORBA_NamedValue();
   CORBA_NamedValue (const CORBA_NamedValue& ) {}
   CORBA_NamedValue& operator=(const CORBA_NamedValue&);
   
   CORBA::String_var _name;
   CORBA_Any_var _value;
   CORBA::Flags _flags;
public: 
   const char *name() const { return _name; }
   CORBA_Any *value() { return _value; }
   CORBA::Flags flags() const { return _flags; }
   
   static CORBA_NamedValue_ptr _duplicate(CORBA_NamedValue_ptr nv);
   static CORBA_NamedValue_ptr _nil() 
   { return (CORBA_NamedValue*)NULL; }
   static void _release (CORBA_NamedValue_ptr ptr);
   ...
};

Обратите внимание на то, что деструктор объявлен как private-метод. Это классический способ гарантировать в С++ использование только динамических объектов такого типа.

Вы можете, как всегда, использовать типы NamedValue_ptr и NamedValue_var.

NVList

Этот псевдо-объект IDL предназначен для хранения списка аргументов.

module CORBA
{
   ...
   pseudo interface NVList
   {
      readonly attribute unsigned long count;
      
      NamedValue add(in Flags flags);
      NamedValue add_item (in Identifier item_name, 
         in Flags flags);
      NamedValue add_value (in Identifier item_name, 
         in any val, in Flags flags);
      
      NamedValue item(in unsigned long index) raises(Bounds);
      void remove(in unsigned long index) raises(Bounds);
   };
};

Фабрикой для NVList является ORB.

module ORB //PIDL
{
   ...
   void create_list(
      in long count, // Число элементов в списке
      out NVList new_list); // Созданный список
};
Отображение для VisiBroker for C++
class CORBA_NVList : private VISResource
{
public:
   class Bounds: public CORBA_UserException { ... };
   
   CORBA::Long count() const { return _count; }
   
   CORBA_NamedValue_ptr add(CORBA::Flags);
   CORBA_NamedValue_ptr add_item(const char *name, 
      CORBA::Flags flags);
   CORBA_NamedValue_ptr add_value(const char *name, 
      const CORBA_Any& any, 
      CORBA::Flags flags);
   CORBA_NamedValue_ptr add_item_consume(char *name, CORBA::Flags);
   CORBA_NamedValue_ptr add_value_consume(char *name, 
      CORBA_Any *value,
      CORBA::Flags flags);
   
   CORBA_NamedValue_ptr item(CORBA::ULong index);
   void remove(CORBA::ULong index);
   
   // Следующий метод оставлен для совместимости с ранними версиями
   void free_out_memory();
   
   static CORBA_NVList_ptr _duplicate(CORBA_NVList_ptr ptr);
   static CORBA_NVList_ptr _nil() { return (CORBA_NVList_ptr)NULL; }
   static void _release(CORBA_NVList_ptr ptr);
   
   CORBA_NVList(CORBA::ULong initial_size=16);
   ~CORBA_NVList();
   private:
      CORBA_NVList(const CORBA_NVList& );
      CORBA_NVList& operator=(const CORBA_NVList& );
      ...
};

Операции с суффиксом _consume соответствуют новому стилю управления памятью, т.е. создаваемый элемент берет на себя управление памятью для имен и значений. Это означает, что список создает копию создаваемого элемента и хранит эту копию.

Обратите внимание на наличие public-конструктора и деструктора.

Environment

Этот тип предназначен для управления исключениями при работе с DII, точнее, для хранения и управления объектом типа «исключение». Программист устанавливает исключительную ситуацию (как объект) или получает к нему доступ с помощью атрибута exception, который в обычном стиле CORBA реализован с помощью пары set/get- методов.

Этот тип очень важен, так возникновение исключительной ситуации при использовании DII-вызовов отслеживается путем явного анализа присутствия (или отсутствия) исключения как объекта, который «упакован» в объект CORBA::Request. Поскольку каждый конкретный вызов может приводить к появлению только одной исключительной ситуации (из некоторого множества возможных для данного метода), то объект Request содержит единственный подобъект типа Environment, который, в свою очередь, может содержать только одно исключение.

Проверка, нее возникло ли исключения при вызове, выполняется путем вызова get-метода exception(). Возврат нулевой ссылки (NULL для C++) говорит о том, что при выполнении кода удаленного метода исключительной ситуации не возникло.

pseudo interface Environment
{
   attribute Exception exception;
   void clear();
};

Задание в качестве параметра при вызове set-метода exception() нулевой ссылки эквивалентно вызову метода clear(), т.е. сбросу установленного исключения.

Отображение для VisiBroker for C++
class CORBA_Environment
{
private:
   CORBA_Exception_var    _exception;
public:
   CORBA_Environment() {}
   ~CORBA_Environment() {}
   
   CORBA::Exception *exception() const { return _exception; }
   void  exception(CORBA::Exception *exp) { _exception = exp; }
   void  clear() { _exception = (CORBA_Exception *)NULL; }
   
   static CORBA_Environment_ptr _duplicate(CORBA_Environment_ptr env);
   static CORBA_Environment_ptr _nil() { return (CORBA_Environment_ptr)NULL; }
   static CORBA_Environment& current_environment();
};

При вызове set-метода exception() объект типа Environment становится владельцем передаваемого ему объекта типа «исключительная ситуация», причем он предполагает, что этот объект создан с помощью операции new. Get-метод exception(), возвращая объект, не передает пользователю «владение» им – по-прежнему за удаление этого объекта отвечает Environment, и вы НЕ должны вызывать функцию delete для результата get-метода exception().

Интерфейс ExceptionList

Объект такого типа позволяет хранить список объектов типа TypeCode, рассматриваемый как список признаков типов исключительных ситуаций, которые могут быть сопоставлены с некоторым удаленным методом.

module CORBA
{
   ...
   pseudo interface ExceptionList
   {
      readonly attribute unsigned long count;
      void add(in TypeCode exc);
      TypeCode item(in unsigned long index) raises(Bounds);
      void remove(in unsigned long index) raises(Bounds);
   };
};

Смысл методов этого интерфейса абсолютно очевиден.

На C++ на него основе будет сгенерирован примерно следующий класс:

module CORBA 
{
   ...
   class ExceptionList
   {
   public:
      ULong count();
      void add(TypeCode_ptr tc);
      void add_consume(TypeCode_ptr tc);
      TypeCode_ptr item(ULong index);
      void remove(ULong index);
   };
};

DII

Спецификация DII содержится в документе 99-07-11.pdf. Автор рекомендует знакомиться с вопросами, имеющими отношение к DII, по спецификациям отображения IDL на конкретный язык программирования – они (по крайней мере, на момент написания статьи) содержат более свежую и полную информацию).

Термин «DII» относится к совокупности интерфейсов, методов, типов данных и пр., которые обеспечивают динамическое (т.е. в процессе работы программы) формирование запроса, который стандартным образом передается ORB, а затем и компонентам CORBA на стороне сервера для его выполнения и, возможно, возврата результата. Концептуально и динамический, и статический способ формирования запроса (более конкретно, объекта типа CORBA::Request) ничем не отличаются друг от друга, просто в одном случае программист вызывает те или иные методы, формирующие данный объект, а в другом эту работу выполняет сгенерированный компилятором с IDL некий код в клиентском стабе.

Для использования DII (и DSI) программист должен свободно ориентироваться в средствах динамического создания как новых типов (интерфейс TypeCode), так и данных таких типов (интерфейсы any и DynAny и производные от него интерфейсы - DynStruct, DynArray и др.)...

Объект CORBA::Request

Объект Request играет главную роль при вызове удаленных методов в режиме DII. Он содержит все необходимые свойства и методы, чтобы выполнить следующие действия (предположим, что этот объект уже тем или иным образом создан):

Ниже приведена IDL-декларация для интерфейса Request. Как и большинство ранее рассмотренных объявлений, это псевдо-IDL, и отображения на конкретные языки программирования имеют многочисленные особенности.

module CORBA 
{
   pseudo interface ExceptionList { ... }
   pseudo interface ContextList { ... }
   
   pseudo interface Request
   {
      readonly attribute Object target; // серверный объект
      readonly attribute Identifier operation; // имя вызываемого 
      // метода
      readonly attribute NVList arguments; // список аргументов
      readonly attribute NamedValue result; // значение результата
      readonly attribute Environment env; // исключение (если 
      // было возбуждено)
      readonly attribute ExceptionList exceptions; // список возможных 
      // исключений
      readonly attribute ContextList contexts; // список свойств
      // контекста
      attribute context ctx;
      
      void invoke();
      void send_oneway();
      void send_deferred();
      void get_response();
      boolean poll_response();
   };
};

Рассмотрению методов собственно выполнения запроса посвящен отдельный раздел. Смысл атрибутов этого объекта, наверное, в объяснениях не нуждается.

Отображение Request на C++

Одной из важных (и очень приятных) особенностей отображения IDL для интерфейса Request на C++ является добавление большого количества вспомогательных и очень удобных для работы методов – методов формирования списка аргументов.

class Request
{
public:
   Object_ptr target() const;
   const char *operation() const;
   NVList_ptr arguments();
   NamedValue_ptr result();
   Environment_ptr env();
   ExceptionList_ptr exceptions();
   ContextList_ptr contexts();
   void ctx(Context_ptr);
   Context_ptr ctx() const;
   
   Any &add_in_arg();
   Any &add_in_arg(const char* name);
   Any &add_inout_arg();
   Any &add_inout_arg(const char* name);
   Any &add_out_arg();
   Any &add_out_arg(const char* name);
   
   void set_return_type(TypeCode_ptr tc);
   Any &return_value();
   
   void invoke();
   void send_oneway();
   void send_deferred();
   void get_response();
   Boolean poll_response();
};

Как всегда, для VisiBroker предусмотрены три дополнительные статические функции – _duplicate(), _release() и _nil().

Для объекта Request определены специальные правила управления памятью для всех методов, сгенерированных для IDL атрибутов, а именно, программист никогда не должен пытаться применять операцию delete (или ее аналоги) к результате вызова методов target(), operation(), arguments(), result(), env(), exceptions(), contexts() и ctx().

Если при формировании списка ExceptionList() вы использовали методы с суффиксом _consume, то список создает копии объектов TypeCode и хранит эти копии. Более того, реализация может после создания копии уничтожить оригинал, поэтому не рекомендуется при создании переносимых программ обращаться к объектам, которые являлись аргументами метода add_consumer().

Методы add_XX_arg() позволяют легко добавлять в список новые аргументы. Вместо получения объекта «список аргументов» с помощью вызова arguments(), а затем использования метода NVList::add(), например, так:

*(request->arguments()->add(ARG_IN)->value()) <<= anArg;

можно сразу обратиться к методам добавления аргументов непосредственно в классе Request:

request->add_in_arg() <<= anArg;

Создание объекта Request

Итак, мы уже немного научились работать с объектом Request, но до сих пор не знаем, как его создать. Стандартным методом, объявленным на уровне IDL, является метод Object::create_request() (об интерфейсе CORBA::Object говорилось в разделе 6.4.1).

interface Object // PIDL
{
   ...
   void create_request (in Context ctx, // контекст для вызываемого метода
      in Identifier operation, 		// имя вызываемого метода
      in NVList arg_list, 		// список аргументов
      inout NamedValue result, 		// значение результата
      out Request request, 		// созданный объект типа Request
      in Flags req_flags 		// флаги для запроса
   );
};

При создании и заполнении списка аргументов (параметр arg_list) вы можете использовать любой из способов, о которых говорилось ранее – либо применять методы NVList::add, либо методы Request::add_XX_arg(), но не оба сразу. При использовании методов add_XX_arg() параметр arg_list предварительно должен быть установлен в значение NULL/null.

Наверное, единственный из оставшихся параметров, нуждающийся в дополнительных пояснениях – это параметр req_flags. Этот параметр лучше устанавливать в значение CORBA::OUT_LIST_MEMORY при наличии out-аргументов и не устанавливать в противном случае. При установленном значении OUT_LIST_MEMORY динамическая память, выделенная для хранений значения out-аргументов, освобождается вместе с уничтожением самого объекта NVList.

Использование C++

При отображении на С++ предусмотрены две версии метода Object::create_request():

void Object::_create_request(Context_ptr ctx,
                             const char *operation,
                             NVList_ptr arg_list,
                             NamedValue_ptr result,
                             Request_out request,
                             Flags req_flags
                             );
void Object::_create_request(Context_ptr ctx,
                             const char *operation,
                             NVList_ptr arg_list,
                             NamedValue_ptr result,
                             ExceptionList_ptr,
                             ContextList_ptr,
                             Request_out request,
                             Flags req_flags
                             );

Вы можете выбрать любую из них, но на практике программисты на C++ обычно используют дополнительный метод, который отсутствует на уровне IDL, но является частью правил отображения интерфейса Object на C++. Это метод Object::_request(). Единственным его аргументом является строка, содержащая имя вызываемого метода серверного объекта, например:

MyInterface_var oRef = ...;
CORBA::Request_var request = oRef->_request (“my_operation”);

После этого происходит создание списка аргументов, собственно вызов удаленного метода и анализ результата.

Вызовы удаленных методов

В настоящий момент, пока (вместе с CORBA 3.0) не утверждена спецификация Messaging Service – спецификация асинхронного взаимодействия клиентов и серверов - для статического режима CORBA возможен только синхронный вызов удаленных методов (oneway-методы мы по ряду причин в расчет не принимаем). Это означает, что поток клиента, из которого был выполнен удаленный запрос, блокируется до завершения этого запроса. Конечно, такой способ далеко не всегда удобен (а иногда и просто неприемлем). Опытный программист может возразить, что такую проблему решить не составляет труда: нужно просто создать отдельный поток и вызвать синхронный метод из него - пусть ждет завершения, сколько надо. Тем не менее, желательно иметь более простые средства выполнения асинхронных вызовов и обработки их результатов. Сейчас это возможно только при использовании DII.

Спецификация предусматривает несколько методов выполнения асинхронных вызовов. Ниже приводятся соответствующие фрагменты IDL-деклараций:

module CORBA {
   ...
   interface Request;
   typedef sequence <Request> RequestSeq;
   
   interface Request 
   {
      void invoke();
      void send_oneway();
      void send_deferred();
      void get_response();
      boolean poll_response();
      ...
   };
   void send_multiple_requests_oneway(in RequestSeq req);
   void send_multiple_requests_deferred(in RequestSeq req);
   
   boolean poll_next_response();
   void get_next_response(out Request req) raises (WrongTransaction);
   ...
};

Метод Request::invoke() выполняет синхронный вызов. Последовательность действий (на C++) выглядит примерно так:

MyInterface_var oRef = ....;
CORBA::Any op1, op2, result;
op1 <<= CORBA::Long (1);
op2 <<= CORBA::Long (2);
CORBA::Request_var request = oRef->_request("operation_add");
request->add_in_arg() <<= op1;
request->add_in_arg() <<= op2;
request->set_return_type (CORBA::_tc_long);
request->invoke();
if(request->env()->exception() == 0) // проверка отсутствия возникновения исключения
   request->return_value() >>= result;
...

Метод Request::send_deferred() (и связанные с ним методы Request::get_response() и poll_response() предполагают другой подход:

...
CORBA::Request_var request = oRef->_request("operation_add");
...
request->send_deferred ();
while(!request->poll_response()) // цикл ожидания прихода ответа 
DoSomething();
request->get_response();
if(request->env()->exception() == 0) 
request->return_value() >>= result;

Периодически вызываемый метод Request::poll_response() возвращает true, если вызов удаленного метода завершен. В этом случае вы вызываете метод Request::get_response() и анализируете полученное значение. Если вы вызовете метод get_response() перед тем, как вызов удаленного метода будет завершен, то поток, обратившийся к этому методу, будет блокирован до получения ответа от сервера.

Спецификация OMG говорит, что результат повторного обращения к методу invoke() или send_deferred() не определен. Та же неопределенность возникает, если вы вызываете эти методы для объекта, который уже был использован в качестве аргумента при вызове метода CORBA::send_multiple_requests_deferred().

Методы
void send_multiple_requests_deferred(in RequestSeq req);
boolean poll_next_response();
void get_next_response(out Request req) raises (WrongTransaction);

позволяют посылать в асинхронном режиме и анализировать состояние сразу для нескольких объектов типа Request. Метод get_next_response() возвращает объект Request для очередного завершенного вызова. Порядок возврата значений зависит от того, какой из методов на стороне сервере был выполнен быстрее, поэтому программисту необходимо определить, ответом на какой запрос является полученный объект Request.

DSI

Спецификация DSI содержится в документе 99-07-12.pdf. Этот документ содержит всего 6 страниц. Как ни прост интерфейс DSI, этого все-таки маловато – автор рекомендует дополнительно обращаться к спецификациям отображения IDL на конкретные языки программирования.

Интерфейс DSI (конечно, «интерфейс» не в смысле «IDL-интерфейс») выполняет на стороне сервера ту же роль, что и интерфейс DII на стороне клиента. При получении клиентского запроса (неважно, каким образом сформированного) на стороне сервера выполняются вполне стандартные действия до тех пор, пока поученный запрос не «доберется» до нужного серванта. Каким образом – статически или динамически – обрабатывать этот запрос, программист решает на стадии создании класса серванта. При работе в статическом режиме вы используете сгенерированный компилятором с IDL класс скелетона, либо создавая на его основе класс серванта (в случае наследования), либо рассматривая этот класс скелетона как класс серванта (в случае делегирования). В обоих случаях вы пишете код для нескольких методов, определенных на уровне IDL.

DSI использует другой подход. Вы создаете класс серванта именно для динамической обработки запросов. Главная идея DSI – использование одного-единственного метода для обработки всех клиентских запросов, адресованных данному объекту. Такой метод (спецификация OMG называет его DIR – Dynamic Implementation Routine) вызывается самим ORB’ом. В качестве единственного INOUT-аргумента ему передается объект типа ServerRequest. Задача программиста – написать код для DIR, который и будет обслуживать все поступающие запросы клиентов.

Объект ServerRequest

Если при использовании DII вы работаете, в основном, с объектом CORBA::Request, то «сердцем» DSI является объект CORBA::ServerRequest. Программисту нет никакой необходимости создавать его - это задача ORB – поэтому использовать интерфейс ServerRequest даже проще, чем интерфейс Request.

Объект типа ServerRequest содержит всю нужную информацию о полученном вызове – имя метода, а также имена, типы и значения его аргументов. Задача программиста – считать значения аргументов типа IN и INOUT, выполнить необходимые действия, а затем установить значения выходных аргументов (типа INOUT и OUT), а также (если надо) значение результата. Кроме того, вы можете создать и поместить в объект ServerRequest объект типа «исключение».

module CORBA 
{
   ...
   interface ServerRequest // PIDL
   {
      readonly attribute Identifier operation;
      void arguments (inout NVList nv);
      Context ctx();
      void set_result(in Any val);
      void set_exception(in Any val);
   };
};

Единственное, что может вызвать некоторые вопросы – это метод arguments(). Вы вызываете это метод, чтобы считать в заранее подготовленный вами список аргументов их текущие значения. Аргумент nv типа NVList имеет атрибут INOUT – сначала вы считываете данные (вызывая метод arguments()), а затем тем или иным образом в этом же объекте устанавливаете значения выходных параметров.

Метод set_exception(), хотя формально получает аргумент любого типа, должен получать только значения типа «исключения» (другими словами, TCKind для TypeCode аргумента должен быть равен) tk_except. В противном случае при попытке установить значение исключения будет возбуждена исключительная ситуация CORBA::BAD_PARAM. Если вы установили исключение, которое не предусмотрено в списке исключений для данного метода, то спецификация разрешает как возбуждение исключения BAD_PARAM при попытке его установки в объекте ServerRequest, так и возбуждение ORB’ом исключения UNKNOWN_EXCEPTION на стороне клиента.

Отображение ServerRequest на C++

Спецификация OMG в качестве стандартного объявляет следующий код:

namespace CORBA
{
   ...
   class ServerRequest
   {
   public:
      const char* operation() const;
      void arguments (NVList_ptr& parameters);
      Context_ptr ctx();
      void set_result(const Any& value);
      void set_exception(const Any& value);
   };
};

Метод operation() возвращает имя вызванной клиентом операции. Если клиент вызвал операцию, используя не IDL-метод, а IDL-атрибут, то имя операции получается конкатенацией префиксов _get_ /_set_ и имени атрибута.

Здесь надо сказать несколько слов об особенностях отображения для этого интерфейса. Спецификация его сильно изменилась по сравнению с версией CORBA 2.0, в которой впервые появилась концепция DSI. Наличие этих изменений, хотя они и не носят принципиального характера (кроме схемы управления памятью), может вызвать затруднение у начинающего CORBA-программиста – реализации поддерживают как старые, так и новые методы, которые, хотя и делают, в общем, одно и то же, но почему-то имеют разные имена или другой способ передачи своих аргументов. Например, «старый» метод result() в качестве аргумента получает указатель на CORBA::Any, а его «новый» аналог - set_result() – сам объект CORBA::Any.

Реализация отображения при работе с VisiBroker выглядит так (полный текст вы можете найти в файле ...\vbroker\include\srequest.h):

class CORBA_ServerRequest : private VISResource
{
private:
   CORBA_ServerRequest (CORBA::MarshalInBuffer& strm, const char* operation,
      VISReplyHandler&);
   ~CORBA_ServerRequest();
   CORBA_ServerRequest(const CORBA_ServerRequest&) {}
   CORBA::ServerRequest& operator=(const CORBA::ServerRequest&);
   
   CORBA::String_var _operation;
   CORBA::Context_var _ctx;
   CORBA::NVList_var _params;
   CORBA::Any_var _result;
   CORBA::Environment _env;
   ...
public:
   CORBA::Context_ptr ctx();
   
   // Методы в "стиле CORBA 2.0"
   const char* op_name() const { return _operation; }
   void params (CORBA::NVList_ptr);
   void result (CORBA::Any_ptr);
   void exception(CORBA::Any_ptr exception);
   
   // Методы в "новом" стиле, ориентированные на поддержку POA
   const char *operation() const { return _operation; }
   void arguments(CORBA::NVList_ptr param) { params(param); }
   void set_result (const CORBA::Any& a) 
   { result(new CORBA::Any(a)); }
   void set_exception (const CORBA::Any& a) 
   { exception(new CORBA::Any(a)); }
   
   static CORBA::ServerRequest_ptr _duplicate(CORBA::ServerRequest_ptr req);
   static CORBA::ServerRequest_ptr _nil() { return 0; }
   static void _release(CORBA::ServerRequest_ptr ptr);
};

Простейший анализ приведенного фрагмента показывает, что в использовании «стиля CORBA 2.0» нет никакой необходимости.

И еще одно изменение по сравнению с CORBA 2.0: объект ServerRequest является владельцем своих объектов, поэтому не надо вызывать delete или free() для результатов методов op_name() и operation().

Тип серванта

Класс серванта, используемого при работе в режиме DSI, должен быть создан как класс, производный либо от CORBA::DynamicImplementation (этот подход объявлен устаревшим), либо от PortableServer::DynamicImplementation. Для пользователя особо большой разницы нет – и в том, и в другом случае необходимо знать (и явно указать) RepositoryID для самого производного в иерархии наследования интерфейса (при работе с C++)., только сделано это немного по-разному. При использовании CORBA::DynamicImplementation вы указываете RepositoryID (и некоторые другие параметры) как аргументы конструктора, а при использовании PortableServer::DynamicImplementation вы должны вернуть нужные значения как результат метода, объявленного в C++ как чисто виртуальный.

Разумеется, при создании новых программ следует использовать класс PortableServer::DynamicImplementation, а не CORBA::DynamicImplementation Поскольку этот класс является классом серванта, т.е. заведомо зависит от языка программирования, то для него не существует IDL-объявления и спецификация определена на уровне конкретного языка программирования.

CORBA не предусматривает никаких специальных процедур регистрации DSI-серванта – создание сервантов и активация CORBA-объектов никак не зависят от того, на базе какого класса создан ваш класс серванта. Все берет на себя объектный адаптер, т.е. POA.

PortableServer::DynamicImplementation
Использование C++
namespace PortableServer
{
   class DynamicImplementation : public virtual ServantBase
   {
   public:
      Object_ptr _this();
      virtual void invoke (ServerRequest_ptr request) = 0;
      virtual RepositoryId _primary_interface(const ObjectId& oid, POA_ptr poa) = 0;
   };
   ...
}

Метод _this() возвращает объектную ссылку для объекта, которому адресован данный вызов. Вызывать этот метод можно в контексте обработки конкретного запроса, обслуживаемого в режиме DSI.

Для VisiBroker этот метод отсутствует в класса PortableServer::DynamicImplementation просто потому, что он уже реализован в его базовом классе – PortableServer::ServantBase.

Два остальных метода вы должны переопределить сами. Метод invoke() и есть та самая DIR, код которой реализует всю необходимую функциональность серверного объекта. Метод _primary_interface() должен возвращать «самый производный» в иерархии наследования интерфейс, методы которых реализованы в методе invoke().

Как invoke(), так и _primary_interface() НЕ должны вызываться явно программистом – результат такого вызова не определен.


Copyright © 1994-2016 ООО "К-Пресс"