Remoteexception java что это
RMI (Remote Method Invocation)
Случилось так, что поставленная задача требовала применения удалённого вызова методов. Порывшись на Хабре, ничего не нашел по данному вопросу (хотелось что-нибудь почитать в качестве первого знакомства, перед чтением документации). Изучив спецификации на java.sun.com спешу поделиться с вами своей первой статьей. 🙂
«Что такое RMI?»
«Зачем это нужно?»
Задача RMI — организация клиент-серверного взаимодействия. Это значит, что вам не придётся беспокоится о передаче и предварительной обработке данных (протокол и т.д.). Удобно? Да. Но не во всех случаях. Если в вашей клиент-серверной среде подразумевается работа программ, написанных не только на java, от RMI толку мало (хотя при большом желании можно попытаться «выкрутиться» при помощи JNI).
«Давайте уже что-нибудь напишем!»
Давайте. Рассмотрим на примере распределённых вычислений. Задача у нас такая: мы будем искать простые числа самым простым способом, перебором. Распределённо же будем проверять числа подбором делителей от 2 до sqrt(n), где n — число, которое проверяем. («Распределённые вычисления» — громкое название для такого примера. Но ведь вычисляем? Да! Распределённо? Распределённо!)
Решать задачу будем так: есть сервер, который будет «скармливать» числа на проверку «зарегистрировавшимся» клиентам, посему взаимодействовать мы будем в обоих направлениях (клиент->сервер — регистрация, сервер->клиент — число на проверку), для этого опишем 2 интерфейса:
Интерфейс ClientRegister ипользуется клиентом для регистрации себя на сервере в роли PrimeChecker`a. Сервер использует PrimeChecker для передачи клиенту числа на проверку.
Как вы уже заметили, удалённый интерфейс должен расширять, прямо или косвенно, интерфейс Remote. Так же среди прочих исключений определим RemoteException (о нём мы говорили выше).
Приступим к реализации сервера (полный код):
Создаём регистратор и связываем наш stub с именем ClientRegister. Регистратор будет принимать соеденения на 12345 порту.
Находим удалённый регистратор и запрашиваем у него stub связанный с именем «ClientRegister». Первый параметр LocateRegistry.getRegistry(null, 12345) — хост (null — localhost), второй — порт.
Далее экспортируем клиентский удалённый объект и передадим серверу stub (уже клиентский) — зарегистрируемся. Сервер добавит клиента в очередь доступных checker’ов и начнёт передавать ему числа для проверки. После проверки, если она завершилась без ошибок, клиент снова попадает в очередь и т.д.
Описание и пример RMI технологии
Технология RMI (Java Remote Method Invocation) позволяет java-приложению, запущенному на одной виртуальной машине, вызвать методы объекта, работающего на другой виртуальной машине JVM (Java Virtual Machine).
RMI основана на более ранней технологии удаленного вызова процедур Remote Procedure Call (RPC), разработанной в 80-х годах и используемой для процедурного программирования. RPC позволяет процедуре одного приложения вызывать функцию на другом компьютере, как будто эта функция является частью программы. Таким образом, RPC выполняет всю работу по организации сетевых взаимодействий и маршалинга данных (пакетирования параметров функций и возврата значений для передачи их по сети). Но механизм RPC, поддерживающий ограниченный набор простых типов данных, не может использовать объекты Java при обмене информацией. Вторым важным недостатком RPC является необходимость использования специального языка определения интерфейса (IDL) для описания функций, допускающих удаленный вызов. С целью устранения этих недостатков и была разработана технология RMI.
RMI содержит набор объектов (классов) для организации удаленного взаимодействия java-приложений. RMI системы часто включают два отдельных приложения : сервер и клиент. Серверное приложение, как правило, создает удаленные объекты (remote objects), делает доступные ссылки на эти объекты и находится в ожидании вызова методов этих объектов. Клиентское приложение получает у сервера ссылку на удаленные объекты, после чего вызывает его методы. Технология RMI, обеспечивающая механизм взаимодействия клиента и сервера передачей между ними соответствующей информацией, реализована в виде java.rmi пакета, содержащего целый ряд вложенных подпакетов; один из наиболее важных подпакетов java.rmi.server реализует функции сервера RMI.
RMI обеспечивает маршалинг данных по сети и позволяет java приложениям передавать объекты с помощью механизма сериализации объектов. В состав J2SE включены инструментальные средства сетевых взаимодействий из определенных интерфейсов программы; это означает, что RMI не требует от программиста знания языка IDL. Кроме того, никакого нейтрального к языку IDL интерфейса не требуется, так как RMI поддерживает только Java; достаточно собственных интерфейсов Java.
Описание удаленного RMI объекта
Серверный RMI объект должен наследовать (extends) свойства класса java.rmi.server.UnicastRemoteObject, который представляет базовые функциональные возможности, необходимые удаленным объектам для обслуживания удаленных запросов. Конструкторы и методы класса UnicastRemoteObject возбуждают исключение RemoteException.
Конструктор класса UnicastRemoteObject обеспечивает экспорт объекта, чтобы он был доступным для приема удаленных вызовов. Экспорт позволяет удаленному RMI объекту ожидать соединений с клиентами для осуществления взаимодействия типа «точка-точка» с использованием стандартных соединений через сокеты. Предполагается, что клиенты RMI должны осуществлять соединение с использованием порта 1099 для поиска удаленного объекта в реестре RMI сервера. Перегруженный конструктор класса UnicastRemoteObject позволяет определить свой номер порта для экспорта удаленного объекта. Ссылка установления связи с удаленным RMI объектом обычно имеет следующий вид :
где
• host представляет собой имя компьютера, в котором определен реестр сервера rmiregistry для удаленных объектов с зарегистрированным удаленным RMI объектом,
• port представляет собой номер порта, на котором работает серверное приложение. По умолчанию для реестра RMI объектов используется порт 1099, который можно в этом случае в ссылке не указывать;
• object – это имя удаленного RMI объекта, зарегистрированного в реестре сервера.
Для связывания удаленного RMI объекта с реестром сервера используется один из методов bind или rebind. Метод rebind регистрирует объект в реестре с предварительной проверкой; если объект был ранее зарегистрирован под этим именем, то метод заменит его новым объектом. Это может потребоваться при регистриции новой версии существующего удаленного объекта.
Пример использования RMI
Рассмотрим использование механизма вызова методов удаленного объекта RMI на примере работы с электронной картой. Пример включает два приложения : серверное и клиентское. Серверное приложение ведет учет электронных карт и остатка средств на карте; деньги можно на карту положить и снять. Клиентское приложение регистрирует на сервере карты, добавляет и снимает денежные средства.
Серверное приложение включает три модуля :
• Card | – электронная карта; |
• BillingService | – интерфейс сервиса объекта RMI; |
• BillingServiceImpl | – объект RMI. |
Клиентское приложение также включает три модуля. Пересылаемый по сети объект Card и интерфейс описания RMI объекта BillingService в клиентском приложении должны совпадать с серверными. Третий модуль BillingClient использует интерфейс описания RMI объекта и Card для взаимодействия с объектом RMI, стартованном на сервере.
Листинг карточки, Card
В представленном ниже листинге класса Card.java не отображены методы get/set. Класс Card реализует интерфейс Serializable, который обеспечивает упаковку объекта в байт-коды в одном приложении и преобразования байт-кодов в объект Card в другом приложении.
Листинг RMI сервиса, BillingService
Интерфейс сервиса BillingService наследует свойства java.rmi.Remote и включает методы работы с картой Card. Во всех методах присутствует объект Card, передаваемый по сети между двумя java-приложениями.
Листинг RMI объекта, BillingServiceImpl
Класс BillingServiceImpl представляет собой удаленный объект, реализующий интерфейс BillingService. Чтобы сервер воспринимал его как RMI объект он наследует свойства класса UnicastRemoteObject. Приложение клиента взаимодействует с RMI-объектом типа BillingServiceImpl, вызывая методы addNewCard, addMoney, subMoney, getCardBalance, определенные в интерфейсе BillingService. Объект BillingServiceImpl хранит сведения о картах в коллекции cards.
При старте объекта в методе main определяется системное свойство ‘java.rmi.server.hostname’ как IP адрес локального компьютера (127.0.0.1), формируется и регистрируется в реестре RMI объект.
Старт серверного приложения
Для старта сервера используем командный файл (Window) run.rmi-server.bat со следующим кодом :
После старта серверного приложения в консоль выводится следующая информация :
Описание клиентского приложения
Как было отмечено выше, клиентское приложение включает 3 файла, два из которых, Card и BillingService, полностью совпадают с серверными. Третий модуль BillingClient для взаимодействия с объектом RMI использует интерфейс BillingService и электронную карту Card.
Разумнее было бы построить проекты таким образом, чтобы общие java-модули (Card и BillingService) были описаны только один раз в одном из приложений. Это можно было бы сделать, например, с использованием maven. Но в этом случае усложниться процесс описания проектов и, к тому же, (кто знает) будет ли у Вас в будущем при разработке собственного проекта доступ к исходным кодам серверного приложения? Возможно, что в Вашем распоряжении будут только интерфейсы взаимодействия и структуры объектов.
Листинг клиентского класса BillingClient
Класс BillingClient включает методы :
• createCard | – создание объекта электронной карты; |
• registerCards | – регистрации карт; |
• addMoney | – добавления денежных средств на карты; |
• getBalance | – чтение остатков средств на картах. |
В конструкторе класса сначала устанавливается соединение с RMI объектом, после чего последовательно вызываются методы регистрации карт, добавления денежных средств на карты и получения остатков средств на картах. Чтобы найти серверный RMI объект на локальном сервере, устанавливается системное свойство RMI_HOSTNAME (127.0.0.1) и определяется ссылка на объект (SERVICE_PATH) с использованием службы имен и каталогов JNDI (Java Naming and Directory Interface). Поскольку сервер при регистрации RMI объекта использовал порт 1099, то в ссылке порт не указывается.
Сообщения клиентского приложения
При старте клиентского приложения в консоль выводятся сообщения о выполнении определенных действий соответствующими методами. В заключении приводятся остатки денежных средств на картах.
Сообщения серверного приложения
Серверное приложение выводит в консоль сообщения при вызове методов :
Скачать пример
Исходный коды рассмотренного примера в виде двух проектов Eclipse можно скачать здесь (15.7 Kб). Проекты включают командные bat-файлы для проверки функционирования примера. Для этого достаточно только разместить проекты не диске C:\ (Windows) и стартовать командный файл сначала сервера, потом клиента.
Remoteexception java что это
— Ничего себе! Звучит очень круто.
— Ага. Но я постараюсь дать только обзорную лекцию. Тут, если глубоко копать, можно запутаться в нюансах работы.
Но если не ударяться в крайности, то RMI не только очень прост, но и значительно упрощает жизнь программиста. За что ему глубокий респект.
Итак, мы хотим, чтобы один объект, находящийся в одной Java-программе, смог вызвать метод у объекта, находящегося в другой Java-программе. Где бы эти программы запущены ни были.
Мы рассмотрим самый простой пример, когда обе программы запущены на одном компьютере. Чтобы программы могли взаимодействовать через интернет, необходимы настройки в правах Java-машины, но сегодня мы это рассматривать не будем.
В Java удаленно можно вызывать только методы интерфейсов, но не классов.
Итак, у нас есть две программы, как же им вызывать методы друг друга?
Давай рассмотрим ситуацию, когда одна программа содержит в себе некоторый объект, а вторая хочет вызвать его методы. Назовем первую программу – сервером, а вторую – клиентом.
Я сначала дам пример кода, а потом мы его разберем.
— А что будет делать наша программа?
— Гм. Ну, давай для простоты, у программы будет один метод, который разворачивает переданную ему строку задом наперед.
Сначала нам понадобится интерфейс, который будет удовлетворять нашим требованиями:
Я создал интерфейс Reverse и добавил ему интерфейс-маркер Remote, а также исключение RemoteException. В процессе вызова метода могут происходить незапланированные сбои – тогда будет кидаться это исключение.
Затем нам нужно написать серверный класс, который бы реализовывал этот интерфейс:
— Вижу. В этом методе, мы разворачиваем строку задом наперед.
А теперь надо сделать этот объект доступным для вызова с другой программы. Вот как это делается:
Рассказываю по строкам.
Строка 1 – в переменной UNIC_BINDING_NAME храним придуманное нами уникальное имя нашего удаленного объекта (объекта, который доступен удаленно). Если программа шарит несколько объектов, у каждого должно быть свое уникальное имя. Уникальное имя нашего объекта — «server.reverse».
Строка 9 — создаем специальный объект – реестр. В нем надо регистрировать объекты, которые мы шарим. Дальше ими занимается Java-машина. 2099 – это порт (уникальный номер, по которому другая программа может обратиться к нашему реестру объектов).
Т.е. чтобы обратиться к объекту, надо знать уникальный номер реестра объектов (порт), знать уникальное имя объекта и иметь такой же интерфейс, как и тот, который реализовывает удаленный объект.
— Ясно. Что-то вроде – позвонить по телефону (нужен номер) и попросить Соню (имя объекта)?
Строка 11 – создание « заглушки ». Заглушка – это специальный объект, который принимает информацию об удаленном вызове, распаковывает ее, десериализует переданные параметры методов и вызывает нужный метод. Затем сериализует результат или исключение, если оно было, и отсылает все это назад вызывающему.
— Ясно. Почти. Ты сказал, что «десериализует параметры метода». Значит, типами аргументов удаленного метода могут быть только сериализуемые?
— Ага. А как же иначе ты будешь пересылать их по сети? Есть, правда, и исключения – так называемые объекты, которые передаются по ссылке, но сегодня мы о них говорить не будем.
Скажем так, пересылать несериализуемые объекты нельзя, но если очень хочется, то можно. Но это хлопотное дело, знаешь ли.
Строка 13 – регистрируем в реестре заглушку нашего объекта под уникальным именем.
Строка 16 – усыпляем главный поток. Все удалённые вызовы обрабатываются в отдельных нитях. Главное, чтобы программа в это время работала. Так что тут просто отправляем главную нить спать, и всё.
— Отлично, тогда пример клиента:
Объясняю код по строкам:
Строка 1 – уникальное имя удаленного объекта. Должно быть одинаковым на клиенте и сервере.
Строка 6 – получение ссылки на « Реестр удаленных объектов » по порту 2099, т.е. такому же, как и у реестра у серверного приложения.
Строка 9 – получаем объект у реестра. Полученный объект является proxy-объектом и приводится к типу интерфейса. Интерфейс должен быть унаследован от интерфейса-маркера Remote.
Строка 12 – вызываем методы интерфейса так, как будто объект был создан в этой же программе. Никакой разницы.
— Круто! Это ж теперь можно писать распределенные приложения. Или игры типа морского боя для Android.
— Побойся бога, Амиго, операционная система Android была запрещена в 27 веке после третьей попытки захватить мир. У роботов к ней вообще доступа нет. Вас же потом от нее не оттянешь. Будете бегать и кричать «Убить всех человеков!».
— Гм. Ладно. Хотя надо будет у Диего еще спросить. Мало ли, может он что-нибудь интересное про нее расскажет.
— Вот и спроси. Ладно, давай до завтра.
— Пока, Риша, спасибо за интересную лекцию.
java.rmi.RemoteException – How to solve RemoteException
Posted by: Nikos Maravitsas in RemoteException July 2nd, 2014 0 Views
If you are familiar with Web Services or the SOAP protocol, you should be already acquainted with the notion of Remote Method calls, as the follow the same principles. One could say that a Web Service is basically RPC over the Web.
1. A simple RMI Application
First of all you need to create a remote interface. This is the interface that make the communication between the client and the server possible. The servers exposes concrete implementations of the methods in that interface. On the other side, the clients uses that interface to invoke the remote procedures provided by the server.
Here is also a configuration class (mostly for convenience) :
OK now let’s go to the server side. Remember that the server has to provide a concrete implementation of RemoteInterface :
And now it has to expose this capitalize implementation to the public:
On on the client side, the client has to connect to the server an simply look up the method it wants.
To get an overview of the structure of the project that I’ve created, take a look at the following picture:
2. An example of java.rmi.RemoteException
Now, imagine that as your remote methods is executed, suddenly something goes wrong and it throws an exception. A java.rmi.RemoteException will be throw to the client.
Now, if you run the server first and then the client, here is the output:
3. How to solve RemoteException
A good starting point when trying to debug this exception is to solve communication issues first. For example be sure that the client and server can communicate with each other. Then make sure that you load the correct remote ID in both sides. What you should mostly do then is try to debug the remote method implementation on the server side.
Создание простой программы с RMI и параллельным доступом на графический интерфейс в Java
Когда мне пришлось сильно углубиться в использование RMI, я поняла, какое большое значение играет умение правильно реализовать параллельность в самом графическом интерфейсе программы. На мое удивление в интернете не было достаточно материала на эту тему и особенно качественных примеров, хотя тема для любого программиста несоменно очень важная. Именно поэтому я решила поделиться своим опытом.
Кратко о RMI: программный интерфейс вызова удаленных методов в языке Java (источник). С помощью него можно например управлять данными на программе сервера с одного или множества компьютеров. Подробнее можно почитать на Хабре. Будем исходить из того, что с его основами вы уже знакомы. Необходимо так же иметь представление о нововведениях в Java 8, а именно — вам понадобятся лямбда-выражения. Хорошее объяснение есть здесь.
Возможности применения RMI очень разнообразны. С помощью него можно сделать, например, чат или программу для голосования на Java. В моем же примере будет простой счетчик с графической оболочкой, которая выглядит следующим образом:
Однако, прежде чем перейти к GUI, создадим сам RMI-объект счетчик и RMI-сервер, на котором он будет хранится.
Счетчик — интерфейс «Counter»:
Клас инициализации счетчика «CounterClass»:
Сервер для RMI-счетчика «Counter_Server»:
Так как я исхожу из того, что RMI вам уже знакомо, то не буду объяснять эти классы по строчкам. Краткое объяснение: метод «reset» приравнивает переменной «counter» значение 0 и возвращает его назад, метод «increment» увеличивает значение переменной «counter» на 1 и возвращает его назад. В сервере создаем свой регистр со скелетоном CounterClass. После этого сервер можно уже запустить.
Наконец, переходим к графике. Создадим класс Counter_Client_GUI, который создает сам фрейм с GUI и одновременно через главный метод берет стаб для удаленного управления счетчика из ранее созданного регистра:
Перед следующим шагом необходимо понять, для чего нужен в данном случае параллельный подход. Если мы реализуем обновления JLable самым обычным способом, например:
То это с огромной вероятностью приведет к постоянному замораживанию фрейма и при этом долгое время невозможно будет нажать ни на одну кнопку, хотя нажатия при этом регистрируются и при размораживании фрейма один за другим начнут исполняться, что приведет к хаосу. Это происходит из-за того, что в данном случае все действия с графической оболочкой будет принимать на себя лишь один единственный Thread — EventDispatchThread. И не стоит забывать, что ходя в данном примере клиент и сервер находятся на одном компьютере, управление счетчиком все-равно совершается удаленно, поэтому может возникнуть сбой в RMI регистре или же задержка доставки команды на сервер (кроме того это только пример, а в реальной программе клиент и сервер конечно же не находятся на localhost).
Теперь приступаем к самой важной части — описываем методы incrementClicked и resetClicked, вводя при этом необходимую параллельность:
Объяснение: для кадого нажатия на кнопку создаем новый Thread и запускаем его.
Внутри каждого Thread будет вот что:
Стоит отметить, что в теле EventQueue.invokeLater(. ) так же находится лямбда-выражение. В этой программе они применены для более лучшего и понятного внешнего вида кода.
Вот и все. Теперь параллельность работы с графической оболочкой программы обеспечена в любом случае даже при работе с RMI.