Использование CORBA для объектного доступа к базе данныхВадим Годунко <[email protected]>, 2007 г. Начальная реализация сервераВ качестве примера будем разрабатывать интерфейс справочника пользователей. Каждый пользователь будет описываться одним объектом. Дополнительно потребуется объект‐фабрика для создания новых пользователей и объект‐поисковик для поиска в справочнике. Файл описания интерфейса users.idl будет выглядеть следующим образом: module Users { interface User { attribute wstring name; attribute wstring surname; }; typedef sequence<User> UserSequence; interface UserFactory { User create(in wstring name, in wstring surname); }; interface UserFinder { UserSequence by_name (in wstring name); }; }; Далее, с помощью программы трансляции IDL в Ada — idlac — сгенерируем необходимые клиентские, серверные файлы и заготовки реализации объектов: idlac -c users.idl idlac -s users.idl idlac -i users.idl Теперь всё готово для написания сервера. Объекты конечно не смогут выполнять свои функции, но сервер будет запускаться создавать необходимые объекты и ожидать запросов. В процессе совершенствования программы приведённый ниже код останется практически неизменным. with Ada.Text_IO; with CORBA.Object; with CORBA.ORB; with PortableServer.POA.Helper; with PortableServer.POAManager; with PolyORB.Setup.No_Tasking_Server; -- Конфигурирование PolyORB для работы в режиме обычного многозадачного -- сервера. with Users.UserFactory.Impl; with Users.UserFinder.Impl; procedure Server is begin -- Инициализация ORB. declare Argv : CORBA.ORB.Arg_List := CORBA.ORB.Command_Line_Arguments; begin CORBA.ORB.Init (CORBA.ORB.To_CORBA_String ("ORB"), Argv); end; declare Root_POA : PortableServer.POA.Local_Ref; begin -- Получение ссылки на корневой объектный адаптер. Root_POA := PortableServer.POA.Helper.To_Local_Ref (CORBA.ORB.Resolve_Initial_References (CORBA.ORB.To_CORBA_String ("RootPOA"))); -- Активация корневого объектного адаптера. PortableServer.POAManager.Activate (PortableServer.POA.Get_The_POAManager (Root_POA)); -- Создание объекта-фабрики и объектной ссылки на этот объект. declare Ref : CORBA.Object.Ref; begin Ref := PortableServer.POA.Servant_To_Reference (Root_POA, new Users.UserFactory.Impl.Object); -- Вывод на экран сформированной объектной ссылки. Ada.Text_IO.Put_Line ("'" & CORBA.To_Standard_String (CORBA.Object.Object_To_String (Ref)) & "'"); end; -- Создание объекта-поисковика и объектной ссылки на этот объект. declare Ref : CORBA.Object.Ref; begin Ref := PortableServer.POA.Servant_To_Reference (Root_POA, new Users.UserFinder.Impl.Object); -- Вывод на экран сформированной объектной ссылки. Ada.Text_IO.Put_Line ("'" & CORBA.To_Standard_String (CORBA.Object.Object_To_String (Ref)) & "'"); end; end; -- Передача нити главной подпрограммы в ведение ORB. CORBA.ORB.Run; end Server; Собрать программу сервера можно командой: gnatmake server.adb `polyorb-config` Для исключения иногда возникающих проблем с отсутствием настройки протокола группового вещания и экспериментальными функциями PolyORB содадим файл polyorb.conf следующего содержания: ## Управление точками доступа. [access_points] srp=disable soap=disable iiop=enable iiop.ssliop=disable diop=disable uipmc=disable ## Управление поддерживаемыми протоколами. [modules] binding_data.srp=disable binding_data.soap=disable binding_data.iiop=enable binding_data.iiop.ssliop=disable binding_data.diop=disable binding_data.uipmc=disable Теперь можно запустить программу и получить что‐то подобное: $ server 'IOR:01aaaaaa1a00000049444c3a55736572732f55736572466163746f72793a312e3000aaaa0 1000000000000005c000000010102aa0a0000003132372e302e302e3100e6841b0000002f30303 03030303031315463663966653565386331623431643032aa01000000010000001c00000001aaa aaa010001000000000000010100020000000101010002010100' 'IOR:01aaaaaa1900000049444c3a55736572732f5573657246696e6465723a312e3000aaaaaa0 1000000000000005c000000010102aa0a0000003132372e302e302e3100e6841b0000002f30303 03030303031325463663966653565386331623431643032aa01000000010000001c00000001aaa aaa010001000000000000010100020000000101010002010100' Две длинные строки непонятных символов есть текстовое представление объектных ссылок. В нашем случае первая является объектной ссылкой на объект‐фабрику, а вторая — на объект‐поисковик. po_catrefТекстовое предстваление объектных ссылок совершенно не предназначено для использования человеком, хотя и несёт в себе много полезной информации. Преобразовать объектную ссылку в понятный человеку вид может программа po_catref. В нашем примере мы получми следующую информацию: $ po_catref 'IOR:01aaaaaa1a00000049444c3a55736572732f55736572466163746f72793a3 12e3000aaaa01000000000000005c000000010102aa0a0000003132372e302e302e3100e6841b0 000002f3030303030303031315463663966653565386331623431643032aa01000000010000001 c00000001aaaaaa010001000000000000010100020000000101010002010100' Parsing stringified reference: IOR:01aaaaaa1a00000049444c3a55736572732f5573657 2466163746f72793a312e3000aaaa01000000000000005c000000010102aa0a0000003132372e3 02e302e3100e6841b0000002f3030303030303031315463663966653565386331623431643032a a01000000010000001c00000001aaaaaa010001000000000000010100020000000101010002010 100 Type Id: IDL:Users/UserFactory:1.0 Found: 1 profiles in IOR Profile number: 1 IIOP Version: 1.2 Host Name: localhost Address: 127.0.0.1 Family: FAMILY_INET Port: 34022 Object_Id: 2f3030303030303031315463663966653565386331623431643032 Tagged components: 1 Component #1: Tag: 1 Type: TAG_Code_Sets SNCS-C: 0x00010001; ISO 8859-1:1987; Latin Alphabet No. 1 SNCS-W: 0x00010100; ISO/IEC 10646-1:1993; UCS-2, Level 1 SCCS-W: 0x00010101; ISO/IEC 10646-1:1993; UCS-2, Level 2 SCCS-W: 0x00010102; ISO/IEC 10646-1:1993; UCS-2, Level 3 $ po_catref 'IOR:01aaaaaa1900000049444c3a55736572732f5573657246696e6465723a312 e3000aaaaaa01000000000000005c000000010102aa0a0000003132372e302e302e3100e6841b0 000002f3030303030303031325463663966653565386331623431643032aa01000000010000001 c00000001aaaaaa010001000000000000010100020000000101010002010100' Parsing stringified reference: IOR:01aaaaaa1900000049444c3a55736572732f5573657 246696e6465723a312e3000aaaaaa01000000000000005c000000010102aa0a0000003132372e3 02e302e3100e6841b0000002f3030303030303031325463663966653565386331623431643032a a01000000010000001c00000001aaaaaa010001000000000000010100020000000101010002010 100 Type Id: IDL:Users/UserFinder:1.0 Found: 1 profiles in IOR Profile number: 1 IIOP Version: 1.2 Host Name: localhost Address: 127.0.0.1 Family: FAMILY_INET Port: 34022 Object_Id: 2f3030303030303031325463663966653565386331623431643032 Tagged components: 1 Component #1: Tag: 1 Type: TAG_Code_Sets SNCS-C: 0x00010001; ISO 8859-1:1987; Latin Alphabet No. 1 SNCS-W: 0x00010100; ISO/IEC 10646-1:1993; UCS-2, Level 1 SCCS-W: 0x00010101; ISO/IEC 10646-1:1993; UCS-2, Level 2 SCCS-W: 0x00010102; ISO/IEC 10646-1:1993; UCS-2, Level 3 Как видно из примеров, объектная ссылка состоит из профайлов. В наших примерах всегда будет только один профайл, но это совершенно не обязательно. Каждый профайл в свою очередь определяет используемый для взаимодействия с объектом протокол (у нас всегда будет IIOP), адресную информацию точки доступа TCP/IP, внутренний ключ объекта и дополнительную информацию в виде тэговых компонентов. Наибольший интерес будет представлять адресная информация: точка доступа и ключ объекта. Точка доступа описывает характеристики используемого транспортного протокола, в нашем случае это TCP/IP. Ключ объекта представляет уникальный идентификатор объекта внутри программы сервера. Именно по ключу объектна ORB различает различные объекты внутри себя. Можно заметить, что информация точки доступа в обоих объектных ссылках одинакова, а объектные ключи — различаются. Если поэксперементировать с перезапуском сервера, то можно заметить, что каждый раз и точка доступа и объектные ключи будут меняться. Доработка сервераТеперь можно реализовать основные функции сервера. Для этого достаточно заполнить сгенерированные idlac‐ом шаблоны реализации. Кроме этого дополнительно добавлен пакет Globals в котором хранится список объектных ссылок всех созданных пользователей. Далее приводится полный текст всех изменённых файлов с необходимыми комментариями. with Ada.Containers.Vectors; with Users.User; package Globals is package User_Vectors is new Ada.Containers.Vectors (Positive, Users.User.Ref, Users.User."="); Users : User_Vectors.Vector; end Globals; with CORBA; with PortableServer; package Users.User.Impl is type Object is new PortableServer.Servant_Base with private; type Object_Ptr is access all Object'Class; function Get_name (Self : access Object) return CORBA.Wide_String; procedure Set_name (Self : access Object; To : in CORBA.Wide_String); function Get_surname (Self : access Object) return CORBA.Wide_String; procedure Set_surname (Self : access Object; To : in CORBA.Wide_String); function New_User (Name : in CORBA.Wide_String; Surname : in CORBA.Wide_String) return Object_Ptr; private type Object is new PortableServer.Servant_Base with record Name : CORBA.Wide_String; Surname : CORBA.Wide_String; end record; end Users.User.Impl; with Users.User.Skel; -- Осуществляем подключение файла скелетона - специального кода для -- преобразования запростов в вызовы подпрограмм. package body Users.User.Impl is function Get_name (Self : access Object) return CORBA.Wide_String is begin return Self.Name; end Get_name; procedure Set_name (Self : access Object; To : in CORBA.Wide_String) is begin Self.Name := To; end Set_name; function Get_surname (Self : access Object) return CORBA.Wide_String is begin return Self.Surname; end Get_surname; procedure Set_surname (Self : access Object; To : in CORBA.Wide_String) is begin Self.Surname := To; end Set_surname; function New_User (Name : in CORBA.Wide_String; Surname : in CORBA.Wide_String) return Object_Ptr is begin return new Users.User.Impl.Object' (PortableServer.Servant_Base with Name => name, Surname => surname); end New_User; end Users.User.Impl; with CORBA; with PortableServer; with Users.User; package Users.UserFactory.Impl is type Object is new PortableServer.Servant_Base with private; type Object_Ptr is access all Object'Class; function create (Self : access Object; name : in CORBA.Wide_String; surname : in CORBA.Wide_String) return Users.User.Ref; private type Object is new PortableServer.Servant_Base with null record; end Users.UserFactory.Impl; with Ada.Characters.Conversions; with Ada.Wide_Text_IO; with CORBA.ORB; with PortableServer.POA.Helper; with Globals; with Users.User.Impl; with Users.User.Helper; with Users.UserFactory.Skel; -- Осуществляем подключение файла скелетона - специального кода для -- преобразования запростов в вызовы подпрограмм. package body Users.UserFactory.Impl is function create (Self : access Object; name : in CORBA.Wide_String; surname : in CORBA.Wide_String) return Users.User.Ref is Root_POA : PortableServer.POA.Local_Ref; Result : Users.User.Ref; begin -- Получение ссылки на корневой объектный адаптер. Root_POA := PortableServer.POA.Helper.To_Local_Ref (CORBA.ORB.Resolve_Initial_References (CORBA.ORB.To_CORBA_String ("RootPOA"))); -- Создание объекта и объектной ссылки на этот объект. Result := Users.User.Helper.To_Ref (PortableServer.POA.Servant_To_Reference (Root_POA, PortableServer.Servant (Users.User.Impl.New_User (name, surname)))); -- Вывод на экран сформированной объектной ссылки. Ada.Wide_Text_IO.Put_Line ("User (" & CORBA.To_Wide_String (name) & ", " & CORBA.To_Wide_String (surname) & ") created. IOR '" & Ada.Characters.Conversions.To_Wide_String (CORBA.To_Standard_String (CORBA.Object.Object_To_String (Result))) & "'"); -- Добавление в список пользователей. Globals.User_Vectors.Append (Globals.Users, Result); return Result; end create; end Users.UserFactory.Impl; with CORBA; with PortableServer; package Users.UserFinder.Impl is type Object is new PortableServer.Servant_Base with private; type Object_Ptr is access all Object'Class; function by_name (Self : access Object; name : in CORBA.Wide_String) return Users.UserSequence; private type Object is new PortableServer.Servant_Base with null record; end Users.UserFinder.Impl; with Globals; with Users.User; with Users.UserFinder.Skel; -- Осуществляем подключение файла скелетона - специального кода для -- преобразования запростов в вызовы подпрограмм. package body Users.UserFinder.Impl is function by_name (Self : access Object; name : in CORBA.Wide_String) return Users.UserSequence is use type CORBA.Wide_String; use type Globals.User_Vectors.Cursor; Result : Users.UserSequence; Cursor : Globals.User_Vectors.Cursor := Globals.User_Vectors.First (Globals.Users); begin while Cursor /= Globals.User_Vectors.No_Element loop if Globals.User_Vectors.Element (Cursor).Get_name = name then Users.Append (Result, Users.User.Convert_Forward.To_Forward (Globals.User_Vectors.Element (Cursor))); end if; Globals.User_Vectors.Next (Cursor); end loop; return Result; end by_name; end Users.UserFinder.Impl; |