Опубликован: 10.10.2010 | Уровень: специалист | Доступ: платный
Лекция 5:

Использование Java RMI

< Лекция 4 || Лекция 5: 12345 || Лекция 6 >

Второй пример

В предыдущем примере мы создали серверный объект, который принимает от клиента (методы изменения баланса) и возвращает клиенту (метод запроса значения баланса) переменные простых типов. Однако с помощью RMI могут создаваться приложения, обмен в которых осуществляется посредством передачи сложных типов - классов, определенных пользователем. Для того чтобы проиллюстрировать эту технику, изменим наш пример. Предположим, что операции начисления и списания наличных с карт передаются на сервер не по одной, а пакетом, т.е. массивом. Предположим также, что у сервера вместо метода запроса баланса должен быть реализован метод запроса карты по ее идентификатору. Карта - класс, который кроме номера содержит также имя владельца, баланс и дату заведения.

Определение удаленного интерфейса

В отличие от предыдущего варианта интерфейс BillingService (пример 5.7) имеет не четыре, а три метода. Метод addNewCard (строка 11) заводит новую карту с указанными параметрами. Метод processOperations (строка 13) производит изменение баланса карт с указанными параметрами. В предыдущей реализации изменение баланса карты производилось двумя методами, один из которых увеличивал текущий баланс карты, другой - уменьшал, а величина изменения в любом случае была положительной. В данном случае величина изменения баланса может быть любой - как положительной, так и отрицательной; в первом случае это означает поступление денежных средств на баланс карты, во втором - списание денежных средств. Метод getCard (строка 15) по номеру карты возвращает экземпляр класса Card.

1  // BillingService.java
2  // Интерфейс BillingService объявляет методы для работы
3  // с пластиковыми картами
4  package com.asw.rmi.ex2; 
5
6  // Набор базовых пакетов Java
7  import java.rmi.*;
8
9  public interface BillingService extends Remote {
10  // определение новой карты
11  public void addNewCard(Card card) throws RemoteException;
12  // изменение баланса карты
13  public void processOperations(CardOperation[] operations) throws RemoteException;
14  // получение баланса карты
15      public Card getCard(String card) throws RemoteException;
16  }
Листинг 5.7. Интерфейс BillingService

Как уже говорилось ранее, RMI использует механизм сериализации по умолчанию Java для передачи параметров методу и возврата значений через сеть. Поэтому все классы, используемые в качестве параметров метода и возвращаемых значений, должны иметь описатель Serializable.

Тип Card (пример 5.8) несет информацию об имени посетителя ( person ), дате получения карты (createDate),номере карты (cardNumber) и балансе (balance).

1  // Card.java
2  // описание типа переменных Card
3  package com.asw.rmi.ex2;
4
5  // Набор базовых пакетов Java
6  import java.io.Serializable;
7  import java.util.*;
8
9  public class Card implements Serializable{
10  public Card(String person, Date createDate, String cardNumber, double balance)]
11  this.person = person;
12  this.createDate = createDate;
13  this.cardNumber = cardNumber;
14  this.balance = balance;
15  }
16  public String person;
17  public Date createDate;
18  public String cardNumber;
19  double balance;
20  public String toString(){
21  return "Card: cardNumber="+cardNumber+"\tBalance="+balance+
22  +"\tPerson="+person+"\tCreateDate="+createDate+"";
23  }
24  }
Листинг 5.8. Описание типа Card

Тип CardOperation (пример 5.9) представляет собой одну операцию изменения баланса карты и несет информацию о номере карты (card),величине изменения баланса карты ( amount ) и дате операции (operationDate).

1  // CardOperation.java
2  // описание типа переменных CardOperation
3  package com.asw.rmi.ex2;
4
5  // Набор базовых пакетов Java
6  import java.util.*;
7  import java.io.*;
8
9  public class CardOperation implements Serializable {
10  public CardOperation(String card,double amount,Date operationDate){
11  this.card = card;
12  this.amount = amount;
13  this.operationDate = operationDate;
14  }
15  public String card;
16  public double amount;
17  public Date operationDate;
18  }
Листинг 5.9. Описание типа CardOperation

Реализация удаленного интерфейса

Класс BillingServiceImpl (пример 5.10) является удаленным объектом, который реализует удаленный интерфейс BillingService.Класс BillingServiceImpl реализует методы addNewCard (строки 22-25), processOperations (строки 28-36), getCard (строки 39-42) интерфейса BillingService,чтобы отвечать на удаленные запросы. Класс BillingServiceImpl хранит сведения о картах в хэш-таблице (Hashtable),содержащей карты (Card),где номер карты (cardNumber) является ключом таблицы.

1  // BillingServicelmpl.java
2  // BillingServiceImpl реализует удаленный интерфейс BillingService
3  // для предоставления удаленного объекта BillingService
4  package com.asw.rmi.ex2;
5
6  // Набор базовых пакетов Java
7  import java.rmi.*;
8  import java.util.*;
9  import java.rmi.server.*;
10
11  public class BillingServiceImpl extends UnicastRemoteObject
12  implements BillingService {
13
14  private   Hashtable hash;   // хэш-таблица для хранения карт
15  // инициализация сервера
16  public BillingServiceImpl() throws RemoteException]
17  super();
18  hash = new Hashtable();
19  }
20
21  // реализация метода addNewCard интерфейса BillingService
22  public void addNewCard(Card card) throws RemoteException {
23
24  hash.put(card.cardNumber, card);
25  }
26
27  // реализация метода processOperations интерфейса BillingService
28  public void processOperations(CardOperation[] operations)
29  throws RemoteException {
30  for (int i=0;i<operations.length;i++){
31  Card c = (Card)hash.get(operations[i].card);
32  if (c==null) throw new NotExistsCardOperation();
33  c.balance+=operations[i].amount;
34  hash.put(operations[i].card,c);
35  }
36  }
37
38  // реализация метода getCard интерфейса BillingService
39  public Card getCard(String card) throws RemoteException{
40  Card c = (Card)hash.get(card);
41  return c;
42  };
43
44  // запуск удаленного объекта BillingService
45  public static void main (String[] args) throws Exception {
46  System.out.println("Initializing BillingService...");
47
48  // создание удаленного объекта
49  BillingService service = new BillingServiceImpl();
50
51  //задание имени удаленного объекта
52  String serviceName = "rmi://localhost/BillingService";
53  // регистрация удаленного объекта BillingService в реестре rmiregistry
54  Naming.rebind(serviceName, service);
55  }
56
57  }
Листинг 5.10. Класс BillingServiceImpl реализует удаленный интерфейс BillingService

Метод main (строки 45-55) создает удаленный объект BillingServiceImpl.В строке 52 определяется URL, который клиент может применить для получения удаленной ссылки на объект для вызова методов удаленного объекта.

В этой программе URL удаленного объекта имеет вид rmi://localhost/BillingService, т.е. реестр RMI выполняется на машине localhost (т.е. на локальном компьютере), а для обнаружения клиентом сервиса должно использоваться имя BillingService.Имя localhost является синонимом IP -адреса 127.0.0.1.

В строке 54 вызывается статический метод rebind класса Naming (пакет java.rmi) для связывания удаленного объекта service класса BillingServiceImpl в реестре RMI с URL rmi://localhost/BillingService.

Класс NotExistsCardOperation (пример 5.11) расширяет класс RemoteException.

1  // NotExistsCardOperation.java
2  package com.asw.rmi.ex2;
3
4  // Набор базовых пакетов Java
5  import java.rmi.RemoteException;
6
7   public class NotExistsCardOperation extends RemoteException {
8
9  }
Листинг 5.11. Класс NotExistsCardOperation реализует обработку исключений
< Лекция 4 || Лекция 5: 12345 || Лекция 6 >
Алмаз Мурзабеков
Алмаз Мурзабеков
Прохожу курс "Построение распределенных систем на Java" в третьей лекции где описывается TCPServer вылетает эта ошибка
"Connection cannot be resolved to a type"


Java version 1.7.0_05
Александр Хвостов
Александр Хвостов
Россия
Максим Лютов
Максим Лютов
Россия, СПб, Политех, 2012