Сервер — это программа, предоставляющая некий сервис, удаленные объекты в случае с CORBA. Серверы CORBA могут активизироваться самостоятельно, будучи запущенными системным администратором либо при загрузке операционной системы. Также программист может зарегистрировать серверы CORBA с помощью утилит, после чего специальный демон будет отслеживать входящие запросы программ-клиентов и активизировать нужные серверы автоматически. Для этой цели в VisiBroker for C++ имеется OAD (Object Activation Daemon), в VisiBroker for Java — OADJ.
Собственно, написать сервер не так сложно, как это может показаться. Схематично процесс работы типичного CORBA-сервера описывается всего несколькими шагами:
Инициализация ORB нужна для создания коммуникационного канала между клиентом и объектами. Ниже приведен вариант инициализации для обеих версий VisiBroker.
Для Си++: CORBA::ORB_var orb = CORBA::ORB_init (argc, argv); Для Java: org.omg.CORBA.ORB orb = org.omg .CORBA.ORB.init(args,System. getProperties());
Объектный адаптер в CORBA играет особую роль. Это по сути координатор действий. Если ORB не отличается интеллектом и просто выполняет транспортные функции, то объектный адаптер занимается сложной работой по запуску и экспорту объектов, а также заведует информацией, хранящейся в репозитарии реализаций (implementation repository) — специальном хранилище данных, которым пользуется демон активизации объектов. Если сравнивать ORB с системной шиной компьютера, то объектный адаптер нечто вроде драйвера платы расширения.
В спецификации CORBA упоминаются два типа объектных адаптеров — основной (BOA) и переносимый (POA). Последний постепенно вытеснит BOA, но окончательно станет доступным лишь в четвертой версии VisiBroker. Так что мы пока будем использовать BOA.
Инициализация BOA выглядит так:
Для Си++: CORBA::BOA_var boa = orb->BOA_init(argc, argv); Для Java: org.omg.CORBA.BOA boa=((com.visigenic .vbroker.orb.ORB)orb). BOA_init();
Существуют разные методы инициализации BOA — с передачей параметров командной строки и без них, поэтому на примере выше показаны две разные вариации.
Перейдем к созданию собственно объекта.
Возможно, вы будете разочарованы, но объект создается тривиальным вызовом конструктора класса его серванта:
Для Си++: ServiceImpl implObject("ServiceObj"); Для Java: Test.Service implObject = new Test .ServiceImpl("ServiceObj");
Разница между реализациями на языках Cи++ и Java состоит лишь в способе его вызова. Язык Cи++ умеет создавать объекты на стеке, поэтому достаточно описать локальную переменную. В Java экземпляры объектов всегда создаются динамически с помощью ключевого слова new. Все это сделано не случайно: использование локальной переменной в Си++ гарантирует вызов деструктора серванта при покидании блока, где он описан. Если создавать объект динамически, то позже придется удалять его явным вызовом ключевого слова delete. Напротив, Java может позволить себе динамическое создание серванта, потому что в конце работы он будет уничтожен сборщиком мусора виртуальной машины.
Oба серванта создаются с параметром, задающим имя для их экземпляров. Это позволяет клиенту найти конкретный экземпляр объекта по имени. Клиент, который при попытке подключиться к объекту не указывает его имя, получит ссылку на произвольный экземпляр запрашиваемого объекта с любого сервера в сети. Такой вариант удобен, когда в CORBA-системе имеется механизм балансировки загрузки серверов. Так или иначе сервант с именем реализует долгоживущий (persistent) объект, т. е. такой объект, который может пережить свой сервер. Не поймите превратно — конечно же, уничтожив программу-сервер, породившую сервант (а вместе с ним и объект), вы уничтожите все, что им было запущено, в том числе и серванты с объектами. Говоря о продолжительности жизни, следует думать о долгоживущих объектах как о сущностях, состояние которых можно как-то сохранить, и впоследствии при следующем запуске сервера восстановить объекты в том виде, который они имели во время предыдущей сессии. Если, скажем, вы создаете объект, описывающий банковский счет, то, разумеется, его лучше делать долгоживущим, потому что данные (например, сумма на счету), из которых складывается состояние объекта, не должны изменяться только потому, что администратор остановил сервер и запустил его на следующий день. Напротив, объект «Часы» не имеет смысла делать долгоживущим — после перезапуска сервера время будет безнадежно сбито, и уж лучше заново обратиться к службе точного времени (вот воистину образцовый долгоживущий объект!) и получить текущее время. Так что во многих книгах по CORBA имеет место терминологическая путаница. Следует отметить, что такие утилиты, как Object Activation Daemon и Smart Agent, регистрируют лишь долгоживущие объекты.
Другой тип объектов — временные (transient) — существует лишь в рамках того процесса, который запустил их серванты. Отслужив свой срок, временный объект «умирает». Как правило, временные объекты являются утилитами, а не сущностями. В примере с банковским счетом временными объектами будут утилиты для изменения суммы на счету, а в примере с часами временный объект может служить программным интерфейсом к службе точного времени. Как бы то ни было, после выключения сервера состояние временных объектов никого уже не интересует — в следующий раз можно создать новые объекты.
Создать временный объект легко: просто используйте для его серванта конструктор без параметров. Таким образом, любой безымянный объект по умолчанию будет временным — зачем нам несколько одинаковых долгоживущих объектов с одинаковым состоянием?! Тем не менее в CORBA есть средства и для создания именованных временных объектов. Отметим, что ссылку на временный объект программа-клиент сможет получить лишь в том случае, если она вернется как результат выполнения некоторого запроса к другому объекту или как параметр вызова, описанный на IDL в виде out или inout.
Наверняка возник вопрос: а где его взять-то, этот сервант? Разумеется, написать. Если вы не используете специальные возможности CORBA, скажем, Dynamic Skeletons (DSI), то это несложно, хотя между реализациями на Cи++ и на Java разница размером в пропасть. С Java все проще простого. После трансляции IDL вы получите не только системные классы, но и пример описания класса серванта. Имя класса будет соответствовать имени интерфейса, но к нему будет добавлен префикс _example_ (например: _example_Service для IDL-интерфейса с именем Service), и найти его можно в одноименном файле с расширением .java. По сути дела, это уже готовый сервант. А вот idl2cpp подобными излишествами разработчика не балует, поэтому писать сервант нужно вручную. Однако важно понять, что техника создания серванта использует наследование его от класса скелета, который генерируется компилятором IDL автоматически. Найти его проще пареной репы. В Java-реализациях имя класса скелета состоит из имени IDL-интерфейса с префиксом _ и суффиксом ImplBase (например: _ServiceImplBase). VisiBroker for С++ все еще использует устаревшую схему именования скелетов, и имя таких классов состоит из имени IDL-интерфейса с префиксом _sk_ (_sk_Service). В любом случае класс создаваемого серванта наследуется от скелета, и требуется реализовать только те методы, которые относятся к вашему объекту, и ни в коем случае не трогать остальные. Как же обнаружить нужные методы? В версиях для Cи++ загляните в файл, имя которого заканчивается на _s.hh, и найдите в описании класса скелета все чистые виртуальные методы. Там же idl2cpp оставляет для вас специальный комментарий:
// The following operations need to be implemented
Но вернемся к разбору порядка действий сервера. Завершающим штрихом к его запуску будет экспорт объекта и переход в состояние ожидания:
Для Си++: boa->obj_is_ready(&implObject); boa->impl_is_ready(); Для Java: boa.obj_is_ready(implObject); boa.impl_is_ready();
Первая строчка, вызывающая метод obj_is_ready, сообщает через адаптер объектов, что сервант готов обслуживать запросы. Если объекты долгоживущие, то BOA попутно зарегистрирует их в таблице запущенных объектов, которую ведет утилита Smart Agent. С этого момента объект может использоваться клиентами и другими объектами. А чтобы предотвратить завершение серверного приложения, его сознательно блокируют вызовом impl_is_ready(). В результате происходит остановка потока, на котором выполняется приложение-сервер. Разблокировать его можно, лишь вызвав из другого потока метод shutdown() серванта или вручную закрыв приложение сервера.
Предыдующее Следующее