В этом примере вы видите вызов статического метода Naming.bind( ). Однако, этот вызов требует, чтобы репозиторий был запущен как отдельный процесс на компьютере. Имя сервера репозитория rmiregistry, в 32-х системе Windows вы используете команду:
start rmiregistry
для запуска его в фоновом режиме. Для системы Unix используется команда:
rmiregistry &
Как и многие сетевые программы, rmiregestry получает IP адрес той машины, на которой она запущена, но она также должна слушать порт. Если вы вызовите rmiregestry как это показано выше, без аргументов, для репозитория будет использован порт по умолчанию 1099. Если вы хотите, чтобы репозиторий был на другом порту, вы добавляете аргумент в командную строку, который указывает порт. Например, интересующий порт 2005, так что rmiregestry должна быть запущена следующей командой под 32-х битной Windows:
start rmiregistry 2005
или для Unix:
rmiregistry 2005 &
Информация о номере порта также должна быть передана в команду bind( ), точно так же, как и IP адрес машины, где расположен репозиторий. Но таким образом мы получаем препятствие, если вы хотите запустить RMI программу локально, способ тестирования сетевых программ был указан в этой главе. В JDK 1.1.1 существует пара проблем:[2]
- localhost не работает с RMI. То есть, экспериментируя с RMI на одной единственной машине, вы должны предоставить имя машины. Чтобы найти имя вашей машины в 32-х битной среде Windows, запустите контрольную панель и выберите "Network". Выберите закладку "Identification", и вы увидите имя вашего компьютера. Во многих случаях я называю мой компьютер "Peppy". Заметьте, что регистр букв игнорируется.
- RMI не будет работать до тех пор, пока ваш компьютер не будет иметь активного TCP/IP соединения, даже если все компоненты просто общаются друг с другом на вашей локальной машине. Это означает, что вы должны соединить ваш компьютер с вашим провайдером Internet прежде, чем пробовать запускать программу или вы получите непонятное сообщение об исключении.
Учитывая все вышесказанное, команда bind( ) принимает вид:
Naming.bind("//peppy:2005/PerfectTime", pt);
Если вы используете порт по умолчанию 1099, вам не нужно указывать порт, так что вы можете написать:
Naming.bind("//peppy/PerfectTime", pt);
Вы должны быть способны выполнить локальное тестирование оставив в покое IP адрес и используя только идентификатор:
Naming.bind("PerfectTime", pt);
Имя службы произвольно, в данном случае это PerfectTime, что совпадает с именем класса, но вы можете использовать любое название, какое хотите. Важно то, что оно должно быть уникальным в известном клиенту репозитории, к которому он обращается для производства удаленного объекта. Если имя уже есть в репозитории, вы получите AlreadyBoundException. Чтобы предотвратить это, вы можете всегда использовать rebind( ) вместе bind( ), так как rebind( ) либо добавляет новую запись, либо заменяет уже существующую.
Даже после того, как main( ) завершит работу, ваш объект будет создан и зарегистрирован, так что он останентся жить в репозитории, ожидая прихода клиентов и запросов от них. До тех пор, пока работает rmiregistry и вы не вызвали метод Naming.unbind( ) с вашим именем, объект будет существовать. По этой причине, когда вы разрабатываете ваш код, вам необходимо выгружать rmiregistry и перезапускать его, когда вы скомпилировали новую версию вашего удаленного объекта.
Вам не нужно принудительно запускать rmiregistry, как внешний процесс. Если вы знаете, что ваше приложение является единственным, использующим репозиторий, вы можете запускать его внутри программы с помощью строки:
LocateRegistry.createRegistry(2005);
Как и прежде, 2005 - это номер порта, который мы будем использовать в этом примере. Это эквивалентно запуску rmiregistry 2005 из командной строки, но такой запуск часто будет более последовательным, когда вы разрабатываете RMI код, так как при этом пропускаются дополнительные шаги запуска и остановки репозитория. Как только вы выполните этот код, вы можете вызвать bind( ), используя Naming, как и прежде.
Создание заглушек и скелетов
Если вы скомпилируете и запустите PerfectTime.java, программа не будет работать, даже если у вас будет правильно запущенный репозиторий. Это происходит потому, что рабочая среда для RMI еще не до конца создана. Вы должны сначала создать заглушки и скелеты, которые обеспечат операцию сетевого соединения и позволят вам считать, что удаленный объект - это просто другой локальный объект на вашей машине.
То, что происходит за сценой достаточно сложно. Любые объекты, котоые вы передаете при вызове или получаете от удаленного объекта должны реализовывать Serializable (если вы хотите передавать удаленную ссылку вместо самого объекта, объектные аргументы могут реализовывать Remote), так что вы можете представить, что заглушки и скелеты автоматически выполняют сериализацию и десериализацию, как будтно они "руководят" всеми аргументами через сеть и возвращают результат. К счастью, вам не нужно знать ничего этого, но вы должны создать заглушки и скелеты. Это простой процесс: вы вызываете инструмент rmic для вашего скомпилированного кода и создаете необходимые файлы. Таким образом требуется просто добавить еще один шаг в процесс компиляции.
Однако, инструмент rmic привередлив к пакетам и classpaths. PerfectTime.java расположен в пакете c15.rmi, и если вы вызовите rmic в том же директории, в котором расположен PerfectTime.class, rmic не найдет файл, так как он ищет в переменной classpath. Таким образом вы должны указать расположение в переменной classpath, например так:
rmic c15.rmi.PerfectTime
Вам не нужно находиться в директории, содержащем PerfectTime.class, когда вы запускаете эту команду, а результат будет помещено в текущий каталог.
Когда запуск rmic завершится успешно, вы получите два новых класс в директории:
PerfectTime_Stub.class
PerfectTime_Skel.class
соответственно это заглушка и скелет. Теперь вы готовы получить сервер и клиент для общения между собой.
Использование удаленного объекта
Главная цель RMI состоит в упрощении использования удаленных объектов. Есть только одна дополнительная вещь, которую вы должны выполнить в клиентской программе, это найти и получить удаленный интерфейс с сервера. Все остальное - это обычное Java программирование: посылка сообщений объекту. Вот программа, которая использует PerfectTime:
//: c15:rmi:DisplayPerfectTime.java
// Использование удаленного объекта PerfectTime.
// {Broken}
package c15.rmi;
import java.rmi.*;
import java.rmi.registry.*;
public class DisplayPerfectTime {
public static void main(String[] args) throws Exception {
System.setSecurityManager(new RMISecurityManager());
PerfectTimeI t = (PerfectTimeI) Naming
.lookup("//peppy:2005/PerfectTime");
for (int i = 0; i < 10; i++)
System.out.println("Perfect time = " + t.getPerfectTime());
}
} // /:~
Строка идентификатора точно такая же, как и используемая для регистрации объекта с помощью Naming, а первая часть представляет URL и номер порта. Так как вы используете URL, вы можете также указать машину в Internet'е.
То, что приходит от Naming.lookup( ) должно быть приведено к удаленному интерфейсу, а не к классу. Если вместо этого вы будете использовать класс, вы получите исключение.
Вы можете видеть вызов метода
t.getPerfectTime()
так как то, что вы имеете - это ссылка на удаленный объект, программирование с которой не отличается от программирования с локальным объектом (с одним отличием: удаленные методы выбрасывают RemoteException).
← | Удаленный вызов методов (RMI) | Соединение с базой данных | → |