Введение в RMI

advertisement
Введение в RMI
Цели
Вы изучите:





Основы в сетевого взаимодействия
Эволюцию распределенных приложений
Компоненты и архитектуру RMI
Классы и интерфейсы пакетов RMI
Этапы создания и размещения приложения RMI
Основы в сетевого взаимодействия
Java был разработан как язык сетевого программирования, чтобы обеспечить
возможность создания клиент/серверных приложений, которые взаимодействуют
друг с другом через сеть. Java поддерживает обширную библиотеку сетевых
классов, которые позволяют быстро получать доступ к сетевым ресурсам.
Существуют два механизма пригодных для общения. Это датаграммы (datagrams),
которые используют пользовательский датаграммный протокол (User Datagram
Protocol) (UDP) и сокеты (sockets), которые используют протокол управления
передачей/протокол Интернет (Transmission Control Protocol/Internet Protocol)
(TCP/IP).
Архитектура клиент/сервер
В ресторане Вас встречает разнообразное меню экзотических блюд, и Вы
заказываете одну из разновидностей пиццу. Несколькими минутами позже Вы
поглощаете горячую пиццу с плавленым сыром. Вы не знаете или не желаете
знать, откуда официант получил пиццу, что входит в приготовление или каким
образом были получены ингредиенты.
Следующие объекты вовлечены в рассмотренный пример: вкусная пицца,
официант, который принимал Ваш заказ, и, конечно, Вы-потребитель. Вы
являетесь заказчиком или клиентом, который заказывал пиццу. Существует
процесс приготовления пиццы, который был скрыт от Вас. Ваш запрос был
обработан на кухне, и официант принес для Вас пиццу.
То, что мы описали, является клиент/серверной моделью. Клиент размещает
запрос или заказ на сервере, а сервер обрабатывает запрос клиента. Общение
между клиентом и сервером является важным компонентом клиент/серверной
модели и происходит обычно через сеть.
Клиент/серверная
модель
представляет
собой
архитектуру
разработки
приложения, разработанную с тем, чтобы отделить представление данных от их
внутренней обработки и хранения. Клиент запрашивает сервисы, и сервер
обслуживает эти запросы. Запрос передается от клиента на сервер через сеть.
Обработка, которая выполняется на сервере, скрывается о клиента и один сервер
может обслуживать несколько клиентов.
1
Лабораторная работа по RMI
Несколько клиентов, имеющих доступ к серверу
Сервер и клиент не обязательно являются компонентами оборудования. Это могут
быть выполняющиеся программы на одной и той же или различных машинах.
Рассмотрим информационный вход системы резервирования авиабилетов. Данные
– имя пассажира, номер рейса и дата рейса и пункт назначения – могут быть
введены в приложение в интерфейсной части (front-end) – клиенте. После ввода
данных клиент передает их на прикладную часть (back-end) – сервер для
дальнейшей обработки.
Серверная
часть
клиент/серверного
приложения
управляет
ресурсами,
распределенными между несколькими пользователями, которые получают доступ
к серверу среди прочих клиентов. Лучшим примером демонстрации серверной
части клиент/серверной программы может быть Веб сервер, который отправляет
HTML страницу через Интернет различным пользователям в ответ на их запрос.
Основополагающее предназначение Java как языка программирования состоит в
переносимости программ, созданных на Java. Переносимость, которой нет в
других языках программирования, позволяет пользователям Java писать
приложение однажды, распространять его на любые клиентские системы и
позволять клиентским системам выполнять программу. Это означает, что Вы
должны писать только одну версию кода, которая будет выполняться на любой
платформе.
Протоколы
Когда Вы общаетесь с Вашим другом (подругой), Вы соблюдаете некоторые
неписанные правила (или протоколы общения). Например, Вы оба не
разговариваете одновременно и не разговариваете непрерывно без остановки.
Никто из Вас не мог бы понять, что говорит другой человек, если Вы будете
следовать этим правилам. Когда Вы говорите, Ваш друг слушает и наоборот. Вы
говорите на языке и в темпе, который оба понимают.
Когда компьютеры общаются, им необходимо также следовать определенным
правилам. Данные посылаются от одной машины на другую в форме пакетов
(packets). Правила управляют упаковкой данных в пакеты, скоростью передачи и
разборкой данных в их исходную форму. Эти правила называются сетевыми
протоколами. Сетевой протокол является набором правил и соглашений,
поддерживаемых системами, которые общаются через сеть. Сетевое программное
Лабораторная работа по RMI
2
обеспечение
обычно
реализуется
несколькими
располагающихся слоями, один над другим.
уровнями
протоколов,
IP адрес и порт
Сервер Интернет может рассматриваться как набор классов сокетов, которые
обеспечивают дополнительные возможности – обычно называемые сервисами.
Примерами сервисов являются электронная почта (electronic mail), сетевой
удаленный доступ (Telnet) и протокол передачи файлов (File Transfer Protocol)
(FTP). Каждый сервер связан с портом (port). Порт является числовым адресом,
через который обрабатывается запрошенный сервис, как например, запрос Веб
страницы.
Протокол TCP запрашивает два элемента данных: IP адрес и номер порта. Как
происходит, что, когда Вы вводите http://www.tpu.ru, Вы получаете домашнюю
страницу ТПУ? Интернет протокол (Internet protocol) (IP) обеспечивает логический
адрес, называемый IP-адресом каждого сетевого компьютера (устройства). IPадреса, обеспечиваемые Интернет, принимают определенную форму и каждый
является 32-разрядным числом, представляющим набор из четырех 8-разрядных
чисел, каждое в диапазоне значений от 0 до 255. ТПУ имеет свое
зарегистрированное имя, позволяющее www.tpu.ru представляться IP адресом
109.123.152.2.
Сервис именования домена (Domain Naming Service) ( DNS) является
сервисом, который переводит www.tpu.ru в 109.123.152.2. Это дает
Вам возможность вводить www.tpu.ru вместо необходимости помнить
цифровой IP адрес. Представьте себе необходимость помнить IP
адреса всех возможных сайтов!
Если номер порта не указан, используется порт сервера в файле сервиса. Каждый
протокол имеет номер порта по умолчанию, который используется, если номер
порта не указан.
Номер
порта
3
Приложение
21
FTP, которое передает файлы
23
Telnet, которое обеспечивает удаленный вход
25
SMTP, которое доставляет почтовые сообщения
67
BOOTP, которое обеспечивает конфигурацию во время загрузки
80
HTTP, которое передает Веб страницы
109
POP, которое дает возможность пользователям получать доступ к
почтовому адресу на удаленной системе
Лабораторная работа по RMI
Давайте посмотрим на URL еще раз:
http://www.tpu.ru
Первый компонент URL (который является, http) означает, что Вы используете
протокол передачи гипертекстовых файлов (Hypertext Transmission Protocol)
(HTTP), протокол для управления Веб документами. Если файл не задан,
большинство Веб серверов конфигурируются таким образом, чтобы представлять
файл с именем index.html или default.html. Следовательно, IP адрес и порт
являются определенными либо явной спецификацией всех частей URL, либо
использованием спецификации по умолчанию.
Сокеты
В клиент/серверном приложении сервер обеспечивает сервисы обработки
запросов базы данных или модификацию данных в базе данных. Общение,
которое происходит между клиентом и сервером должно быть надежным и
переданные данные не должны быть потеряны и быть доступны клиенту в той же
последовательности, в которой сервер их посылал.
TCP обеспечивает надежный канал соединения точка-точка (point-to-point) для
взаимодействия клиент/серверных приложений. Для взаимодействия по протоколу
TCP, программы клиента и сервера устанавливают соединение и связывают сокет.
Сокеты используются для установления коммуникационных связей между
приложениями по сети. Дальнейшее общение между клиентом и сервером
происходит через сокет.
Преимущество
модели
сокетов,
использующих
TCP,
над
другими
коммуникационными моделями, такими как NetBEUI и Apple Talk, состоит в том,
что сервер не находится под воздействием источника клиентских запросов. Он
обслуживает все запросы до тех пор, пока клиенты используют программное
обеспечение протокола TCP/IP. Это означает, что клиентом может быть компьютер
любого типа и не ограничивается платформами UNIX, Windows, DOS или MAC OS.
Следовательно, все компьютеры в сети, применяющие TCP/IP, могут общаться
друг с другом через сокеты.
Java был задуман как сетевой язык и это способствует упрощению сетевого
программирования, обеспечивая различные классы и интерфейсы в пакете
java.net.
Понятие распределенных приложений
В последнее время существуют три различных подхода к разработке приложений
- традиционный подход, клиент/серверный подход и компонентный подход.
В традиционном подходе существует единственное приложение, которое
управляет логикой представления, логикой обработки и взаимодействием с базой
данных. Такие приложения также называются монолитные приложения.
Недостатком такого подхода является то, что даже при незначительных
изменениях, расширении и развитии, требуемых в приложении, все приложение
должно перекомпилироваться и собираться снова, что делает очень высокой
стоимость сопровождения и распространения приложения.
Лабораторная работа по RMI
4
Стремление устранить недостатки традиционного подхода, привело к введению
клиент/серверной архитектуры (также называемой двух-слойной (two-tier)
архитектурой), в которой данные отделяются от клиентской стороны и хранятся
централизованно на компьютере, который функционирует как сервер. Логика
представления размещается на стороне клиента, а логика обработки
объединяется с логикой представления либо на стороне клиента, либо на стороне
сервера и содержит код для связи с базой данных. Если логика обработки
объединяется с логикой представления, то клиент называется толстым клиентом.
Если логика обработки объединяется с сервером базы данных, то сервер
называется толстым сервером.
Однако
клиент/серверная
недостатками:
архитектура
также
обладает
определенными

Любое изменение политики бизнеса требует изменений в логике обработки.
Чтобы изменить логику обработки, вся логика представления или код связи
с базой данных нуждается в изменении, в зависимости от места логики
обработки.

Реализованные приложения, использующие двухслойную архитектуру, могут
трудно масштабироваться из-за ограниченного количества доступных для
клиента соединителей с базой данных. Запросы соединения, превышающие
определенное количество, просто отбрасываются сервером.
Недостатки клиент/серверной архитектуры привели к разработке трехслойной
архитектуры, в которой логика представления размещается на стороне клиента,
доступ к базе данных контролируется на стороне сервера, и логика обработки
располагается между клиентом и сервером баз данных. Этот слой логики
обработки принадлежит серверу приложений (также называемый средним слоем
трехслойной компонентной архитектуры). Этот тип архитектуры называется
серверо-центрическим, поскольку он дает возможность компонентам приложения
выполняться на среднем слое сервера приложений, применяющего правила
обработки независимо от интерфейса представления и применения базы данных.
Эти компоненты могут быть разработаны с использованием любого языка
программирования, который позволяет создание компонентов. Компоненты могут
быть централизованы для упрощения процедур разработки, сопровождения и
развертывания. Поскольку средний слой управляет логикой обработки, нагрузка
распределяется между клиентом, сервером базы данных и сервером,
управляющим логикой обработки. Эта архитектура также обеспечивает
эффективный доступ к данным, поскольку проблема с ограничением соединений
базы данных минимизируется, так как база данных видит только слой логики
обработки и не всех ее клиентов. При этом в случае двухслойного приложения
соединение базы данных устанавливается заблаговременно и поддерживается, в
то время, как в трехслойном приложении соединение устанавливается только
когда требуется доступ к данным и освобождается, как только данные получены
или переданы на сервер.
5
Лабораторная работа по RMI
Распределенное приложение
Приложения, в которых логика представления, логика обработки и база данных
размещаются на нескольких компьютерах, называется распределенными
(distributed) приложениями.
Вызов удаленного метода
Одним из способов взаимодействия процессов, выполняющихся на различных
компьютерах является вызов удаленного метода (Remote Method Invocation)
(RMI). RMI представляет собой спецификацию, которая дает возможность одной
виртуальной Java машине - Java Virtual Machine - (JVM) вызывать методы в
объектах, расположенных в другой JVM. Эти две JVM, могут быть запущены на
различных компьютерах или выполняться в отдельных процессах одного
компьютера. RMI применяется на среднем слое трехслойного архитектурного
построения, и, таким образом, облегчает возможность программистам вызывать
распределенные компоненты в сетевой среде.
Для использования RMI, программисту нет необходимости знать
программирование сокета (альтернативы взаимодействия компьютеров в Java)
или многопоточное исполнение, и необходимо, единственно, концентрироваться
на разработку логики обработки.
Компоненты приложения RMI
Распределенное RMI приложение содержит два компонента:


RMI сервер
RMI клиент
Сервер RMI содержит объекты, методы которых должны вызываться удаленно.
Сервер создает несколько удаленных объектов и обеспечивает ссылку на эти
объекты в регистре RMI. Регистр RMI является сервисом, который выполняется на
сервере RMI. Удаленные объекты, созданные сервером регистрируются в регистре с
использованием уникальных имен. Клиент получает ссылку на один или несколько
удаленных объектов из регистра, просматривая имена объектов. Затем клиент
вызывает методы на удаленном объекте(ах) для получения доступа к серверу
удаленного
объекта(ов).
Рисунок
ниже
иллюстрирует
функциональность
приложений в RMI.
Лабораторная работа по RMI
6
Функциональность приложений в RMI
После получения клиентом ссылки на удаленный объект методы удаленного
объекта могут вызываться также как методы локального объекта и не существует
отличия, вызываются ли клиентом методы в удаленном объекте или в локальных
объектах.
Архитектура RMI
Архитектура RMI состоит из трех уровней:



Уровень стаб/скелет
Уровень удаленной ссылки
Транспортный уровень
Архитектура RMI
Уровень стаб/скелет
Уровень стаб/скелет (стаб/скелет) слушает вызовы удаленных методов,
сделанные клиентом и направляет эти вызовы на удаленные RMI сервисы на
сервере. Этот уровень содержит стаб и скелет соответственно на клиенте и на
сервере.
7
Лабораторная работа по RMI
Для вызова методов удаленного объекта, запрос на клиентской стороне
начинается со стаб, который представляет собой прокси (proxy) на стороне
клиента, представляющий удаленный объект. К нему ссылаются программы, как к
любому другому локальному объекту, выполняющемуся на клиенте и он
обеспечивает методы удаленного объекта. Стаб соединяет вызовы метода с
удаленным объектом через скелет, который используется на сервере.
Скелет представляет собой прокси на стороне сервера, который продолжает
общение со стаб посредством следующих операций:




Чтение параметров для вызова метода
Выполненияе вызова удаленного сервиса объекта реализации
Получение возвращаемого значения
Запись возвращенного значения обратно на стаб.
Уровень удаленной ссылки
Уровень удаленной ссылки (Remote Reference Layer) (RRL) интерпретирует и
управляет ссылками, сделанными клиентами к удаленному объекту на сервере.
Этот уровень представлен как на клиенте, так и на сервере. RRL на стороне
клиента принимает запросы для методов от стаб, который передается, как
упакованный поток данных на RRL на стороне сервера. Упаковка представляет
собой процесс, в котором параметры, переданные клиентом, преобразуются в
формат, который может быть передан по сети (маршалинг). RRL на стороне
сервера выполняет распаковку параметров, которые посланы удаленному методу
через скелетон. Распаковка представляет собой процесс, в котором обработанные
ранее упакованные параметры, переданные клиентом RRL клиентской стороны,
преобразуются в формат, который понимает скелет. Во время возвращения
значений от скелета, данные вновь обрабатываются маршалингом и передаются
клиенту через RRL со стороны сервера.
Транспортный уровень
Транспортный уровень представляет собой связь между RRL на стороне сервера и
RRL на стороне клиента. Транспортный уровень отвечает за установление нового
соединения и управление существующими соединениями. Он также отвечает за
управление удаленными объектами, которые размещаются в пространстве
адресов транспортного уровня. Ниже описываются шаги, используемые для
соединения клиента с сервером:
1. При получении запроса от RRL со стороны клиента, транспортный уровень
устанавливает соединение сокета (адрес-порт) с сервером через RRL со
стороны сервера.
2. Затем, транспортный уровень передает установленное соединение с RRL
на стороне клиента и добавляет ссылку на соединение в свою таблицу
соединений.
Лабораторная работа по RMI
8
Пакеты RMI
Пакеты RMI состоят из различных классов и интерфейсов для создания и запуска
различных распределенных приложений, используя RMI. Java содержит
следующие пакеты для вызова метода вызова:

Пакет java.rmi обеспечивает удаленный интерфейс и класс для доступа по
удаленным именам, зарегистрированным на сервере и менеджер
безопасности для RMI.

Пакет java.rmi.registry содержит классы и интерфейсы, которые
используются удаленным регистром.

Пакет The java.rmi.server содержит классы и интерфейсы, которые
применяются удаленными объектами, стабы и скелеты и поддерживают
общение RMI.

Пакет java.rmi.dgc содержит классы и интерфейсы, которые используются
RMI распределенным сборщиком мусора (RMI-distributed garbage collector).
Пакет java.rmi
Пакет
java.rmi
объявляет
интерфейс
Remote
и
классы
Naming
и
RMISecurityManager. Он также определяет ряд классов исключительных
ситуаций, которые используются с RMI.
Все удаленные объекты должны реализовать интерфейс Remote. Этот
интерфейс не содержит методов и используется для идентификации удаленных
объектов. Пакет java.rmi состоит из следующих классов:


9
Класс Naming: Содержит статические методы для доступа удаленных
объектов через URL, такие как:

rebind(): Связывает имя удаленного объекта с заданным URL и обычно
используются объектом сервера.


unbind(): Удаляет связь между именем объекта и URL.

list():Возвращает список URL, которые в настоящее время известны
RMI регистру.
lookup(): Возвращает удаленный объект, заданный URL и обычно
используется объектом клиента.
Класс RMISecurityManager: Определяет политику безопасности по
умолчанию для удаленного объекта стаб. Политика применяется только для
приложений. Апплеты используют класс AppletSecurityManager для RMI.
Метод setSecurityManager() класса System используется, чтобы назначить
объект RMISecurityManager в качестве менеджера безопасности, который
должен быть использован для стаб RMI.
Лабораторная работа по RMI
Пакет java.rmi определяет ряд исключительных ситуаций. Класс
RemoteException является родительским для всех исключительных ситуаций,
которые генерируются во время RMI.
Удаленным объектам, которые доступны локально, нет необходимости
вызывать исключительную ситуацию RemoteException.
Пакет java.rmi.registry
Пакет java.rmi.registry содержит интерфейсы Registry и RegistryHandler и
определяет местоположение регистра. Эти интерфейсы используются для
регистрации и получения доступа к удаленным объектам по именам. Объекты
Remote регистрируются, когда они связываются с процессом регистра хоста.
Процесс регистра запускается, когда выполняется команда start rmiregistry.
Команда определяет методы rebind(), unbind(), list() и lookup(), которые
используются классом Naming связывания имени и ссылки URL RMI.
Пакет java.rmi.server
Пакет java.rmi.server реализует интерфейсы и классы, которые поддерживают
как клиентскую и серверную части RMI. Пакет java.rmi.server состоит из
следующих классов и интерфейсов:

Класс RemoteObject: Реализует интерфейс Remote и обеспечивает
удаленную реализацию класса Object. Все объекты, которые реализуют
удаленные объекты, расширяют класс RemoteObject.

Класс RemoteServer: Расширяет класс RemoteObject и является общим
классом, который подразделяется по определенным типам реализаций
удаленных объектов.

Класс UnicastRemoteObject: Расширяет класс RemoteServer и обеспечивает
реализацию RemoteObject по умолчанию. Классы, которые реализуют
объект RemoteObject обычно подкласс объекта UnicastRemoteObject. Путем
расширения UnicastRemoteObject, подкласс может:


Использовать по умолчанию RMI для общения на основе транспортного
сокета.
Выполняться все время.

Класс RemoteStub: Обеспечивает абстрактную реализацию стаб на стороне
клиента. Статический метод setRef() используется, чтобы связать стаб на
стороне клиента с соответствующими ему удаленными объектами.

Интерфейс RemoteCall: Обеспечивает методы, которые используются всеми
стаб и скелет в RMI.

Интерфейс Skeleton: Обеспечивает метод для доступа к методам удаленного
объекта и этот интерфейс реализуется удаленными скелетом.
Лабораторная работа по RMI
10

Интерфейс Unreferenced: Дает возможность определить, когда клиент
больше не обращается к удаленному объекту. Этот интерфейс реализуется
классом Remote.
Пакет java.rmi.dgc
Пакет java.rmi.dgc содержит классы и интерфейсы, которые используются
распределенным сборщиком мусора. Серверная сторона распределенного
сборщика мусора реализует этот интерфейс. Этот пакет состоит:

Класс Lease, который создает объекты, которые используются для
отслеживания ссылок объектов.

Метод dirty(), который используется для отметки, что клиент ссылается к
удаленному объекту.

Метод clean(), который используется для отметки, что удаленная ссылка
завершена.
Распределенная сборка мусора
Java содержит распределенный механизм сборки мусора, который автоматически
уничтожает удаленные объекты, которые RMI-клиенты не используют. RMI
использует алгоритм подсчета ссылок сборки мусора для управления ссылками на
удаленные объекты. Уровни стаб и скелет используют интерфейс java.rmi.dgc
для реализации механизма распределенной сборки мусора. Удаленный объект,
который
применяет
интерфейс
java.rmi.server.Unreferenced
получает
извещение, когда ссылки клиента больше не существует. Когда больше не
существует локальной или удаленной ссылки на удаленный объект, применяется
сборка мусора.
Когда какой-либо новый удаленный объект вводится в JVM, выполняющийся
объект RMI класса Lease, увеличивает счетчик ссылок на единицу и отмечает
удаленный объект как измененный. Аналогично, когда существующий удаленный
объект удаляется из JVM, объект класса Lease уменьшает счетчик на единицу и
отмечает объект как чистый. В механизме подсчета ссылок удаленная ссылка
может быть не активной в JVM в течение выделенного времени. Системное
свойство java.rmi.dgc.leaseValue устанавливает выделенное время. Если
ссылка не возобновляется соединением к удаленному объекту в течение
завершения выделенного времени, RMI перераспределяет память, выделенную
удаленному объекту, используя механизм распределенной сборки мусора.
Выделенное время устанавливается в миллисекундах и по умолчанию значение
выделенного времени равняется 10 минутам.
Создание распределенных приложений, используя RMI
Распределенное приложение, использующее RMI, состоит из несколько файлов, в
том числе, файл интерфейса, файл сервера и файл клиента. В соответствии со
11
Лабораторная работа по RMI
спецификацией RMI, необходимо следовать определенным шагам для создания
распределенного приложение RMI:





Создается удаленный интерфейс.
Реализуется удаленный интерфейс.
Создается сервер RMI.
Создается клиент RMI.
Запускается приложение RMI.
Создание удаленного интерфейса
Удаленный интерфейс определяет методы, которые могут быть вызваны удаленно
клиентом. Удаленный интерфейс должен объявить каждый из методов, который
должен быть вызван из других JVM. Удаленные интерфейсы имеют следующие
характеристики:

Удаленный интерфейс должен быть объявлен public. Это делается потому,
что в большинстве приложений, клиент не является частью того же самого
пакета с удаленным интерфейсом.


Удаленный интерфейс расширяет интерфейс java.rmi.Remote.
Каждый метод должен объявлять java.rmi.RemoteException в своих
условиях активизируется, чтобы перехватывать ошибки сетевого соединения
сервера.
Можно использовать следующий код для определения удаленного интерфейса,
который представляет удаленный объект:
import java.rmi.*;
public interface Hello extends Remote
{
/* Объявляется удаленный метод */
public String sayHello() throws RemoteException;
}
В коде интерфейс Hello объявляет метод sayHello(), который определяет
RemoteException.
Если удаленно предполагается использовать несколько методов, то они должны
быть все описаны в составе удаленного объекта, например, следующим образом:
import java.rmi.*;
import java.util.List;
public interface RemoteServer extends Remote
{
public byte[] getPicture(int id) throws
RemoteException;
Лабораторная работа по RMI
12
public List<Picture> getPictureList() throws
RemoteException;
}
Реализация удаленного интерфейса
На сервере необходимо реализовывать удаленный интерфейс, чтобы создать
удаленный класс сервера, который обеспечивает информацию об объектах сервера.
Удаленный сервисный класс определяет все методы, которые объявлены в
удаленном интерфейсе. Необходимо импортировать java.rmi и java.rmi.server
и для этого можно использовать следующий фрагмент кода:
import java.rmi.*;
import java.rmi.server.*;
Удаленный сервисный класс расширяет класс UnicastRemoteObject для реализации
метода удаленного интерфейса. Класс UnicastRemoteObject расширяет класс
RemoteServer в java.rmi и определяет методы класса RemoteServer. Следующий
рисунок показывает иерархию класса UnicastRemoteObject:
Иерархия класса UnicastRemoteObject
Вы можете создать класс HelloImpl, реализуя удаленный интерфейс Hello после
импортирования пакетов Java. Вы можете использовать следующий фрагмент
кода, чтобы создать удаленный сервисный класс:
public class HelloImpl implements Hello extends
UnicastRemoteObject
{
. .
. .
}
Удаленный образец объекта должен быть импортирован. Это дает возможность
удаленному объекту получать доступ к запросам удаленного метода, прослушивая
13
Лабораторная работа по RMI
определенный
порт.
Так
как
класс
HelloImpl
UnicastRemoteObject, он экспортируется автоматически.
расширяет
класс
Если Ваш реализуемый класс уже расширяет класс отличный от
UnicastRemoteObject, Вам необходимо явно экспортировать удаленный
объект, вызывая метод UnicastRemoteObject.exportObject() из
конструктора реализации класса
Метод super() вызывает конструктор класса UnicastRemoteObject, который
экспортирует удаленный объект. Можно использовать следующий фрагмент кода,
чтобы определить конструктор удаленного сервисного класса по умолчанию:
public HelloImpl() throws RemoteException
{
super();
}
Вы определяете все методы удаленного интерфейса в удаленном сервисном классе
после определения конструктора по умолчанию. Можно использовать следующий
фрагмент кода, чтобы определить удаленный метод в удаленном сервисном классе:
public String sayHello() throws RemoteException
{
return "Hello! Peter Smith.";
}
Можно использовать следующий фрагмент кода, чтобы реализовать удаленный
интерфейс в удаленном сервисном классе:
import java.rmi.*;
import java.rmi.server.*;
public class HelloImpl extends UnicastRemoteObject implements Hello
{
/* Определяется конструктор по умолчанию */
public HelloImpl() throws RemoteException
{
super();
}
/* Определяется удаленный метод */
public String sayHello() throws RemoteException
{
return "Hello! Peter Smith.";
}
}
В предыдущем коде класс сервисный HelloImpl состоит из реализации метода
sayHello(), объявленного в интерфейсе Hello.
Создание сервера RMI
Класс сервера RMI содержит объекты, которые удаленно вызываются клиентом.
Необходимо создать объект удаленного класса сервера в методе main() класса
сервера RMI, чтобы создать объект сервера. Для создания объект сервера можно
использовать следующее утверждение:
Лабораторная работа по RMI
14
Hello h = new HelloImpl();
Необходимо зарегистрировать объект сервера в регистре самозагрузки перед тем,
как объект сервер будет готов принимать запросы от клиента. В RMI регистр
передаете имя и ссылку объекта сервера для регистрации объектов сервера. Имя
объекта сервера используется, чтобы получить доступ к объекту стаб,
использующему механизм поиска. Можно использовать следующий оператор для
регистрации объекта сервер в регистре:
Naming.rebind("server",h);
Метод rebind() принимает два параметра:

Первый параметр представляет собой стоку URL, которая содержит
местоположение и имя удаленного объекта. Если порт не задан, регистр RMI
использует по умолчанию порт 1099. Если пользователь должен определить
порт, строка URL должна быть изменена на "rmi://ipaddress: 1234/server".

Второй параметр является ссылкой на реализацию объекта.
Метод setSecurityManager() класса SecurityManager используется в классе
сервера, чтобы установить менеджера безопасности для приложения RMI, так что
любой клиент не может вызвать объект сервера. Чтобы установить подлинность
клиента RMI client, Вам необходимо создать файл политики безопасности, который
содержит все требуемые разрешения. Можно использовать следующий код, чтобы
создать сервер RMI для распределенного приложения:
import java.rmi.*;
import java.rmi.server.*;
public class HelloServer
{
public static void main(String args[])
{
try
{
/* Устанавливается менеджер безопасности */
System.setSecurityManager(new RMISecurityManager());
/* Создается удаленный объект */
Hello h = new HelloImpl();
/* Связывается удаленный объект с регистром RMI */
Naming.rebind("server",h);
System.out.println("Объект регистрируется.");
System.out.println("Сейчас сервер ожидает запроса
клиента...");
}
catch(Exception e)
{
System.out.println("Ошибка : "+e);
}
}
}
15
Лабораторная работа по RMI
В рассмотренном коде метод rebind() класса Naming генерирует удаленную
исключительную ситуацию. В результате, Вы определяете метод rebind()внутри
блока try-catch.
Политика безопасности
Файл политики является простым текстовым файлом, созданным утилитой Policy
Tool. Необходимо создать файл политики с именем .java.policy в своем HOME
подкаталоге, который содержит утверждения, дающие разрешения требуемым
объектам. Можно создать файл политики безопасности следующим образом:
1. Введите команду policytool в командной строке, чтобы отобразить
утилиту Policy Tool Java. Окно Policy Tool отображается с диалоговым
окном Error (Ошибка). (В последних версиях ошибка не возникает).
Следующий рисунок показывает окно Policy Tool:
Инструментальное окно политики
2. Щелкните кнопку OK диалогового окна Error. Затем щелкните кнопку Add
Policy Entry (Добавить политику) чтобы добавить новый файл политики
безопасности. Следующий рисунок отображает диалоговое окно Policy
Entry:
Лабораторная работа по RMI
16
Диалоговое окно ввода политики
3. Щелкните кнопку Add Permission (Добавить разрешение) чтобы
выдать требуемые разрешения на получение доступа к ресурсам сервера
клиентами. Следующий рисунок отображает диалоговое окно Permissions
(Разрешения):
Диалоговое окно разрешений
4. Выберите AllPermission из списка Permission диалогового окна
Permissions
5. Щелкните кнопку OK, чтобы закрыть диалоговое окно Permissions.
6. Щелкните кнопкуDone диалогового окна Policy Entry.
7. Выберите команду File->Save As в окне Policy Tool.
8. Сохраните файл политики безопасности как .java.policy в подкаталоге
HOME операционной системы. Сообщение подтверждения отображается в
окне сообщения Status. Следующий рисунок отображает окно сообщения
Status:
17
Лабораторная работа по RMI
Окно сообщения Status
9. Щелкните кнопку OK в окне сообщения Status.
10. Выберите команду File->Exit в окне Policy Tool, чтобы закрыть утилиту
Java Policy Tool.
Приложение RMI может быть также выполнено и без установки менеджера
безопасности. В этом случае, код не будет подвергаться проверке
безопасности перед его выполнением на JVM, и, таким образом, это может
привести к сбою безопасности.
Если клиент представляет собой апплет, Вы не должны устанавливать
менеджер безопасности, поскольку апплеты используют уже
установленный менеджер безопасности на браузере клиента. Однако, если
клиентом является приложение не апплет, необходимо установить
менеджер безопасности как изложено выше.
Создание клиента RMI
Клиент использует объект стаб для получения доступа к удаленному объекту,
который находится на сервере. Для поиска стаб имя объекта сервера задается в
методе lookup() класса java.rmi.Naming. Можно использовать следующий
оператор для получения доступа к объекту стаб, используя метод lookup():
Hello h = (Hello)Naming.lookup("rmi://192.208.174.40/server");
В предыдущем операторе метод lookup() генерирует удаленную исключительную
ситуацию и в результате метод lookup() следует окружить блоком try-catch.
Server представляет имя объекта сервера. Когда Вы запускаете это приложение
на локальном компьютере, то используется localhost вместо IP адреса сервера
RMI. Можно использовать следующий код для создания клиента RMI для
распределенного приложения:
import java.rmi.*;
public class HelloClient
{
public static void main(String args[])
{
try
{
/* Поиск удаленного объекта в регистре RMI */
Hello h =
(Hello)Naming.lookup("rmi://192.208.174.40/server");
System.out.println("Client: Hello!");
System.out.println("Server: " + h.sayHello());
}
catch(Exception e)
{
Лабораторная работа по RMI
18
System.out.println("Error : "+e);
}
}
}
После создания исходных файлов Java, все исходные файлы распределенного
приложения Hello компилируются, используя стандартный компилятор Java javac
для генерации файлов class.
После компиляции исходных файлов Java, должны быть сгенерированы стаб и
скелет, которые взаимодействуют с клиентом и сервером.
Выполнение приложения RMI
Перед запуском приложения объекты сервера необходимо зарегистрировать в
регистре RMI. Клиент может вызвать объекты, зарегистрированные на сервере, по
сети. Для выполнения RMI приложения необходимо выполнить следующие этапы:
1. Сгенерируйте стаб и скелет удаленного сервисного класса.
2. Запустите регистр RMI.
3. Запустите сервер RMI распределенного приложения.
4. Запустите клиент RMI распределенного приложения.
1. Генерация стаб и скелет
Компилятор RMI rmic компилирует удаленные сервисные классы, которые
реализуют удаленный интерфейс и генерирует стаб и скелет. Стаб дает
возможность клиенту общаться с определенным удаленным объектом. Скелет
представляет объект клиента, который размещается на удаленном хосте.
Следующая команда используется для генерирации стаб и скелет:
rmic [опции] <ClassFile>
В этой команде ClassFile представляет имя удаленного сервисного класса.
option представляет параметры, которые обеспечивают дополнительные
возможности RMI компилятора. Следующая таблица представляет различные
опции RMI компилятора:
Опции
19
Описание
-bootclasspath
<path>
Изменяет размещение файла класса автоматической
загрузки.
-classpath
<path>
Изменяет путь к классу переменных среды по умолчанию.
-d <directory>
Задает имя подкаталога, где необходимо генерировать
стаб и скелет. По умолчанию, стаб и скелет генерируются
на текущем накопителе.
Лабораторная работа по RMI
Опции
Описание
-depend
Компилирует все файлы, которые связаны с удаленным
сервисным классом.
-extdirs
<path>
Изменяет размещение установленных расширений.
-g
Генерирует номера линий и локальные переменные в
форме таблицы.
-keep
Сохраняет файл 'java', который генерирует стаб и
скелет.
-nowarn
Не отображает предупреждения, когда генерируется стаб
и скелет.
-vcompat
Создает стаб и скелет, который совместим с ранними
версиями протоколов RMI.
-verbose
Отображает сообщение, когда удаленный файл сервера
скомпилирован rmic.
-v <version>
Создает стаб и скелет для заданной версии Java (JDK)
Команда, чтобы сгенерировать стаб и скелет для приложения RMI:
rmic HelloImpl
Два файла, HelloImpl_Stub.class и HelloImpl_Skel.class, генерируются в
одном подкаталоге, в котором Вы сохраняете удаленный сервисный класс
HelloImpl. Вы можете проверить работу распределенного приложения на
локальном компьютере, сохраняя все файлы в одном подкаталоге перед
развертыванием его в сети. Для выполнения приложения в сети следует создать
два подкаталога: серверный подкаталог на серверном компьютере и клиентский
подкаталог на клиентском компьютере.
Следующие файлы, которые сохраняются серверном подкаталоге, на серверном
компьютере:




Hello.class
HelloImpl.class
HelloServer.class
HelloImpl_Stub.class
Следующие файлы, которые сохраняются в клиентском подкаталоге на
клиентском компьютере:
Лабораторная работа по RMI
20



Hello.class
HelloImpl_Stub.class
HelloClient.class
2. Запуск регистра RMI
Чтобы запустить регистр RMI на сервере, выполните команду пуск rmiregistry в
командной строке. По умолчанию регистр запускается на порт 1099.
Чтобы запустить регистр RMI на другой порт, Вам необходимо задать номер порта
в командной строке следующим образом:
start rmiregistry 1234
Если регистр запускается на порт отличный от 1099, Вы должны задать номер
порта в строке URL, заданный в методах rebind() и lookup() класса Naming.
Следующая команда для запуска регистра RMI на порт по умолчанию:
start rmiregistry
Предыдущая команда открывает новое окно Command Prompt в котором
выполняется rmiregistry.
Вы должны остановить и перезапустить сервис rmiregistry, когда
модифицируется удаленный интерфейс.
3. Выполнение сервера RMI
Сначала необходимо запустить сервер для обслуживания запросов клиента с
помощью следующей команды:
java HelloServer
Следующий рисунок отображает вывод приложения сервера RMI:
Выполнение приложения сервера RMI
4. Выполнение клиента RMI
Следующая команда запускает клиента RMI:
java HelloClient
Если Вы запускаете клиента с того же самого компьютера, задайте
команду в отдельном командном окне.
Следующий рисунок отображает вывод приложение клиента:
21
Лабораторная работа по RMI
Выполнение приложения RMI клиента
Клиент RMI вызывает метод sayHello() для вывода сообщения, полученного от
сервера RMI. Следующий рисунок отображает взаимодействие между клиентом и
сервером RMI:
Работа распределенного приложения, используя RMI
Реализация приложения RMI
Постановка задачи
Издательский дом Earnest имеет отделения во всех главных городах мира. Они
имеют центральную базу данных в Чикаго. Издательский дом хочет создать
приложение, которое будет позволять его авторам регистрироваться в
издательском доме. Данные, которые представляются авторами, должны
модифицироваться на сервере базы данных.
Решение
Для решения этой задачи необходимо создать распределенное приложение,
использующее RMI, которое содержит два модуля: клиент и сервер. Клиент
представляет собой приложение на основе графического интерфейса, которое
принимает данные авторов и посылает их на сервер, вызывая удаленный метод.
Лабораторная работа по RMI
22
Серверное приложение создает соединение с сервером базы данных и сохраняет
данные в базе данных. Для создания такого распределенного приложения,
использующего RMI следует выполнить следующие этапы:
1.
Создайте удаленный интерфейс AuthorServer.
2.
Создайте класс AuthorServerImpl.
3.
Создайте класс Client.
4.
Выполните приложение.
1. Создание удаленного интерфейса AuthorServer
Интерфейс AuthorServer объявляет удаленные объекты, которые будут вызываться
клиентом RMI. Интерфейс AuthorServer объявляет метод insertDetails(), который
хранит характеристики автора на сервере базы данных.
Следующий код создает интерфейс AuthorServer:
import java.rmi.*;
public interface AuthorServer extends Remote
{
/* Объявляется удаленный метод */
String insertDetails(String authorID, String lastName, String
firstName, String phone, String address, String city, String state,
String zip) throws RemoteException;
}
Сохраните предыдущий код как AuthorServer.java.
2. Создание класса AuthorServerImpl
Класс AuthorServerImpl сохраняет персонадьные данные авторов на сервере
базы данных. Для создания класса AuthorServerImpl необходимо выполнить
следующие этапы:






Реализуйте интерфейс ServerRemote.
Определите конструктор для удаленного объекта.
Выполните реализацию удаленных методов.
Создайте и установите менеджер безопасности.
Создайте экземпляр удаленного объекта.
Зарегистрируйте удаленный объект в регистре RMI.
Следующий создает класс AuthorServerImpl:
import java.rmi.*;
import java.rmi.server.UnicastRemoteObject;
23
Лабораторная работа по RMI
import java.sql.*;
public class AuthorServerImpl extends UnicastRemoteObject implements
AuthorServer
{
static ResultSet result;
static Connection con;
static PreparedStatement stat;
/* Определяется по умолчанию конструктор класса Impl */
public AuthorServerImpl() throws RemoteException
{
super();
}
/* Определение удаленного метода */
public String insertDetails(String authorID, String lastName, String
firstName, String phone, String address, String city, String state,
String zip) throws RemoteException
{
int rowsAffected = 0;
String sReturn = "fail";
try
{
/*Регистрация драйвера базы данных. */
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
/* Создание соединения. */
con=DriverManager.getConnection("jdbc:odbc:MyDataSource", "", "");
/* Создание prepareStatement для ввода значений в базу данных. */
stat=con.prepareStatement("insert into dbo_authors(au_id, au_lname,
au_fname, phone, address, city, state, zip) values (?, ?, ?, ?, ?, ?, ?,
?)");
stat.setString(1, authorID);
stat.setString(2, lastName);
stat.setString(3, firstName);
stat.setString(4, phone);
Лабораторная работа по RMI
24
stat.setString(5, address);
stat.setString(6, city);
stat.setString(7, state);
stat.setString(8, zip);
rowsAffected = stat.executeUpdate();
if(rowsAffected>0)
{
sReturn = "success";
}
}
catch(Exception v)
{
System.out.println("Ошибка при вводе значения" + v);
}
return sReturn;
}
/* Определение метода main()*/
public static void main(String args[])
{
/* Установка менеджера безопасности. */
System.setSecurityManager(new RMISecurityManager());
try
{
/* Создание экземпляра класса Impl. */
AuthorServerImpl instance = new AuthorServerImpl();
/* Связь объекта сервера с регистром RMI. */
Naming.rebind("AuthorServer", instance);
System.out.println("Сервер зарегистрирован");
}
catch(Exception e)
{
System.out.println("Ошибка при запуске сервера"+e);
25
Лабораторная работа по RMI
}
}
}
Сохраните код с именем AuthorServerImpl.java.
3. Создание класса Client
Класс Client получает ссылку на удаленный объект и вызывает удаленные
методы. Для создания класса Client, Вам необходимо выполнить следующие
задачи:


Создайте экземпляр класса Client, чтобы соединить его с регистром RMI.
Разместите серверный объект, который регистрируется на сервере RMI.
Следующий код для создания класса Client:
import javax.swing.*;
import java.rmi.*;
import java.awt.event.*;
import java.awt.*;
public class Client
{
/* Объявляются переменные */
static JFrame frame;
static JPanel panel;
static JPanel panel1;
JLabel labelAuthorID;
JLabel labelLastName;
JLabel labelFirstName;
JLabel labelPhone;
JLabel labelAddress;
JLabel labelCity;
JLabel labelState;
JLabel labelZip;
JTextField textAuthorID;
JTextField textLastName;
Лабораторная работа по RMI
26
JTextField textFirstName;
JTextField textPhone;
JTextField textAddress;
JTextField textCity;
JTextField textState;
JTextField textZip;
JButton submit;
static String authorID;
static String lastName;
static String firstName;
static String phone;
static String address;
static String city;
static String state;
static String zip;
/* Define the default constructor */
public Client()
{
/* Create the JFrame */
frame=new JFrame("Earnest Publishing House");
panel = new JPanel();
panel1 = new JPanel();
/* Set the Layout managers */
panel.setLayout(new GridLayout(8,2));
panel1.setLayout(new GridLayout(1,1));
frame.setVisible(true);
frame.setSize(400, 400);
frame.getContentPane().setLayout(new BorderLayout());
/* Определяем компоненты swing на JFrame */
27
Лабораторная работа по RMI
labelAuthorID = new JLabel("Author ID");
labelLastName = new JLabel("Last Name");
labelFirstName = new JLabel("First Name");
labelAddress = new JLabel("Phone");
labelPhone = new JLabel("Address");
labelCity = new JLabel("City");
labelState = new JLabel("State");
labelZip = new JLabel("Zip");
textAuthorID = new JTextField(5);
textLastName = new JTextField(15);
textFirstName = new JTextField(15);
textPhone = new JTextField(10);
textAddress = new JTextField(50);
textCity = new JTextField(10);
textState = new JTextField(10);
textZip = new JTextField(6);
submit = new JButton("Submit");
/* Добавляем компоненты swing на panel */
panel.add(labelAuthorID);
panel.add(textAuthorID);
panel.add(labelLastName);
panel.add(textLastName);
panel.add(labelFirstName);
panel.add(textFirstName);
panel.add(labelPhone);
panel.add(textPhone);
panel.add(labelAddress);
panel.add(textAddress);
panel.add(labelCity);
panel.add(textCity);
panel.add(labelState);
panel.add(textState);
Лабораторная работа по RMI
28
panel.add(labelZip);
panel.add(textZip);
/* Добавляем компоненты swing на panel1 */
panel1.add(submit);
submit.setHorizontalAlignment(SwingConstants.CENTER);
ButtonListener blisten = new ButtonListener();
submit.addActionListener(blisten);
frame.getContentPane().add(new JPanel(), BorderLayout.WEST);
frame.getContentPane().add(new JPanel(), BorderLayout.EAST);
frame.getContentPane().add(panel, BorderLayout.CENTER);
frame.getContentPane().add(panel1, BorderLayout.SOUTH);
frame.setVisible(true);
}
/* Create a ButtonListener class */
class ButtonListener implements ActionListener
{
/* Define the actionPerformed() method */
public void actionPerformed(ActionEvent evt)
{
JButton source=(JButton)evt.getSource();
MyDialog myDialog;
try
{
/* Поиск объекта на сервере */
AuthorServer server =
(AuthorServer)Naming.lookup("rmi://localhost/AuthorServer");
/* Получение данных об авторе из frame Client */
authorID=textAuthorID.getText();
lastName=textLastName.getText();
firstName=textFirstName.getText();
phone=textPhone.getText();
address=textAddress.getText();
29
Лабораторная работа по RMI
city=textCity.getText();
state=textState.getText();
zip=textZip.getText();
/* Вызов удаленного метода */
String str=server.insertDetails(authorID, lastName, firstName, phone,
address, city, state, zip);
System.out.println(str);
if(str.equals("success"))
{
/* Создаем объект класса MyDialog */
myDialog = new MyDialog(frame, "Inserted Successfully");
}
else
{
/* Создаем объект класса MyDialog */
myDialog = new MyDialog(frame, "No Record Inserted");
}
myDialog.setVisible(true);
}
catch(Exception e)
{
System.out.println(e);
}
}
}
/* Определяем метод main()*/
public static void main(String args[])
{
/* Создаем объект класса Client */
new Client();
}
}
Лабораторная работа по RMI
30
/* Создаем класс MyDialog */
class MyDialog extends Dialog implements ActionListener
{
/* Определяем конструктор по умолчанию для класса MyDialog */
MyDialog(Frame parent, String title)
{
super(parent, title, false);
setLayout(new FlowLayout());
setSize(200, 80);
add(new JLabel (title));
JButton btn_OK = new JButton("OK");
add(btn_OK);
btn_OK.addActionListener(this);
}
/* Определяем метод actionPerformed()*/
public void actionPerformed(ActionEvent ae)
{
dispose();
}
}
Сохраните предыдущий код с именем Client.java.
4. Выполнение приложения
Следующие шаги, чтобы запустить распределенное приложение, созданное на
предыдущих шагах:
1. Выберите команду Пуск Программы Стандартные Командная
строка, для отображения Командного окна.
2. Измените текущий подкаталог на подкаталог, который содержит файлы
AuthorServer.java, AuthorServerImpl.java и Client.java.
3. Откомпилируйте классы, используя javac компилятор Java. Следующая
команда, чтобы скомпилировать все файлы:
javac *.java
4. Сгенерируйте стаб и скелет, используя следующую команду:
rmic AuthorServerImpl
31
Лабораторная работа по RMI
5. Запустите регистр RMI, используя следующую команду:
start rmiregistry
6. Запустите сервер RMI, используя следующую команду:
java AuthorServerImpl
Следующий рисунок показывает вывод приложения сервера RMI:
Окно консоли приложения сервера RMI
7. Откройте новое командное окно и перейдите в подкаталог, в котором
сохранены файлы AuthorServer.java и Client.java. Выполните класс
client, используя следующую команду:
java Client
Окно консоли приложения клиента RMI
Следующий рисунок показывает вывод приложения клиента RMI:
Лабораторная работа по RMI
32
Графический интерфейс клиента RMI
33
Лабораторная работа по RMI
Related documents
Download