Краткий сравнительный анализ технологий
Я не хочу даже пытаться сосчитать количество разработчиков,
принадлежащих к тому или иному лагерю, но все же рискну предположить что 90% из
вас относятся к 3-му лагерю и просто хотят иметь прочную технологию, решающую те
задачи, для которых она и предназначалась. При сравнении двух схожих технологий
приходится сталкиваться со свойственными им индивидуальными особенностями,
достоинствами и недостатками. То же самое наблюдается при сравнении RMI и CORBA.
(Обратите внимание, термины CORBA и Java IDL взаимозаменяемы. CORBA является
открытой спецификацией, "принадлежащей" OMG, а Java IDL является реализацией
этой спецификации.)
Некоторые считают самым большим преимуществом RMI тот факт, что эта
технология позволяет создавать решения, полностью базирующиеся на Java. Это
означает что создание RMI приложений весьма просто, и все удаленные объекты
обладают теми же возможностями, что и локальные объекты (так что вы можете
приводить их к типу и к родительскому класса). Разумеется, самое большое
достоинство RMI является также и самым большим ее недостатком: эта технология
позволяет создавать решения, полностью базирующиеся на Java, что весьма сужает
сферу ее применения!
CORBA хотя и является независимым от языка решением, но в то же
время усложняет процесс разработки приложений и делает недоступными возможности
Java по сборке мусора и, возможную в RMI, передачу через
экземпляр(pass-by-copy). Пока формальные запросы for proposals (RFP) CORBA
передаются через экземпляры, они никогда не будет поддерживаться RMI, из-за
сложностей возникающих при преобразовании объектов из одного языка в другой.
В следующей таблице предпринята попытка подвести итоги сравнения
возможностей, предоставляемых этими двумя архитектурами. Рассмотрев эту таблицу
мы приступим к разработке двух простых Java приложений используя RMI и Java IDL.
Запомните, Java IDL просто реализация (хотя и является
подмножеством) спецификации CORBA. Следовательно термины Java IDL и CORBA по
праву могут считаться взаимозаменяемыми.
Сравнительный анализ особенностей RMI и CORBA |
Возможности |
CORBA |
RMI |
Отклики сервера |
Да |
Да |
Передача по значению |
Нет |
Да |
Динамическое обнаружение |
Да |
Нет |
Динамические вызовы |
Да |
Да |
Поддержка множественной транспортировки |
Да |
Да |
Именование URL |
Нет (ORB-зависимый) |
Да |
Firewall Proxy |
Нет (ORB-зависимый) |
Да |
Независимость от языка |
Да |
Нет |
Языково-независимый Wire Protocol |
Да (через IIOP) |
Нет (В будущем, IIOP?!?) |
Постоянные имена |
Да |
No |
Низкоуровневая (Wire-level) безопасность |
Да (через CORBASecurity) |
Да (через SSL) |
Низкоуровневые(Wire-level) транзакции |
Да (через CORBA OTS) |
Да (через JTS) |
MortgageCalc 1.0 -- одно приложение, две технологии
Для того чтобы подчеркнуть сходства и различия между RMI и Java
IDL, я счел полезным создать две версии одного и того же приложения --
MortgageCalc 1.0 -- каждая из которых базируется на разных технологиях (см. ссылки на полные
исходные коды для этих двух приложений). Каждое приложение включает
клиентскую и серверную части, используемые для вычислений и отображения
ипотечных платежей, зависящих от ряда начальных условий (продолжительность
займа, процентная ставка, налоги, страхование и авансовые платежи). Поскольку
оба приложения выполняют одни и те же вычисления, мы можем инкапсулировать код,
отвечающий за вычисления, в отдельный класс Java, который будет использоваться
обеими программами.
Это позволит нам сфокусировать внимание на особенностях разработки
приложений при помощи RMI и Java IDL. Прежде чем начать, я дам краткие
разъяснения формул, используемые для вычислений. Примечание: Для этих
вычислений использована формула ипотеки, принятая в США. В других странах,
например в Канаде, используется поквартальная система процентных ставок и, как
следствие, другие вычисления.
Месячные платежи (PMT) включают долю от основную суммы займа плюс
проценты, которые выплачиваются банку ежемесячно. Кроме этого, ваш суммарный
платеж (TOTPMT) также включает городской и общий по стране налоги (TAX) и
страховку дома (INS). Другими словами, формула ежемесячных платежей выглядит
следующим образом:
TOTPMT = PMT + TAX + INS
Суммы TAX и INS весьма просты для вычисления (Общая сумма
займа/12), но с PMT дела обстоят немного сложнее. Не вдаваясь в детали, для
вычислений принята следующая формула:
PMT = BAL * (INT / (1 - (1 + INT) ** -MON))
BAL - это первоначальная сумма займа, INT - ежемесячный процент, а
MON - число месяцев погашения займа (Кол-во лет * 12 ).
Класс PaymentCalc
Для того чтобы в дальнейшем использовать код для расчетов в обоих
примерах, нам потребуется создать простой Java класс, PaymentCalc .
Если эти финансовые приложения в будущем будут усложняться, можно будет внести
изменения в расчеты обоих приложений, модифицировав только
PaymentCalc . Сейчас PaymentCalc содержит два метода --
PI() и PITI() . Метод PI() вычисляет сумму
платежей, включающую основную сумму и процент, тогда как метод
PITI() вычисляет сумму платежей, включающую основную сумму,
процент, налоги и страховку.
class PaymentCalc { /* PI
вычисляет только основную сумму и процент */
double PI(double Balance, double AnnualInterestRate, int
YearsLength) { double BAL =
Balance; double INT =
(AnnualInterestRate / 12); double MON =
(YearsLength * 12);
double PMT = BAL
* (INT / (1 - java.lang.Math.pow(1 + INT,
-MON)));
return PMT;
}
/* PITI вычисляет общую сумму
платежей, включая налоги и страховку */ double
PITI(double Balance, double AnnualInterestRate, int YearsLength,
int AnnualInsurance, int
AnnualTaxes) { double BAL =
Balance; double INT =
(AnnualInterestRate / 12); double MON =
(YearsLength * 12);
double PMT = BAL
* (INT / (1 - java.lang.Math.pow(1 + INT,
-MON))); double INS = (AnnualInsurance /
12); double TAX = (AnnualTaxes /
12); double TOTPMT = PMT + INS +
TAX;
return
TOTPMT; } }
Серверная часть RMI и Java IDL приложений использует класс
PaymentCalc для выполнения вычислений и в зависимости от
продолжительности и размера займа и возвращает клиентскому приложению массив с
указанием возможных вариантов займов и платежи по ним.
Основанное на RMI приложение MortgageCalc
Разработка RMI приложений состоит из следующих этапов:
- Описание удаленного интерфейса
- Реализация удаленного интерфейса путем создания класса (серверное
приложение)
- Создание клиентских stubs и скелетов сервера используя rmic
- Запуск реестра RMI и регистрация удаленных объектов
- Создание клиентского приложения
- Запуск клиентского приложения и подключение к серверу
Обратите внимание на то, что эти этапы несколько похожи на
рассмотренные несколько месяцев назад примеры использования CORBA. Основное
различие заключается в первом этапе. Используя RMI, мы описываем удаленный
интерфейс на языке программирования Java вместо IDL.
1 этап: Описание удаленного интерфейса
На первом этапе потребуется указать операции, которые должен
выполнять наш сервер. Простейшее приложение может вычислять отдельные платежи,
исходя из основной суммы займа, процентной ставки и срока займа, но мы пойдем
немного дальше. Вместо расчетов отдельных платежей, пусть наш сервер
рассчитывает варианты платежей, базирующиеся на таких данных, как сумма
кредита (5%, 10% или 20%) и продолжительность займа (15 или 30 лет). Эти
данные дадут нам возможность изучить передачу типов данных клиента и сервера. В
случае с RMI, мы возвращаем объект Vector Чтобы получить список
объектов, содержащих вычисленные данные.
Серверное приложение реализует интерфейс Calculate .
public interface Calculate extends
java.rmi.Remote { public java.util.Vector calcPI(int
HousePrice,
double
InterestRate) throws java.rmi.RemoteException; public
java.util.Vector calcPITI(int HousePrice, double InterestRate,
int
Insurance, int Taxes) throws java.rmi.RemoteException; }
Методы этого объекта возвращают объект Vector ,
играющего роль контейнера для других объектов. Теперь мы должны описать сам
класс, ResultSet , хранящий данные расчетов для методов интерфейса.
Запомните что отдельное обращение к одному из методов Calculate
возвращает несколько объектов ResultSet , хранимых в объекте
Vector . Также запомните что класс ResultSet реализует
интерфейс java.io.Serializable . При использовании RMI необходимо
чтобы все элементы данных, передаваемых между локальными и удаленными объектами
реализовали интерфейс Serializable .
import java.io.*;
public class ResultSet implements
Serializable { int Years; double
DownPayment; double Balance; double
InterestRate; double MonthlyPayment;
private
void writeObject(ObjectOutputStream s) throws IOException
{ // save the data to a
stream s.writeInt(Years); s.writeDouble(DownPayment); s.writeDouble(Balance); s.writeDouble(InterestRate); s.writeDouble(MonthlyPayment); }
private
void readObject(ObjectInputStream s) throws IOException
{ Years =
s.readInt(); DownPayment =
s.readDouble(); Balance =
s.readDouble(); InterestRate =
s.readDouble(); MonthlyPayment =
s.readDouble(); } }
2 этап: Реализация удаленного интерфейса
Второй этап на самом деле состоит из двух небольших операций. На
первой мы реализуем интерфейс Calculate , поскольку вторая операция
заключается в разработке серверного приложения, которое представляет собой
класс, реализующий этот интерфейс. Это делает класс сервера доступным для всех
клиентских приложений, которые его используют. Большая часть кода отведенного на
реализацию этого этапа весьма проста.
Единственный фрагмент, который может показаться непонятным - вызов
Naming.rebind() из конструктора CalculateImpl . Класс
Naming определен в java.rmi.Naming и используется для
регистрации объекта в реестре Java RMI. (Эта операция схожа с концепцией
регистрации объектов CORBA при помощи адаптера основных объектов, BOA,
в ORB.)
Начнем с кода для реализации класса CalculateImpl :
import java.rmi.*; import
java.rmi.server.UnicastRemoteObject;
public class CalculateImpl extends
UnicastRemoteObject implements
Calculate { private PaymentCalc
calc;
public CalculateImpl(String name) throws
RemoteException { super(); try { Naming.rebind(name,
this); calc = new
PaymentCalc(); } catch
(Exception
e) { System.out.println("Исключительная
ситуация: " +
e.getMessage()); e.printStackTrace(); } }
public
java.util.Vector calcPI(int HousePrice, double
InterestRate) throws
java.rmi.RemoteException { java.util.Vector
retObject = new java.util.Vector(6);
//5%, 15
лет ResultSet set1 = new
ResultSet(); set1.Years =
15; set1.DownPayment = HousePrice *
.05; set1.Balance = HousePrice *
.95; set1.InterestRate =
InterestRate; set1.MonthlyPayment =
calc.PI(set1.Balance, set1.InterestRate,
set1.Years); retObject.addElement(set1);
//5%,
30 лет ResultSet set2 = new
ResultSet(); set2.Years =
30; set2.DownPayment = HousePrice *
.05; set2.Balance = HousePrice *
.95; set2.InterestRate =
InterestRate; set2.MonthlyPayment =
calc.PI(set2.Balance, set2.InterestRate,
set2.Years); retObject.addElement(set2);
//10%,
15 лет ResultSet set3 = new
ResultSet(); set3.Years =
15; set3.DownPayment = HousePrice *
.10; set3.Balance = HousePrice *
.90; set3.InterestRate =
InterestRate; set3.MonthlyPayment =
calc.PI(set3.Balance, set3.InterestRate,
set3.Years); retObject.addElement(set3);
//10%,
30 лет ResultSet set4 = new
ResultSet(); set4.Years =
30; set4.DownPayment = HousePrice *
.10; set4.Balance = HousePrice *
.90; set4.InterestRate =
InterestRate; set4.MonthlyPayment =
calc.PI(set4.Balance, set4.InterestRate,
set4.Years); retObject.addElement(set4);
//20%,
15 лет ResultSet set5 = new
ResultSet(); set5.Years =
15; set5.DownPayment = HousePrice *
.20; set5.Balance = HousePrice *
.80; set5.InterestRate =
InterestRate; set5.MonthlyPayment =
calc.PI(set5.Balance, set5.InterestRate,
set5.Years); retObject.addElement(set5);
//20%,
30 лет ResultSet set6 = new
ResultSet(); set6.Years =
30; set6.DownPayment = HousePrice *
.20; set6.Balance = HousePrice *
.80; set6.InterestRate =
InterestRate; set6.MonthlyPayment =
calc.PI(set6.Balance, set6.InterestRate,
set6.Years); retObject.addElement(set6);
return
retObject; }
public java.util.Vector calcPITI(int
HousePrice, double InterestRate,
int
Insurance, int Taxes) throws
java.rmi.RemoteException { java.util.Vector
retObject = new java.util.Vector(6);
//5%, 15
лет ResultSet set1 = new
ResultSet(); set1.Years =
15; set1.DownPayment = HousePrice *
.05; set1.Balance = HousePrice *
.95; set1.InterestRate =
InterestRate; set1.MonthlyPayment =
calc.PITI(set1.Balance, set1.InterestRate,
set1.Years,
Insurance,
Taxes); retObject.addElement(set1);
//5%,
30 лет ResultSet set2 = new
ResultSet(); set2.Years =
30; set2.DownPayment = HousePrice *
.05; set2.Balance = HousePrice *
.95; set2.InterestRate =
InterestRate; set2.MonthlyPayment =
calc.PITI(set2.Balance, set2.InterestRate,
set2.Years,
Insurance,
Taxes); retObject.addElement(set2);
//10%,
15 лет ResultSet set3 = new
ResultSet(); set3.Years =
15; set3.DownPayment = HousePrice *
.10; set3.Balance = HousePrice *
.90; set3.InterestRate =
InterestRate; set3.MonthlyPayment =
calc.PITI(set3.Balance, set3.InterestRate,
set3.Years,
Insurance,
Taxes); retObject.addElement(set3);
//10%,
30 лет ResultSet set4 = new
ResultSet(); set4.Years =
30; set4.DownPayment = HousePrice *
.10; set4.Balance = HousePrice *
.90; set4.InterestRate =
InterestRate; set4.MonthlyPayment =
calc.PITI(set4.Balance, set4.InterestRate,
set4.Years,
Insurance,
Taxes); retObject.addElement(set4);
//20%,
15 лет ResultSet set5 = new
ResultSet(); set5.Years =
15; set5.DownPayment = HousePrice *
.20; set5.Balance = HousePrice *
.80; set5.InterestRate =
InterestRate; set5.MonthlyPayment =
calc.PITI(set5.Balance, set5.InterestRate,
set5.Years,
Insurance,
Taxes); retObject.addElement(set5);
//20%,
30 лет ResultSet set6 = new
ResultSet(); set6.Years =
30; set6.DownPayment = HousePrice *
.20; set6.Balance = HousePrice *
.80; set6.InterestRate =
InterestRate; set6.MonthlyPayment =
calc.PITI(set6.Balance, set6.InterestRate,
set6.Years,
Insurance,
Taxes); retObject.addElement(set6);
return
retObject; } }
Теперь нам просто нужно создать основное серверное приложение,
MortageCalcServer , которое реализует класс и регистрирует его при
помощи менеджера безопасности RMI. Эта задача может быть реализована всего лишь
несколькими строками кода:
import java.rmi.*; import java.rmi.server.*;
public class
MortgageCalcServer { public static void main(String
args[]) { //Set up the server's
security manager System.setSecurityManager(new
RMISecurityManager());
try { CalculateImpl
calculator = new CalculateImpl("JavaWorld
MortgageCalc"); System.out.println("JavaWorld
MortgageCalc
запущен!"); } catch
(Exception
e) { System.out.println("Exception:
" +
e.getMessage()); e.printStackTrace(); } } }
Объект RMISecurityManager используется для того чтобы
убедиться в правильности загрузки классов. После того как это сделано (это
необходимо), мы можем просто создать экземпляр объекта
CalculateImpl , который регистрирует объект
CalculateImpl в реестре RMI.
3 этап: Генерация stubs используя rmic
Инструмент rmic, приложение, входящее в состав JDK,
получает в качестве входящих данных все созданные вами реализации удаленных
объектов (в файлах форм или классов) и в качестве выходных данных выдает stubs
клиентов и скелеты сервера. Stub для удаленного объекта - это прокси со
стороны клиента для удаленного объекта. Этот прокси отвечает за содержание
удаленного объекта, размещение параметров этого объекта и информирование
удаленных сылок о завершении вызова. Скелет - окружение сервера, которое
содержит метод, посылающий вызовы реальным реализациям удаленных объектов. Этот
скелет отвечает за удаление (unmarshalling) параметров, вызов реальной
реализации, а затем размещении (marshalling) результирующих значений.
Запуская rmic, он генерирует файлы CalculateImpl_Stub.class и
CalculateImpl_Skel.class:
rmic CalculateImpl
4 этап: Регистрация объектов
Подобно OSAgent, который мы запускаем для настройки используя
Visigenic VisiBroker ORB, появившуюся в октябре "Corba meets Java"
feature, JDK 1.2 поставляется с инструментом, rmiregistry ,
регистрируюем все запускаемые реализации объектов на локальном компьютере.
Начните использование этого инструмента с запуска следующей команды:
start rmiregistry
Вы регистрируете и запускаете сервер запуская стандартный интерпретатор Java:
java MortgageCalcServer
5 этап: Создание клиента
Теперь вы можете расслабиться, поскольку самая трудоемкая часть
работы уже позади. Теперь мы должны сосредоточиться на сервере RMI, ожидающем
пока какой-нибудь клиент не подключится к нему. Теперь давайте создадим
клиентское приложение, которое сделает это. Все клиентские приложения RMI
вначале должны установить менеджер безопасности RMI (как мы уже делали это при
разработке сервера RMI), затем находят удаленный объект, который они будут
использовать. Как только эти задачи выполнены, приложение может использовать
объект так, как если бы он запускался из памяти окружения программы. Приложению
MortgageCalcClient передаются три параметра командной строки:
ServerName , HousePrice и InterestRate .
Используя эти параметры она вычисляет ваши платежи по закладной и выводит
процентные ставки.
import java.rmi.*; import java.rmi.registry.*; import
java.rmi.server.*;
public class
MortgageCalcClient { public static void main(String
args[]) { if (args.length !=
3) {
System.out.println("Применение: java MortgageCalcClient ИмяСервера СтоимостьДома
ПроцентнаяСтавка");
System.out.println("Процентная ставка должна задаваться в десятичной
форме!");
System.exit(0); }
String
ServerName = args[0]; Integer HousePrice= new
Integer(args[1]); Double InterestRate = new
Double(args[2]);
//Установка менеджера
безопасности System.setSecurityManager(new
RMISecurityManager());
try {
Calculate calc =
(Calculate)Naming.lookup("rmi://"
+ args[0] + "/" + "JavaWorld
MortgageCalc");
java.util.Vector v =
calc.calcPI(HousePrice.intValue(),
InterestRate.doubleValue());
for (int i=0; i < 6; i++)
{ ResultSet s =
(ResultSet)v.elementAt(i);
System.out.println("Условия займа=" + s.Years + "
лет");
System.out.println("Авансовый платеж=$" +
s.DownPayment);
System.out.println("Процентная ставка=" + s.InterestRate * 100 +
"%");
System.out.println("Ежемесячные платежи=$" +
s.MonthlyPayment);
System.out.println(); } } catch
(Exception
e) { System.err.println("Исключительная
ситуация" +
e); } System.exit(0); } }
В настоящее время утилита rmiregistry не может
находить автоматически запускаемые объекты, хранимые на сервере. В ней также
отсутствуют динамический поиск и динамические вызовы. Java IDL, базирующаяся на
CORBA 2.0, обладает этими возможностями и будет рассмотрена далее.
Основанное на IDL Java приложение MortgageCalc
Для этого примера мы используем компоненты Java IDL, включенные в
состав Java Development Kit 1.2. Поскольку CORBA уже детально обсуждалась в моих
предыдущих статьях, давайте не будем углубляться в теорию CORBA и сосредоточим
внимание на собственно использовании инструментов JDK 1.2 для создания
Java-CORBA приложений. Для разработки этого и других апплетов и/или CORBA
приложений, нам потребуется реализовать следующие этапы:
- Создание описаний всех объектов используя IDL
- Компиляция кода IDL используя компилятор
idltojava для
создания кода скелета сервера и кода stub клиента
- Реализация объекта сервера
- Создание серверного приложения
- Создание клиентского приложения
Единственной сложностью, отличающую это приложение от Java/CORBA
приложений, которые мы разрабатывали ранее, является использование сервиса имен
Java IDL. Причина этого отличия заключается в том, что Java IDL пока что не
включает поддержку для хранилища интерфейса (interface
repository). (Рассматривая эту проблему в более ранних статьях,
используя Visigenic Visibroker ORB, мы использовали утилиту osagent
для нахождения объектов и их получения из структуры хранилища интерфейса).
Вместо этого, Java IDL поддерживает полнофункциональную реализацию сервиса имен
"Сервис общих объектов CORBA" (COS). (для более полной
информации о сервере имен COS см. раздел ссылок) Включение сервиса имен
позволяет Java IDL и его сервису имен взаимодействовать с другими CORBA
2.0-совместимыми ORB'ами через набор OMG интерфейсов.
Несколько слов о сервесе имен Java IDL (naming service)
Сервис имен Java IDL обеспечивает древовидный каталог для ссылок на
объекты, подобный тому который представляет файловая система для хранения
файлов. Комбинация ссылок на объект и присвоенного ей имени, называется
именной связкой (name binding). Именные связки хранятся в ветвях дерева
каталогов и называются оглавлением имен (naming context). Продолжая
аналогию, ссылка на объект по отношению к оглавлению имен то же, что и файл по
отношению к директории в вашей файловой системе.
Для связи с сервисом имен Java IDL, клиентское приложение должно
знать имя хоста и номер порта запущенного сервера. После установления соединения
клиент запрашивает оглавление имен а затем извлекает ссылку на необходимый Java
объект, чтобы в последствии использовать его в приложении. Обратите внимание,
поскольку этот сервис CORBA-совместимый, даже нет необходимости использовать
сервис имен, включенный в состав JDK 1.2. Другими словами, клиентское приложение
JDK 1.2 может быть создано при помощи Java IDL, но может быть подключено к
сервису имен Visigenic используя стандартизованные интерфейсы сервиса имен. Этот
сервис весьма прост (в конце концов, это всего лишь первое предложение от
JavaSoft). Ссылки на объект временные и будут удалены после завершения работы
сервиса имен. Более полную реализацию сервиса имен можно найти у других
производителей програмного обеспечения CORBA (Expersoft, IONA, Visigenic,
и многие другие) и любые апплеты или приложения, которые вы напишите для работы
с сервисом имен Java IDL будут прекрасно работать с сервисами от других
производителей. Поскольку CORBA является объектно-ориентированной архитектурой,
для вас не должен стать сюрпризом тот факт, что сервис имен сам доступен
через набор интерфейсов, хранящихся в модуле CosNaming .
Первый из этих интерфейсов, NamingContext , содержит
методы необходимые для обеспечения именных связок и их разрешения (resolution).
Второй интерфейс, BindingIterator , содержит методы, используемые
для перебора именных связок для поиска искомого объекта.
Сервис имен Java IDL будет использоваться для обнаружения ссылки на
объект. В нашем примере, на объект сервера Calculate .
1 этап: Описание интерфейса с использованием IDL
Примечание: Java IDL не является реализацией Языка
описания интерфейсов OMG. Как мы уже говорили, Java IDL по сути дела
CORBA 2.0 ORB, который использует IDL для описания интерфейсов. Это может
привести к недоразумениям, связанным с использованием буквенной абривеатуры
"IDL" в названии ORB, но это не остановило его разработчиков.
Процесс описания интерфейса Calculate в IDL немного
отличается от процесса описания соответствующего интерфейса в Java (см. исходный
код выше, для Java RMI интерфейса Calculate ).
Причина заключается в том, что в RMI мы можем вернуть результат
нашего платежа по закладной, хранящийся в объекте java.lang.Vector .
Мы также описали отдельный Java класс (ResultSet ) для данных,
хранящихся в объекте Vector .
IDL позволит нам получить группы объектов в массиве или
последовательности. Изучение Java-в-IDL преобразования покажет чтооба этих IDL
типа данных преобразуются в массив Java, поэтому не имеет значения который из
них мы выберем. Для этого примера, мы используем IDL последовательность. На
следующем листинге представлен файл Calculate.IDL который будет
использован для генерации кода для нашего stub и скелета на 2-м шаге.
struct ResultSet { long
Years; double
DownPayment; double
Balance; double
InterestRate; double
MonthlyPayment; };
typedef sequence<ResultSet>
MortgagePayments;
interface
Calculate { MortgagePayments calcPI(in long
HousePrice, in double InterestRate); MortgagePayments
calcPITI(in long HousePrice, in double InterestRate,
in
long Insurance, in long Taxes); };
Как вы видите, структура ResultSet практически
идентична нашему Java классу ResultSet описаный в примере для RMI.
Интерфейс Calculate также очень напомминает RMI интерфейс
Calculate , за исключением того что этот возвращает
последовательность MortgagePayments .
2 этап: Применение idltojava
Компилятор idltojava - это IDL компилятор от JavaSoft.
На время написания этой статьи он не был включен в состав JDK 1.2 Beta 2, и
поэтому должен быть скачан отдельно (см. Ссылки).
Для работы этого компилятора потребуется препроцессор Microsoft
Visual C++, следовательно вы должны установить на свою систему Visual C++.
(Убедитесь что запустили файл vcvars32 для для правильной настройки
вашего окружения.) После того как все установлено, просто запустите следующую
команду для создания необходимых клиентских stubs и скелетов сервера:
idltojava Calculate.idl
В результате будут созданы 10 исходных файлов Java, включая
конечный Calculate.java, представленный далее:
public interface Calculate extends org.omg.CORBA.Object
{ ResultSet[] calcPI(int HousePrice, double
InterestRate); ResultSet[] calcPITI(int HousePrice, double
InterestRate,
int Insurance, int Taxes); }
Как вы видите, эот интерфейс практически идентичен RMI интерфейсу
Calculate . Версия для RMI возвращает Java класс
Vector , тогда как версия Java IDL возвращает массив объектов
ResultSet . В остальном эти два класса идентичны.
3 этап: Реализация объекта сервера
В документации по Java IDL рассматриваются две части вашего
серверного приложения, называемые служба (servant) и сервер.
Это хороший способ разобраться во взаимодействии между вашей реализации класса
(мы использовали имя CalculateImpl ради большего сходства) и
серверным приложением, ответственным за инициализацию ORB, регистрацию службы и
перехвате клиентских запросов. Поскольку реализация осталась практически без
изменений, класс Java IDL CalculateImpl весьма похож на класс RMI
CalculateImpl .
class CalculateImpl extends
_CalculateImplBase { ResultSet[] calcPI(int HousePrice, double
InterestRate) { ResultSet[] retObject
= new ResultSet[6];
//5%, 15
лет ResultSet set1 = new
ResultSet(); set1.Years =
15; set1.DownPayment = HousePrice *
.05; set1.Balance = HousePrice *
.95; set1.InterestRate =
InterestRate; set1.MonthlyPayment =
calc.PI(set1.Balance, set1.InterestRate,
set1.Years); retObject[0] =
set1;
//5%, 30
лет ResultSet set2 = new
ResultSet(); set2.Years =
30; set2.DownPayment = HousePrice *
.05; set2.Balance = HousePrice *
.95; set2.InterestRate =
InterestRate; set2.MonthlyPayment =
calc.PI(set2.Balance, set2.InterestRate,
set2.Years); retObject[1] =
set2;
//10%, 15
лет ResultSet set3 = new
ResultSet(); set3.Years =
15; set3.DownPayment = HousePrice *
.10; set3.Balance = HousePrice *
.90; set3.InterestRate =
InterestRate; set3.MonthlyPayment =
calc.PI(set3.Balance, set3.InterestRate,
set3.Years); retObject[2] =
set3;
//10%, 30
лет ResultSet set4 = new
ResultSet(); set4.Years =
30; set4.DownPayment = HousePrice *
.10; set4.Balance = HousePrice *
.90; set4.InterestRate =
InterestRate; set4.MonthlyPayment =
calc.PI(set4.Balance, set4.InterestRate,
set4.Years); retObject[3] =
set4;
//20% down, 15
years ResultSet set5 = new
ResultSet(); set5.Years =
15; set5.DownPayment = HousePrice *
.20; set5.Balance = HousePrice *
.80; set5.InterestRate =
InterestRate; set5.MonthlyPayment =
calc.PI(set5.Balance, set5.InterestRate,
set5.Years); retObject[4] =
set5;
//20%, 30
лет ResultSet set6 = new
ResultSet(); set6.Years =
30; set6.DownPayment = HousePrice *
.20; set6.Balance = HousePrice *
.80; set6.InterestRate =
InterestRate; set6.MonthlyPayment =
calc.PI(set6.Balance, set6.InterestRate,
set6.Years); retObject[5] =
set6;
return
retObject; }
ResultSet[] calcPITI(int
HousePrice, double InterestRate,
int Insurance, int
Taxes) { ResultSet[] retObject = new
ResultSet[6];
//5%, 15
лет ResultSet set1 = new
ResultSet(); set1.Years =
15; set1.DownPayment = HousePrice *
.05; set1.Balance = HousePrice *
.95; set1.InterestRate =
InterestRate; set1.MonthlyPayment =
calc.PITI(set1.Balance, set1.InterestRate,
set1.Years,
Insurance, Taxes); retObject[0] =
set1;
//5%, 30
лет ResultSet set2 = new
ResultSet(); set2.Years =
30; set2.DownPayment = HousePrice *
.05; set2.Balance = HousePrice *
.95; set2.InterestRate =
InterestRate; set2.MonthlyPayment =
calc.PITI(set2.Balance, set2.InterestRate,
set2.Years,
Insurance, Taxes); retObject[1] =
set2;
//10%, 15
лет ResultSet set3 = new
ResultSet(); set3.Years =
15; set3.DownPayment = HousePrice *
.10; set3.Balance = HousePrice *
.90; set3.InterestRate =
InterestRate; set3.MonthlyPayment =
calc.PITI(set3.Balance, set3.InterestRate,
set3.Years,
Insurance, Taxes); retObject[2] =
set3;
//10%, 30
лет ResultSet set4 = new
ResultSet(); set4.Years =
30; set4.DownPayment = HousePrice *
.10; set4.Balance = HousePrice *
.90; set4.InterestRate =
InterestRate; set4.MonthlyPayment =
calc.PITI(set4.Balance, set4.InterestRate,
set4.Years,
Insurance, Taxes); retObject[3] =
set4;
//20%, 15
лет ResultSet set5 = new
ResultSet(); set5.Years =
15; set5.DownPayment = HousePrice *
.20; set5.Balance = HousePrice *
.80; set5.InterestRate =
InterestRate; set5.MonthlyPayment =
calc.PITI(set5.Balance, set5.InterestRate,
&nbs
set5.Years,
Insurance, Taxes); retObject[4] =
set5;
//20%, 30
лет ResultSet set6 = new
ResultSet(); set6.Years =
30; set6.DownPayment = HousePrice *
.20; set6.Balance = HousePrice *
.80; set6.InterestRate =
InterestRate; set6.MonthlyPayment =
calc.PITI(set6.Balance, set6.InterestRate,
set6.Years,
Insurance, Taxes); retObject[6] =
set6;
return
retObject; } }
4 этап: Создание серверного приложения
Как я уже отмечал раньше, основное различие между серверным
приложением и приложением RMI MortgageCalcServer заключается в том, что первое
использует сервис имен Java IDL. Мы делаем это, когда сначала ищем корневое
содержание имен (все равно что искать корневой каталог в файловой системе),
затем когда добавляем в него NameComponent для привязки нашего
объекта calculator . После выполнения этих операций, приложение
просто переходит в режим ожидания и перехватывает запросы клиентов.
import org.omg.CosNaming.*; import
org.omg.CosNaming.NamingContextPackage.*; import
org.omg.CORBA.*;
public class
MortgageCalcServer { public static void main(String
args[]) { try { //
create and initialize the ORB ORB orb =
ORB.init(args, null);Э
// create
servant and register it with the
ORB CalculateImpl calculator = new
CalculateImpl(); orb.connect(calculator);
//
получение содержания корневых
имен org.omg.CORBA.Object objRef =
orb.resolve_initial_references("NameService"); NamingContext
ncRef =
NamingContextHelper.narrow(objRef);Э
//
привязка ссылки на объект к
Naming NameComponent nc = new
NameComponent("JavaWorld MortgageCalc",
""); NameComponent path[] =
{nc}; ncRef.rebind(path,
calculator);Э
// ожидание вызовов от
клиентов java.lang.Object sync = new
java.lang.Object(); synchronized
(sync) {
sync.wait(); }Э } catch
(Exception
e) {
System.err.println("ОШИБКА: " +
e); e.printStackTrace(System.out); } } }
5 этап: Создание клиентского приложения
Клиентское приложение (MortgageCalcClient) инициализирует
ORB, устанавливает ссылки на объекты используя сервис имен, затем осуществляет
те же операции, что и клиентское приложение RMI.
import org.omg.CosNaming.*; import org.omg.CORBA.*;
public
class MortgageCalcClient { public static void main(String
args[]) { if (args.length !=
3) {
System.out.println("Применение: java MortgageCalcClient
");
System.out.println("Процентная ставка должна задаваться в десятичной
форме!");
System.exit(0); }
String
ServerName = args[0]; Integer HousePrice= new
Integer(args[1]); Double InterestRate = new
Double(args[2]);
try {
// создает и инициализирует ORB ORB orb
= ORB.init(args, null);
// get the
root naming context org.omg.CORBA.Object
objRef =
orb.resolve_initial_references("NameService");
NamingContext ncRef =
NamingContextHelper.narrow(objRef);
// resolve the Object Reference in
Naming NameComponent nc = new
NameComponent("JavaWorld MortgageCalc",
""); NameComponent path[] =
{nc}; Calculate calc =
CalculateHelper.narrow(ncRef.resolve(path));
ResultSet[] v =
calc.calcPI(HousePrice.intValue(),
InterestRate.doubleValue()); for (int
i=0; i < 6; i++)
{ ResultSet s =
v[i];
System.out.println("Условия займа=" + s.Years + "
лет");
System.out.println("Авансовый платеж=$" +
s.DownPayment);
System.out.println("Процентная ставка=" + s.InterestRate * 100 +
"%");
System.out.println("Ежемесячные платежи=$" +
s.MonthlyPayment);
System.out.println();
} } catch (Exception
e) {
System.out.println("Ошибка : " + e) ;
e.printStackTrace(System.out); }
} }
Заметьте что, за исключением кода использованного для инициализации
ORB и resolve ссылки на объект, остальной код этого приложения практически
идентичен коду клиентского приложения RMI. для запуска этого приложения сначала
запустите сервис имен Java IDL используя команду: tnameserv
|