410013796724260
• Webmoney
R335386147728
Z369087728698
Описание и пример 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 объектом обычно имеет следующий вид : rmi://host:port/object, где Для связывания удаленного RMI объекта с реестром сервера используется один из методов bind или rebind. Метод rebind регистрирует объект в реестре с предварительной проверкой; если объект был ранее зарегистрирован под этим именем, то метод заменит его новым объектом. Это может потребоваться при регистриции новой версии существующего удаленного объекта. Пример использования RMIРассмотрим использование механизма вызова методов удаленного объекта RMI на примере работы с электронной картой. Пример включает два приложения : серверное и клиентское. Серверное приложение ведет учет электронных карт и остатка средств на карте; деньги можно на карту положить и снять. Клиентское приложение регистрирует на сервере карты, добавляет и снимает денежные средства. Серверное приложение включает три модуля :
Клиентское приложение также включает три модуля. Пересылаемый по сети объект Card и интерфейс описания RMI объекта BillingService в клиентском приложении должны совпадать с серверными. Третий модуль BillingClient использует интерфейс описания RMI объекта и Card для взаимодействия с объектом RMI, стартованном на сервере. Листинг карточки, CardВ представленном ниже листинге класса Card.java не отображены методы get/set. Класс Card реализует интерфейс Serializable, который обеспечивает упаковку объекта в байт-коды в одном приложении и преобразования байт-кодов в объект Card в другом приложении. import java.io.Serializable; public class Card implements Serializable { private static final long serialVersionUID = 1L; private String name ; private String number; private double money ; public Card(String name, String number, double money) { super(); this.name = name; this.number = number; this.money = money; } @Override public boolean equals(Object card) { Card crd = (Card)card; return this.getNumber().equals(crd.getNumber()); } } Листинг RMI сервиса, BillingServiceИнтерфейс сервиса BillingService наследует свойства java.rmi.Remote и включает методы работы с картой Card. Во всех методах присутствует объект Card, передаваемый по сети между двумя java-приложениями. import java.rmi.*; public interface BillingService extends Remote { // Регистрация новой карты public void addNewCard(Card card) throws RemoteException; // Добавление денежных средств на карту public void addMoney(Card card, double money) throws RemoteException; // Снятие денежных средств с карты public void subMoney(Card card, double money) throws RemoteException; // Получение баланса карты public double getCardBalance(Card card) throws RemoteException; } Листинг 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 объект. import java.util.List; import java.util.ArrayList; import java.rmi.RemoteException; import java.rmi.registry.Registry; import java.rmi.registry.LocateRegistry; import java.rmi.server.UnicastRemoteObject; import com.rmi.shared.BillingService; import com.rmi.shared.Card; public class BillingServiceImpl extends UnicastRemoteObject implements BillingService { private static final long serialVersionUID = 1L; private List<Card> cards; // инициализация сервера public BillingServiceImpl() throws RemoteException { super(); cards = new ArrayList<Card>(); } @Override public void addNewCard(Card card) throws RemoteException { cards.add(card); System.out.println("register card : " + card.getNumber()); } @Override public void addMoney(Card card, double money) throws RemoteException { for (Card crd : cards) { if (crd.equals(card)) { crd.setMoney(crd.getMoney() + money); System.out.println("add money : " + "card " + card.getNumber() + ", summa = " + money); break; } } } @Override public void subMoney(Card card, double money) throws RemoteException { for (Card crd : cards) { if (crd.equals(card)) { crd.setMoney(crd.getMoney() - money); System.out.println("sub money : " + "card : " + card.getNumber() + ", summa = " + money); break; } } } @Override public double getCardBalance(Card card) throws RemoteException { double balance = 0; for (Card crd : cards) { if (crd.equals(card)) { balance = crd.getMoney(); System.out.println("balance : " + "card : " + card.getNumber() + ", summa = " + balance); break; } } return balance; } /** * Старт удаленного RMI объекта BillingService * @param args аргументы * @throws Exception */ public static void main (String[] args) throws Exception { String localhost = "127.0.0.1"; String RMI_HOSTNAME = "java.rmi.server.hostname"; try { System.setProperty(RMI_HOSTNAME, localhost); // Создание удаленного RMI объекта BillingService service = new BillingServiceImpl(); // Определение имени удаленного RMI объекта String serviceName = "BillingService"; System.out.println("Initializing " + serviceName); /* * Регистрация удаленного RMI объекта BillingService * в реестре rmiregistry */ Registry registry = LocateRegistry.createRegistry(1099); registry.rebind(serviceName, service); System.out.println("Start " + serviceName); } catch (RemoteException e) { System.err.println("RemoteException : "+e.getMessage()); System.exit(1); } catch (Exception e) { System.err.println("Exception : " + e.getMessage()); System.exit(2); } } } Старт серверного приложенияДля старта сервера используем командный файл (Window) run.rmi-server.bat со следующим кодом : set CLASSPATH=C:\rmi-server\bin java com.rmi.server.BillingServiceImpl PAUSE После старта серверного приложения в консоль выводится следующая информация : C:\rmi-server>set CLASSPATH=C:\rmi-server\bin C:\rmi-server>java com.rmi.server.BillingServiceImpl Initializing BillingService Start BillingService Сервер переходит в режим ожидания ... Описание клиентского приложенияКак было отмечено выше, клиентское приложение включает 3 файла, два из которых, Card и BillingService, полностью совпадают с серверными. Третий модуль BillingClient для взаимодействия с объектом RMI использует интерфейс BillingService и электронную карту Card. Разумнее было бы построить проекты таким образом, чтобы общие java-модули (Card и BillingService) были описаны только один раз в одном из приложений. Это можно было бы сделать, например, с использованием maven. Но в этом случае усложниться процесс описания проектов и, к тому же, (кто знает) будет ли у Вас в будущем при разработке собственного проекта доступ к исходным кодам серверного приложения? Возможно, что в Вашем распоряжении будут только интерфейсы взаимодействия и структуры объектов. Листинг клиентского класса BillingClientКласс BillingClient включает методы :
Все вызовы методов интерфейса BillingService обрамлены конструкцией try ... catch с перехватыванием исключений типа RemoteException. В конструкторе класса сначала устанавливается соединение с RMI объектом, после чего последовательно вызываются методы регистрации карт, добавления денежных средств на карты и получения остатков средств на картах. Чтобы найти серверный RMI объект на локальном сервере, устанавливается системное свойство RMI_HOSTNAME (127.0.0.1) и определяется ссылка на объект (SERVICE_PATH) с использованием службы имен и каталогов JNDI (Java Naming and Directory Interface). Поскольку сервер при регистрации RMI объекта использовал порт 1099, то в ссылке порт не указывается. import java.net.MalformedURLException; import java.rmi.*; import com.rmi.shared.BillingService; import com.rmi.shared.Card; public class BillingClient { String localhost = "127.0.0.1"; String RMI_HOSTNAME = "java.rmi.server.hostname"; String SERVICE_PATH = "rmi://localhost/BillingService"; String[][] CARDS = {{"Ivanov", "1213-456-7890"}, {"Petrov", "987-654-3210"}}; double[] MONEYS = {135790.0, 24680.0}; public BillingClient() { try { System.setProperty(RMI_HOSTNAME, localhost); // URL удаленного объекта String objectName = SERVICE_PATH; BillingService bs; bs = (BillingService) Naming.lookup(objectName); System.out.println("\nRegister cards ..."); registerCards(bs); System.out.println("Add moneys ..."); addMoney(bs); System.out.println("Get balance ...\n"); getBalance(bs); } catch (MalformedURLException e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } catch (NotBoundException e) { System.err.println("NotBoundException : " + e.getMessage()); } } private Card createCard (final int idx) { return new Card(CARDS[idx][0], CARDS[idx][1], 0); } private void registerCards(BillingService bs) { for (int i = 0; i < CARDS.length; i++) { Card card = createCard (i); try { bs.addNewCard(card); } catch (RemoteException e) { System.err.println("RemoteException : " + e.getMessage()); } } } private void addMoney(BillingService bs) { for (int i = 0; i < CARDS.length; i++) { Card card = createCard (i); try { bs.addMoney(card, MONEYS[i]); } catch (RemoteException e) { System.err.println("RemoteException : " + e.getMessage()); } } } private void getBalance(BillingService bs) { for (int i = 0; i < CARDS.length; i++) { Card card = createCard (i); try { System.out.println("card : " + card.getNumber() + ", balance = " + bs.getCardBalance(card)); } catch (RemoteException e) { System.err.println("RemoteException : " + e.getMessage()); } } } public static void main(String[] args) { new BillingClient(); System.exit(0); } } Сообщения клиентского приложенияПри старте клиентского приложения в консоль выводятся сообщения о выполнении определенных действий соответствующими методами. В заключении приводятся остатки денежных средств на картах. C:\rmi-client>set CLASSPATH=C:\rmi-client\bin C:\rmi-client>java com.rmi.client.BillingClient Register cards ... Add moneys ... Get balance ... card : 1213-456-7890, balance = 135790.0 card : 987-654-3210, balance = 24680.0 Сообщения серверного приложенияСерверное приложение выводит в консоль сообщения при вызове методов : C:\rmi-server>set CLASSPATH=C:\rmi-server\bin C:\rmi-server>java com.rmi.server.BillingServiceImpl Initializing BillingService Start BillingService register card : 1213-456-7890 register card : 987-654-3210 add money : card 1213-456-7890, summa = 135790.0 add money : card 987-654-3210, summa = 24680.0 balance : card : 1213-456-7890, summa = 135790.0 balance : card : 987-654-3210, summa = 24680.0 Скачать примерИсходный коды рассмотренного примера в виде двух проектов Eclipse можно скачать здесь (15.7 Kб). Проекты включают командные bat-файлы для проверки функционирования примера. Для этого достаточно только разместить проекты не диске C:\ (Windows) и стартовать командный файл сначала сервера, потом клиента. |