Вы находитесь на странице: 1из 43

Основы RMI

By jGuru
Об этом кратком курсе
Февраль 2000г.

Java Developer ConnectionSM (JDC) представляет «Краткий курс», который знакомит вас с Remote Method
Invocation API, написанным jGuru (прежнее название - MageLang Institute), являющимся обладателем
лицензии Java™ Software. Jguru – ведущий поставщик обучающих курсов по технологии Java™ и
регулярный спонсор JDC с 1996 года.

jGuru является преданным участником в деле расширения сообщества Java™ посредством пропаганды,
обучения и программного обеспечения с 1995 года. Вы можете найти дополнительную информацию об их
деятельности, включая поддерживаемые сообществом FAQ (часто задаваемые вопросы) и интерактивное
обучение, на www.jGuru.com.

Протяните руку и дотроньтесь до кого-нибудь. Объектно-ориентированные распределенные вычисления –


это взаимодействие между объектами, находящимися на различных виртуальных машинах. Remote Method
Invocation (RMI) делает передачу сообщения объекту, расположенному в Timbuktu, такой же простой, как и
вызов метода локального объекта. RMI является 100% Pure Java TM. И главное, - она встроена в ядро
библиотек Java (версии 1.1 и выше). Этот курс знакомит вас с RMI и рассматривает ее сильные и слабые
стороны как платформы для распределенных вычислений.

Цели
После завершения курса вы поймете:

• Что именно распределенные вычисления добавляют к платформе Java


• Архитектуру RMI
• Распределенную объектную модель, определенную и поддерживаемую RMI

В конце курса вы сможете:

• Посылать объект различным JVM при помощи сериализации объекта


• Обеспечить новые варианты для установки и распространения программного кода.

Предварительные условия
Общее знакомство с концепциями объектно-ориентированного программирования и с языком
программирования Java. Если вы не знакомы с этими понятиями, смотрите «Учебник по Java». Упражнения
требуют умения модифицировать и создавать программы Java.

Об авторе

Govind Seshadri является приверженцем Java и лектором по расширенным понятиям Java-разработок на


конференциях Java/Object по всему миру. Область его интересов охватывает: Java на серверной части,
распределенные объектные вычисления и серверы приложений. До присоединения к jGuru он был старшим
техническим архитектором в фирме Strategic Technology Resources, расположенной в Chicago и
занимающейся консультациями по объектной технологии. Govind выполнял различные функции в течение
своей карьеры: архитектор, куратор, разработчик и руководитель проекта.

Обратная связь с читателем


Сообщите нам, что вы думаете об этом учебном пособии.

Очень ценная информация. Ценная информация. Ничего особенного.

Если у вас есть другие комментарии или идеи для будущих статей, пожалуйста, напишите
нам.
Краткий курс
В этом курсе рассмотрены основы технологии Remote Method Invocation (RMI), относящейся к платформе
Java 2™.

Содержание курса
• Введение в распределенные вычисления с использованием RMI
o Цели
o Сравнение распределенных и нераспределенных Java-программ

• Архитектура Java RMI


o Интерфейсы: основа RMI
o Уровни архитектуры RMI
Уровень заглушки и скелета
Уровень удаленной ссылки
Транспортный уровень

• Именование удаленных объектов

• Использование RMI
o Интерфейсы
o Реализация
o Заглушки и скелеты
o Хост-сервер
o Клиент
o Запуск системы RMI

• Параметры в RMI

• Параметры в виртуальной машине Single Java™


o Простые параметры
o Объектные параметры
o Удаленные объектные параметры

• Клиентские обратные вызовы RMI

• Распространение и установка программного обеспечения RMI


o Распределенные классы RMI
o Автоматическое распространение классов
o Брандмауэр

• Распределенная сборка мусора

• Сериализация удаленных объектов

• Архитектуры мобильных агентов

• Альтернативные реализации

• Дополнительные ресурсы
o Книги и статьи

Введение в распределенные вычисления с использованием RMI


Технология Remote Method Invocation (RMI), впервые представленная в JDK 1.1, продвинула сетевое
программирование на более высокий уровень. Хотя RMI относительно проста в использовании, она является
необыкновенно мощной технологией и раскрывает перед обычным Java-программистом полностью новую
парадигму – мир распределенных объектных вычислений.

Этот курс представляет собой углубленное введение в эту универсальную технологию. RMI получила
значительное развитие в JDK 1.1 и во многом была улучшена в Java 2 SDK. При необходимости, различия
между этими двумя версиями будут отмечены.

Цели

Главной целью разработчиков RMI было предоставление возможности программистам разрабатывать


распределенные Java-программы, используя такие же синтаксис и семантику, как и при разработке обычных
нераспределенных программ. Для этого они должны были преобразовать модель работы классов и объектов
в одной виртуальной машине Java™ (JVM) в новую модель работы классов и объектов в распределенной
(несколько JVM) вычислительной среде. (Примечание: в данном документе термин «виртуальная машина
Java™» или «JVM» означает виртуальную машину платформы Java.)

В данном разделе рассматривается архитектура RMI с точки зрения распределенных или удаленных
объектов Java, а также их отличие от локальных объектов Java. Архитектура RMI определяет поведение
объектов, время и способы возникновения исключительных ситуаций, способы управления памятью и
способы передачи параметров и их возврата из удаленных методов.

Сравнение распределенных и нераспределенных Java-программ

Разработчики RMI стремились сделать использование распределенных Java-объектов таким же, как и
использование локальных объектов. В следующей таблице перечислены некоторые важные отличия.

Не беспокойтесь о том, что не все отличия вам понятны. Все прояснится после рассмотрения архитектуры
RMI. Вы можете использовать эту таблицу в качестве ссылки во время изучения RMI.

Локальный объект Удаленный объект


Определение Локальный объект Экспортируемое поведение удаленного объекта
объекта определяется при помощи определяется при помощи интерфейса, который
класса Java. должен быть расширен из интерфейса Remote.
Реализация объекта Локальный объект реализуется Поведение удаленного объекта определяется классом
своим классом Java. Java, который реализует удаленный интерфейс.
Создание объекта Новый экземпляр локального Новый экземпляр удаленного объекта создается на
объекта создается оператором компьютере хоста оператором new. Клиент не может
new. непосредственно создать новый удаленный объект
(если не использует технологию Java 2 Remote Object
Activation).
Доступ к объекту Доступ к локальному объекту Доступ к удаленному объекту осуществляется через
осуществляется переменную-ссылку на объект, указывающую на
непосредственно через реализацию замещающей заглушки удаленного
переменную-ссылку на объект. интерфейса.
Ссылки В одной JVM, ссылка на объект «Удаленная ссылка» представляет собой указатель на
указывает непосредственно на замещающий объект («заглушку») в локальной
объект в динамической памяти. динамической памяти. Заглушка содержит
информацию, которая дает возможность соединиться
с удаленным объектом, содержащим реализацию
методов.
Активные ссылки В одной JVM, объект считается В распределенной среде удаленная JVM может
«живым», если существует хотя разрушиться, и сетевое соединение может быть
бы одна ссылка на него. потеряно. Считается, что удаленный объект имеет
активную удаленную ссылку на него, если к нему
производился доступ в течение определенного
периода времени (срока аренды). Если все удаленные
ссылки были удалены явно, или если у всех
удаленных ссылок закончился срок аренды, тогда
удаленный объект становится доступен для
удаленной сборки мусора.
Финализация Если объект реализует метод Если удаленный объект реализует интерфейс
finalize(), он вызывается перед Unreferenced, при удалении всех удаленных ссылок
тем, как объект утилизируется вызывается метод unreferenced этого интерфейса.
сборщиком мусора.
Сборка мусора При удалении всех локальныхУдаленный сборщик мусора работает совместно с
ссылок на объект, он локальным. Если нет удаленных ссылок и удалены
становится кандидатом на все локальные ссылки на объект, он становится
удаление сборщиком мусора. кандидатом для сборщика мусора в обычном
значении этого понятия.
Исключительные Исключительные ситуации RMI заставляет программу иметь дело с любыми
ситуации являются либо возможными объектами RemoteException, которые
исключительными ситуациями могут генерироваться. Это сделано для гарантии
времени исполнения, либо устойчивости распределенных приложений.
классом Exceptions.
Компилятор Java заставляет
программу обрабатывать все
Exceptions.

Архитектура Java RMI


Целью разработки архитектуры RMI было создание распределенной объектной модели Java, которая
свободно интегрируется в язык программирования Java и локальную объектную модель. Разработчики RMI
достигли этой цели; была создана система, которая переносит безопасность и устойчивость архитектуры
Java в мир распределенных вычислений.

Интерфейсы: основа RMI

Архитектура RMI основана на одном важном принципе: определение поведения и реализация этого
поведения считаются разными понятиями. RMI дает возможность разделить и выполнить на разных JVM
код, определяющий поведение, и код, реализующий поведение.

Это прекрасно соответствует требованиям распределенных систем, в которых клиенты знают об


определениях служб, а серверы предоставляют эти службы.

Конкретно в RMI определение удаленной службы кодируется при помощи интерфейса Java. Реализация
удаленной службы кодируется в классе. Таким образом, ключ к пониманию RMI – помнить, что
интерфейсы определяют поведение, а классы определяют реализацию.

Следующий рисунок иллюстрирует это разделение:

Помните, что интерфейсы Java не содержат исполняемого кода. RMI поддерживает два класса,
реализующих один и тот же интерфейс. Первый класс является реализацией поведения и исполняется на
сервере. Второй класс работает как промежуточный интерфейс для удаленной службы и исполняется на
клиентской машине. Это показано на следующей диаграмме.
Клиентская программа вызывает методы прокси-объекта, RMI передает запрос на удаленную JVM и
направляет его в реализацию объекта. Любые возвращаемые из реализации значения передаются назад в
прокси-объект и затем в клиентскую программу.

Уровни архитектуры RMI

Рассмотрев высокоуровневую архитектуру RMI, взглянем на ее реализацию. Реализация RMI, по существу,


состоит из трех абстрактных уровней. Первый – это уровень заглушки и скелета, расположенный
непосредственно перед разработчиком. Этот уровень перехватывает вызовы методов, произведенные
клиентом при помощи переменной-ссылки на интерфейс, и переадресует их в удаленную службу RMI.

Следующий уровень – уровень удаленной ссылки. Этот уровень понимает, как интерпретировать и
управлять ссылками на удаленные объекты служб. В JDK 1.1 этот уровень соединяет клиентов с
удаленными объектами служб, которые исполняются на сервере. Это соединение является связью типа один
к одному (однонаправленное соединение). В Java 2 SDK этот уровень был расширен поддержкой активации
пассивных удаленных объектов при помощи технологии Remote Object Activation.

Транспортный уровень основан на соединениях TCP/IP между сетевыми машинами. Он обеспечивает


основные возможности соединения и некоторые стратегии защиты от несанкционированного доступа.

При использовании уровневой архитектуры каждый из уровней может быть изменен или заменен без
воздействия на остальную систему. Например, транспортный уровень может быть заменен протоколом
UDP/IP без изменения остальных уровней.

Уровень заглушки и скелета

Уровень заглушки и скелета RMI расположен непосредственно перед разработчиком Java. На этом уровне
RMI использует прокси-модель проектирования, которая описана в книге Gamma, Helm, Johnson и Vlissides
«Design Patterns». В прокси-модели объект одного контекста представляется другим (прокси-объектом) в
отдельном контексте. Прокси-объект знает, как направлять вызовы методов между этими объектами. На
следующей диаграмме классов показана прокси-модель.
В прокси-модели, используемой в RMI, роль прокси выполняет класс заглушки, а роль RealSubject
выполняет класс, реализующий удаленную службу.

Скелет является вспомогательным классом, который создается для использования RMI. Скелет понимает,
как взаимодействовать с заглушкой при RMI-соединении. Скелет поддерживает общение с заглушкой; он
читает параметры для вызова метода из соединения, производит вызов объекта, реализующего удаленную
службу, принимает возвращаемое значение и записывает его обратно в заглушку.

В реализации RMI Java 2 SDK новый протокол связи сделал классы скелетов не нужными. RMI использует
отражение для установления соединения с объектом удаленной службы. Вы должны использовать классы и
объекты скелетов только в JDK 1.1 и совместимых с ним реализациях систем.

Уровень удаленных ссылок

Уровни удаленных ссылок определяют и поддерживают семантику вызовов соединения RMI. Этот уровень
предоставляет объект RemoteRef, который обеспечивает соединение с объектами, реализующими
удаленные службы.

Объекты заглушки используют метод invoke() в объекте RemoteRef для направления вызова метода.
Объект RemoteRef понимает семантику вызова удаленных служб.

Реализация RMI в JDK 1.1 обеспечивает только один способ соединения клиентов с реализациями
удаленных служб: однонаправленное соединение типа точка-точка. Перед тем, как клиент сможет
использовать удаленную службу, экземпляр объекта, реализующего ее, должен быть создан на сервере и
экспортирован в систему RMI. (Если это основная служба, она также должна быть поименована и
зарегистрирована в реестре RMI).

Реализация RMI в Java 2 SDK добавляет новую семантику для соединения клиент-сервер. В этой версии
RMI поддерживает способные к активизации удаленные объекты. Когда производится вызов метода прокси
для такого объекта, RMI определяет, находится ли объект, реализующий удаленную службу, в пассивном
состоянии. Если да, то RMI создаст экземпляр объекта и восстановит его состояние из дискового файла. Как
только объект активизируется в памяти, он начинает вести себя так же, как и объект, реализующий
удаленную службу JDK 1.1.

Доступны и другие типы семантики соединений. Например, в случае широковещательного соединения, один
прокси-объект может передать запрос метода нескольким реализациям одновременно и принять первый
ответ (это уменьшает время отклика и, возможно, повышает доступность объекта). В будущем Sun
возможно добавит дополнительные типы семантики в RMI.

Транспортный уровень

Транспортный уровень осуществляет соединение между различными JVM. Все соединения представляют
собой основанные на потоках сетевые соединения, использующие TCP/IP.

Даже если две JVM работают на одном и том же физическом компьютере, они соединяются через стек
сетевых протоколов TCP/IP. (Вот почему вы должны иметь действующую конфигурацию TCP/IP на вашем
компьютере для выполнения упражнений этого курса). На следующей диаграмме показаны TCP/IP
соединения между разными JVM.

Как вы знаете, протокол TCP/IP обеспечивает постоянное, основанное на потоках, соединение между двумя
машинами. Этот соединение основано на адресе IP и номере порта. Обычно вместо IP-адреса используется
имя DNS; это означает, что можно говорить, например, о соединении между
flicka.magelang.com:3452 и rosa.jguru.com:4432. В текущей реализации RMI соединения
TCP/IP используются как основа для всех межмашинных соединений.

На вершине TCP/IP RMI использует протокол уровня соединения, называемый Java Remote Method Protocol
(JRMP). JRMP является частным, основанным на потоках, протоколом, который только частично
специфицирован в настоящее время в двух версиях. Первая версия была выпущена в реализации RMI в JDK
1.1 и требовала использования классов скелетов на сервере. Вторая версия была выпущена с Java 2 SDK.
Она была оптимизирована по производительности и не требовала использования скелетных классов.
(Обратите внимание, что некоторые альтернативные реализации, такие как BEA Weblogic и NinjaRMI, не
используют RMI, а используют вместо него свои собственные протоколы уровня соединения. Voyager от
ObjectSpace распознает JRMP и будет взаимодействовать с RMI на уровне соединения.)

Sun и IBM совместно разработали следующую версию RMI, называемую RMI-IIOP, которая будет доступна
в версии 1.3 Java 2 SDK. Интересным моментом в RMI-IIOP является то, что она будет использовать
протокол Object Management Group (OMG) Internet Inter-ORB Protocol, IIOP для взаимодействия между
клиентами и серверами.

OMG представляет собой группу из более чем 800 членов, которая определяет независимую от
производителей, распределенную объектную архитектуру, называемую Common Object Request Broker
Architecture (CORBA). Клиенты и серверы CORBA Object Request Broker (ORB) взаимодействуют между
собой, используя IIOP. С принятием в CORBA расширения Objects-by-Value и предложений Java Language to
IDL Mapping работа была направлена на интеграцию RMI и CORBA. Эта новая реализация RMI-IIOP
поддерживает большинство возможностей RMI, за исключением:

• java.rmi.server.RMISocketFactory
• UnicastRemoteObject
• Unreferenced
• Интерфейсов DGC

Транспортный уровень RMI был разработан для осуществления соединения между клиентами и сервером
даже с учетом сетевых помех.

Хотя транспортный уровень предпочитает использовать несколько TCP/IP соединений, некоторые сетевые
конфигурации разрешают только одно TCP/IP-соединение между клиентом и сервером (некоторые броузеры
ограничивают апплеты одним сетевым соединением с их сервером).

В этом случае, транспортный уровень распределяет несколько виртуальных соединений внутри одного
TCP/IP-соединения.

Именование удаленных объектов


При рассмотрении архитектуры RMI постоянно откладывался один вопрос: «Как клиент находит удаленную
службу RMI?» Сейчас вы получите ответ на этот вопрос. Клиенты находят удаленные службы, используя
службу имен или каталогов. Это может показаться хождением по кругу. Как клиент может найти службу,
используя службу? И это действительно так. Служба имен или каталогов исполняется на хорошо известном
хосте и имеет известный номер порта. (Хорошо известный означает, что все в организации знают об этом).

RMI может использовать много различных служб каталогов, включая Java Naming and Directory Interface
(JNDI). RMI и сама включает в себя простую службу, называемую реестром RMI, rmiregistry. Реестр
RMI работает на каждой машине, содержащей объекты удаленных служб и принимающей запросы на
обслуживание, по умолчанию используя порт 1099.

На хосте программа сервера создает удаленную службу, предварительно создавая локальный объект,
реализующий эту службу. Затем она экспортирует этот объект в RMI. Как только объект экспортирован,
RMI создает службу прослушивания, ожидающую соединения с клиентом и запроса службы. После
экспорта, сервер регистрирует объект в реестре RMI, используя общедоступное имя.

На стороне клиента к реестру RMI доступ обеспечивается через статический класс Naming. Он
предоставляет метод lookup(), который клиент использует для запросов к реестру. Метод lookup() принимает
URL, указывающий на имя хоста и имя требуемой службы. Метод возвращает удаленную ссылку на
обслуживающий объект. URL принимает следующий вид:

rmi://<host_name>
[:<name_service_port>]
/<service_name>

где host_name - это имя, распознаваемое в локальной сети (LAN), или DNS-имя в сети Internet.
Необходимо только указать name_service_port, если служба имен исполняется на порте, отличном от
принимаемого по умолчанию 1099.

Использование RMI
Сейчас наступило время создать рабочую RMI-систему и получить практический опыт. В данном разделе,
вы создадите простую удаленную службу, реализующую калькулятор, и попробуете использовать ее из
клиентской программы.

Рабочая RMI-система состоит из нескольких частей.

• Определение интерфейсов для удаленных служб


• Реализация удаленных служб
• Файлы заглушки и скелета
• Сервер, предоставляющий удаленные службы
• Служба имен RMI, дающая возможность клиентам найти удаленные службы
• Поставщик файла классов (HTTP или FTP-сервер)
• Клиентская программа, которая нуждается в удаленных службах

В следующих разделах вы создадите простую RMI-систему пошаговым способом. Советуем создать новый
подкаталог на вашем компьютере и создавать файлы по мере чтения текста.

Для упрощения задачи вы будете использовать один и тот же каталог для кода как клиента, так и сервера.
При запуске клиента и сервера из одного и того же каталога вам не придется настраивать HTTP или FTP
серверы для доступа к файлам классов. (Использование серверов HTTP и FTP в качестве поставщиков
файлов классов детально рассматривается в разделе «Распространение и установка программного
обеспечения RMI»)

Если предположить, что RMI-система уже спроектирована, для ее создания необходимо выполнить
следующие шаги:

1. Написать и откомпилировать Java-код для интерфейсов


2. Написать и откомпилировать Java-код для классов реализации
3. Создать файлы классов заглушки и скелета из классов реализации
4. Написать Java-код программы хоста для удаленного обслуживания
5. Разработать Java-код для клиентской программы RMI
6. Установить и запустить RMI-систему
1. Интерфейсы

Первым шагом является написание и компилирование Java-кода для интерфейсов служб. Интерфейс
Calculator определяет все удаленные возможности, предлагаемые службой:

public interface Calculator


extends java.rmi.Remote {
public long add(long a, long b)
throws java.rmi.RemoteException;

public long sub(long a, long b)


throws java.rmi.RemoteException;

public long mul(long a, long b)


throws java.rmi.RemoteException;

public long div(long a, long b)


throws java.rmi.RemoteException;
}

Обратите внимание, что этот интерфейс расширяет интерфейс Remote, и в сигнатуре каждого
метода определяется, что он может генерировать объект RemoteException.

Скопируйте этот файл в ваш каталог и откомпилируйте его при помощи компилятора Java:

>javac Calculator.java

2. Реализация

Теперь вы пишете реализацию удаленной службы. Ниже приведен класс CalculatorImpl:

public class CalculatorImpl


extends
java.rmi.server.UnicastRemoteObject
implements Calculator {

// Реализации должны иметь


// явный конструктор для
// того, чтобы объявить
// исключительную ситуацию RemoteException
public CalculatorImpl()
throws java.rmi.RemoteException {
super();
}

public long add(long a, long b)


throws java.rmi.RemoteException {
return a + b;
}

public long sub(long a, long b)


throws java.rmi.RemoteException {
return a - b;
}

public long mul(long a, long b)


throws java.rmi.RemoteException {
return a * b;
}

public long div(long a, long b)


throws java.rmi.RemoteException {
return a / b;
}
}

И опять, скопируйте этот код в ваш каталог и откомпилируйте его.

Класс реализации использует UnicastRemoteObject для присоединения к системе RMI. В


данном примере класс реализации непосредственно расширяет UnicastRemoteObject. Это не
является обязательным требованием. Класс, не расширяющий UnicastRemoteObject, может
использовать свой метод exportObject() для присоединения к RMI.

Если класс расширяет UnicastRemoteObject, он должен обеспечить конструктор,


объявляющий, что он может сгенерировать объект RemoteException. Если этот конструктор
вызывает метод super(), он активизирует код в UnicastRemoteObject, который выполняет
RMI-соединение и инициализацию удаленного объекта.

3. Заглушки и скелеты

Дальше вы используете компилятор RMI, rmic, для генерации файлов заглушки и скелета.
Компилятор запускается с указанием файла класса, реализующего удаленную службу.

>rmic CalculatorImpl

Попробуйте выполнить это в вашем каталоге. После запуска rmic вы должны найти файл
Calculator_Stub.class и, если вы используете Java 2 SDK, Calculator_Skel.class.

Ключи запуска для версии RMI-компилятора JDK 1.1 следующие:

Usage: rmic <options> <class names>

where <options> includes:


-keep Do not delete intermediate
generated source files
-keepgenerated (same as "-keep")
-g Generate debugging info
-depend Recompile out-of-date
files recursively
-nowarn Generate no warnings
-verbose Output messages about
what the compiler is doing
-classpath <path> Specify where
to find input source
and class files
-d <directory> Specify where to
place generated class files
-J<runtime flag> Pass argument
to the java interpreter

В версии rmic платформы Java 2 добавляется три новых ключа:

-v1.1 Create stubs/skeletons


for JDK 1.1 stub
protocol version
-vcompat (default)
Create stubs/skeletons compatible
with both JDK 1.1 and Java 2
stub protocol versions
-v1.2 Create stubs for Java 2 stub protocol
version only
4. Хост-сервер

Удаленные службы RMI должны быть помещены в процесс сервера. Класс CalculatorServer
является очень простым сервером, предоставляющим простые элементы для размещения.

import java.rmi.Naming;

public class CalculatorServer {

public CalculatorServer() {
try {
Calculator c = new CalculatorImpl();
Naming.rebind("
rmi://localhost:1099/
CalculatorService", c);
} catch (Exception e) {
System.out.println("Trouble: " + e);
}
}

public static void main(String args[]) {


new CalculatorServer();
}
}

5. Клиент

Исходный код клиента следующий:

import java.rmi.Naming;
import java.rmi.RemoteException;
import java.net.MalformedURLException;
import java.rmi.NotBoundException;

public class CalculatorClient {

public static void main(String[] args) {


try {
Calculator c = (Calculator)
Naming.lookup(
"rmi://remotehost
/CalculatorService");
System.out.println( c.sub(4, 3) );
System.out.println( c.add(4, 5) );
System.out.println( c.mul(3, 6) );
System.out.println( c.div(9, 3) );
}
catch (MalformedURLException murle) {
System.out.println();
System.out.println(
"MalformedURLException");
System.out.println(murle);
}
catch (RemoteException re) {
System.out.println();
System.out.println(
"RemoteException");
System.out.println(re);
}
catch (NotBoundException nbe) {
System.out.println();
System.out.println(
"NotBoundException");
System.out.println(nbe);
}
catch (
java.lang.ArithmeticException
ae) {
System.out.println();
System.out.println(
"java.lang.ArithmeticException");
System.out.println(ae);
}
}
}

6. Запуск RMI-системы

Теперь вы готовы к запуску системы! Вы должны запустить три консоли, одну для сервера, одну
для клиента и одну для реестра RMI.

Начните с реестра. Вы должны находиться в каталоге, в котором находятся написанные вами


классы. Напишите следующее:

rmiregistry

Если все пройдет хорошо, реестр начнет работать, и вы можете перейти к следующей консоли.

Во второй консоли запустите сервер, содержащий CalculatorService, и наберите следующее:

>java CalculatorServer

Программа запустится, загрузит реализацию в память и будет ждать соединения клиента.

В последней консоли запустите клиентскую программу.

>java CalculatorClient

Если все пройдет хорошо, вы увидите следующую информацию:

1
9
18
3

Вот и все: вы создали работающую систему RMI. Даже если вы запустили три консоли на одном и том же
компьютере, RMI использует стек протоколов TCP/IP вашей сети для взаимодействия между тремя
отдельными JVM. Это вполне законченная RMI-система.

Упражнения

1. UML-определение примера системы RMI


2. Простая банковская система

Параметры в RMI
RMI поддерживает вызовы методов удаленных объектов. Если эти вызовы содержат параметры или
принимают возвращаемое значение, как RMI передает их между JVM? Какая используется семантика?
Поддерживает ли RMI передачу по значению или передачу по ссылке? Ответ зависит от того, являются ли
параметры переменными простого типа, объектами или удаленными объектами.

Параметры в одной JVM


Сначала рассмотрим, как передаются параметры в одной JVM. Нормальной семантикой для Java является
передача по значению. Когда параметр передается в метод, JVM создает копию значения, помещает копию в
стек и затем выполняет метод. Когда этот параметр используется внутри метода, происходит обращение к
стеку и используется копия параметра. Значения, возвращаемые из методов, тоже передаются по значению.

Когда в метод передаются данные простого типа (boolean, byte, short, int, long, char, float,
или double), используется передача по значению. Механизм передачи в качестве параметров объектов
является более сложным. Вспомните, что объекты расположены в памяти, и к ним можно получить доступ
при помощи одной или нескольких переменных-ссылок. И хотя в следующем фрагменте программы
кажется, что в метод println()передается объект

String s = "Test";
System.out.println(s);

на самом деле в метод передается переменная-ссылка. В примере делается копия переменной-ссылки s


(увеличивая на единицу счетчик ссылок объекта String) и помещается в стек. Внутри метода код
использует копию ссылки для доступа к объекту.

Теперь давайте посмотрим, как RMI передает параметры и возвращаемые значения между удаленными
JVM.

Простые параметры

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

Значения передаются между JVM в стандартном, машинно-независимом формате. Это позволяет JVM,
работающим на разных платформах, надежно взаимодействовать друг с другом.

Объектные параметры

Когда в удаленный метод передается объект, семантика отличается от семантики, используемой в случае с
одной JVM. RMI передает между JVM сам объект, а не ссылку на него. Т.е., объект передается по значению.
Подобным же образом, когда удаленный метод возвращает объект, в вызывающую программу передается
полная копия объекта.

В отличие от простых типов данных, передача объекта в удаленную JVM – не простая задача. Объект Java
может быть простым и самодостаточным, но может содержать сложную, похожую на граф, структуру
ссылок на другие объекты Java. Поскольку различные JVM не разделяют динамическую память, RMI
должен послать указанный объект и все объекты, на которые он ссылается. (Передача большой структуры
объектов может потребовать много времени процессора и пропускной способности сети.)

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

Удаленные объектные параметры

RMI представляет третий тип параметров: удаленные объекты. Как вы уже видели, удаленная программа
может получить ссылку на удаленный объект через реестр RMI. Существует другой способ передачи
клиенту ссылки на удаленный интерфейс – она может быть возвращена клиенту из вызова метода. В
следующем коде для получения ссылки на удаленную службу Account используется метод
getAccount()службы BankManager.

BankManager bm;
Account a;
try {
bm = (BankManager) Naming.lookup(
"rmi://BankServer
/BankManagerService"
);
a = bm.getAccount( "jGuru" );
// Код, использующий счет
}
catch (RemoteException re) {
}

В реализации метода getAccount() метод возвращает локальную ссылку на удаленную службу.

public Account
getAccount(String accountName) {
// Код для поиска соответствующего счета
AccountImpl ai =
// возврат найденной ссылки
return AccountImpl;
}

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

На следующей диаграмме показано, как могут использоваться вызовы метода RMI:

• Возвратить удаленную ссылку с сервера клиенту А


• Передать удаленную ссылку от клиента А клиенту В
• Передать удаленную ссылку от клиента В назад на сервер

Обратите внимание, что когда клиенту А возвращается объект AccountImpl, прокси-объект Account
замещается. Последовательные вызовы метода продолжают передавать ссылку сначала клиенту В, а затем
назад серверу. На протяжении этого процесса ссылки продолжают обращаться к одному экземпляру
удаленной службы.

Что особенно интересно отметить, когда ссылка возвращается назад на сервер, она не преобразовывается в
локальную ссылку на реализованный объект. Это дает увеличение скорости, а поддержка такой косвенной
передачи дает гарантию того, что семантика использования удаленной ссылки не нарушается.

Упражнение
3. Параметры RMI

Клиентские обратные вызовы RMI


Во многих архитектурах серверу может понадобиться сделать обратный вызов клиенту. Например,
поддержка отображения текущего хода процесса (progress bar), извещение о тике таймера, предупреждения
о проблемах и т.д.

Для достижения этого клиент тоже должен работать как сервер RMI. Здесь нет ничего особенного,
поскольку RMI работает одинаково хорошо между всеми компьютерами. Однако, расширение клиентом
java.rmi.server.UnicastRemoteObject может быть не практичным. В этих случаях удаленный
объект может подготовить себя для удаленного использования, вызвав статический метод

UnicastRemoteObject.exportObject (<remote_object>).

Упражнение
4. Обратные вызовы RMI-клиента

Распространение и установка программного обеспечения RMI


RMI добавляет к платформе Java поддержку модели распределенного класса и расширяет технологию Java
до использования ее на нескольких JVM. Не должно быть неожиданным, что установка RMI-систем
является более сложной, чем установка исполняющей системы Java на одном компьютере. В этом разделе
вы изучите темы, связанные с установкой и распространением систем, основанных на RMI.

Для целей этого раздела предполагается, что общий процесс разработки DC-системы привел вас к тому
моменту, когда вы должны подумать о размещении обработки на узлах сети, и определить, как установить
систему на каждом сетевом узле.

Распространение классов RMI

Для запуска RMI-приложения файлы поддерживающих классов должны быть расположены в таких местах,
где бы они могли быть найдены сервером и клиентами.

Для сервера должны быть доступны (для загрузчика классов) классы:

• Определения интерфейса удаленной службы


• Реализации удаленных служб
• Скелеты для классов реализации (только для серверов, основанных на JDK 1.1)
• Заглушки для классов реализации
• Все остальные классы сервера

Для клиента должны быть доступны (для загрузчика классов) классы:

• Определения интерфейса удаленной службы


• Заглушки для классов, реализующих удаленную службу
• Классы сервера для объектов, используемых клиентом (таких, как возвращаемое значение)
• Все остальные классы клиента

Если вы знаете, какие файлы должны быть размещены на различных узлах сети, то сделать их доступными
для каждого загрузчика классов JVM не составит труда.

Автоматическое распространение классов

Разработчики RMI расширили концепцию загрузки классов, включив загрузку классов из серверов HTTP и
FTP. Это является мощным расширением, поскольку означает, что классы могут быть размещены в одном
или в небольшом количестве мест, но в то же время все узлы в RMI-системе смогут получить нужный для
функционирования класс.

RMI поддерживает удаленную загрузку классов при помощи RMIClassLoader. Если на клиенте или
сервере выполняется RMI-система и для дальнейшей работы необходимо загрузить класс из удаленного
места, вызывается метод RMIClassLoader.
Способ загрузки классов управляется множеством свойств. Эти свойства могут быть установлены при
запуске JVM:

java [ -D<PropertyName>=<PropertyValue> ]+<ClassFile>

Свойство java.rmi.server.codebase используется для задания URL. Этот URL указывает на


месторасположение file:, ftp: или http:, обеспечивающих классы для объектов, переданных из этой
JVM. Если программа, выполняющаяся в JVM, посылает объект другой JVM (в качестве возвращаемого из
метода значения), эта другая JVM должна загрузить файл классов для этого объекта. Когда RMI передает
объект при помощи сериализации, указанный в этом параметре URL встраивается в поток вместе с самим
объектом.

Примечание: RMI не передает файлы классов вместе с сериализованными объектами.

Если удаленная JVM нуждается в файле классов для объекта, она ищет встроенный URL и связывается с
сервером по этому URL для загрузки файла.

Если свойство java.rmi.server.useCodebaseOnly установлено в значение true, JVM будет


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

Используя различные комбинации доступных системных свойств, может быть создано большое количество
различных системных конфигураций RMI.

Closed. Все классы, используемые клиентами и сервером, должны быть расположены в JVM и ссылка на
это место должна быть указана в переменной окружения CLASSPATH. Динамическая загрузка классов не
поддерживается.

Server based. Клиентский апплет загружается из CODEBASE сервера вместе со всеми


поддерживающими классами. Это похоже на способ загрузки апплетов с того же самого HTTP-сервера,
который поддерживает веб-страницу этих апплетов.

Client Dynamic. Основные классы загружаются из места, которое указано в переменной окружения
CLASSPATH JVM клиента. Классы поддержки загружаются при помощи
java.rmi.server.RMIClassLoader из HTTP или FTP-сервера по сети из места, указанного сервером.

Server Dynamic. Основные классы загружаются из места, которое указано в переменной окружения
CLASSPATH JVM клиента. Классы поддержки загружаются при помощи
java.rmi.server.RMIClassLoader из HTTP или FTP-сервера по сети из места, указанного клиентом.

Bootstrap client. В этой конфигурации весь код клиента загружается из HTTP или FTP-сервера по
сети. Единственный код, расположенный на клиентской машине, - это небольшой начальный загрузчик.

Bootstrap server. В этой конфигурации весь код сервера загружается из HTTP или FTP-сервера по
сети. Единственный код, расположенный на серверной машине, - это небольшой начальный загрузчик.

Упражнение этого раздела включает создание конфигурации bootstrap client. Внимательно следуйте
указаниям, так как необходимо разместить и откомпилировать разные файлы в разных каталогах.

Упражнение
5. Пример начальной загрузки

Брандмауэр

Любое сетевое корпоративное приложение, которое должно работать вне границ интранет, неизбежно
столкнется с брандмауэрами. В общем случае, брандмауэры блокируют весь сетевой трафик, за
исключением трафика, проходящего через определенные «хорошо известные» порты.
Поскольку транспортный уровень RMI открывает динамические соединения сокетов между клиентом и
сервером, трафик JRMP обычно блокируется большинством брандмауэров. К счастью, разработчики RMI
предвидели эту проблему. Ее решение обеспечивается самим транспортным уровнем RMI. Для прохождения
через брандмауэры RMI использует HTTP-туннелирование, помещая вызовы RMI в запрос HTTP POST.

Теперь исследуем, как работает HTTP-туннелирование трафика RMI, более детально рассмотрев возможные
сценарии: RMI-клиент, сервер или оба вместе могут исполняться за брандмауэром. На следующей
диаграмме показан сценарий, когда RMI-клиент, расположенный за брандмауэром, взаимодействует с
внешним сервером.

В этом сценарии транспортный уровень при попытке установить соединение с сервером блокируется
брандмауэром. Если это происходит, транспортный уровень RMI автоматически повторяет попытку,
помещая данные вызова JRMP в запрос HTTP POST. Заголовок HTTP POST для вызова выглядит так:

http://hostname:port

Если клиент находится за брандмауэром, важно также установить в соответствующее значение системное
свойство http.proxyHost. Поскольку большинство брандмауэров распознают HTTP-протокол,
указанный прокси-сервер должен быть способен направлять запрос прямо в порт, который прослушивает
удаленный сервер. Как только заключенные в HTTP данные JRMP принимаются сервером, они
автоматически декодируются и отправляются по назначению транспортным уровнем RMI. Ответ также
передается клиенту в виде заключенных в HTTP данных.

На следующей диаграмме показан сценарий, когда и клиент и сервер находятся за брандмауэром, или когда
прокси-сервер клиента может направлять данные только в HTTP-порт, имеющий номер 80.

В этом случае транспортный уровень RMI использует еще один дополнительный обходной уровень.
Причиной этого является то, что клиент не может больше передавать заключенные в HTTP JRMP-вызовы в
произвольные порты, поскольку сервер находится за брандмауэром. Вместо этого, транспортный уровень
RMI размещает JRMP-вызов внутри HTTP-пакетов и передает эти пакеты в порт 80 сервера. Заголовок
HTTP POST имеет теперь следующую форму:

http://hostname:80/cgi-bin/java-rmi?forward=<port>

Это вызывает выполнение сценария CGI, java-rmi.cgi, который в свою очередь вызывает локальную
JVM, распаковывает HTTP-пакет и направляет вызов серверному процессу в назначенный порт. RMI JRMP-
ответы от сервера передаются назад как пакеты HTTP REPLY в нужный порт клиента, где RMI опять
распаковывает информацию и передает ее в соответствующую заглушку RMI.

Естественно, для того, чтобы все это работало, сценарий java-rmi.cgi, который включен в стандартный
JDK 1.1 или в платформу Java 2, должен: быть предварительно сконфигурирован, указывать путь к
интерпретатору Java и располагаться в каталоге веб-сервера cgi-bin. Также очень важно при запуске RMI-
сервера указывать полное доменное имя хоста в системном свойстве для устранения любых проблем
разрешения DNS-имен, например:

java.rmi.server.hostname=host.domain.com

Примечание: Вместо использования CGI-сценария для направления вызова более эффективной является
реализация той же задачи с использованием сервлета. Вы можете получить исходный код такого сервлета из
RMI FAQ от Sun (http://java.sun.com/products/jdk/1.2/docs/guide/rmi/faq.html).

Необходимо отметить, что, несмотря на встроенный механизм прохождения через брандмауэр, RMI
испытывает значительное снижение производительности из-за HTTP-туннелирования. Существуют также и
другие трудности при использовании HTTP-туннелирования. Например, ваше RMI-приложение не сможет
больше мультиплексировать JRMP-вызовы в одном соединении, поскольку в этом случае применяется
дискретный протокол типа запрос-ответ. Кроме того, использование сценария java-rmi.cgi открывает
довольно большую дыру в безопасности вашего сервера, так как теперь сценарий может перенаправить
любой входящий запрос на любой порт, полностью обходя механизм защиты брандмауэра. Разработчики
должны также отметить, что использование HTTP-туннелирования запрещает RMI-приложениям
использовать обратные вызовы, что может быть основным ограничением при разработке. Поэтому, если
клиент обнаружит брандмауэр, он всегда может запретить имеющуюся по умолчанию возможность HTTP-
туннелирования, установив свойство:

java.rmi.server.disableHttp=true

Распределенная сборка мусора


Одним из преимуществ программирования для платформы Java является отсутствие беспокойства о
распределении памяти. JVM имеет автоматический сборщик мусора, который освобождает память,
занимаемую любым объектом, который больше не используется исполняющейся программой.

Одним из требований к разработке RMI была ее бесшовная интеграция в язык программирования Java,
включая и сборку мусора. Разработка эффективного сборщика мусора для одной машины является тяжелой
задачей; разработка распределенного сборщика мусора является очень тяжелой задачей.

RMI-система обеспечивает подсчитывающий ссылки алгоритм распределенной сборки мусора, основанный


на сетевых объектах, используемых в Modula-3. Эта система при работе следит за тем, какие клиенты
запросили доступ к удаленным объектам, выполняющимся на сервере. Когда появляется ссылка, сервер
помечает объект как «грязный2, а когда клиент удаляет ссылку, объект помечается как «чистый».

Интерфейс к DGC (распределенный сборщик мусора) скрыт на уровне заглушек и скелетов. Однако
удаленный объект может реализовать интерфейс java.rmi.server.Unreferenced и получить
уведомление через метод unreferenced, когда нет больше ни одного клиента, содержащего живую
ссылку.

В дополнение к механизму подсчета ссылок живая ссылка в клиенте имеет срок аренды с указанным
временем. Если клиент не обновляет соединение к удаленному объекту до истечения срока аренды, ссылка
считается мертвой и удаленный объект может быть утилизирован сборщиком мусора. Время аренды
управляется системным свойством java.rmi.dgc.leaseValue. Его значение указывается в
миллисекундах и по умолчанию равно 10 минутам.

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

В следующем примере вы будете иметь возможность поэкспериментировать с распределенным сборщиком


мусора.

Упражнение
6. Распределенная сборка мусора

Сериализация удаленных объектов


При разработке системы, использующей RMI, бывают ситуации, когда вы хотели бы гибко управлять тем,
где работают удаленные объекты. В настоящее время если удаленный объект начинает работать на
определенной JVM, он будет оставаться на этой JVM. Вы не можете «передать» удаленный объект на
другую машину для работы на новом месте. RMI усложняет возможность локального или удаленного
запуска службы.

Та же самая причина, по которой RMI делает легкой разработку какого-либо распределенного приложения,
может сделать трудным перемещение объектов между JVM. Если вы объявляете, что объект реализует
интерфейс java.rmi.Remote, RMI будет защищать его от сериализации и передачи между JVM в
качестве параметра. Вместо передачи класса реализации для интерфейса java.rmi.Remote RMI
замещает класс заглушки. Так как это замещение происходит во внутреннем коде RMI, никто не может
перехватить эту операцию.

Существуют два различных пути для решения этой проблемы. Первый заключается в ручной сериализации
удаленного объекта и передачи его в другую JVM. Для этого есть две стратегии. Первая стратегия – создать
ObjectInputStream и ObjectOutputStream соединения между двумя JVM. При этом вы можете
явно поместить удаленный объект в поток. Второй путь – сериализовать объект в массив byte и передать
массив byte как возвращаемое значение в вызове RMI-метода. Оба этих способа требуют кодирования на
уровне ниже RMI, и это может привести к дополнительному кодированию и сложностям в эксплуатации.

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

- Не реализует java.rmi.Remote
- Реализует java.io.Serializable

Затем вы разрабатываете удаленный интерфейс, объявляющий удаленный доступ к своей


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

Теперь рассмотрим основные блоки этой модели. Обратите внимание, что это очень простой пример.
Реальный пример будет иметь значительное количество локальных полей и методов.

// Разместите функциональность в локальном объекте.


public class LocalModel
implements java.io.Serializable
{
public String getVersionNumber()
{
return "Version 1.0";
}
}

Затем вы объявляете интерфейс java.rmi.Remote, определяющий такую же функциональность:

interface RemoteModelRef
extends java.rmi.Remote
{
String getVersionNumber()
throws java.rmi.RemoteException;
}

Реализация удаленной службы принимает ссылку на LocalModel и делегирует реальную работу этому
объекту:

public class RemoteModelImpl


extends
java.rmi.server.UnicastRemoteObject
implements RemoteModelRef
{
LocalModel lm;

public RemoteModelImpl (LocalModel lm)


throws java.rmi.RemoteException
{
super();
this.lm = lm;
}

//Делегируйте в реализацию
//локальной модели
public String getVersionNumber()
throws java.rmi.RemoteException
{
return lm.getVersionNumber();
}
}

И, наконец, вы определяете удаленную службу, обеспечивающую доступ к клиентам. Это делается при
помощи интерфейса java.rmi.Remote и его реализации:

interface RemoteModelMgr extends java.rmi.Remote


{
RemoteModelRef getRemoteModelRef()
throws java.rmi.RemoteException;

LocalModel getLocalModel()
throws java.rmi.RemoteException;
}
public class RemoteModelMgrImpl
extends
java.rmi.server.UnicastRemoteObject
implements RemoteModelMgr
{
LocalModel lm;
RemoteModelImpl rmImpl;

public RemoteModelMgrImpl()
throws java.rmi.RemoteException
{
super();
}

public RemoteModelRef getRemoteModelRef()


throws java.rmi.RemoteException
{
// Инсталляция delgatee
if (null == lm)
{
lm = new LocalModel();
}

//Создание экземпляра
//Remote Interface Wrapper
if (null == rmImpl)
{
rmImpl = new RemoteModelImpl (lm);
}

return ((RemoteModelRef) rmImpl);


}

public LocalModel getLocalModel()


throws java.rmi.RemoteException
{
//Возвратить ссылку на
//ту же самую LocalModel,
//которая существует как delgatee
//RMI remote
//object wrapper

//Инсталляция delgatee
if (null == lm)
{
lm = new LocalModel();
}

return lm;
}
}

Упражнения
7. Сериализация удаленных объектов: Сервер
8. Сериализация удаленных объектов: Клиент

Архитектуры мобильных агентов


Использование RMI мобильными агентами находится пока еще в стадии разработки. По этой теме были
разработаны также другие распределенные архитектуры Java. Они называются архитектурами мобильных
клиентов. Например, Aglets Architecture от IBM, или Voyager System от ObjectSpace. Эти системы
разработаны специально для разрешения и поддержки перемещения между JVM объектов Java, содержащих
свои данные вместе с инструкциями.

Альтернативные реализации
Этот курс рассматривает реализацию архитектуры RMI от Sun. Существуют и другие реализации,
например:

• NinjaRMI
Свободно распространяемая реализация, созданная в University of California, Berkeley. Ninja
подерживает JDK 1.1 версию RMI с расширениями.
• BEA Weblogic Server
BEA Weblogic Server является высокопроизводительным, безопасным сервером приложений,
поддерживающим RNI, Microsoft COM, CORBA, EJB (Enterprise JavaBeans) и другие службы.
• Voyager
Voyager от ObjectSpace поддерживает RMI вместе с собственной службой DOM, CORBA, EJB,
Microsoft DCOM и службы транзакций.
Дополнительные ресурсы
Книги и статьи

• Design Patterns, by Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides (The Gang of Four)
• Sun's RMI FAQ
• RMI over IIOP
• RMI-USERS Mailing List Archive
• Implementing Callbacks with Java RMI, by Govind Seshadri, Dr. Dobb's Journal, March 1998

Упражнения
Добро пожаловать в раздел «Упражнения jGuru» курса «Основы RMI».

Эти упражнения иллюстрируют использование библиотеки Remote Method Invocation (RMI) для реализации
распределенных объектов.

После выполнения упражнений вы узнаете об основных концепциях построения распределенных систем


Java, основанных на RMI. Используя эти знания, вы сможете разрабатывать и реализовывать сложные,
многомашинные программы Java™.

Содержание упражнений
• Об упражнениях
o Анатомия упражнения
o Цели разработки упражнений

• Упражнения по основам RMI


1. Пример UML-определения системы RMI
2. Простая банковская система
3. Параметры RMI
4. Обратные вызовы RMI-клиента
5. Пример начальной загрузки
6. Распределенная сборка мусора
7. Сериализация удаленных объектов: сервер
8. Сериализация удаленных объектов: клиент

Об упражнениях
Упражнение jGuru является гибким упражнением, обеспечивающим различные уровни помощи,
соответствующие требованиям студентов. Некоторые студенты могут выполнить упражнение, используя
только информацию и список задач в теле упражнения; некоторые захотят просмотреть несколько советов
(Помощь); в то время как остальные захотят использовать пошаговое руководство для успешного
выполнения упражнения (Решение). Но так как в дополнение к помощи предоставляется полное решение,
они смогут выполнить другие упражнения, зависящие от пропущенных.

Анатомия упражнения
Каждое упражнение включает список требуемых упражнений, скелетный код для начала, ссылки на
необходимые страницы API и текстовое описание образовательной цели упражнения. В дополнение, кнопки
отсылают вас к следующей информации:

• Помощь: Помощь или советы по текущему упражнению и аннотированное решение. Для простоты
использования информация о задаче на странице помощи дублирует фактическую информацию,
вставленную ниже ее.
• Решение: Тэг <applet> и исходный код Java приводят к ожидаемому поведению.
• Документация по API: Прямая ссылка на любую необходимую документацию по API.

Цели разработки упражнения


Существуют три фундаментальных типа упражнений, с которыми вы можете столкнуться:

«Чистый экран»

Вы находитесь напротив «чистого экрана» и создаете всю требуемую функциональность полностью


самостоятельно.

Расширение

Вы расширяете функциональность существующей, корректно работающей программы.

Восстановление

Вы исправляете нежелательное поведение существующей программы.

Для того чтобы облегчить обучение, упражнения, там где возможно, адресуются только к определенным
приемам, для обучения которым и создавалось это упражнение. Несущественные, несвязанные и слишком
сложные материалы не используются.

Там где это возможно, упражнения выполняются в Web. Однако упражнения, которые требуют доступа к
функциям Java или библиотечным функциям, способным вызвать нарушение безопасности, не выполняются
в Web.

Упражнения по основам RMI


1. Пример UML-определения системы RMI

Это упражнение познакомит вас с определением удаленных служб RMI с использованием


интерфейсов Java.

Цели обучения:
• Познакомиться с UML-определением банковской системы
• Закончить исходный код Java для системных интерфейсов

2. Простая банковская система

В этом упражнении вы запустите вашу первую RMI-систему. Она основывается на банковской


системе, созданной в предыдущем упражнении.

Цели обучения:
• Научиться запускать RMI Registry как отдельный процесс
• Запустить сервер, поддерживающий удаленные объекты RMI
• Реализовать RMI-клиент, использующий удаленные службы

3. Параметры RMI

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

Цели обучения:
• Попрактиковаться в определении и реализации удаленных интерфейсов
• Изучить, как удаленные объекты и нормальные объекты передаются в качестве параметров

4. Обратные вызовы RMI-клиента


В некоторых RMI-системах серверу может понадобиться сделать вызов в клиентскую программу.
Это может быть сделано при использовании обратных вызовов клиента.

Построить клиентские обратные вызовы с RMI очень легко. Можно просто создать на клиенте
удаленный объект и передать ссылку на него серверу. Сервер затем может использовать эту
удаленную ссылку для вызова методов в клиентской программе.

Цели обучения:
• Изучить, как создать удаленные объекты на клиенте
• Подготовить и экспортировать удаленные объекты, прямо не расширяющие интерфейс
java.rmi.server.UnicastRemoteObject

5. Пример начальной загрузки

Это упражнение содержит три компонента: начальный загрузчик HTTP-сервера, начальный


загрузчик RMI-сервера и начальный загрузчик RMI-клиента. Эти компоненты покажут, как RMI
поддерживает распределенные, загружаемые по требованию файлы классов посредством FTP и
HTTP-серверов.

Сначала вы запустите сервер, который передает файлы классов Java по протоколу HTTP.

Затем вы создадите и запустите начальный загрузчик RMI-сервера. Этот сервер очень похож на
другие серверы, которые вы запускали. Главным его отличием в данном упражнении является то,
что код RMI-клиента находится в том же каталоге, что и сервер. Передача классов клиента на
клиентский компьютер является задачей HTTP-сервера.

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


программы является запуск и затем запрос «реальной» клиентской программы по сети. Запросы для
классов Java будут обслуживаться HTTP-сервером, созданным ранее в этом упражнении.

Цели обучения:
• Рассмотреть классы поддержки HTTP-сервера для RMI-системы
• Рассмотреть, как написать простой HTTP-сервер
• Создать и запустить программу RMI-сервера
• Создать программу начальной загрузки клиентской программы по сети
• Запустить начальный загрузчик клиента и рассмотреть, как программа HTTP-сервера
сообщает о запрошенных и переданных классах
• Изучить системное свойство java.rmi.server.codebase

6. Распределенная сборка мусора

Распределенный и локальный сборщики мусора работают совместно для управления динамической


памятью в каждой Java™ Virtual Machine (JVM), работающей в RMI-системе. В этом упражнении
вы будете иметь возможность поэкспериментировать с RMI-сервером и двумя RMI-клиентами для
исследования управления динамической памятью.

Цели обучения:
• Исследовать поведение распределенного сборщика мусора
• Исследовать взаимодействие распределенного и локального сборщиков мусора
• Поэкспериментировать с различными установками для кучи Java и отметить, как это влияет
на действия двух сборщиков мусора

7. Сериализация удаленных объектов: сервер

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

Это простая версия архитектуры делегирования. Более изощренные архитектуры могут быть
построены путем расширения этого примера.
Упражнение состоит из двух частей. Сначала запустите сервер, а затем клиент для выполнения
примера.

Цели обучения:
• Понять, как RMI ограничивает возможность перемещения удаленных объектов между JVM
• Посмотреть, как использовать модель делегирования для преодоления этих ограничений

8. Сериализация удаленных объектов: клиент

Клиентская программа сделает запрос на обслуживание как удаленной ссылке так и локальной
копии объекта.

Цели обучения:
• Увидеть, как создается RMI-клиент, использующий либо удаленную ссылку для
обслуживания, либо локальную копию объекта, реализующего эту службу.

Пример UML-определения системы RMI


Это упражнение знакомит вас с определением удаленных служб RMI при помощи интерфейсов Java. Вы
будете использовать простую банковскую систему, определенную в следующей UML-диаграмме:

Скелетный код
Account.java
BankManager.java
Client.java

Задачи
1. Исследовать UML-диаграмму для банковской системы

2. Закончить исходный код для определения интерфейсов в этой системе

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

Исходные коды решения


Account.java
BankManager.java
Client.java

Демонстрация
Никакого поведения еще не существует. Это упражнение включает только написание исходного кода.

Помощь
Помощь доступна для каждой задачи.

Задача 1
Исследовать UML-диаграмму для банковской системы.

Вы найдете UML-диаграмму в начале этого упражнения.

Задача 2
Закончить исходный код для определения интерфейсов в этой системе.

Проверьте, что каждый интерфейс расширяет Remote и что каждый метод объявляет, что он может
создавать исключительную ситуацию RemoteException.

Простая банковская система


Это упражнение ведет вас к созданию вашей первой RMI-системы.

В этом упражнении вы запустите RMI Registry (который будет управлять публикацией удаленных служб
RMI), запустите программу сервера, создающую фактические удаленные службы и, наконец, завершите
кодирование программы BankUser, которая будет использовать эти удаленные службы RMI.

Предварительные условия
Пример UML-определения системы RMI

Скелетный код
Account.class
AccountImpl.class
AccountImpl_Stub.class
AccountImpl_Skel.class
BankManager.class
BankManagerImpl.class
BankManagerImpl_Stub.class
BankManagerImpl_Skel.class
Client.class
ClientImpl.class
ClientImpl_Stub.class
ClientImpl_Skel.class
NoCashAvailableException.class
BankSystemServer.class
BankUser.java

Задачи
1. Запустите программу RMI Registry.

2. Скопируйте все .class–файлы в каталог.

3. Запустите RMI-сервер, который содержит удаленные объекты.

4. Создайте и запустите программу, которая будет использовать экспортированные RMI-службы.


Найдите владельца счета 4461 и определите сумму денег на этом счету.

Там, где существует файл помощи, номера вышеперечисленных задач ссылаются на страницу пошаговой
помощи.

После выполнения упражнения вы получите вашу первую запущенную RMI-систему.

Она состоит из трех основных частей:

• Программа RMI Registry, которая содержит ссылки на удаленные службы.


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

Исходные коды решения


Account.java
Account.class
AccountImpl.java
AccountImpl.class
AccountImpl_Stub.class
AccountImpl_Skel.class
BankManager.java
BankManager.class
BankManagerImpl.java
BankManagerImpl.class
BankManagerImpl_Stub.class
BankManagerImpl_Skel.class
BankUser.java
BankUser.class
Client.java
Client.class
ClientImpl.java
ClientImpl.class
ClientImpl_Stub.class
ClientImpl_Skel.class
NoCashAvailableException.java
NoCashAvailableException.class
BankSystemServer.java
BankSystemServer.class

Демонстрация
Когда запущены RMI Registry и RMI-сервер, нет никаких видимых признаков этого. Когда запущена
клиентская программа BankUser, на экране отобразится сообщение, которое вы включили в программу. В
данном случае отобразится следующее сообщение: Charlie's account has $600.00
Помощь
Помощь доступна для каждой задачи.

Задача 1
Запустите программу RMI Registry.

Программа RMI Registry поставляется как отдельный исполняемый файл rmiregistry. Создайте
консоль и перейдите в каталог, содержащий ваш исходный код из этого упражнения. Запустите реестр,
выполнив следующую команду:

rmiregistry

Задача 2
Скопируйте все .class–файлы в каталог.

Нажмите правую кнопку мыши на каждом файле для его сохранения. Исходные файлы предоставлены с
решением, на случай, если вы не захотите написать его самостоятельно.

Задача 3
Запустите RMI-сервер, который содержит удаленные объекты.

В этом упражнении сервер и экспортируемые удаленные объекты написаны за вас.


Вам нужно просто запустить хост-программу BankSystemServer.

Это осуществляется путем создания новой консоли, переходом в каталог, содержащий код этого
упражнения и выполнением следующей команды: java BankSystemServer

Задача 4
Создайте и запустите программу, которая будет использовать экспортированные RMI-службы. Найдите
владельца счета 4461 и определите сумму денег на этом счету.

Скелетный код для клиентской программы BankUser предоставляется.

Завершите код для BankUser, используя в качестве ориентиров определения интерфейсов удаленных
служб. Затем откомпилируйте и запустите эту программу для проверки RMI-системы в действии.

Параметры RMI
В этом примере вы создадите RMI-службу, возвращающую два объекта клиенту. Как и в упражнении
«Простая банковская система» вам необходимо изменить константу HOST_NAME для приведения ее в
соответствие с сетевым именем вашего компьютера.

Вы также расширите интерфейс Hello и класс HelloImpl для передачи объекта Java MessageObject из
сервера клиенту.

Во время этих изменений обратите особое внимание на MessageObject. Он определяет и переменную


класса и переменную объекта, которые инкрементируются каждый раз при создании экземпляра класса.
Наблюдая, как эти счетчики инкрементируются на сервере и клиенте, вы можете заметить, что RMI
посылает между JVM информацию об экземпляре объекта, а не статическую информацию или информацию
уровня класса.

Материалы курса по этому упражнению рассмотрены в разделе «Параметры в RMI».


Предварительные условия
Простая банковская система

Скелетный код
Hello.java
HelloImpl.java
MessageObject.java
RMIServer.java
RMIClient.java

Задачи

1. Добавьте метод getMessageObject в интерфейс Hello

2. Создайте реализацию метода getMessageObject в классе HelloImpl

3. Откомпилируйте все файлы классов для сервера и его удаленных объектов

4. Создайте файлы заглушки и скелета для реализации удаленного объекта

5. Запустите RMI-сервер в отдельной DOS-консоли

6. Запустите RMI-клиент в отдельной DOS-консоли

Там, где существует файл помощи, номера вышеперечисленных задач ссылаются на страницу пошаговой
помощи.

Исходные коды решения


Hello.java
HelloImpl.java
MessageObject.java
RMIServer.java
RMIClient.java

Демонстрация
Когда сервер запустится в своей DOS-консоли, отобразится следующая информация:

Registry created on host computer <your_computer_name> on port 10002 Remote


HelloService implementation object created Bindings Finished, waiting for
client requests.

Затем, когда клиент запустится в своей DOS-консоли, появится следующая информация:

HelloService lookup successful The server says: Hello!

отображая сообщение («Hello»), которое вы определили в реализации объекта.

Так как на вашем компьютере исполняется и сервер и клиент, на экране отображается сообщения из
MessageObject. На консоли сервера отображается:

MessageObject: Class Number is #0 Object Number is #0


MessageObject: Class Number is #1 Object Number is #1
MessageObject: Class Number is #2 Object Number is #2
MessageObject: Class Number is #3 Object Number is #3
MessageObject: Class Number is #4 Object Number is #4
MessageObject: Class Number is #5 Object Number is #5
MessageObject: Class Number is #6 Object Number is #6
MessageObject: Class Number is #7 Object Number is #7
MessageObject: Class Number is #8 Object Number is #8
MessageObject: Class Number is #9 Object Number is #9

На консоли клиента отображается:

MessageObject: Class Number is #0 Object Number is #0


MessageObject: Class Number is #0 Object Number is #1
MessageObject: Class Number is #0 Object Number is #2
MessageObject: Class Number is #0 Object Number is #3
MessageObject: Class Number is #0 Object Number is #4
MessageObject: Class Number is #0 Object Number is #5
MessageObject: Class Number is #0 Object Number is #6
MessageObject: Class Number is #0 Object Number is #7
MessageObject: Class Number is #0 Object Number is #8
MessageObject: Class Number is #0 Object Number is #9

Обратите внимание на различие в номерах класса.

Когда экземпляр MessageObject перемещается между сервером и клиентом, две JVM загружают свои
собственные независимые копии файла класса. Переменная класса инкрементируется на сервере, но это
никак не отражается на клиенте.

Помощь
Помощь доступна для каждой задачи.

Задача 1
Добавьте метод getMessageObject в интерфейс Hello.

В файле Hello.java добавьте следующий метод для определения интерфейса: MessageObject


getMessageObject() throws RemoteException;

Задача 2
Создайте реализацию метода getMessageObject в классе HelloImpl.

Добавьте удаленный метод getMessageObject и возвратите новый экземпляр класса


MessageObject.

Вы можете использовать следующий код:

public MessageObject getMessageObject()


throws RemoteException
{
return new MessageObject();
}

Задача 3
Откомпилируйте все файлы классов для сервера и его удаленных объектов.

Используйте программу javac:

javac *.java
Задача 4
Создайте файлы заглушки и скелета для реализации удаленного объекта.

Используйте программу rmic с классом реализации HelloImpl.

rmic HelloImpl

Задача 5
Запустите RMI-сервер в отдельной DOS-консоли.

Запустите новую DOS-консоль. Перейдите из текущего каталога в каталог, содержащий ваши .java-
файлы для сервера. Запустите RMIServer, используя программу java:

java RMIServer

Задача 6
Запустите RMI-клиент в отдельной DOS-консоли.

Запустите новую DOS-консоль. Перейдите из текущего каталога в каталог, содержащий ваши .java-
файлы для клиента. Запустите RMIClient, используя программу java:

java RMIClient

Обратные вызовы RMI-клиента


В этом упражнении вы создадите клиентский апплет, который будет работать и как RMI-клиент, и как RMI-
сервер. Обычно апплеты работают только как клиенты, однако в некоторых случаях серверу может
понадобиться сделать обратный вызов в апплет. В этом упражнении сервер будет периодически вызывать
апплет и передавать ему текущую дату и время.

Для поддержки обратных вызовов, апплет должен действовать как RMI-сервер. Это осуществляется при
помощи экспорта и реализации удаленного интерфейса. Наш апплет определяет и реализует интерфейс
TimeMonitor. Он предназначен для вызова службой времени, обеспечивающей текущую дату и время.

Сервер не может сделать обратный вызов в апплет до тех пор, пока не узнает его расположение. Апплет
должен зарегистрировать себя на сервере. Он делает это при помощи метода сервера
registerTimeMonitor в TimeServer и передает ссылку на себя в интерфейс сервера.

В этом упражнении вам необходимо определить интерфейсы и реализации как для сервера, так и для
апплета.

Предварительные условия
Клиентские обратные вызовы RMI

Скелетный код
TimeMonitor.java
TimeServer.java
RMIServer.java
Applet1.java

Задачи
1. Определите и откомпилируйте интерфейс TimeMonitor
2. Определите и откомпилируйте интерфейс TimeServer

3. Закончите реализацию метода registerTimeMonitor в RMIServer

4. Закончите определение класса TimeTicker

5. Откомпилируйте сервер, RMIServer и создайте файлы заглушки и скелета

6. Подготовьте апплет для исполнения

7. Запустите RMI-сервер в отдельной DOS-консоли

8. Запустите апплет при помощи appletviewer

Там, где существует файл помощи, номера вышеперечисленных задач ссылаются на страницу пошаговой
помощи.

Исходные коды решения


TimeMonitor.java
TimeServer.java
RMIServer.java
Applet1.java

Демонстрация
Когда сервер запускается в DOS-консоли, отображается следующая информация:

Registry created
Bindings Finished
Waiting for Client requests

Когда апплет запускается в своей DOS-консоли, отображается следующая информация:

Exporting the Applet


Looking up TimeService at:
rmi://<your_computer_name>:10005/TimeServer
We have been registered!

Апплет появится в окне и начнет отображать текущую дату и время каждые две секунды.

Помощь
Помощь доступна для каждой задачи.

Задача 1
Определите и откомпилируйте интерфейс TimeMonitor.

Создайте интерфейс TimeMonitor, имеющий один метод. Этот метод называется tellMeTheTime.
Он принимает один параметр типа Date и возвращает void.

Помните, что удаленные интерфейсы должны расширять java.rmi.Remote и все удаленные методы
могут генерировать java.rmi.RemoteException.

Откомпилируйте файл при помощи следующей команды:

javac TimeMonitor.java
Задача 2
Определите и откомпилируйте интерфейс TimeServer.

Создайте интерфейс TimeServer, имеющий один метод. Этот метод называется


registerTimeMonitor. Он принимает один параметр типа TimeMonitor и возвращает void.

Помните, что удаленные интерфейсы должны расширять java.rmi.Remote и все удаленные методы
могут генерировать java.rmi.RemoteException.

Откомпилируйте файл при помощи следующей команды:


javac TimerServer.java

Задача 3
Закончите реализацию метода registerTimeMonitor в RMIServer.

Класс RMIServer реализует интерфейс TimeServer. Вам необходимо откомпилировать реализацию


registerTimeMonitor в RMIServer. Обратитесь к комментариям в исходном файле за подробной
информацией.

Задача 4
Закончите определение класса TimeTicker.

Класс TimeTicker определяется внутри файла для RMIServer. Вам необходимо откомпилировать
реализацию этого класса, после добавления вызова метода tellMeTheTime интерфейса
TimeMonitor. В этом методе выполняется обратный вызов в апплет.

Задача 5
Откомпилируйте сервер, RMIServer и создайте файлы заглушки и скелета.

Используйте программу javac для компиляции файла RMIServer:

javac RMIServer

Используйте программу rmic с RMIServer.

rmic RMIServer

Обратите внимание, что RMIServer реализует экспортируемую службу. Поскольку RMIServer


расширяет RMIServer, он не может также расширить UnicastRemoteObject. Вместо этого
RMIServer должен сделать явный вызов UnicastRemoteObject.exportObject перед тем, как
он сможет зарегистрировать себя в реестре. Этот вызов обычно делается автоматически в конструкторе
объекта UnicastRemoteObject.

Задача 6
Подготовьте апплет для исполнения.

Код апплета написан за вас. Вы должны исследовать его, чтобы понять, как реализуется интерфейс
TimeMonitor и как он подготавливает себя для использования в RMI с помощью вызова
UnicastRemoteObject.exportObject.

Прежде всего, вам необходимо откомпилировать его при помощи следующей команды:

javac Applet1.java
Далее, создайте файлы заглушки и скелета, используя программу rmic:

rmic Applet1

Задача 7
Запустите RMI-сервер в отдельной DOS-консоли.

Запустите класс RMIServer, используя програму java.

java RMIServer

Задача 8
Запустите апплет при помощи appletviewer.

Используйте программу appletviewer и выполните файл Applet.html. Этот HTML-файл


содержит команды для запуска Applet1.class.

appletviewer Applet.html

Пример начальной загрузки


Это упражнение содержит три компонента: начальный загрузчик HTTP-сервера, начальный загрузчик RMI-
сервера и начальный загрузчик RMI-клиента. Эти компоненты покажут, как RMI поддерживает
распределенные, загружаемые по требованию файлы классов посредством FTP и HTTP-серверов.
Сначала вы запустите простой, «легковесный» HTTP-сервер. Он работает как сетевой сервер для файлов
классов, которые требуются для RMI-клиента и RMI-сервера.

HTTP-сервер реализуется двумя классами, предоставленными Sun Microsystems. Файл


ClassLoader.java является абстрактным классом, а файл ClassFileLoader.java является классом
реализации для HTTP-сервера. Этот сервер очень простой и служит только для передачи файлов классов
Java.

Вы должны поместить файлы HTTP-сервера в подкаталог, отличный от каталога, содержащего файлы RMI-
сервера и клиента. После компиляции запустите HTTP-сервер из своего подкаталога и добавьте параметр
пути, который укажет HTTP-серверу переместить файлы классов из подкаталога RMI-сервера.

Затем вы создадите и запустите начальный загрузчик RMI-сервера. Этот сервер очень похож на другие
серверы, которые вы запускали. Главным его отличием в данном упражнении является то, что код RMI-
клиента находится в том же каталоге, что и код сервера. Обратите внимание, что все файлы уже созданы за
вас. Передача классов клиента на клиентский компьютер является задачей HTTP-сервера.

И, наконец, вы создадите RMI-программу начальной загрузки RMIClientLoader.java. Проверьте, что


этот файл расположен в отдельном каталоге, отличном от каталогов, содержащих файлы HTTP-сервера и
файлы RMI-клиента и RMI-сервера. Единственной целью этой программы является запуск и затем запрос
«реальной» клиентской программы по сети. Запросы для классов Java будут обслуживаться HTTP-сервером,
созданным ранее в этом упражнении.

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

Обратите внимание, что в этом упражнении важно запускать программы в следующей последовательности:

1. Начальный загрузчик HTTP-сервера


2. Начальный загрузчик RMI-сервера
3. Начальный загрузчик RMI-клиента
Это упражнение является примером модели распространения классов, называемой «Начальная загрузка».
Эта модель рассматривается в разделе «Автоматическое распространение классов» данного курса.

Скелетный код
ClassServer.java
ClassFileServer.java
runhttp.bat
MessageObject.java
Hello.java
RMIServer.java
HelloImpl.java
runserver.bat
RMIClient.java
RMIClientBootstrapSecurityManager.java
RMIClientLoader.java
runclient.bat

Задачи
1. Сохраните два файла программы и командный файл runhttp.bat в какой-нибудь каталог и
откомпилируйте файлы HTTP-сервера

2. Запустите HTTP-сервер

3. Откомпилируйте все файлы классов для клиента и сервера

4. Создайте файлы заглушки и скелета для реализации удаленного объекта

5. Запустите программу RMI-сервера

6. Откомпилируйте файлы классов для программы начальной загрузки

7. Отредактируйте командный файл runclient.bat, включив имя вашего компьютера

8. Запустите командный файл runclient.bat

Там, где существует файл помощи, номера вышеперечисленных задач ссылаются на страницу пошаговой
помощи.

Исходные коды решения


ClassServer.java
ClassFileServer.java
runhttp.bat
MessageObject.java
Hello.java
RMIServer.java
HelloImpl.java
runserver.bat
RMIClient.java
RMIClientBootstrapSecurityManager.java
RMIClientLoader.java
runclient.bat

Демонстрация
Когда HTTP-сервер запускается в DOS-консоли, отображается следующая информация:
ClassFileServer started...

Когда клиент запускается и загружает файлы из HTTP-сервера, обратите внимание, что HTTP-сервер
отображает файлы, которые он читает с диска. Первая программа, которую он передает, представляет собой
собственно клиентскую программу RMIClient. После этого, он передает файлы заглушки и другие файлы
классов, необходимые для работы клиента.

Когда RMI-сервер запускается в DOS-консоли, отображается следующая информация:

Registry created on host computer localhost on port 10009


Remote Hello implementation object created
Bindings Finished, waiting for client requests.

Наконец, когда клиентская программа начальной загрузки выполняется в своей консоли, отображается
следующая информация:

http://ROSA:2002/
After loading Client Class
Message from Server: Hello!
MessageObject: Class Number is #0 Object Number is #0
MessageObject: Class Number is #0 Object Number is #1
MessageObject: Class Number is #0 Object Number is #2
MessageObject: Class Number is #0 Object Number is #3
MessageObject: Class Number is #0 Object Number is #4
MessageObject: Class Number is #0 Object Number is #5
MessageObject: Class Number is #0 Object Number is #6
MessageObject: Class Number is #0 Object Number is #7
MessageObject: Class Number is #0 Object Number is #8
MessageObject: Class Number is #0 Object Number is #9

В то же время вы можете заметить, что в консоли RMI-сервера отображается:

MessageObject: Class Number is #0 Object Number is #0


MessageObject: Class Number is #1 Object Number is #1
MessageObject: Class Number is #2 Object Number is #2
MessageObject: Class Number is #3 Object Number is #3
MessageObject: Class Number is #4 Object Number is #4
MessageObject: Class Number is #5 Object Number is #5
MessageObject: Class Number is #6 Object Number is #6
MessageObject: Class Number is #7 Object Number is #7
MessageObject: Class Number is #8 Object Number is #8
MessageObject: Class Number is #9 Object Number is #9

Помощь
Помощь доступна для каждой задачи.

Задача 1
Сохраните два файла программы и командный файл runhttp.bat в какой-нибудь каталог и
откомпилируйте файлы HTTP-сервера.

Используйте программу javac для компиляции всех файлов Java:

javac ClassServer.java
javac ClassFileServer.java

Задача 2
Запустите HTTP-сервер
Создайте новое консольное окно и перейдите в каталог, содержащий откомпилированные классы. Когда
вы запускаете HTTP-сервер, вы передаете ему два параметра. Первый – это номер порта, который он
прослушивает. Второй – это путь к каталогу, содержащему файлы классов RMI, предназначенные для
передачи по сети.

Используйте следующую команду для запуска HTTP-сервера:

java ClassFileServer 2002 <path to class file directory>

В предоставленном пакетном файле runhttp.bat перед его запуском требуется изменить


соответствующий путь.

Задача 3
Откомпилируйте все файлы классов для клиента и сервера

В отдельном консольном окне сохраните файлы клиента и сервера в отдельном подкаталоге и


откомпилируйте все исходные .java–файлы:

javac MessageObject.java
javac Hello.java
javac RMIServer.java
javac HelloImpl.java
javac RMIClient.java

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

Задача 4
Создайте файлы заглушки и скелета для реализации удаленного объекта

Используйте программу rmic с классом реализации HelloImpl.

rmic HelloImpl

Задача 5
Запустите программу RMI-сервера

Командный (пакетный) файл runserver.bat создан для того, чтобы помочь вам запустить RMI-
сервер. Отредактируйте этот файл и запишите в него корректную информацию для вашего компьютера,
а затем запустите сервер в отдельном консольном окне.

Задача 6
Откомпилируйте файлы классов для программы начальной загрузки

В отдельном консольном окне сохраните файлы клиентской программы начальной загрузки и запустите
командный файл runclient.bat в отдельном подкаталоге, скомпилируйте исходные .java-файлы:

javac RMIClientBootstrapSecurityManager.java
javac RMIServer.java

Единственной целью программы начальной загрузки является запуск и затем запрос «реальной»
клиентской программы по сети.

Задача 7
Отредактируйте командный файл runclient.bat, включив имя вашего компьютера
Измените имя «ROSA» на имя вашего компьютера. Командный файл будет готов к запуску.

Задача 8
Запустите командный файл runclient.bat.

Распределенная сборка мусора


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

В этом упражнении не требуется писать код, но вы должны изменить значение константы HOST_NAME на
соответствующее имя вашего компьютера. Вам также следует посмотреть на код классов этого примера,
особенно MessageObjectImpl, для того, чтобы понять, как он работает.

Упражнение содержит два удаленных объекта: Hello и MessageObject. Их реализации разработаны для
того, чтобы вывести сообщения при их создании, удалении ссылок на них, финализации и уничтожении.

Удаленный объект может реализовать интерфейс Unreferenced и его метод unreferenced. Этот метод
вызывается DGC во время удаления им последней ссылки на объект. MessageObjectImpl и HelloImpl
разработаны так, что выводят сообщение об этой ситуации.

MessageObjectImpl и HelloImpl также реализуют метод finalize. Он вызывается тогда, когда


локальный сборщик мусора собирается уничтожить объект и освободить занимаемую им память. В этой
реализации MessageObjectImpl и HelloImpl отображают сообщение об этом.

Материалы курса для этого упражнения рассмотрены в разделе «Распределенный сборщик мусора».

Для выполнения упражнения, запустите RMIServer и две копии клиента RMIClient.

При запуске упражнения вы, возможно, захотите поэкспериментировать с установками размера кучи Java
(используйте аргумент командной строки – mx) и явно установить значение leaseValue для удаленных
ссылок DGC. Для этого используйте следующую командную строку:

java -Djava.rmi.dgc.leaseValue=10000 RMIServer

где время для leaseValue указывается в миллисекундах.

Скелетный код
Hello.java
HelloImpl.java
MessageObject.java
MessageObjectImpl.java
RMIServer.java
RMIClient.java

Задачи
1. Измените значение константы HOST_NAME в RMIClient.java и RMIServer.java на
соответствующее имя вашего компьютера

2. Откомпилируйте все исходные Java-файлы

3. Из подкаталога Solution запустите сервер RMIServer с наименьшим возможным размером кучи

4. Запустите два экземпляра клиента


Там, где существует файл помощи, номера вышеперечисленных задач ссылаются на страницу пошаговой
помощи.

Исходные коды решения


Hello.java
HelloImpl.java
MessageObject.java
MessageObjectImpl.java
RMIServer.java
RMIClient.java

Демонстрация
Когда сервер запускается в DOS-консоли, отображается следующая информация:

Registry created on host computer ROSA on port 10007 Remote HelloService


implementation object created Bindings Finished, waiting for client requests.

Когда клиент запускается в DOS-консоли, отображается следующая информация:

HelloService lookup successful Message from Server: Hello!

При работе двух клиентов в каждой DOS-консоли будут отображаться сообщения.

На консоли сервера будет отображаться следующая информация:

MessageObject: Class Number is #1882 Object Number is #1882


MessageObject: Unreferenced for object #: 1841
MessageObject: Finalize for object #: 893

На каждой из клиентских консолей будет отображаться примерно следующее:

MessageObject: Class Number is #1882 Object Number is #1882

Исследуйте, как консоль сервера отображает процесс работы GDC и локального сборщика мусора.

Помощь
Помощь доступна для каждой задачи.

Задача 1
Измените значение константы HOST_NAME в RMIClient.java и RMIServer.java на
соответствующее имя вашего компьютера.

Задача 2
Откомпилируйте все исходные Java-файлы.

Используйте программу javac:

javac *.java

Задача 3
Из подкаталога Solution запустите сервер RMIServer с наименьшим возможным размером кучи.

Запустите новую DOS-консоль и перейдите в каталог, содержащий java-код.


Выполните следующую команду: java -mx1m RMIServer Она запустит Java Virtual Machine с
кучей, которая не может расти.

Задача 4

Запустите два экземпляра клиента.

Запустите две дополнительные DOS-консоли и перейдите в каталог с Java-кодом.


В каждой DOS-консоли подготовьтесь выполнить команду:

java RMIClient

но не нажимайте пока клавишу «Enter».

Когда готовы обе консоли, нажмите клавишу «Enter» в обеих, так чтобы запустить программу примерно
в одно и то же время.

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


когда он создает каждый объект. Когда каждый объект MessageObject теряет ссылку, сервер будет
выводить сообщение; также будет выводиться сообщение при уничтожении каждого MessageObject
локальным сборщиком мусора.

Заметьте, что локальный сборщик мусора не уничтожает объекты сразу, как только они закончили свою
работу. Вы также можете заметить, что после завершения демонстрации сервер полностью освобождает
все объекты примерно через 10 минут. Это указывает на то, что срок аренды этих удаленных объектов
был установлен в значение 10 минут. Так как клиенты завершили свою работу, сервер будет ждать
окончания срока аренды перед переводом объектов в состояние «чистый».

Сериализация удаленных объектов: сервер


В этом упражнении вы запустите серверную часть демонстрации RMI. Все файлы созданы за вас.

В этом упражнении вы увидите, как используется модель делегирования для разрешения миграции службы.
Эта тема описана в разделе «Cериализация удаленных объектов».

Скелетный код
RemoteModelMgr.java
RemoteModelMgrImpl.java
RemoteModelImpl.java
LocalRemoteServer.java
LocalModel.java
RemoteModelRef.java

Задачи
1. Откомпилируйте все файлы классов сервера

2. Создайте файлы заглушки и скелета для реализации удаленного объекта

3. Запустите программу RMI-сервера

Там, где существует файл помощи, номера вышеперечисленных задач ссылаются на страницу пошаговой
помощи.

После окончания этого упражнения перейдите к следующему для выполнения данного проекта.
Исходные коды решения
RemoteModelMgr.java
RemoteModelMgrImpl.java
RemoteModelImpl.java
LocalRemoteServer.java
LocalModel.java
RemoteModelRef.java

Демонстрация
Когда сервер запускается в DOS-консоли, отображается следующая информация:

Registry created on host computer ROSA on port 10009 RemoteModelImpl object


created Bindings Finished, waiting for client requests.

После работы клиентской программы вы увидите следующую информацию:

RemoteModelImpl...Constructor finished LocalModel...Returning: Version 1.0


RemoteModelImpl...Returning delegated VersionNumber: Version 1.0
LocalModel...Returning: Version 1.0 RemoteModelImpl...Returning delegated
VersionNumber: Version 1.0

Помощь
Помощь доступна для каждой задачи.

Задача 1
Откомпилируйте все файлы классов сервера.

В отдельной DOS-консоли перейдите в подкаталог server для этого примера. Откомпилируйте все java-
файлы, используя команду:

javac *.java

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

Задача 2
Создайте файлы заглушки и скелета для реализации удаленного объекта.

Используйте программу rmic с классом реализации RemoteModelMgrImpl и классом


RemoteModelImpl.

rmic RemoteModelMgrImpl
rmic RemoteModelImpl

Задача 3
Запустите программу RMI-сервера.

Используйте программу java для запуска сервера

java LocalRemoteServer

Сериализация удаленных объектов: клиент


В этом упражнении вы запустите клиентскую часть проекта сериализации удаленных объектов. Все файлы
созданы за вас.

В этом упражнении вы увидите, как используется модель делегирования для разрешения миграции службы.
Эта тема описана в разделе «Cериализация удаленных объектов».

Скелетный код
LocalRemoteClient.java

Задачи
1. Переместите файлы заглушек из подкаталога Server

2. Откомпилируйте все файлы классов для клиента

3. Запустите клиентскую программу RMI

Там, где существует файл помощи, номера вышеперечисленных задач ссылаются на страницу пошаговой
помощи.

Исходные коды решения


LocalRemoteClient.java

Демонстрация
Когда клиент запускается в DOS-консоли, отображается следующая информация:

RemoteModelManager lookup successful


Remote version: Version 1.0
Local Version of the model loaded
LocalModel...Returning: Version 1.0

Помощь
Помощь доступна для каждой задачи.

Задача 1
Переместите файлы заглушек из подкаталога Server.

Переместите следующие файлы в подкаталог Client:

LocalModel.class
RemoteModelMgrImpl_Stub.class
RemoteModelImpl_Stub.class
RemoteModelRef.class
RemoteModelMgr.class

Задача 2
Откомпилируйте все файлы классов для клиента.

В отдельной DOS-консоли перейдите в подкаталог Client для этого примера. Откомпилируйте все java-
файлы, используя команду:

javac *.java
Она откомпилирует все клиентские классы.

Задача 3
Запустите клиентскую программу RMI.

Используйте программу java для запуска сервера

java LocalRemoteClient

Вам также может понравиться