— Привет! И еще одна радостная тема – RMI. RMI расшифровывается Remote Method Invocation – удаленный вызов методов. Или другими словами RMI – это механизм, который позволяет объекту в одной Java-машине вызывать методы объекта в другой Java-машине, даже если они находятся на разных компьютерах, в разных странах, на разных сторонах земного шара.

RMI - 1

— Ничего себе! Звучит очень круто.

— Ага. Но я постараюсь дать только обзорную лекцию. Тут, если глубоко копать, можно запутаться в нюансах работы.

Но если не ударяться в крайности, то RMI не только очень прост, но и значительно упрощает жизнь программиста. За что ему глубокий респект.

Итак, мы хотим, чтобы один объект, находящийся в одной Java-программе, смог вызвать метод у объекта, находящегося в другой Java-программе. Где бы эти программы запущены ни были.

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

В Java удаленно можно вызывать только методы интерфейсов, но не классов.

Итак, у нас есть две программы, как же им вызывать методы друг друга?

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

Я сначала дам пример кода, а потом мы его разберем.

— А что будет делать наша программа?

— Гм. Ну, давай для простоты, у программы будет один метод, который разворачивает переданную ему строку задом наперед.

— Вроде ничего.

— Гуд, тогда начнем:

Сначала нам понадобится интерфейс, который будет удовлетворять нашим требованиями:

Интерфейс для межпрограммного взаимодействия
interface Reverse extends Remote
{
 public String reverse(String str) throws RemoteException;
}

Я создал интерфейс Reverse и добавил ему интерфейс-маркер Remote, а также исключение RemoteException. В процессе вызова метода могут происходить незапланированные сбои – тогда будет кидаться это исключение.

Затем нам нужно написать серверный класс, который бы реализовывал этот интерфейс:

Класс для сервера
class ReverseImpl implements Reverse
{
 public String reverse(String str) throws RemoteException
 {
  return new StringBuffer(str).reverse().toString();
 }
}

— Вижу. В этом методе, мы разворачиваем строку задом наперед.

— Ага.

А теперь надо сделать этот объект доступным для вызова с другой программы. Вот как это делается:

Шаринг объекта
public static final String UNIC_BINDING_NAME = "server.reverse";

public static void main(String[] args) throws Exception
{
 //создание объекта для удаленного доступа
 final ReverseImpl service = new ReverseImpl();

 //создание реестра расшареных объектов
 final Registry registry = LocateRegistry.createRegistry(2099);
 //создание "заглушки" – приемника удаленных вызовов
 Remote stub = UnicastRemoteObject.exportObject(service, 0);
 //регистрация "заглушки" в реесте
 registry.bind(UNIC_BINDING_NAME, stub);

//усыпляем главный поток, иначе программа завершится
 Thread.sleep(Integer.MAX_VALUE);
}

Рассказываю по строкам.

Строка 1 – в переменной UNIC_BINDING_NAME храним придуманное нами уникальное имя нашего удаленного объекта (объекта, который доступен удаленно). Если программа шарит несколько объектов, у каждого должно быть свое уникальное имя. Уникальное имя нашего объекта — «server.reverse».

Строка 6 – собственно, создаем объект ReverseImpl, который будет доступен удаленно, и чьи методы будут вызваться.

Строка 9 — создаем специальный объект – реестр. В нем надо регистрировать объекты, которые мы шарим. Дальше ими занимается Java-машина. 2099 – это порт (уникальный номер, по которому другая программа может обратиться к нашему реестру объектов).

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

— Ясно. Что-то вроде – позвонить по телефону (нужен номер) и попросить Соню (имя объекта)?

— Да. Теперь дальше.

Строка 11 – создание «заглушки». Заглушка – это специальный объект, который принимает информацию об удаленном вызове, распаковывает ее, десериализует переданные параметры методов и вызывает нужный метод. Затем сериализует результат или исключение, если оно было, и отсылает все это назад вызывающему.

Ясно. Почти. Ты сказал, что «десериализует параметры метода». Значит, типами аргументов удаленного метода могут быть только сериализуемые?

— Ага. А как же иначе ты будешь пересылать их по сети? Есть, правда, и исключения – так называемые объекты, которые передаются по ссылке, но сегодня мы о них говорить не будем.

Скажем так, пересылать несериализуемые объекты нельзя, но если очень хочется, то можно. Но это хлопотное дело, знаешь ли.

— Ок.

— Тогда дальше.

Строка 13 – регистрируем в реестре заглушку нашего объекта под уникальным именем.

Строка 16 – усыпляем главный поток. Все удалённые вызовы обрабатываются в отдельных нитях. Главное, чтобы программа в это время работала. Так что тут просто отправляем главную нить спать, и всё.

— Ок.

— Отлично, тогда пример клиента:

Работа с удаленным объектом
public static final String UNIC_BINDING_NAME = "server.reverse";

public static void main(String[] args) throws Exception
{
 //создание реестра расшареных объетов
 final Registry registry = LocateRegistry.getRegistry(2099);

 //получаем объект (на самом деле это proxy-объект)
 Reverse service = (Reverse) registry.lookup(UNIC_BINDING_NAME);

 //Вызываем удаленный метод
 String result = service.reverse("Home sweet home.");
}

Объясняю код по строкам:

Строка 1 – уникальное имя удаленного объекта. Должно быть одинаковым на клиенте и сервере.

Строка 6 – получение ссылки на «Реестр удаленных объектов» по порту 2099, т.е. такому же, как и у реестра у серверного приложения.

Строка 9 – получаем объект у реестра. Полученный объект является proxy-объектом и приводится к типу интерфейса. Интерфейс должен быть унаследован от интерфейса-маркера Remote.

Строка 12 – вызываем методы интерфейса так, как будто объект был создан в этой же программе. Никакой разницы.

— Круто! Это ж теперь можно писать распределенные приложения. Или игры типа морского боя для Android.

— Побойся бога, Амиго, операционная система Android была запрещена в 27 веке после третьей попытки захватить мир. У роботов к ней вообще доступа нет. Вас же потом от нее не оттянешь. Будете бегать и кричать «Убить всех человеков!».

— Гм. Ладно. Хотя надо будет у Диего еще спросить. Мало ли, может он что-нибудь интересное про нее расскажет.

— Вот и спроси. Ладно, давай до завтра.

— Пока, Риша, спасибо за интересную лекцию.