Добавление к Java Development Kit 1.1 средств вызова удаленного метода (RMI — remote method invocation) упростило сложную задачу объектно-ориентированной разработки распределенных систем. RMI обеспечивает удобную интеграцию с Java, но не поддерживает интероперабельность с иными языками. С другой стороны, архитектура CORBA, предусматривает службы, которые не охватываются средствами RMI (например, управляемая безопасность и устойчивость транзакций).

На примере небольшого приложения chat room мы проиллюстрируем, каким образом программисты могут объединить удобство использования механизма Java RMI с независимостью CORBA от языка. Начнем с реализации на основе набора распределенных объектов, использующих RMI. Затем адаптируем этот пример к CORBA, или, точнее, к спецификации RMI-over-IIOP, разработанной в Sun Microsystems и IBM. Протокол RMI-over-IIOP позволяет при работе с CORBA обойтись без изучения еще одного языка, а именно, языка определения интерфейса IDL. Данный протокол практически полностью реализует семантику RMI за счет поддержки соответствия Java-to-IDL. Необходимое усовершенствование CORBA — Objects-by-Value — к настоящему времени стандартизовано и также является частью IDL. Поскольку JDK 1.3 поставляется вместе с брокером объектных запросов, Java-программисту для работы с CORBA не нужно дополнительное ПО.

Прежде всего, остановимся на том, что собой представляют RMI и CORBA. Даже краткое вводное описание функциональности обеих спецификаций выходит за рамки данной статьи. В силу чего мы советуем обратиться к дополнительной литературе, указанной во врезке «Ресурсы Java RMI и CORBA».

Прежде, чем начать этот урок, вам следует убедиться, что на вашей машине установлен JDK 1.3 (http://java.sun.com/ j2se/1.3/). Пример, описанный в статье, полностью можно загрузить с сайта IC Online (http://computer.org/internet/ v5n1/rmitut.htm).

Введение в RMI

Технология RMI впервые была включена в состав JDK 1.1 с тем, чтобы упростить разработку распределенных приложений и обойтись без использования дополнительного программного обеспечения независимых производителей. Задание удаленно доступных объектов начинается с определения Java-интерфейса, который должен быть унаследован из java.rmi.Remote. Удаленный интерфейс специфицирует каждый вызываемый удаленно метод. Типы параметров, используемых в удаленных методах, могут быть либо примитивными (скажем, float), либо сериализуемыми, либо задаваемыми удаленными интерфейсами. Сериализуемые объекты передаются как копии с помощью службы сериализации объектов Java, а доступный удаленно объект — как удаленная ссылка.

После определения удаленного интерфейса надлежит предоставить класс реализации. Здесь можно указать дополнительные методы, но только те, которые в удаленном интерфейсе указаны как доступные удаленно. Во время исполнения класс реализации после создания должен быть экспортирован. Это делается либо путем наследования класса реализации из java.rmi.server.UnicastRemoteObject, либо путем прямого вызова статического метода exportObject этого класса. Наконец, необходимо создать скелетон (skeleton) и суррогат (stub) с помощью компилятора rmic, который поставляется вместе с JDK. Классы обеспечивают удаленный доступ к методам реализации (рис. 1).

Рис. 1. Скелетон и суррогат RMI

Пусть объект Caller, содержащий удаленную ссылку на удаленный объект X, представлен удаленным интерфейсом и классом реализации X_Imp. Вместо того чтобы напрямую вызвать метод X_Imp, объект Caller вызывает метод X_Stub, который совместим с ним по типу, поскольку также реализует удаленный интерфейс X. Суррогат формирует параметры и обеспечивает их передачу в скелетон X_Skel, который создается на стороне сервера при экспорте удаленного объекта X. При взаимодействии суррогата и скелетона используется протокол удаленных методов JRMP. Скелетон получает параметры, вызывает требуемый метод реализации и передает результаты и исключения. Если результирующий тип — это класс, который сам реализует удаленный интерфейс (скажем, Y_Imp), скелетон вместо этого создает и передает соответствующий сериализованный суррогат, содержащий удаленную ссылку. Поскольку суррогат совместим по типу с удаленным интерфейсом, клиент, ожидающий результат этого типа, получит объект требуемого типа. Теперь он содержит другой суррогат, который перенаправляет каждый вызов удаленному объекту Y.

Хотя, как правило, клиент получает удаленную ссылку именно таким образом, предполагается, что он уже имеет удаленную ссылку на X. Чтобы извлечь первоначальные ссылки, служба именования связывает удаленные ссылки с именами. RMI предоставляет для этого реестр — простую промежуточную службу именования, которая сама является удаленным объектом. Последняя версия RMI включает в себя распределенную сборку мусора, которая безопасным образом выполняет для удаленных объектов процедуру, обратную экспорту, чтобы освободить ресурсы на сервере.

Введение в CORBA

Основу архитектуры управления объектами OMA (object management architecture) составляет брокер объектных запросов ORB (object request broker), который служит посредником при передаче потока информации между объектами. Каждый объект, участвующий в сети CORBA, соединен с брокером, который на практике выступает в роли библиотеки, подходящей для языка программирования, используемого в данном проекте разработки. Взаимодействие между различными брокерами стандартизуется посредством протокола GIOP (general inter-ORB protocol). Протокол IIOP — версия этого протокола для сетей на базе TCP/IP. Данные стандарты составляют основу интероперабельности, которую обеспечивает CORBA между объектами, написанными на разных языках.

Службы CORBA (services) — стандартизованные объекты, реализующие такую функциональность, как именование, долговременное хранение и транзакции. Домены CORBA (domains) описывают некоторые стандарты для вертикальных рынков, например, для финансовых институтов. Такие функции, как управление информацией и управление системами, которые едины для всех прикладных областей, группируются в утилитах CORBA (facilities).

Еще один важный стандарт — IDL (interface definition language), который описывает интерфейс удаленного объекта CORBA. Определения IDL сравнимы с удаленными интерфейсами RMI. Поскольку CORBA не зависит от языка, IDL можно считать универсальным средством описания служб, реализованных на многих языках. В IDL несколько базовых типов данных, пришедших, главным образом, из Си, таких как array (массив), union (объединение) или struct (структура). Чтобы предоставить возможность конкретизировать язык программирования для реализации объектов CORBA, в OMG стандартизовали набор соответствий конструкций IDL языкам Си, Кобол, Лисп и Smalltalk. Соответствие IDL-to-Java — основа инструментария idlj, который может применяться для генерации суррогатов, скелетонов и связей Java из IDL-определений.

Изначально архитектура CORBA допускала передачу при вызове удаленных методов только примитивных типов данных или удаленных ссылок. Теперь же IDL-определение включает в себя предложения Objects-by-Value («объект по значению»), благодаря чему можно передавать состояние объектов и поведение так, как это делается с помощью RMI. Активное использование этой возможности и поддержки соответствия Java-to-IDL позволяет Java-программистам разрабатывать приложения на базе CORBA, даже не изучая IDL. Разработчик может указать интерфейсы средствами Java. Новый компилятор rmic, включенный в состав JDK 1.3, может создавать суррогаты, скелетоны и связи CORBA непосредственно из определений интерфейса в терминах Java. Кроме того, чтобы создать службы или клиенов на других языках программирования, можно генерировать IDL-определение автоматически и использовать его как входную информацию для других компиляторов.

Пример использования RMI

Наш пример приложения, реализованного на базе RMI, использует одноранговое соединение: клиент взаимодействует с сервером, а сервер активно передает сообщения клиенту. На рис. 2 представлены задействованные типы объектов.

User состоит из интерфейса User и реализации класса UserImp, а chat room — из интерфейса ChatRoom и реализации класса ChatRoomImp.

Для того чтобы войти в «переговорную» (chat room), «пользователь» (user) представляет ранее созданный объект UserImp как аргумент метода enter («вход»). Поскольку UserImp — это класс реализации удаленного объекта, а enter — удаленный метод chat room, для UserImp будет создан суррогат и передан в chat room. Затем chat room передает удаленную ссылку в user, где та помещается в хэш-таблицу. Чтобы отослать сообщение, user вызывает метод distributeMessage, в результате чего исполняется код, помеченный символом *1* на рис. 3.

Заметим, что вызов удаленных методов может породить удаленное исключение. В нашем примере это, вероятно, свидетельствует о том, что пользователь user закрыл клиента, не выйдя сначала из chat room. Чтобы стабилизировать работу chat room, удаленные ссылки на такого user должны быть удалены, поскольку вызов методов недоступных удаленных объектов может оказаться очень затратным по времени. Всякий раз, когда chat room обнаруживает ошибочный user, она информирует всех остальных, вызывая метод leave («выход»), чтобы те могли обновить свое представление.

Флаг clearFailedUsers позволяет избежать множественных вызовов leave в том случае, если обнаружен другой ошибочный user при многоадресной рассылке сообщения о выходе. Все методы синхронизированы с тем, чтобы не допустить входа или выхода пользователя из переговорной во время рассылки сообщения.

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

Компиляция и исполнение

Будем считать, что вам удалось загрузить и разархивировать полный исходный текст в каталог с именем , упомянутый в параметре окружения CLASS-PATH. В среде Windows скомпилировать и создать суррогаты и скелетоны RMI можно, вызвав следующие команды.

cd 
javac example 
mi *.java
javac example 
mi ui *.java
rmic example.rmi.ChatRoomImp
rmic example.rmi.UserImp

Прежде, чем запустить приложение chat room, инициируйте регистрацию. Убедитесь, что все определения классов объектов, которые вы хотите зарегистрировать, присутствуют в CLASS-PATH реестра.

rmiregistry.exe

Теперь вы можете запустить сервер и клиента с помощью следующих команд.

java example.rmi.ChatRoomImp

java example.rmi.ui.ChatClient
Рис. 4. Клиентская часть приложения chat room

Вы должны увидеть небольшое окно наподобие изображенного на рис. 4, где следует ввести URL переговорной и требуемое имя пользователя.

Пример использования RMI-over-IIOP

Вместо того чтобы определять интерфейс IDL, можно повторно использовать определения интерфейса Java из предыдущего примера. Помимо этого необходимо внести небольшие изменения в код реализации. Поскольку Java теперь содержит ORB, не требуется никаких дополнительных программ сторонних производителей.

Класс реализации ChatRoomImp должен наследоваться из PortableRemoteObject пакета javax.rmi.*, а не из UnicastRemoteObject. И опять-таки, в качестве альтернативы можно выбрать экспорт объекта напрямую посредством вызова PortableRemoteObject.export. Это действие косвенно инициализирует ORB. Те же изменения необходимо внести и в класс UserImp.

Следующее изменение касается регистрации chat room в службе именования. Для этого мы используем общий интерфейс каталогов и именования Java (JNDI — Java naming and directory interface), который определяет набор операций службы именования. Фактическая реализация может быть службой именования CORBA, которая указывается в командной строке инициализации. JNDI также будет использоваться при извлечении ссылки на служебный объект переговорной *3*, предполагается, что пользователь предоставил имя, которое можно извлечь с помощью метода getNameText.

Последнее изменение в коде реализации состоит в использовании статического метода narrow в PortableRemoteObject для определения типа удаленной ссылки при получении ссылки type-unknown из службы именования. Это гарантирует сохранение типа.

Компиляция и исполнение

Компиляция и создание суррогатов, связей и скелетонов CORBA осуществляется с помощью следующих команд.

cd 
javac example 
miiiop *.java
javac example 
miiiop ui *.java
rmic -iiop -always
example.rmiiiop.ChatRoomImp
rmic -iiop -always
example.rmiiiop.UserImp

Вызов rmic с параметром -iiop создает каталог stubs/skeletons/ties. Если вы хотите уберечь от изменений файл IDL, сгенерированный по спецификациям интерфейса, можно использовать параметр -idl.

Теперь запускается промежуточный сервер именования, который поставляется вместе с Java. Его можно рассматривать как эквивалент CORBA для реестра RMI.

tnameserv.exe

Создание и связывание удаленного объекта переговорной с данным именем:

java - Djava.naming.factory.initial=com.sun.j ndi.cosnaming.CNCtxFactory - Djava.naming.provider.url=iiop://local host:900 example.rmiiiop.ChatRoomImp

Помните, что интерфейс JNDI является общим в том смысле, что он может взаимодействовать с любыми службами именования. Аналогичным образом выглядит и вызов клиента.

.java -
Djava.naming.factory.initial=com.sun.j
ndi.cosnaming.CNCtxFactory
-
Djava.naming.provider.url=iiop://local
host:900
example.rmiiiop.ui.ChatClient

Заключение

В статье пример приложения на базе RMI адаптируется к новому стандарту RMI-over-IIOP. Вместо того чтобы задавать удаленные интерфейсы с помощью CORBA IDL, теперь можно использовать интерфейсы Java. С помощью нового стандартизованного соответствия Java-to-IDL при удаленном вызове методов можно даже передавать объекты по значению. Это расширяет исходную семантику удаленного вызова методов в CORBA до семантики RMI. Поскольку стандарт Java-to-IDL относительно новый, трудно рассчитывать на то, что его поддерживают все существующие системы CORBA. Однако с помощью RMI-over-IIOP приложения Java могут легко связываться с любыми службами CORBA, предложенными ведущими производителями программного обеспечения, поскольку теперь программы могут взаимодействовать посредством IIOP.

Франк Маурер (maurer@cpsc.ucalgary.ca) — содиректор консорциума Alberta Software Engineering Research Consortium и адьюнкт-профессор университета Калгари. Мартин Шааф (schaaf@informatik.uni-kl.de) работает над диссертацией в университете Кайзерслаутерна. К области его научных интересов относятся методы управления знаниями и распределенные системы.


Martin Schaaf, Frank Maurer, Integrating Java and CORBA: A Programmer?s Perspective. IEEE Internet Computing, January/February 2001. IEEE CS, 2001. All rights reserved. Reprinted with permission.