![]() Проекты на Аде Уроки Ады Документация Конференции Разработки Примеры Каталог ПО Полезные ссылки Поиск в Сети Скачать Прочее |
Вечная жизнь. Воскрешение объектаВадим Годунко <vgodunko@gmail.com>, 2007-2008 г. Создав объект-пользователя с помощью программы-клиента и сохранив ссылку в файле, после перезапуска клиента и сервера обращение по этой ссылке теперь даёт не ошибку связи, а ошибку отсутствия объекта. Т.е. ограничение на срок действия объектной ссылки снято, но объект был уничтожен при остановке сервера и необходимо научить сервер воскрешать объект по требованию пользователя. Но как можно понять какой объект необходимо воскресить? Где взять информацию для идентификации объектов на стороне сервера? Объектная ссылка хотя и содержит в себе информацию о доступе к объекту, но в виду своего строения и назначения совершенно непригодна для идентификации объектов. Для однозначной идентификации объектов на стороне сервера переносимым способом имеется возможность включения в состав объектного ключа объектной ссылки дополнительной пользовательской информации. Научно это называется назначением объектного ключа пользователем. Первым делом необходимо настроить объектный адаптер соответствующим образом, а именно, задать для политики IdAssignmentPolicy значение USER_ID, а для политики ImplicitActivationPolicy значение NO_IMPLICIT_ACTIVATION (последнее необходимо в связи с тем, что используемое по умолчанию значение IMPLICIT_ACTIVATION несовместимо со значением USER_ID для политики IdAssignmentPolicy). Код сервера примет следующий вид:
with Ada.Text_IO;
with CORBA.Object;
with CORBA.ORB;
with CORBA.Policy;
with PortableServer.IdAssignmentPolicy;
with PortableServer.ImplicitActivationPolicy;
with PortableServer.LifespanPolicy;
with PortableServer.POA.Helper;
with PortableServer.POAManager;
with PolyORB.Setup.No_Tasking_Server;
-- Конфигурирование PolyORB для работы в режиме обычного многозадачного
-- сервера.
with Globals;
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
Policies : CORBA.Policy.PolicyList;
Lifespan : PortableServer.LifespanPolicy.Ref
:= PortableServer.POA.Create_Lifespan_Policy
(PortableServer.PERSISTENT);
Id_Assignment : PortableServer.IdAssignmentPolicy.Ref
:= PortableServer.POA.Create_Id_Assignment_Policy
(PortableServer.USER_ID);
Activation : PortableServer.ImplicitActivationPolicy.Ref
:= PortableServer.POA.Create_Implicit_Activation_Policy
(PortableServer.NO_IMPLICIT_ACTIVATION);
begin
CORBA.Policy.IDL_SEQUENCE_Policy.Append
(Policies, CORBA.Policy.Ref (Lifespan));
CORBA.Policy.IDL_SEQUENCE_Policy.Append
(Policies, CORBA.Policy.Ref (Id_Assignment));
CORBA.Policy.IDL_SEQUENCE_Policy.Append
(Policies, CORBA.Policy.Ref (Activation));
Globals.User_POA :=
PortableServer.POA.Local_Ref
(PortableServer.POA.Create_POA
(Root_POA,
CORBA.To_CORBA_String ("UserPOA"),
PortableServer.POA.Get_The_POAManager (Root_POA),
Policies));
end;
-- Создание объекта-фабрики и объектной ссылки на этот объект.
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;Теперь необходимо изменить код фабрики объектов в части создания объектных ссылок и регистрации сервантов объектов. Один вызов подпрограммы PortableServer.POA.Servant_To_Reference предётся заменить двумя вызовами: подпрограммы PortableServer.POA.Activate_Object_With_Id для активации объекта с задаваемым пользователем идентификатором; и подпрограммы PortableServer.POA.Create_Reference_With_Id для создания объектной ссылки. Код фабрики будет выглядеть следующим образом:
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
-- Создание объекта и объектной ссылки на этот объект.
PortableServer.POA.Activate_Object_With_Id
(Globals.User_POA,
PortableServer.String_To_ObjectId
(Integer'Image
(Integer (Globals.User_Vectors.Length (Globals.Users)) + 1)),
PortableServer.Servant (Users.User.Impl.New_User (name, surname)));
Result :=
Users.User.Helper.To_Ref
(PortableServer.POA.Create_Reference_With_Id
(Globals.User_POA,
PortableServer.String_To_ObjectId
(Integer'Image
(Integer (Globals.User_Vectors.Length (Globals.Users)) + 1)),
CORBA.To_CORBA_String (Users.User.Repository_Id)));
-- Вывод на экран сформированной объектной ссылки.
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;Пытливый читатель может убедиться, что объектный ключ, выводимый программой po_catref, в объектной ссылке снова изменился. Во всём остальном поведение программы осталось неизменным. Теперь всё готово к следующему важному этапу на пути к вечной жизни - подключению пользовательского механизма воскрешения объектов - а именно, написанию и регистрации менеджера сервантов. Менеджер сервантов это специальный локальный CORBA объект, закреплённый за объектным адаптером, к которому обращается объектный адаптер в случае невозможности найти сервант объекта. Напишем сначала соответствующий файл CORBA IDL:
import ::PortableServer;
local interface Activator : PortableServer::ServantActivator {};сгенерируем все необходимые файлы: idlac activator.idl `polyorb-config --idls` и создадим файлы спецификации и реализации пакета реализации менеджера сервантов. Файл спецификации будет выглядеть следующим образом:
with PortableServer.ServantActivator.Impl;
package Activator.Impl is
type Object is new PortableServer.ServantActivator.Impl.Object with private;
type Object_Ptr is access all Object'Class;
function Incarnate
(Self : access Object;
Oid : in PortableServer.ObjectId;
Adapter : in PortableServer.POA_Forward.Ref)
return PortableServer.Servant;
private
type Object is
new PortableServer.ServantActivator.Impl.Object with null record;
function Is_A
(Self : access Object;
Logical_Type_Id : PolyORB.Std.String)
return Boolean;
end Activator.Impl;Файл реализации будет содержать заглушку реализации:
with Ada.Wide_Text_IO;
with CORBA;
with PortableServer.ServantActivator;
with PortableServer.ServantManager;
package body Activator.Impl is
function Incarnate
(Self : access Object;
Oid : in PortableServer.ObjectId;
Adapter : in PortableServer.POA_Forward.Ref)
return PortableServer.Servant
is
begin
Ada.Wide_Text_IO.Put_Line ("Incarnate");
return null;
end Incarnate;
function Is_A
(Self : access Object;
Logical_Type_Id : PolyORB.Std.String)
return Boolean
is
begin
return CORBA.Is_Equivalent
(Logical_Type_Id,
Activator.Repository_Id)
or else CORBA.Is_Equivalent
(Logical_Type_Id,
"IDL:omg.org/CORBA/Object:1.0")
or else CORBA.Is_Equivalent
(Logical_Type_Id,
PortableServer.ServantActivator.Repository_Id)
or else CORBA.Is_Equivalent
(Logical_Type_Id,
PortableServer.ServantManager.Repository_Id);
end Is_A;
end Activator.Impl;Единственная переопределённая диспетчеризируемая подпрограмма Incarnate как раз и будет вызываться объектным адаптером при отсутствии уже созданного объекта. По спецификации Incarnate должна вернуть указатель на экземпляр зарегистрированного в объектном адаптере серванта объекта или null, если по каким либо причинам создать объект не удалось. В параметре Oid будет передан объектный ключ объекта, который необходимо создать. Теперь пора настроить объектный адаптер. Для настройки необходимо задать значение USE_SERVANT_MANAGER для политики RequestProcessingPolicy и зарегистрировать менеджер сервантов вызовом PortableServer.POA.Set_Servant_Manager:
with Ada.Text_IO;
with CORBA.Object;
with CORBA.ORB;
with CORBA.Policy;
with PortableServer.IdAssignmentPolicy;
with PortableServer.ImplicitActivationPolicy;
with PortableServer.LifespanPolicy;
with PortableServer.POA.Helper;
with PortableServer.POAManager;
with PortableServer.RequestProcessingPolicy;
with PortableServer.ServantManager;
with PolyORB.Setup.No_Tasking_Server;
-- Конфигурирование PolyORB для работы в режиме обычного многозадачного
-- сервера.
with Activator.Impl;
with Globals;
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
Policies : CORBA.Policy.PolicyList;
Lifespan : PortableServer.LifespanPolicy.Ref
:= PortableServer.POA.Create_Lifespan_Policy
(PortableServer.PERSISTENT);
Id_Assignment : PortableServer.IdAssignmentPolicy.Ref
:= PortableServer.POA.Create_Id_Assignment_Policy
(PortableServer.USER_ID);
Activation : PortableServer.ImplicitActivationPolicy.Ref
:= PortableServer.POA.Create_Implicit_Activation_Policy
(PortableServer.NO_IMPLICIT_ACTIVATION);
Processing : PortableServer.RequestProcessingPolicy.Ref
:= PortableServer.POA.Create_Request_Processing_Policy
(PortableServer.USE_SERVANT_MANAGER);
begin
CORBA.Policy.IDL_SEQUENCE_Policy.Append
(Policies, CORBA.Policy.Ref (Lifespan));
CORBA.Policy.IDL_SEQUENCE_Policy.Append
(Policies, CORBA.Policy.Ref (Id_Assignment));
CORBA.Policy.IDL_SEQUENCE_Policy.Append
(Policies, CORBA.Policy.Ref (Activation));
CORBA.Policy.IDL_SEQUENCE_Policy.Append
(Policies, CORBA.Policy.Ref (Processing));
Globals.User_POA :=
PortableServer.POA.Local_Ref
(PortableServer.POA.Create_POA
(Root_POA,
CORBA.To_CORBA_String ("UserPOA"),
PortableServer.POA.Get_The_POAManager (Root_POA),
Policies));
-- Создание активатора и его регистрация в объектном адаптере.
declare
Ref : Activator.Local_Ref;
begin
Activator.Set (Ref, new Activator.Impl.Object);
PortableServer.POA.Set_Servant_Manager (Globals.User_POA, Ref);
end;
end;
-- Создание объекта-фабрики и объектной ссылки на этот объект.
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;Теперь можно собрать сервер, запустить его и, прочитав ссылку на объект-пользователя из файла, убедиться, что при попытке чтения значений атрибутов объекта на стороне сервера будет отображена строка "Incarnate", что означает, что объектный адаптер выполняет запрос на воскрешения не активного объекта. Осталось только научить наш менеджер сервантов создавать сервант и активировать его в объектном адаптере. Для активиции осуществляется вызов уже использовавшейся ранее подпрограммы PortableServer.POA.Activate_Object_With_Id, однако в отличии от кода фабрики нет необходимости создавать объектную ссылку - она и так уже известна клиенту. После модификации реализация менеджера сервантов будет выглядеть следующим образом:
with CORBA;
with PortableServer.POA;
with PortableServer.ServantActivator;
with PortableServer.ServantManager;
with Globals;
with Users.User.Impl;
package body Activator.Impl is
function Incarnate
(Self : access Object;
Oid : in PortableServer.ObjectId;
Adapter : in PortableServer.POA_Forward.Ref)
return PortableServer.Servant
is
Result : Users.User.Impl.Object_Ptr;
begin
Result
:= Users.User.Impl.Load_User
(Integer'Value (PortableServer.ObjectId_To_String (Oid)));
PortableServer.POA.Activate_Object_With_Id
(Globals.User_POA,
Oid,
PortableServer.Servant (Result));
return PortableServer.Servant (Result);
end Incarnate;
function Is_A
(Self : access Object;
Logical_Type_Id : PolyORB.Std.String)
return Boolean
is
begin
return CORBA.Is_Equivalent
(Logical_Type_Id,
Activator.Repository_Id)
or else CORBA.Is_Equivalent
(Logical_Type_Id,
"IDL:omg.org/CORBA/Object:1.0")
or else CORBA.Is_Equivalent
(Logical_Type_Id,
PortableServer.ServantActivator.Repository_Id)
or else CORBA.Is_Equivalent
(Logical_Type_Id,
PortableServer.ServantManager.Repository_Id);
end Is_A;
end Activator.Impl;Поскольку теперь необходимо ещё реально сохранять и восстанавливать состояние объектов, были измененый также и файлы реализации фабрики реализации объекта. Поскольку способ хранения выходит за пределы обсуждаемой темы, ниже приводятся все изменённые файлы без особых комментариев.
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 (Id : in Integer;
Name : in CORBA.Wide_String;
Surname : in CORBA.Wide_String)
return Object_Ptr;
function Load_User (Id : in Integer) return Object_Ptr;
private
type Object is new PortableServer.Servant_Base with record
Id : Integer;
Name : CORBA.Wide_String;
Surname : CORBA.Wide_String;
end record;
end Users.User.Impl;
with Ada.Strings.Wide_Unbounded.Wide_Text_IO;
with Ada.Wide_Text_IO;
with Users.User.Skel;
-- Осуществляем подключение файла скелетона - специального кода для
-- преобразования запростов в вызовы подпрограмм.
package body Users.User.Impl is
procedure Save (Self : in Object'Class);
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;
Save (Self.all);
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;
Save (Self.all);
end Set_surname;
function New_User (Id : in Integer;
Name : in CORBA.Wide_String;
Surname : in CORBA.Wide_String)
return Object_Ptr
is
Result : Object_Ptr
:= new Users.User.Impl.Object'
(PortableServer.Servant_Base with
Id => Id, Name => Name, Surname => Surname);
begin
Save (Result.all);
return Result;
end New_User;
function Load_User (Id : in Integer) return Object_Ptr is
File : Ada.Wide_Text_IO.File_Type;
Image : constant String := Integer'Image (Id);
Name : Ada.Strings.Wide_Unbounded.Unbounded_Wide_String;
Surname : Ada.Strings.Wide_Unbounded.Unbounded_Wide_String;
begin
Ada.Wide_Text_IO.Open
(File, Ada.Wide_Text_IO.In_File, Image (Image'First + 1 .. Image'Last));
Ada.Strings.Wide_Unbounded.Wide_Text_IO.Get_Line (File, Name);
Ada.Strings.Wide_Unbounded.Wide_Text_IO.Get_Line (File, Surname);
Ada.Wide_Text_IO.Close (File);
return
new Object'(PortableServer.Servant_Base with
Id => Id,
Name =>
CORBA.To_CORBA_Wide_String
(Ada.Strings.Wide_Unbounded.To_Wide_String (Name)),
Surname =>
CORBA.To_CORBA_Wide_String
(Ada.Strings.Wide_Unbounded.To_Wide_String
(Surname)));
end Load_User;
procedure Save (Self : in Object'Class) is
File : Ada.Wide_Text_IO.File_Type;
Image : constant String := Integer'Image (Self.Id);
begin
Ada.Wide_Text_IO.Create
(File,
Ada.Wide_Text_IO.Out_File,
Image (Image'First + 1 .. Image'Last));
Ada.Wide_Text_IO.Put_Line
(File, CORBA.To_Standard_Wide_String (Self.Name));
Ada.Wide_Text_IO.Put_Line
(File, CORBA.To_Standard_Wide_String (Self.Surname));
Ada.Wide_Text_IO.Close (File);
end Save;
end Users.User.Impl;
with Ada.Characters.Conversions;
with Ada.Integer_Wide_Text_IO;
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;
Id : Integer := 1;
begin
-- Загрузка идентификатора из файла. Если файл не существует - не
-- страшно, начинаем нумерацию с 1.
declare
File : Ada.Wide_Text_IO.File_Type;
begin
Ada.Wide_Text_IO.Open (File, Ada.Wide_Text_IO.In_File, "id");
Ada.Integer_Wide_Text_IO.Get (File, Id);
Ada.Wide_Text_IO.Close (File);
exception
when Ada.Wide_Text_IO.Name_Error =>
null;
end;
-- Создание объекта и объектной ссылки на этот объект.
PortableServer.POA.Activate_Object_With_Id
(Globals.User_POA,
PortableServer.String_To_ObjectId (Integer'Image (Id)),
PortableServer.Servant
(Users.User.Impl.New_User (Id, name, surname)));
Result :=
Users.User.Helper.To_Ref
(PortableServer.POA.Create_Reference_With_Id
(Globals.User_POA,
PortableServer.String_To_ObjectId (Integer'Image (Id)),
CORBA.To_CORBA_String (Users.User.Repository_Id)));
-- Сохранение идентификатора в файл.
declare
File : Ada.Wide_Text_IO.File_Type;
begin
Id := Id + 1;
Ada.Wide_Text_IO.Create (File, Ada.Wide_Text_IO.Out_File, "id");
Ada.Integer_Wide_Text_IO.Put (File, Id);
Ada.Wide_Text_IO.Close (File);
end;
-- Вывод на экран сформированной объектной ссылки.
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;Собрав сервер, запустив клиента и сервер можно убедиться, что все функции клиента теперь полностью работают, даже несмотря на возможные перебои в работе сервера. |
|