Смекни!
smekni.com

Удаленный вызов методом RMI (стр. 2 из 3)

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

Уровни удаленных ссылок определяют и поддерживают семантику вызовов соединения 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. Транспортный уровень RMI был разработан для осуществления соединения между клиентами и сервером даже с учетом сетевых помех.

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

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

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

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

RMIможет использовать много различных служб каталогов, включая JavaNamingandDirectoryInterface (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‑сервер)

• Клиентская программа, которая нуждается в удаленных службах

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

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

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

2. Написать и откомпилировать Java‑код для классов реализации

3. Создать файлы классов заглушки и скелета из классов реализации

4. Написать Java‑код программы хоста для удаленного обслуживания

5. Разработать Java‑код для клиентской программы RMI

6. Установить и запустить RMI‑систему

1. Интерфейсы

Первым шагом является написание и компилирование Java‑кода для интерфейсов служб.

Когда вы создаете удаленный интерфейс, вы должны следовать следующим правилам:

1. Удаленный интерфейс должен быть публичным – public (он не может иметь «доступ на уровне пакета», так же он не может быть «дружественным»). В противном случае клиенты будут получать ошибку при попытке загрузки объекта, реализующего удаленный интерфейс.

2. Удаленный интерфейс должен расширять интерфейс java.rmi. Remote.

3. Каждый метод удаленного интерфейса должен объявлять java.rmi. RemoteException в своем предложении throws в добавок к любым исключениям, специфичным для приложения.

4. Удаленный объект, передаваемый как аргумент или возвращаемое значение (либо напрямую, либо как к части локального объекта), должен быть объявлен как удаленный интерфейс, а не реализация класса.

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

public interface Calculator

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

throws j ava.rmi. RemoteException;

public long sub (long a, long b)

throws j ava.rmi. RemoteException;

public long mul (long a, long b)

throws j ava.rmi. RemoteException;

public long div (long a, long b)

throws j ava.rmi. RemoteException;

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

>javacCalculator.java

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

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

public class CalculatorImpl extends

java.rmi.server. UnicastRemoteObj ect 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, для генерации файлов заглушки и скелета. Компилятор запускается с указанием файла класса, реализующего удаленную службу.

>rmicCalculatorImpl

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

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();

В этом примере вы видите вызов статического метода Naming.rebind(). Однако этот вызов требует, чтобы регистрация была запущена отдельным процессом на вашем компьютере. Имя сервера регистрации – это rmiregistry, и под 32‑битной Windows вы пишете:

start rmiregistry

для запуска в фоновом режиме.

Как и многие другие сетевые программы, rmiregistry обращается по IP адресу машины, на которой она установлена, но она также слушает порт. Если вы вызовите rmiregistry как показано выше, без аргументов, будет использован порт по умолчанию 1099. Если вы хотите использовать другой порт, вы добавляете аргумент в командную строку, указывающий порт. Следующий пример устанавливает порт 2005, так что rmiregistry под управлением 32-битной Windows должна запускаться так: