Построение распределенных систем на Java

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

Разбить на страницы
Показывать лекцию целиком

Рабочий каталог расположен в Practice.

Java RMI подходит в том случае, если все компоненты распределенного приложения реализованы на базе Java. В случае если необходимо взаимодействие компонентов, разработанных с использованием различных средств, может быть использована другая технология - CORBA.

Введение

CORBA представляет собой технологию построения распределенных систем, стандартизованную и поддерживаемую консорциумом OMG. Эта технология предоставляет механизмы для описания спецификаций интерфейсов взаимодействующих систем (предполагается, что они могут быть реализованы на различных языках программирования, с использованием различных платформ, и функционировать в различной сетевой инфраструктуре), а также обеспечивает некоторые важные сервисы - такие как сервисы именования, событий, транзакций, безопасности и т.д. Несмотря на то, что CORBA использует объектную идеологию, она может применяться в системах программирования, не поддерживающих объектный подход.

CORBA означает Common Object Request Broker Architecture (общая архитектура брокера объектных запросов). CORBA - это своего рода "клей", интегрирующая технология. Она позволяет программам на разных языках, работающих в различных узлах сети, взаимодействовать друг с другом так же просто, как если бы они находились в адресном пространстве одного процесса. Задача CORBA сделать возможной интеграцию изолированных систем. Клиенты, работающие с CORBA, получают преимущества в трех областях: прозрачность вызова ( invocation transparency ), прозрачность реализации ( implementation transparency ) и прозрачность локализации ( location transparency ). Прозрачность - основная цель CORBA. Для решения этой задачи CORBA использует все важнейшие достижения объектной технологии: инкапсуляцию, наследование, полиморфизм, динамическое связывание.

Прозрачность вызова определяет точку зрения клиента, посылающего сообщение серверу. Язык, используемый при реализации клиента, определяет, каким образом вызываемый объект получает сообщение, как параметры передаются в стек, как осуществляется обращение к методам данного объекта, как передаются возвращаемые значения. Если сервер создан в соответствии с правилами CORBA, то клиент может вызывать методы этого совместимого с CORBA объекта так, как и любого другого объекта, реализованного в используемом языке программирования. CORBA поддерживает ряд языков программирования (всего их 10), включая C, C++, Java, COBOL, Ada, Lisp, Smalltalk, PL/1, Python, IDLscript.

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

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

Ответственным за CORBA является консорциум Object Management Group ( OMG ). Отличительным признаком CORBA по сравнению с другими технологиями распределенных систем является архитектура Object Management Architecture ( OMA ). ОМА (в качестве эталонной архитектуры) описывает систему, которая состоит из взаимодействующих сервисов, решающих прикладную задачу. Уровней модульности у такого решения больше, но концепция объектов (или компонентов), совместно работающих для достижения общей цели, остается той же самой.

Чтобы объекты могли взаимодействовать так, как если бы они "обращались" на одном языке, они должны использовать общее для языков, на которых они реализованы, внешнее представление. Это внешнее представление - промежуточный язык - должно быть простым в использовании, легким в изучении и обусловливать минимальные накладные расходы. Таким промежуточным языком является язык OMG Interface Definition Language,обычно называемый просто IDL.

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

IDL -компилятор создает для каждого компилируемого IDL -файла целый ряд файлов, ориентированных на конкретные языки программирования. Проекту потребуется столько IDL -компиляторов, сколько языков программирования используется при разработке системы. Для создания на Java интерфейса к унаследованной COBOL -системе потребуется два IDL -компилятора: один - для создания клиентской программы на языке Java, второй - для создания серверной программы на языке COBOL. Например, внешнему интерфейсу на языке Java потребуются следующие файлы, сгенерированные на языке Java, для взаимодействия с серверной системой, которая называется BillingService:

BilingService.java
BillingServiceOperations.java
BillingServiceHolder.java
BillingServiceHelper.java
_ BillingServiceStub.java

BilingService.java содержит основное определение удаленного сервера. Исходный файл BillingServiceOperations содержит определения методов (операций), поддерживаемых CORBA -объектом. Класс BillingServiceStub отвечает за взаимодействие на стороне клиента, а файл BillingServiceHelper определяет класс, используемый клиентом для выполнения задач, которые связаны с CORBA.

Эти файлы скрывают механизм реализации вызова от вызывающей программы. BillingService.java и BillingServiceOperations.java определяют интерфейсы для использованиями классами, связанными с реализацией; _BillingServiceStub.java определяет код CORBA -вызова для осуществления вызовов удаленных методов на сервере. Файлы BillingServiceHelper.java и BillingServiceHolder.java - вспомогательные служебные файлы.

Поставщик серверного компонента предоставляет IDL -файл, описывающий API своего серверного объекта. Используя этот файл и IDL -компилятор, разработчик может создать клиентские файлы, необходимые для непосредственного обращения к этому серверному объекту.

Если бы в написанном на С или любом другом языке графическом пользовательском интерфейсе системы BillingService потребовалось бы посылать сообщения серверным объектам, написанным на Java ( CORBA -совместимые Java -объекты), с помощью IDL -компилятора потребовалось бы создать следующие файлы, чтобы серверные Java -объекты могли получать сообщения от любых вызывающих программ:

BillingService.java
BillingServiceOperations.java
BillingServicePOA.java

Файлы BillingService.java и BillingServiceOperations.java - те же, что и раньше (интерфейсы для использования классами, связанными с реализацией), а BillingServicePOA.java представляет собой код, используемый сервером для приема вызовов методов и передачи возвращаемых значений через сеть.

Дополнительно к объявлению Java -интерфейсов, применяемых разработчиками, и клиентские, и серверные файлы, определенные выше, реализуют базовые классы посредников (proxies) - "заместителей" других объектов. Посредники создают для клиента видимость того, что он посылает сообщение одному объекту, в то время как на самом деле этот клиент посылает сообщение другому объекту. CORBA определяет двух взаимосвязанных посредников: заглушку (stub) и скелет (skeleton).Заглушка является посредником на стороне клиента, скелет - на стороне сервера. Оба посредника скрывают использование ORB и от клиента, и от сервера.

Object Request Broker - это важнейший элемент технологии CORBA, связанный с брокером объектных запросов. Основным назначением ORB является функциональная совместимость. Все CORBA -совместимые объекты должны применять какой-либо брокер объектных запросов для передачи или приема запросов методов, но сами объекты редко имеют дело непосредственно с брокером объектных запросов. Способность к взаимодействию, заложенная в CORBA, остается одной и той же независимо от конкретной реализации CORBA.

Когда клиент запрашивает операцию, реализуемую распределенным объектом, брокер объектных запросов данного клиента для выполнения вызова использует объектную ссылку. Объектная ссылка, используемая клиентом (поддерживаемая открытым API вызываемого объекта, описанным в IDL -файле), содержит непрозрачную сетевую ссылку (непрозрачную - так как ни клиент, ни сервер не могут извлечь из этой ссылки информацию о деталях реализации). Клиент запрашивает операцию, реализуемую распределенным объектом посредством объектной ссылки, поскольку Interoperable Object Reference ( IOR ) ссылается именно на объектную ссылку, структура которой хорошо известна брокерам ORB при условии использования OMG -совместимых протоколов (например, IIOP ). Источником объектной ссылки является объектный адаптер, указывающий на удаленный объект. Объектная ссылка содержит три важных фрагмента информации: местоположение распределенного объекта; ссылку на адаптер, создавший объектную ссылку; идентификатор объекта серванта. Все CORBA -совместимые объектные брокеры "знают", что такое объектные ссылки и как их использовать для установления соединений клиентов с сервантами. Последнее, что необходимо для обеспечения функциональной совместимости, - это создание и синтаксический анализ IOR в протоколе для отправки запроса через сеть. Чтобы сделать возможным надежное и уверенное взаимодействие брокеров объектных запросов различных производителей, консорциум OMG разработал спецификацию протокола Internet Inter ORB Protocol ( IIOP ). IIOP - это протокол обмена сообщениями между объектными брокерами.

Все производители должны обеспечивать поддержку IIOP, чтобы их объектные брокеры были CORBA -совместимыми. Производители могут также поддерживать свои собственные протоколы, но, как минимум, они должны поддерживать IIOP. Объектные брокеры, которые предполагают обмениваться между собой сообщениями, должны "говорить" на языке одного и того же протокола. В том случае, если объектные брокеры поддерживают разные протоколы, должны быть разработаны специальные адаптеры для преобразования протоколов, чтобы обеспечить обмен сообщениями между объектными брокерами, поддерживающими нестандартные протоколы. При установке брокера объектных запросов IIOP должен устанавливаться по умолчанию в качестве стандартного протокола, хотя IIOP может являться не единственным протоколом.

IIOP является реализацией другого стандарта OMG - General Inter ORB Protocol ( GIOP ). GIOP определяет сообщения, необходимые объектным брокерам для обмена данными между собой, и обеспечивает поддержку лежащего в основе этого взаимодействия транспорта для платформы, на которой функционирует данный брокер объектных запросов. TCP/IP является предпочтительным транспортным средством, хотя спецификация включает также поддержку Novell IPX и OSI. Производители могут обеспечивать поддержку и других транспортных механизмов. Java, с ее встроенной сетевой поддержкой, и CORBA, с ее механизмами обеспечения соединений распределенных объектов, являются взаимодополняющими технологиями.

Для каждого языка программирования, поддерживаемого OMG, должно существовать преобразование из IDL на этот язык. Отображение IDL-Java определяет, как следует преобразовывать ключевые слова и типы данных IDL в конструкции Java. Java 2 был первой версией, выпущенной корпорацией Sun, непосредственно поддерживающей OMG -отображение.

Последовательность действий

Реализация распределенной системы - это нетривиальная задача. Для реализации распределенной системы с использованием Java и CORBA необходимы следующие шаги:

  • выполнить анализ и проектирование на основе моделирования предметной области, моделирования системы и декомпозиции системы на ряд подсистем;
  • создать IDL -описание путем спецификации API распределенных подсистем и спецификации структур данных, которые передаются через границы системы;
  • реализовать сервант, используя файлы, сгенерированные IDL -компилятором;
  • реализовать клиент, используя файлы заглушек, сгенерированные IDL -компилятором;
  • принять решение о методе распространения объектных ссылок сервантов (обычно это делается с помощью сервиса именования, но не обязательно);
  • запустить реализацию серванта;
  • запустить клиент.
  • После определения подсистем нужно решить, каким из них придать форму распределенных сервисов. Если предположить, что структура всех подсистем реализует функциональность распределенных совместно используемых сервисов, то перевод таких подсистем на платформу CORBA существенно упрощается. В любом случае, открытый API такого сервиса будет доступен клиентам. Для объектно-ориентированного подхода обычно интерфейс, описываемый в IDL, может являться некоторым подмножеством всех реализуемых сервером функций. И даже после реализации сервера (мы используем Java в качестве языка программирования) разработчиков продолжает волновать вопрос о том, сколько дополнительных функций (административных, связанных с защитой и т.д.) предоставить серверу.

    Если существуют структуры данных, которые сервер должен возвращать клиенту (или принимать от него), то описание и определение этих структур данных должно присутствовать в IDL -файле. Модель CORBA предоставляет возможность процедурным языкам программирования описывать конструкции в "псевдообъектном" стиле. Две конструкции CORBA поддерживают этот механизм: struct и valuetype. Клиент и сервер передают IDL -структуры ( struct ) туда и обратно в качестве обычных данных. Стандартная оптимизация включает в себя отправку этих данных непосредственно клиенту, чтобы освободить сервер от избыточных запросов. Конструкция valuetype является IDL -описанием некоторой сущности, включающей и данные, и методы. Конструкция struct состоит только из данных (конструкция struct, скомпилированная в Java -код, определяет открытые атрибуты), тогда как valuetype инкапсулирует данные, и добавляет логику, необходимую для создания объекта, простого или сложного, какой требуется.

    После объявления API сервера в виде IDL -файла единственное, чего не достает, - это собственно реализация сервера. Компилятор IDL анализирует IDL -файл и генерирует соответствующие файлы на целевом языке. После того как созданы вспомогательные файлы, разработчик может писать код реализации; до этого момента единственное, что было сделано разработчиком, - это объявлены интерфейс, атрибуты и данные, необходимые серверу для обработки запросов. Сначала разработчик может реализовать фиктивный сервер, который просто возвращает текстовые данные, чтобы другой разработчик мог написать клиент (или, возможно, текстовый клиент) для тестирования обращений к серверу. Более типичный пример - сервер, который является оболочкой унаследованного приложения, но скрывает это от клиентов.

    Допустим, что объявленное API сервера находится в IDL -файле и IDL -компилятор скомпилировал этот файл. Теперь клиент может использовать сгенерированные для клиента файлы, необходимые ему для взаимодействия с сервером. Клиенту нужны только файлы, сгенерированные IDL -компилятором, чтобы обеспечить прозрачное сетевое взаимодействие. Можно создать локальную версию сервера (вместо того, чтобы задействовать объектную ссылку CORBA, можно просто выполнить операцию new ServerImpl()) и отправлять сообщения напрямую, но такой сервер уже не будет распределенным объектом, он будет работать в адресном пространстве клиента, утрачивая преимущества слабой связи между компонентами системы, имевшимися первоначально, благодаря использованию интегрирующего кода, размещенного в сгенерированных файлах.

    Первый пример

    Данный пример реализует решение задачи про сеть столовых, использующую в своей работе с клиентами пластиковые карты.

    Интерфейс сервера BillingService включает четыре метода: addNewCard (добавляет новую карту), addMoney (добавляет денежные средства на карту), subMoney (снимает денежные средства с карты), getCardBalance (показывает текущий баланс карты).

    Файл BillingService.idl

    В примере 6.1 приведено IDL -описание сервера BillingService.

    CORBA не является предписанием реализации, поэтому IDL -описание формально программным кодом не является. Ключевое слово module связывает данное имя непосредственно с пакетом Java. Вложенные имена module, соединенные вместе, дают полное имя пакета. Имя module в примере 6.1 - BillingServiceModule.

    Фигурные скобки в строках 4 и 13 обозначают границы области действия блока. В IDL точка с запятой всегда завершает блок (включая объявление module ). Java, C, C++ являются языками с блочной структурой и используют похожий синтаксис, но в них блок не рассматривается как строка, требующая для завершения точки с запятой. Отсутствие точки с запятой в IDL является синтаксической ошибкой.

    1  // BillingService.idl
    2  // IDL-описание BillingService
    3  module    BillingServiceModule 4{
    5  interface BillingService 6{
    7  //определение CORBA-совместимого сервиса
    8  void addNewCard(in string personName, in string card);
    9  void addMoney(in string card, in double money);
    10  void subMoney(in string card, in double money);
    11  double getCardBalance(in string card);
    12  };
    13  };

    Строки 5-12 являются объявлением интерфейса сервера BillingService. Указанный сервер представляет собой производный класс (конкретную реализацию) интерфейса BillingService. Отсутствие привязки к клиенту является сознательным, допускается произвольная реализация сервера.

    Строки 8-11 осуществляют объявление методов/функций/сообщений. Все, что объявляется на IDL, является public,поэтому в IDL -интерфейсах отсутствуют специальные ключевые слова для public, private, protected (хотя у метатипов component и valuetype есть ключевые слова public и private).

    Следующим шагом в реализации нашего приложения будет генерация из IDL -определения вспомогательных классов. В самом деле, то, что написано в IDL -файле, очень напоминает определение интерфейса java или абстрактного класса C++. Вполне естественно, что созданы инструменты, автоматически генерирующие соответствующие конструкции на базовом языке. Кроме того, мы говорили о том, что базовые типы IDL должны транслироваться в соответствующие типы целевой системы программирования. И опять же, такого рода трансляция может осуществляться автоматически, соответствующим образом сгенерированным кодом.

    Компиляция файла idl

    Компиляция файла BillingService.idl компилятором Java-IDL idlj,входящим в состав пакета jdk фирмы Sun, осуществляется так:

    idlj -td c:\src -pkgPrefix BillingServiceModule com.asw.corba.ex1 
       -fall BillingService.idl

    Позднее мы рассмотрим утилиту Java idlj и ее параметры командной строки. По умолчанию утилита idlj для каждого определенного в idl модуля создает пакет java, а для элементов модуля создает набор java -классов, включенных в этот пакет.

    Автоматически сгенерированные файлы

    После компиляции файла BillingService.idl компилятор Java-IDL создает следующие файлы на стороне сервера в вновь созданной директории BillingServiceModule:

    BillingService.java,
    BillingServiceHelper.java,
    BillingServiceHolder.java,
    BillingServiceOperations.java,
    BillingServicePOA.java,
    _BillingServiceStub.java.

    Рассмотрим подробнее эти файлы (в этих файлах определены классы и интерфейсы) и то, как мы можем их использовать при создании нашего приложения.

    BillingService.java и BillingServiceOperations.java - интерфейсы. BillingService.java (пример 6.2) представляет собой интерфейс BillingService.

    1  package com.asw.corba.ex1.BillingServiceModule;
    2 
    3
    4 /**
    5 *  com/asw/corba/ex1/BillingServiceModule/BillingService.java .
    6 *  Generated by the IDL-to-Java compiler (portable), version "3.1"
    7 *  from com/asw/corba/ex1/BillingService.idl
    8 *  16 Июль 2006 г.  14:12:21 MSD
    9 */ 
    10
    11  public interface BillingService extends BillingServiceOperations,
    12  org.omg.CORBA.Object, org.omg.CORBA.portable.IDLEntity
    13  {
    14  } // interface BillingService

    В строках 11 и 12 объявлен интерфейс BillingService и три интерфейса, от которых наследует BillingService. Два из трех интерфейсов являются определенными в CORBA типами, от которых должны наследовать все CORBA -совместимые объекты, org.omg.CORBA.Object и org.omg.CORBA.portable. IDLEntity.Третий интерфейс - BillingServiceOperations (пример 6.3) - создается на основе IDL -описания и объявляет открытые операции данного сервера. BillingService определяет базовый класс, который наследует от BillingServiceOperations и от CORBA -интерфейсов, упомянутых ранее. Это часть структуры, необходимой производным классам, чтобы быть настоящими CORBA -объектами. BillingServiceOperations,ни от кого не наследуя, объявляет четыре метода, изначально определенные в IDL.

    1  package com.asw.corba.ex1.BillingServiceModule;
    2
    3
    4 /**
    5 * com/asw/corba/ex1/BillingServiceModule/BillingServiceOperations.java . 
    6 * Generated by the IDL-to-Java compiler (portable), version "3.1" 
    7 * from com/asw/corba/ex1/BillingService.idl
    8 * 16 Июль 2006 г. 14:12:21 MSD
    9 */
    10
    11  // определение CORBA-совместимого сервиса
    12  public interface BillingServiceOperations
    13  {
    14  void addNewCard (String personName, String card);
    15  void addMoney (String card, double money);
    16  void subMoney (String card, double money);
    17  double getCardBalance (String card);
    18  } // interface BillingServiceOperations

    Файлы BillingServiceHelper.java (пример 6.4) и BillingServiceHolder.java (пример 6.5) содержат вспомогательные классы, содержащие методы приведения типов и методы маршаллинга/демаршаллинга сообщений.

    1  package com.asw.corba.ex1.BillingServiceModule;
    2 
    3
    4 /**
    5 * com/asw/corba/ex1/BillingServiceModule/BillingServiceHelper.java . 
    6 * Generated by the IDL-to-Java compiler (portable), version "3.1" 
    7 * from com/asw/corba/ex1/BillingService.idl
    8 * 16 Июль 2006 г. 14:12:21 MSD
    9 */
    10
    11  // определение класса BillingServiceHelper
    12  abstract public class BillingServiceHelper
    13  {
    14  private static String   _id = 
        "IDL:BillingServiceModule/BillingService:1.0";
    15
    16  public static void insert (org.omg.CORBA.Any a, 
        com.asw.corba.ex1.BillingServiceModule.BillingService that)
    17  {
    18  org.omg.CORBA.portable.OutputStream out = a.create_output_stream ();
    19  a.type (type ());
    20  write (out, that);
    21  a.read_value (out.create_input_stream (), type ());
    22  }
    23
    24  public static com.asw.corba.ex1.BillingServiceModule.BillingService 
      extract (org.omg.CORBA.Any a)
    25  {
    26  return read (a.create_input_stream ());
    27  }
    28
    29  private static org.omg.CORBA.TypeCode __typeCode = null;
    30  synchronized public static org.omg.CORBA.TypeCode type ()
    31  {
    32  if (__typeCode == null)
    33  {
    34  __typeCode = org.omg.CORBA.ORB.init ().create_interface_tc
      (com.asw.corba.ex1.BillingServiceModule.BillingServiceHelper.id (), 
      "BillingService");
    35  }
    36  return__typeCode;
    37  }
    38
    39  public static String id ()
    40  {
    41  return _id;
    42  }
    43
    44  public static com.asw.corba.ex1.BillingServiceModule.BillingService 
      read (org.omg.CORBA.portable.InputStream istream)
    45  {
    46  return narrow (istream.read_Object (_BillingServiceStub.class));
    47  }
    48
    49  public static void write (org.omg.CORBA.portable.OutputStream 
      ostream, com.asw.corba.ex1.BillingServiceModule.BillingService 
      value)
    50  {
    51  ostream.write_Object ((org.omg.CORBA.Object) value);
    52  }
    53
    54  public static com.asw.corba.ex1.BillingServiceModule.BillingService 
      narrow (org.omg.CORBA.Object obj)
    55  {
    56  if (obj == null)
    57  return null;
    58  else if (obj instanceof 
      com.asw.corba.ex1.BillingServiceModule.BillingService)
    59  return (com.asw.corba.ex1.BillingServiceModule.BillingService)obj;
    60  else if (!obj._is_a (id ()))
    61  throw new org.omg.CORBA.BAD_PARAM ();
    62  else
    63  {
    64  org.omg.CORBA.portable.Delegate delegate = 
      ((org.omg.CORBA.portab-le.ObjectImpl)obj)._get_delegate ();
    65  com.asw.corba.ex1.BillingServiceModule._BillingServiceStub stub = 
      new com.asw.corba.ex1.BillingServiceModule._BillingServiceStub ();
    66  stub._set_delegate(delegate);
    67  return stub;
    68  }
    69  }
    70  }

    Необходимость в Holder -классах возникает в тот момент, когда у методов появляются возвращаемые параметры.

    1  package com.asw.corba.ex1.BillingServiceModule;
    2
    3 /**
    4 * com/asw/corba/ex1/BillingServiceModule/BillingServiceHolder.java
    5 * Generated by the IDL-to-Java compiler (portable), version "3.1" 
    6 * from com/asw/corba/ex1/BillingService.idl 
    7 * 16 Июль 2006 г.  14:12:21 MSD
    8 */ 
    9
    10    // определение класса BillingServiceHolder
    11  public final class BillingServiceHolder implements
      org.omg.CORBA.portable.Streamable
    12  {
    13  public com.asw.corba.ex1.BillingServiceModule.BillingService value
      = null; 
    14
    15  public BillingServiceHolder ()
    16  {
    17  }
    18
    19  public BillingServiceHolder (com.asw.corba.ex1. 
      BillingServiceModule.BillingService initialValue)
    20  {
    21  value = initialValue;
    22  } 23
    
    24  public void _read (org.omg.CORBA.portable.InputStream i)
    25  {
    26  value = com.asw.corba.ex1.BillingServiceModule. 
      BillingServiceHelper.read (i);
    27  }
    28
    29  public void _write (org.omg.CORBA.portable.OutputStream o)
    30  {
    31  com.asw.corba.ex1.BillingServiceModule.BillingServiceHelper.write 
      (o, value);
    32  } 
    33
    34  public org.omg.CORBA.TypeCode _type ()
    35  {
    36  return com.asw.corba.ex1.BillingServiceModule. 
      BillingServiceHelper.type ();
    37  }
    38
    39   }

    Файл _BillingServiceStub.java (пример 6.6) содержит клиентскую заглушку, с которой будет иметь дело клиент. Класс _BillingServiceStub наследует от org.omg.CORBA.portable.ObjectImpl и реализует интерфейс BillingService.При вызове клиентом методов этого класса происходят упаковка вызова (и параметров) и их передача по сети серверному классу. Поскольку этот метод реализует интерфейс BillingService,клиент будет пребывать в полной уверенности, что вызывает непосредственно методы серверного класса.

    1  package com.asw.corba.ex1.BillingServiceModule;
    2
    3 /**
    4 * com/asw/corba/ex1/BillingServiceModule/_BillingServiceStub.java . 
    5 * Generated by the IDL-to-Java compiler (portable), version "3.1" 
    6 * from com/asw/corba/ex1/BillingService.idl
    7 * 16 Июль 2006 г. 14:12:21 MSD 
    8 */
    9
    10  // определение класса _BillingServiceStub
    11  public class _BillingServiceStub extends 
      org.omg.CORBA.portable.ObjectImpl implements 
      com.asw.corba.ex1.BillingServiceModule.BillingService
    12  {
    13  public void addNewCard (String personName, String card)
    14  {
    15  org.omg.CORBA.portable.InputStream $in = null;
    16  try {
    17  org.omg.CORBA.portable.OutputStream $out = 
        _request ("addNewCard", true);
    18  $out.write_string (personName);
    19  $out.write_string (card);
    20  $in = _invoke ($out);
    21  return;
    22  } catch (org.omg.CORBA.portable.ApplicationException $ex) {
    23  $in = $ex.getInputStream ();
    24  String _id = $ex.getId ();
    25  throw new org.omg.CORBA.MARSHAL (_id);
    26  } catch (org.omg.CORBA.portable.RemarshalException $rm) {
    27  addNewCard (personName, card  );
    28  } finally {
    29  _releaseReply ($in);
    30  }
    31  } // addNewCard
    32
    33  public void addMoney (String card, double money)
    34  {
    35  org.omg.CORBA.portable.InputStream $in = null;
    36  try {
    37  org.omg.CORBA.portable.OutputStream $out = 
        _request ("addMoney", true);
    38  $out.write_string (card);
    39  $out.write_double (money);
    40  $in = _invoke ($out);
    41  return;
    42  } catch (org.omg.CORBA.portable.ApplicationException $ex) {
    43  $in = $ex.getInputStream ();
    44  String _id = $ex.getId ();
    45  throw new org.omg.CORBA.MARSHAL (_id);
    46  } catch (org.omg.CORBA.portable.RemarshalException $rm) {
    47  addMoney (card, money  );
    48  } finally {
    49  _releaseReply ($in);
    50  }
    51  } // addMoney
    52
    53  public void subMoney (String card, double money)
    54  {
    55  org.omg.CORBA.portable.InputStream $in = null;
    56  try {
    57  org.omg.CORBA.portable.OutputStream $out = 
        _request ("subMoney", true);
    58  $out.write_string (card);
    59  $out.write_double (money);
    60  $in = _invoke ($out);
    61  return;
    62  } catch (org.omg.CORBA.portable.ApplicationException $ex) {
    63  $in = $ex.getInputStream ();
    64  String _id = $ex.getId ();
    65  throw new org.omg.CORBA.MARSHAL (_id);
    66  } catch (org.omg.CORBA.portable.RemarshalException $rm) {
    67  subMoney (card, money  );
    68  } finally {
    69  _releaseReply ($in);
    70  }
    71  } // subMoney
    72
    73  public double getCardBalance (String card)
    74  {
    75  org.omg.CORBA.portable.InputStream $in = null;
    76  try {
    77  org.omg.CORBA.portable.OutputStream $out = 
        _request ("getCardBalance", true);
    78  $out.write_string (card);
    79  $in = _invoke ($out);
    80  double $result = $in.read_double ();
    81  return $result;
    82  } catch (org.omg.CORBA.portable.ApplicationException $ex) {
    83  $in = $ex.getInputStream ();
    84  String _id = $ex.getId ();
    85  throw new org.omg.CORBA.MARSHAL (_id);
    86  } catch (org.omg.CORBA.portable.RemarshalException $rm) {
    87  return getCardBalance (card  );
    88  } finally {
    89  _releaseReply ($in);
    90  }
    91  } // getCardBalance
    92
    93  // Type-specific CORBA::Object operations
    94  private static String[]__ids = {
    95  "IDL:BillingServiceModule/BillingService:1.0"};
    96
    97  public String[] _ids ()
    98  {
    99  return (String[])__ids.clone ();
    100  }
    101
    102  private void readObject (java.io.ObjectInputStream s) 
      throws java.io.IOException
    103  {
    104  String str = s.readUTF ();
    105  String[] args = null;
    106  java.util.Properties props = null;
    107  org.omg.CORBA.Object obj = org.omg.CORBA.ORB.init (args, 
      props).string_to_object (str);
    108  org.omg.CORBA.portable.Delegate delegate = 
      ((org.omg.CORBA.portable.ObjectImpl) obj)._get_delegate ();
    109  _set_delegate (delegate);
    110  }
    111
    112  private void writeObject (java.io.ObjectOutputStream s) 
      throws java.io.IOException
    113  {
    114  String[] args = null;
    115  java.util.Properties props = null;
    116  String str = org.omg.CORBA.ORB.init (args, props).object_to_string
      (this);
    117  s.writeUTF (str);
    118  }
    119  } // class _BillingServiceStub

    И, наконец, последний файл BillingServicePOA.java (пример 6.7) содержит класс, выполняющий всю техническую работу по диспетчеризации сообщений клиента, - этот класс будет являться предком для нашего серверного класса, в котором мы переопределим методы, выполняющие содержательные операции. Класс BillingServicePOA наследует от org.omg.PortableServer.Servant и реализует CORBA -интерфейс (InvokeHandler) и интерфейс BillingServiceOperations.Интерфейс InvokeHandler объявляет метод _invoke,который является реализацией BillingServicePOA. ORB использует метод _invoke для вызова различных методов BillingService в обобщенном виде. Метод _invoke создается IDL -компилятором на основе IDL -интерфейса, объявленного в IDL -файле BillingService.idl.

    1  package com.asw.corba.ex1.BillingServiceModule;
    2
    3 /**
    4 * com/asw/corba/ex1/BillingServiceModule/BillingServicePOA.java . 
    5 * Generated by the IDL-to-Java compiler (portable), version "3.1" 
    6 * from com/asw/corba/ex1/BillingService.idl 
    7 * 16 Июль 2006 г.  14:12:21 MSD
    8 */ 
    9
    10  // определение класса BillingServicePOA
    11  public abstract class BillingServicePOA extends 
        org.omg.PortableServer.Servant
    12  implements
      com.asw.corba.ex1.BillingServiceModule.BillingServiceOperations, 
      org.omg.CORBA.portable.InvokeHandler
    13  {
    14
    15  // Constructors
    16
    17  private static java.util.Hashtable _methods = new java.util.Hashtable ();
    18  static
    19  {
    20  _methods.put ("addNewCard", new java.lang.Integer (0));
    21  _methods.put ("addMoney", new java.lang.Integer (1));
    22  _methods.put ("subMoney", new java.lang.Integer (2));
    23  _methods.put ("getCardBalance", new java.lang.Integer (3));
    24  }
    25
    26  public org.omg.CORBA.portable.OutputStream _invoke (String $method,
    27  org.omg.CORBA.portable.InputStream in,
    28  org.omg.CORBA.portable.ResponseHandler $rh)
    29  {
    30  org.omg.CORBA.portable.OutputStream out = null;
    31  java.lang.Integer __method = (java.lang.Integer)_methods.get ($method);
    32  if (__method == null)
    33  throw new org.omg.CORBA.BAD_OPERATION (0, 
      org.omg.CORBA.CompletionStatus.COMPLETED_MAYBE);
    34
    35  switch __method.intValue ())
    36  {
    37  case 0:   // BillingServiceModule/BillingService/addNewCard
    38  {
    39  String personName = in.read_string ();
    40  String card = in.read_string ();
    41  this.addNewCard (personName, card);
    42  out = $rh.createReply();
    43  break;
    44  }
    45
    46  case 1:   // BillingServiceModule/BillingService/addMoney
    47  {
    48  String card = in.read_string ();
    49  double money = in.read_double ();
    50  this.addMoney (card, money);
    51  out = $rh.createReply();
    52  break;
    53  }
    54
    55  case 2:   // BillingServiceModule/BillingService/subMoney
    56  {
    57  String card = in.read_string ();
    58  double money = in.read_double ();
    59  this.subMoney (card, money);
    60  out = $rh.createReply();
    61  break;
    62  }
    63
    64  case 3:   // BillingServiceModule/BillingService/getCardBalance
    65  {
    66  String card = in.read_string ();
    67  double $result = (double)0;
    68  $result = this.getCardBalance (card);
    69  out = $rh.createReply();
    70  out.write_double ($result);
    71  break;
    72  }
    73
    74  default:
    75  throw new org.omg.CORBA.BAD_OPERATION (0, 
      org.omg.CORBA.CompletionStatus.COMPLETED_MAYBE);
    76  }
    77  return out;
    78  } // _invoke
    79
    80  // Type-specific CORBA::Object operations
    81  private static String[] __ids = {
    82  "IDL:BillingServiceModule/BillingService:1.0"};
    83
    84  public String[] _all_interfaces 
      (org.omg.PortableServer.POA poa, byte[] objectId)
    85  {
    86  return (String[])__ids.clone ();
    87  }
    88
    89  public BillingService _this()
    90  {
    91  return BillingServiceHelper.narrow(super._this_object());
    92  }
    93
    94  public BillingService _this(org.omg.CORBA.ORB orb)
    95  {
    96  return BillingServiceHelper.narrow(super._this_object(orb));
    97  }
    98  } // class BillingServicePOA

    Файлы BillingServiceImpl.java и BillingServiceServer.java

    Следующим шагом после генерации необходимых вспомогательных файлов будет реализация серверного объекта, реализующего описанные нами методы.

    BillingServiceImpl (пример 6.8) является реализацией интерфейса BillingService.В соответствии с принятым нами ранее соглашением конкретный класс, реализующий открытый интерфейс распределенного CORBA -объекта, имеет имя <IDL interface name>Impl.Этот класс не обязан объявлять метод main, но может это сделать.

    1  // BillingServiceImpl.java
    2  package com.asw.corba.ex1;
    3
    4  // базовые пакеты Java
    5  import java.util.Hashtable;
    6
    7  import com.asw.corba.ex1.BillingServiceModule.*;
    8
    9  // пакеты OMG CORBA
    10  import org.omg.CORBA.*;
    11
    12  public class BillingServiceImpl   extends BillingServicePOA {
    13  private ORB orb;
    14  private   Hashtable hash = new Hashtable();
    15
    16  public void setORB(ORB orb_val) {
    17  orb = orb_val;
    18  }
    19
    20  public void addNewCard(String personName, String card) {
    21  hash.put(card, new Double(0.0));
    22  }
    23
    24  public void addMoney(String card, double money) {
    25  Double d = (Double)hash.get(card);
    26
    27  if (d!=null) hash.put(card,new Double(d.doubleValue()+money));
    28  }
    29
    30  public void subMoney(String card, double money) {
    31  Double d = (Double)hash.get(card);
    32
    33  if (d!=null) hash.put(card,new Double(d.doubleValue()-money));
    34  }
    35
    36  public double getCardBalance(String card) {
    37  Double d = (Double)hash.get(card);
    38
    39  if (d!=null) return d.doubleValue();
    40  else return 0;
    41  }
    42  }

    Класс BillingServiceImpl является наследником класса BillingServicePOA (который был построен автоматически) и, таким образом, уже обладает всей базовой функциональностью серверного класса. Все, что мы должны сделать, - определить реализацию методов addNewCard, addMoney, subMoney и getCardBalance.Таким образом, для реализации серверного класса нам пришлось написать всего несколько строк кода - весь остальной код был построен автоматически!

    Класс BillingServiceImpl реализует методы сервера, а запуск сервера производится классом BillingServiceServer (пример 6.9), основная задача которого - "положить" экземпляр BillingService в систему, то есть зарегистрировать его.

    Дело в том, что при создании распределенных приложений существует проблема поиска необходимых компонент. Если система распределенная, как определить, на каком узле находится тот или иной ее компонент (с тем, чтобы отправлять запросы на этот узел)? В CORBA эта проблема решается вводом специального сервиса - сервиса имен (Naming Service),который составляет список ресурсов для будущего их использования клиентами. Факт регистрации в сервисе имен не означает, что сервер может решать какие-то задачи; сервер просто предоставляет клиентам возможность доступа к нему. Сервис должен быть готов в любой момент обрабатывать входящие запросы на выполнение функций, необходимых вызывающим клиентам. Любой объект, который должен обрабатывать клиентские запросы, может зарегистрироваться в этом сервисе под каким-то именемВ нашем случае имена объектов должны быть уникальны, однако сервисы именования могут поддерживать режим, при котором на одно имя регистрируются сразу несколько объектов. При обращении пользователя по имени объекта ему возвращается ссылка на один из объектов с таким именем. Таким образом могут реализоваться механизмы обеспечения отказоустойчивости системы при отказе одного из объектов, а также механизмы балансировки нагрузки . В различных реализациях CORBA поддерживается различный набор сервисов. Минимально необходимым из них является только сервис имен. Не необходимыми, но очень полезными (и поддерживаемыми во многих реализациях) являются сервис сообщений (Event Service) и сервис уведомлений (Notification Service).Кроме перечисленных, поставщик может предоставить следующие сервисы:

  • ES позволяет создавать каналы, по которым приложения могут обмениваться сообщениями;
  • NS расширяет эту функциональность, позволяя фильтровать и типизировать сообщения;
  • сервис безопасности ( Security service ) обеспечивает аутентификацию и контроль доступа к объектам, аудит узлов;
  • сервис транзакций ( Transaction service ) TS обеспечивает поддержку системой механизма транзакций;
  • сервис контроля параллельности ( Concurrency control ) CCS обеспечивает поддержку механизма блокировок и синхронизации;
  • сервис долговременного хранения ( Persistent object service ) обеспечивает сохранение состояния объектов в постоянном хранилище (БД) и их восстановление при запуске системы.
  • Таким образом, клиенту для того, чтобы обратиться к объекту, необходимо знать его имя. Обратившись к сервису именования, клиент по имени получает ссылку на нужный ему объект.

    Класс BillingServiceServer как раз и регистрирует серверный объект BillingServiceImpl в сервисе имен.

    Рассмотрим этот класс подробнее.

    Класс BillingServiceServer (пример 6.9) содержит метод main, являющийся точкой входа в программу.

    1  // BillingServiceServer.java
    2  package com.asw.corba.ex1;
    3
    4  // пакеты OMG CORBA
    5  import  org.omg.CosNaming.*;
    6  import  org.omg.CORBA.*;
    7  import  org.omg.PortableServer.*;
    8  import  org.omg.PortableServer.POA;
    9  import  com.asw.corba.ex1.BillingServiceModule.*;
    10
    11  public class BillingServiceServer {
    12  public static void main(String args[]) {
    13  try{
    14  // create and initialize the ORB
    15  ORB orb = ORB.init(args, null);
    16
    17  // get reference to rootpoa  activate the POAManager
    18  POA rootpoa =
    19  POAHelper.narrow(orb.resolve_initial_references("RootPOA"));
    20  rootpoa.the_POAManager().activate();
    21
    22  // create servant and register it with the ORB
    23  BillingServiceImpl BSImpl = new BillingServiceImpl();
    24  BSImpl.setORB(orb); 
    25
    26  // get object reference from the servant
    27  org.omg.CORBA.Object ref = rootpoa.servant_to_reference(BSImpl);
    28  BillingService href = BillingServiceHelper.narrow(ref);
    29
    30  // get the root naming context
    31  // NameService invokes the name service
    32  org.omg.CORBA.Object objRef =
    33  orb.resolve_initial_references("NameService");
    34  // Use NamingContextExt which is part of the Interoperable
    35  // Naming Service (INS) specification.
    36  NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);
    37
    38  // bind the Object Reference in Naming
    39  String name = "BillingService";
    40  NameComponent path[] = ncRef.to_name( name );
    41  ncRef.rebind(path, href);
    42
    43  System.out.println("BillingServiceServer ready and waiting ...");
    44
    45  // wait for invocations from clients
    46  orb.run();
    47  }
    48
    49  catch (Exception e) {
    50  System.err.println("ERROR: " + e);
    51  e.printStackTrace(System.out);
    52  }
    53  System.out.println("BillingServer Exiting ...");
    54  }
    55  }

    Первое, что делает этот класс, - создает ссылку на orb (брокер объектных запросов) (строка 15), передавая в статический метод init переданные ему аргументы командной строки (что это за аргументы, мы увидим, когда перейдем к запуску приложений). Корректный входной параметр - это опция командной cтроки, указывающая брокеру объектных запросов, какой коммуникационный порт использовать. Существует версия init,не имеющая параметров, которая возвращает брокер объектных запросов, создаваемый по умолчанию, вместо брокера объектных запросов, сконфигурированного в соответствии с входными параметрами. Параметры метода init могут иметь значения null.

    Затем идут несколько строк (строки 18-20), отвечающих за получение и активизацию ссылки на корневой POA ( Portable Object Adapter )Существуют зарезервированные имена в сервисе имен, такие как RootPOA или NameService, под которыми зарегистрированы системные объекты. Соответственно, для обращения к этим системным объектам могут быть использованы объектные ссылки, полученные по этим именам . Строка "RootPOA" является стандартным именем, определенным в спецификации CORBA (вместе со списком других имен). Метод resolve_initial_ references имеет доступ к специальному списку сервисов, непосредственно доступных данному брокеру объектных запросов. Статический метод narrow класса POAHelper используется для преобразования возвращенной объектной ссылки в объект нужного типа. Метод narrow является CORBA -механизмом надежного приведения ссылки одного типа к ссылке другого типа. Метод narrow проверяет, что интерфейс, которому мы пытаемся передать целевой объект, поддерживает целевые объекты этого типа. Обычное приведение не работает с объектными ссылками CORBA, так как объектная ссылка - это посредник на пути к удаленной информации. Все классы Helper имеют метод static narrow,который дает нам возможность осуществлять приведение родительских классов к производным классам.

    В строке 23 создается экземпляр нашего класса BillingServiceImpl,и ему передается ссылка на orb (строка 24). Затем формируется объектная ссылка на наш серверный класс (сроки 27-28).

    В строках 32-33 извлекается объектная ссылка на сервис имен. Проблема обнаружения сервером сервиса имен при первом запуске решается брокером объектных запросов с помощью метода resolve_initial_references.Брокер объектных запросов имеет эффективный мини-сервис имен, с помощью которого он может выполнять обнаружение базовых сервисов. Однако чтобы метод resolve_initial_references мог работать с любым из сервисов, он возвращает объектную ссылку в виде объекта типа org.omg.CORBA.Object.Строка "NameService" является стандартным именем, определенным в спецификации CORBA. Статический метод narrow класса NamingContextExtHelper применяется для преобразования возвращенной объектной ссылки в объект нужного типа.

    Затем задается имя для нашего объекта (строка 39), которое помещается в массив объектов типа NameComponent (строка 40). Сервер не может быть зарегистрирован и использован другими объектами, если у него нет имени, - эта ошибка достаточно серьезна. Чтобы имя было правильно зарегистрировано, ресурс должен установить контекст именования. Сервер регистрируется в основном (или корневом) контексте именования, поэтому единственное, что нужно сервису имен, - это имя сервера, которое сервер посылает с помощью метода rebind (строка 41). Метод rebind вводит NameComponent, если он отсутствует в сервисе имен, или вводит его повторно, если он уже присутствует там. Однако такое использование rebind позволяет за одно обращение получить доступ только к одному экземпляру BillingService.Последним зарегистрированным BillingService является сервер, с которым связаны клиенты.

    Вызов orb.run переводит объект в состояние ожидания запросов от клиентов, более того, этот вызов является блокирующим.

    Таким образом, мы создали серверный объект, способный принимать и обрабатывать запросы пользователей и зарегистрировали его в сервисе имен (в его корневом разделе) под именем BillingService.

    Файл BillingServiceClient.java

    BillingServiceClient (пример 6.10) представляет собой клиент, который соединяется с BillingService.Клиент содержит один метод main,который добавляет денежные средства на карту с номером 1 и затем выводит текущий баланс этой карты (строки 29-30). Логика, используемая клиентами для чтения данных в сервисе имен, похожа на логику, применяемую сервером для записи в нее данных.

    1  // BillingServiceClient.java
    2  package com.asw.corba.ex1;
    3
    4  import  com.asw.corba.ex1.BillingServiceModule.*;
    5  import  org.omg.CosNaming.*;
    6  import  org.omg.CORBA.*;
    7
    8  public class BillingServiceClient {
    9  static BillingService BSImpl;
    10
    11  public static void main(String args[])
    12  {
    13  try{
    14  // create and initialize the ORB
    15  ORB orb = ORB.init(args, null);
    16
    17  // get the root naming context
    18  org.omg.CORBA.Object objRef =
    19  orb.resolve_initial_references("NameService");
    20  // Use NamingContextExt instead of NamingContext. This is
    21  // part of the Interoperable naming Service.
    22  NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);
    23
    24  // resolve the Object Reference in Naming
    25  String name = "BillingService";
    26  BSImpl = BillingServiceHelper.narrow(ncRef.resolve_str(name));
    27
    28  System.out.println("Obtained a handle on server object: " +
    BSImpl);
    29  BSImpl.addMoney("1",1234);
    30  System.out.println(BSImpl.getCardBalance("1"));
    31  } catch (Exception e) {
    32  System.out.println("ERROR : " + e) ;
    33  e.printStackTrace(System.out);
    34  }
    35  }
    36  }

    В строке 15 вызывается статический метод init класса ORB для создания брокера объектных запросов. Массив args типа String позволяет динамически настраивать брокер объектных запросов во время выполнения (массив получен из командной строки). После того как клиент получает ORB, в строках 18-19 вызывается метод resolve_initial_references этого брокера объектных запросов, чтобы получить объектную ссылку на сервис имен. В строке 22 осуществляется приведение полученной объектной ссылки к объекту типа NamingContextExt с помощью метода NamingContextExtHelper.narrow.

    Клиент должен запросить у сервиса имен объектную ссылку на сервис, который он разыскивает, - сервис BillingService,имеющий имя "BillingService" (объявлен в классе BillingServiceServer).С помощью метода resolve_str имя сервера "BillingService" передается переменной BSImpl типа BillingService.Статический метод BillingServiceHelper.narrow осуществляет приведение объектной ссылки к требуемому типу производного класса. С этого момента у клиента есть активный распределенный объект.

    Желательно сохранять имена сервисов в файлах типа .properties вместо того, чтобы "зашивать" их в исходный текст класса. Включение имен в исходный текст класса требует дополнительного сопровождения файлов классов при изменении имен. Файл свойств дает возможность настраивать уже развернутую систему, тем самым делая настройку вопросом администрирования, а не разработки.

    Выполнение примера

    Для выполнения примера требуется следующая последовательность действий:

  • скомпилировать IDL -файл с помощью idlj ;
  • реализовать и скомпилировать серверный класс;
  • реализовать и скомпилировать клиентский класс;
  • запустить сервис именования;
  • запустить сервер;
  • запустить клиент.
  • Для компиляции IDL -файла из командной строки используется компилятор idlj,входящий в состав пакета jdk фирмы Sun. Неполный список опций командной строки idlj выглядит так:

  • -f<client | server | all>
  • -pkgPrefix <имя модуля или тип IDL> <добавляемый префикс>
  • -td <выходной каталог>
  • Опция -f управляет созданием кода для заглушек и скелетов. Опция -fclient создает только клиентские файлы, -fserver - только серверные файлы, а -fall - и те, и другие. Опция -pkgPrefix генерирует имена пакетов. Эта опция используется с именем модуля. При компиляции модуля с именем modeName с опциями -pkgPrefix modeName prefix будут созданы файлы Java с именем пакета prefix.modeName.Опция -td предписывает записывать созданные файлы в указанный каталог.

    Например, если исходный код находится с каталоге C:\src,командная строка будет выглядеть следующим образом (исходя из IDL -описания, представленного в примере 6.1):

    idlj -td c:\src -pkgPrefix BillingServiceModule com.asw.corba.ex1 
       -fall BillingService.idl

    Эта командная строка создает и серверные, и клиентские файлы CORBA-Java. После реализации сервера и клиента скомпилируйте серверный и клиентский код. При компиляции никаких особенностей нет, все, что необходимо сделать - написать в командной строке

    javac *.java

    находясь в директории проекта. Необходимо проследить, чтобы пакет org.omg.*,содержащий реализацию поддержки CORBA, присутствовал в путях classpath.

    Для того чтобы запустить нашу систему, кроме запуска клиентской и серверной части нам необходимо запустить сервис имен. Мы воспользуемся сервисом имен, который входит в состав пакета jdk и называется tnameserv.exe - базовая реализация CORBA Object Service ( COS ) Naming Service. tnameserv не является готовым средством, реализующим сервис имен; скорее, оно служит средством тестирования, позволяющим проверить правильность взаимодействия клиентов и серверов. Сервис имен должен быть приведен в активное состояние до того, как будет запущен сервер. Поскольку нашей целью было создание распределенного приложения, предположим, что сервис имен будет работать на узле с именем alpha, серверный объект - на узле с именем beta,а клиент - на узле с именем gamma. Предполагаем, что на всех узлах установлен пакет jdk.

    На узле alpha запускаем сервис имен:

    H:\j2sdkL4.2_06\bin\tnameserv.exe

    Выполнение команды приведет к следующему результату:

    Initial Naming Context:
    IOR:000000000000002b49444c3a6f6d672e6f72672f436f734e616d696e672f4e 616d696e67436f
    6e746578744578743a312e30000000000001000000000000008000010200000000 0c3139322e3136
    382e312e31000384000000000035afabcb00000000202ba83e8000000001000000 00000000010000
    000d544e616d65536572766963650000000000000004000000000a000000000000 01000000010000
    00200000000000010001000000020501000100010020000101090000000100010100
    TransientNameServer: setting port for initial object references
    to: 900
    Ready.

    По умолчанию сервис ожидает обращений на 900 порте, однако иногда этот порт доступен только для администраторов. После того как сервис имен запущен, мы можем запустить на узле beta серверный объект:

    G:\BillingService >java com.asw.corba.ex1.BillingServiceServer 
      -ORBInitialHost=alpha

    Результат выполнения предыдущей команды:

    BillingServiceServer ready and waiting ...

    Мы должны каким-то образом указать, где находится сервис имен. Это делается с помощью ключей ORBInitialHost и ORBInitialPort. Именно эти параметры командной строки и передаются при инициализации orb в метод init. Поскольку мы использовали порт по умолчанию, параметр ORBInitialPort можно не указывать.

    Далее на узле gamma мы запускаем клиента:

    G:\BillingService>java com.asw.corba.ex1.BillingServiceClient 
      -ORBInitialHost=alpha

    Выполнение команды:

    Obtained a handle on server object:
    BillingServiceApp._BillingServiceStub:IOR:00000000000000174944
    4c3a48656c6c6f4170702f48656c6c6f3a312e3000000000000100000000000000
    6c000102000000
    000c3139322e3136382e312e31000bd2000000000021afabcb00000000202baba5 49000000010000
    00000000000000000004000000000a000000000000010000000100000020000000 00000100010000
    00020501000100010020000101090000000100010100 1234

    Поскольку клиент тоже нуждается в сервисе имен для получения объектной ссылки, ему также передается имя узла, на котором сервис имен запущен. Далее клиент получает объектную ссылку, распечатывает ее, вызывает метод addMoney и метод getCardBalance сервера, распечатывает строку, которую ему этот метод вернул, и, наконец, сервер выводит строку:

    BillingServer Exiting ...

    и прекращает работу.

    Таким образом, мы создали простое распределенное приложение, компоненты которого работают на разных узлах, вызывая методы друг друга. В нашем случае и клиент, и сервер были написаны с использованием языка программирования java, однако мы вполне могли написать одну из частей системы на другом языке, например, на C++.

    Страницы:

    Рабочий каталог расположен в Practice.

    Java RMI подходит в том случае, если все компоненты распределенного приложения реализованы на базе Java. В случае если необходимо взаимодействие компонентов, разработанных с использованием различных средств, может быть использована другая технология - CORBA.

    Введение

    CORBA представляет собой технологию построения распределенных систем, стандартизованную и поддерживаемую консорциумом OMG. Эта технология предоставляет механизмы для описания спецификаций интерфейсов взаимодействующих систем (предполагается, что они могут быть реализованы на различных языках программирования, с использованием различных платформ, и функционировать в различной сетевой инфраструктуре), а также обеспечивает некоторые важные сервисы - такие как сервисы именования, событий, транзакций, безопасности и т.д. Несмотря на то, что CORBA использует объектную идеологию, она может применяться в системах программирования, не поддерживающих объектный подход.

    CORBA означает Common Object Request Broker Architecture (общая архитектура брокера объектных запросов). CORBA - это своего рода "клей", интегрирующая технология. Она позволяет программам на разных языках, работающих в различных узлах сети, взаимодействовать друг с другом так же просто, как если бы они находились в адресном пространстве одного процесса. Задача CORBA сделать возможной интеграцию изолированных систем. Клиенты, работающие с CORBA, получают преимущества в трех областях: прозрачность вызова ( invocation transparency ), прозрачность реализации ( implementation transparency ) и прозрачность локализации ( location transparency ). Прозрачность - основная цель CORBA. Для решения этой задачи CORBA использует все важнейшие достижения объектной технологии: инкапсуляцию, наследование, полиморфизм, динамическое связывание.

    Прозрачность вызова определяет точку зрения клиента, посылающего сообщение серверу. Язык, используемый при реализации клиента, определяет, каким образом вызываемый объект получает сообщение, как параметры передаются в стек, как осуществляется обращение к методам данного объекта, как передаются возвращаемые значения. Если сервер создан в соответствии с правилами CORBA, то клиент может вызывать методы этого совместимого с CORBA объекта так, как и любого другого объекта, реализованного в используемом языке программирования. CORBA поддерживает ряд языков программирования (всего их 10), включая C, C++, Java, COBOL, Ada, Lisp, Smalltalk, PL/1, Python, IDLscript.

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

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

    Ответственным за CORBA является консорциум Object Management Group ( OMG ). Отличительным признаком CORBA по сравнению с другими технологиями распределенных систем является архитектура Object Management Architecture ( OMA ). ОМА (в качестве эталонной архитектуры) описывает систему, которая состоит из взаимодействующих сервисов, решающих прикладную задачу. Уровней модульности у такого решения больше, но концепция объектов (или компонентов), совместно работающих для достижения общей цели, остается той же самой.

    Чтобы объекты могли взаимодействовать так, как если бы они "обращались" на одном языке, они должны использовать общее для языков, на которых они реализованы, внешнее представление. Это внешнее представление - промежуточный язык - должно быть простым в использовании, легким в изучении и обусловливать минимальные накладные расходы. Таким промежуточным языком является язык OMG Interface Definition Language,обычно называемый просто IDL.

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

    IDL -компилятор создает для каждого компилируемого IDL -файла целый ряд файлов, ориентированных на конкретные языки программирования. Проекту потребуется столько IDL -компиляторов, сколько языков программирования используется при разработке системы. Для создания на Java интерфейса к унаследованной COBOL -системе потребуется два IDL -компилятора: один - для создания клиентской программы на языке Java, второй - для создания серверной программы на языке COBOL. Например, внешнему интерфейсу на языке Java потребуются следующие файлы, сгенерированные на языке Java, для взаимодействия с серверной системой, которая называется BillingService:

    BilingService.java
    BillingServiceOperations.java
    BillingServiceHolder.java
    BillingServiceHelper.java
    _ BillingServiceStub.java

    BilingService.java содержит основное определение удаленного сервера. Исходный файл BillingServiceOperations содержит определения методов (операций), поддерживаемых CORBA -объектом. Класс BillingServiceStub отвечает за взаимодействие на стороне клиента, а файл BillingServiceHelper определяет класс, используемый клиентом для выполнения задач, которые связаны с CORBA.

    Эти файлы скрывают механизм реализации вызова от вызывающей программы. BillingService.java и BillingServiceOperations.java определяют интерфейсы для использованиями классами, связанными с реализацией; _BillingServiceStub.java определяет код CORBA -вызова для осуществления вызовов удаленных методов на сервере. Файлы BillingServiceHelper.java и BillingServiceHolder.java - вспомогательные служебные файлы.

    Поставщик серверного компонента предоставляет IDL -файл, описывающий API своего серверного объекта. Используя этот файл и IDL -компилятор, разработчик может создать клиентские файлы, необходимые для непосредственного обращения к этому серверному объекту.

    Если бы в написанном на С или любом другом языке графическом пользовательском интерфейсе системы BillingService потребовалось бы посылать сообщения серверным объектам, написанным на Java ( CORBA -совместимые Java -объекты), с помощью IDL -компилятора потребовалось бы создать следующие файлы, чтобы серверные Java -объекты могли получать сообщения от любых вызывающих программ:

    BillingService.java
    BillingServiceOperations.java
    BillingServicePOA.java

    Файлы BillingService.java и BillingServiceOperations.java - те же, что и раньше (интерфейсы для использования классами, связанными с реализацией), а BillingServicePOA.java представляет собой код, используемый сервером для приема вызовов методов и передачи возвращаемых значений через сеть.

    Дополнительно к объявлению Java -интерфейсов, применяемых разработчиками, и клиентские, и серверные файлы, определенные выше, реализуют базовые классы посредников (proxies) - "заместителей" других объектов. Посредники создают для клиента видимость того, что он посылает сообщение одному объекту, в то время как на самом деле этот клиент посылает сообщение другому объекту. CORBA определяет двух взаимосвязанных посредников: заглушку (stub) и скелет (skeleton).Заглушка является посредником на стороне клиента, скелет - на стороне сервера. Оба посредника скрывают использование ORB и от клиента, и от сервера.

    Object Request Broker - это важнейший элемент технологии CORBA, связанный с брокером объектных запросов. Основным назначением ORB является функциональная совместимость. Все CORBA -совместимые объекты должны применять какой-либо брокер объектных запросов для передачи или приема запросов методов, но сами объекты редко имеют дело непосредственно с брокером объектных запросов. Способность к взаимодействию, заложенная в CORBA, остается одной и той же независимо от конкретной реализации CORBA.

    Когда клиент запрашивает операцию, реализуемую распределенным объектом, брокер объектных запросов данного клиента для выполнения вызова использует объектную ссылку. Объектная ссылка, используемая клиентом (поддерживаемая открытым API вызываемого объекта, описанным в IDL -файле), содержит непрозрачную сетевую ссылку (непрозрачную - так как ни клиент, ни сервер не могут извлечь из этой ссылки информацию о деталях реализации). Клиент запрашивает операцию, реализуемую распределенным объектом посредством объектной ссылки, поскольку Interoperable Object Reference ( IOR ) ссылается именно на объектную ссылку, структура которой хорошо известна брокерам ORB при условии использования OMG -совместимых протоколов (например, IIOP ). Источником объектной ссылки является объектный адаптер, указывающий на удаленный объект. Объектная ссылка содержит три важных фрагмента информации: местоположение распределенного объекта; ссылку на адаптер, создавший объектную ссылку; идентификатор объекта серванта. Все CORBA -совместимые объектные брокеры "знают", что такое объектные ссылки и как их использовать для установления соединений клиентов с сервантами. Последнее, что необходимо для обеспечения функциональной совместимости, - это создание и синтаксический анализ IOR в протоколе для отправки запроса через сеть. Чтобы сделать возможным надежное и уверенное взаимодействие брокеров объектных запросов различных производителей, консорциум OMG разработал спецификацию протокола Internet Inter ORB Protocol ( IIOP ). IIOP - это протокол обмена сообщениями между объектными брокерами.

    Все производители должны обеспечивать поддержку IIOP, чтобы их объектные брокеры были CORBA -совместимыми. Производители могут также поддерживать свои собственные протоколы, но, как минимум, они должны поддерживать IIOP. Объектные брокеры, которые предполагают обмениваться между собой сообщениями, должны "говорить" на языке одного и того же протокола. В том случае, если объектные брокеры поддерживают разные протоколы, должны быть разработаны специальные адаптеры для преобразования протоколов, чтобы обеспечить обмен сообщениями между объектными брокерами, поддерживающими нестандартные протоколы. При установке брокера объектных запросов IIOP должен устанавливаться по умолчанию в качестве стандартного протокола, хотя IIOP может являться не единственным протоколом.

    IIOP является реализацией другого стандарта OMG - General Inter ORB Protocol ( GIOP ). GIOP определяет сообщения, необходимые объектным брокерам для обмена данными между собой, и обеспечивает поддержку лежащего в основе этого взаимодействия транспорта для платформы, на которой функционирует данный брокер объектных запросов. TCP/IP является предпочтительным транспортным средством, хотя спецификация включает также поддержку Novell IPX и OSI. Производители могут обеспечивать поддержку и других транспортных механизмов. Java, с ее встроенной сетевой поддержкой, и CORBA, с ее механизмами обеспечения соединений распределенных объектов, являются взаимодополняющими технологиями.

    Для каждого языка программирования, поддерживаемого OMG, должно существовать преобразование из IDL на этот язык. Отображение IDL-Java определяет, как следует преобразовывать ключевые слова и типы данных IDL в конструкции Java. Java 2 был первой версией, выпущенной корпорацией Sun, непосредственно поддерживающей OMG -отображение.

    Последовательность действий

    Реализация распределенной системы - это нетривиальная задача. Для реализации распределенной системы с использованием Java и CORBA необходимы следующие шаги:

  • выполнить анализ и проектирование на основе моделирования предметной области, моделирования системы и декомпозиции системы на ряд подсистем;
  • создать IDL -описание путем спецификации API распределенных подсистем и спецификации структур данных, которые передаются через границы системы;
  • реализовать сервант, используя файлы, сгенерированные IDL -компилятором;
  • реализовать клиент, используя файлы заглушек, сгенерированные IDL -компилятором;
  • принять решение о методе распространения объектных ссылок сервантов (обычно это делается с помощью сервиса именования, но не обязательно);
  • запустить реализацию серванта;
  • запустить клиент.
  • После определения подсистем нужно решить, каким из них придать форму распределенных сервисов. Если предположить, что структура всех подсистем реализует функциональность распределенных совместно используемых сервисов, то перевод таких подсистем на платформу CORBA существенно упрощается. В любом случае, открытый API такого сервиса будет доступен клиентам. Для объектно-ориентированного подхода обычно интерфейс, описываемый в IDL, может являться некоторым подмножеством всех реализуемых сервером функций. И даже после реализации сервера (мы используем Java в качестве языка программирования) разработчиков продолжает волновать вопрос о том, сколько дополнительных функций (административных, связанных с защитой и т.д.) предоставить серверу.

    Если существуют структуры данных, которые сервер должен возвращать клиенту (или принимать от него), то описание и определение этих структур данных должно присутствовать в IDL -файле. Модель CORBA предоставляет возможность процедурным языкам программирования описывать конструкции в "псевдообъектном" стиле. Две конструкции CORBA поддерживают этот механизм: struct и valuetype. Клиент и сервер передают IDL -структуры ( struct ) туда и обратно в качестве обычных данных. Стандартная оптимизация включает в себя отправку этих данных непосредственно клиенту, чтобы освободить сервер от избыточных запросов. Конструкция valuetype является IDL -описанием некоторой сущности, включающей и данные, и методы. Конструкция struct состоит только из данных (конструкция struct, скомпилированная в Java -код, определяет открытые атрибуты), тогда как valuetype инкапсулирует данные, и добавляет логику, необходимую для создания объекта, простого или сложного, какой требуется.

    После объявления API сервера в виде IDL -файла единственное, чего не достает, - это собственно реализация сервера. Компилятор IDL анализирует IDL -файл и генерирует соответствующие файлы на целевом языке. После того как созданы вспомогательные файлы, разработчик может писать код реализации; до этого момента единственное, что было сделано разработчиком, - это объявлены интерфейс, атрибуты и данные, необходимые серверу для обработки запросов. Сначала разработчик может реализовать фиктивный сервер, который просто возвращает текстовые данные, чтобы другой разработчик мог написать клиент (или, возможно, текстовый клиент) для тестирования обращений к серверу. Более типичный пример - сервер, который является оболочкой унаследованного приложения, но скрывает это от клиентов.

    Допустим, что объявленное API сервера находится в IDL -файле и IDL -компилятор скомпилировал этот файл. Теперь клиент может использовать сгенерированные для клиента файлы, необходимые ему для взаимодействия с сервером. Клиенту нужны только файлы, сгенерированные IDL -компилятором, чтобы обеспечить прозрачное сетевое взаимодействие. Можно создать локальную версию сервера (вместо того, чтобы задействовать объектную ссылку CORBA, можно просто выполнить операцию new ServerImpl()) и отправлять сообщения напрямую, но такой сервер уже не будет распределенным объектом, он будет работать в адресном пространстве клиента, утрачивая преимущества слабой связи между компонентами системы, имевшимися первоначально, благодаря использованию интегрирующего кода, размещенного в сгенерированных файлах.

    Первый пример

    Данный пример реализует решение задачи про сеть столовых, использующую в своей работе с клиентами пластиковые карты.

    Интерфейс сервера BillingService включает четыре метода: addNewCard (добавляет новую карту), addMoney (добавляет денежные средства на карту), subMoney (снимает денежные средства с карты), getCardBalance (показывает текущий баланс карты).

    Файл BillingService.idl

    В примере 6.1 приведено IDL -описание сервера BillingService.

    CORBA не является предписанием реализации, поэтому IDL -описание формально программным кодом не является. Ключевое слово module связывает данное имя непосредственно с пакетом Java. Вложенные имена module, соединенные вместе, дают полное имя пакета. Имя module в примере 6.1 - BillingServiceModule.

    Фигурные скобки в строках 4 и 13 обозначают границы области действия блока. В IDL точка с запятой всегда завершает блок (включая объявление module ). Java, C, C++ являются языками с блочной структурой и используют похожий синтаксис, но в них блок не рассматривается как строка, требующая для завершения точки с запятой. Отсутствие точки с запятой в IDL является синтаксической ошибкой.

    1  // BillingService.idl
    2  // IDL-описание BillingService
    3  module    BillingServiceModule 4{
    5  interface BillingService 6{
    7  //определение CORBA-совместимого сервиса
    8  void addNewCard(in string personName, in string card);
    9  void addMoney(in string card, in double money);
    10  void subMoney(in string card, in double money);
    11  double getCardBalance(in string card);
    12  };
    13  };

    Строки 5-12 являются объявлением интерфейса сервера BillingService. Указанный сервер представляет собой производный класс (конкретную реализацию) интерфейса BillingService. Отсутствие привязки к клиенту является сознательным, допускается произвольная реализация сервера.

    Строки 8-11 осуществляют объявление методов/функций/сообщений. Все, что объявляется на IDL, является public,поэтому в IDL -интерфейсах отсутствуют специальные ключевые слова для public, private, protected (хотя у метатипов component и valuetype есть ключевые слова public и private).

    Следующим шагом в реализации нашего приложения будет генерация из IDL -определения вспомогательных классов. В самом деле, то, что написано в IDL -файле, очень напоминает определение интерфейса java или абстрактного класса C++. Вполне естественно, что созданы инструменты, автоматически генерирующие соответствующие конструкции на базовом языке. Кроме того, мы говорили о том, что базовые типы IDL должны транслироваться в соответствующие типы целевой системы программирования. И опять же, такого рода трансляция может осуществляться автоматически, соответствующим образом сгенерированным кодом.

    Компиляция файла idl

    Компиляция файла BillingService.idl компилятором Java-IDL idlj,входящим в состав пакета jdk фирмы Sun, осуществляется так:

    idlj -td c:\src -pkgPrefix BillingServiceModule com.asw.corba.ex1 
       -fall BillingService.idl

    Позднее мы рассмотрим утилиту Java idlj и ее параметры командной строки. По умолчанию утилита idlj для каждого определенного в idl модуля создает пакет java, а для элементов модуля создает набор java -классов, включенных в этот пакет.

    Автоматически сгенерированные файлы

    После компиляции файла BillingService.idl компилятор Java-IDL создает следующие файлы на стороне сервера в вновь созданной директории BillingServiceModule:

    BillingService.java,
    BillingServiceHelper.java,
    BillingServiceHolder.java,
    BillingServiceOperations.java,
    BillingServicePOA.java,
    _BillingServiceStub.java.

    Рассмотрим подробнее эти файлы (в этих файлах определены классы и интерфейсы) и то, как мы можем их использовать при создании нашего приложения.

    BillingService.java и BillingServiceOperations.java - интерфейсы. BillingService.java (пример 6.2) представляет собой интерфейс BillingService.

    1  package com.asw.corba.ex1.BillingServiceModule;
    2 
    3
    4 /**
    5 *  com/asw/corba/ex1/BillingServiceModule/BillingService.java .
    6 *  Generated by the IDL-to-Java compiler (portable), version "3.1"
    7 *  from com/asw/corba/ex1/BillingService.idl
    8 *  16 Июль 2006 г.  14:12:21 MSD
    9 */ 
    10
    11  public interface BillingService extends BillingServiceOperations,
    12  org.omg.CORBA.Object, org.omg.CORBA.portable.IDLEntity
    13  {
    14  } // interface BillingService

    В строках 11 и 12 объявлен интерфейс BillingService и три интерфейса, от которых наследует BillingService. Два из трех интерфейсов являются определенными в CORBA типами, от которых должны наследовать все CORBA -совместимые объекты, org.omg.CORBA.Object и org.omg.CORBA.portable. IDLEntity.Третий интерфейс - BillingServiceOperations (пример 6.3) - создается на основе IDL -описания и объявляет открытые операции данного сервера. BillingService определяет базовый класс, который наследует от BillingServiceOperations и от CORBA -интерфейсов, упомянутых ранее. Это часть структуры, необходимой производным классам, чтобы быть настоящими CORBA -объектами. BillingServiceOperations,ни от кого не наследуя, объявляет четыре метода, изначально определенные в IDL.

    1  package com.asw.corba.ex1.BillingServiceModule;
    2
    3
    4 /**
    5 * com/asw/corba/ex1/BillingServiceModule/BillingServiceOperations.java . 
    6 * Generated by the IDL-to-Java compiler (portable), version "3.1" 
    7 * from com/asw/corba/ex1/BillingService.idl
    8 * 16 Июль 2006 г. 14:12:21 MSD
    9 */
    10
    11  // определение CORBA-совместимого сервиса
    12  public interface BillingServiceOperations
    13  {
    14  void addNewCard (String personName, String card);
    15  void addMoney (String card, double money);
    16  void subMoney (String card, double money);
    17  double getCardBalance (String card);
    18  } // interface BillingServiceOperations

    Файлы BillingServiceHelper.java (пример 6.4) и BillingServiceHolder.java (пример 6.5) содержат вспомогательные классы, содержащие методы приведения типов и методы маршаллинга/демаршаллинга сообщений.

    1  package com.asw.corba.ex1.BillingServiceModule;
    2 
    3
    4 /**
    5 * com/asw/corba/ex1/BillingServiceModule/BillingServiceHelper.java . 
    6 * Generated by the IDL-to-Java compiler (portable), version "3.1" 
    7 * from com/asw/corba/ex1/BillingService.idl
    8 * 16 Июль 2006 г. 14:12:21 MSD
    9 */
    10
    11  // определение класса BillingServiceHelper
    12  abstract public class BillingServiceHelper
    13  {
    14  private static String   _id = 
        "IDL:BillingServiceModule/BillingService:1.0";
    15
    16  public static void insert (org.omg.CORBA.Any a, 
        com.asw.corba.ex1.BillingServiceModule.BillingService that)
    17  {
    18  org.omg.CORBA.portable.OutputStream out = a.create_output_stream ();
    19  a.type (type ());
    20  write (out, that);
    21  a.read_value (out.create_input_stream (), type ());
    22  }
    23
    24  public static com.asw.corba.ex1.BillingServiceModule.BillingService 
      extract (org.omg.CORBA.Any a)
    25  {
    26  return read (a.create_input_stream ());
    27  }
    28
    29  private static org.omg.CORBA.TypeCode __typeCode = null;
    30  synchronized public static org.omg.CORBA.TypeCode type ()
    31  {
    32  if (__typeCode == null)
    33  {
    34  __typeCode = org.omg.CORBA.ORB.init ().create_interface_tc
      (com.asw.corba.ex1.BillingServiceModule.BillingServiceHelper.id (), 
      "BillingService");
    35  }
    36  return__typeCode;
    37  }
    38
    39  public static String id ()
    40  {
    41  return _id;
    42  }
    43
    44  public static com.asw.corba.ex1.BillingServiceModule.BillingService 
      read (org.omg.CORBA.portable.InputStream istream)
    45  {
    46  return narrow (istream.read_Object (_BillingServiceStub.class));
    47  }
    48
    49  public static void write (org.omg.CORBA.portable.OutputStream 
      ostream, com.asw.corba.ex1.BillingServiceModule.BillingService 
      value)
    50  {
    51  ostream.write_Object ((org.omg.CORBA.Object) value);
    52  }
    53
    54  public static com.asw.corba.ex1.BillingServiceModule.BillingService 
      narrow (org.omg.CORBA.Object obj)
    55  {
    56  if (obj == null)
    57  return null;
    58  else if (obj instanceof 
      com.asw.corba.ex1.BillingServiceModule.BillingService)
    59  return (com.asw.corba.ex1.BillingServiceModule.BillingService)obj;
    60  else if (!obj._is_a (id ()))
    61  throw new org.omg.CORBA.BAD_PARAM ();
    62  else
    63  {
    64  org.omg.CORBA.portable.Delegate delegate = 
      ((org.omg.CORBA.portab-le.ObjectImpl)obj)._get_delegate ();
    65  com.asw.corba.ex1.BillingServiceModule._BillingServiceStub stub = 
      new com.asw.corba.ex1.BillingServiceModule._BillingServiceStub ();
    66  stub._set_delegate(delegate);
    67  return stub;
    68  }
    69  }
    70  }

    Необходимость в Holder -классах возникает в тот момент, когда у методов появляются возвращаемые параметры.

    1  package com.asw.corba.ex1.BillingServiceModule;
    2
    3 /**
    4 * com/asw/corba/ex1/BillingServiceModule/BillingServiceHolder.java
    5 * Generated by the IDL-to-Java compiler (portable), version "3.1" 
    6 * from com/asw/corba/ex1/BillingService.idl 
    7 * 16 Июль 2006 г.  14:12:21 MSD
    8 */ 
    9
    10    // определение класса BillingServiceHolder
    11  public final class BillingServiceHolder implements
      org.omg.CORBA.portable.Streamable
    12  {
    13  public com.asw.corba.ex1.BillingServiceModule.BillingService value
      = null; 
    14
    15  public BillingServiceHolder ()
    16  {
    17  }
    18
    19  public BillingServiceHolder (com.asw.corba.ex1. 
      BillingServiceModule.BillingService initialValue)
    20  {
    21  value = initialValue;
    22  } 23
    
    24  public void _read (org.omg.CORBA.portable.InputStream i)
    25  {
    26  value = com.asw.corba.ex1.BillingServiceModule. 
      BillingServiceHelper.read (i);
    27  }
    28
    29  public void _write (org.omg.CORBA.portable.OutputStream o)
    30  {
    31  com.asw.corba.ex1.BillingServiceModule.BillingServiceHelper.write 
      (o, value);
    32  } 
    33
    34  public org.omg.CORBA.TypeCode _type ()
    35  {
    36  return com.asw.corba.ex1.BillingServiceModule. 
      BillingServiceHelper.type ();
    37  }
    38
    39   }

    Файл _BillingServiceStub.java (пример 6.6) содержит клиентскую заглушку, с которой будет иметь дело клиент. Класс _BillingServiceStub наследует от org.omg.CORBA.portable.ObjectImpl и реализует интерфейс BillingService.При вызове клиентом методов этого класса происходят упаковка вызова (и параметров) и их передача по сети серверному классу. Поскольку этот метод реализует интерфейс BillingService,клиент будет пребывать в полной уверенности, что вызывает непосредственно методы серверного класса.

    1  package com.asw.corba.ex1.BillingServiceModule;
    2
    3 /**
    4 * com/asw/corba/ex1/BillingServiceModule/_BillingServiceStub.java . 
    5 * Generated by the IDL-to-Java compiler (portable), version "3.1" 
    6 * from com/asw/corba/ex1/BillingService.idl
    7 * 16 Июль 2006 г. 14:12:21 MSD 
    8 */
    9
    10  // определение класса _BillingServiceStub
    11  public class _BillingServiceStub extends 
      org.omg.CORBA.portable.ObjectImpl implements 
      com.asw.corba.ex1.BillingServiceModule.BillingService
    12  {
    13  public void addNewCard (String personName, String card)
    14  {
    15  org.omg.CORBA.portable.InputStream $in = null;
    16  try {
    17  org.omg.CORBA.portable.OutputStream $out = 
        _request ("addNewCard", true);
    18  $out.write_string (personName);
    19  $out.write_string (card);
    20  $in = _invoke ($out);
    21  return;
    22  } catch (org.omg.CORBA.portable.ApplicationException $ex) {
    23  $in = $ex.getInputStream ();
    24  String _id = $ex.getId ();
    25  throw new org.omg.CORBA.MARSHAL (_id);
    26  } catch (org.omg.CORBA.portable.RemarshalException $rm) {
    27  addNewCard (personName, card  );
    28  } finally {
    29  _releaseReply ($in);
    30  }
    31  } // addNewCard
    32
    33  public void addMoney (String card, double money)
    34  {
    35  org.omg.CORBA.portable.InputStream $in = null;
    36  try {
    37  org.omg.CORBA.portable.OutputStream $out = 
        _request ("addMoney", true);
    38  $out.write_string (card);
    39  $out.write_double (money);
    40  $in = _invoke ($out);
    41  return;
    42  } catch (org.omg.CORBA.portable.ApplicationException $ex) {
    43  $in = $ex.getInputStream ();
    44  String _id = $ex.getId ();
    45  throw new org.omg.CORBA.MARSHAL (_id);
    46  } catch (org.omg.CORBA.portable.RemarshalException $rm) {
    47  addMoney (card, money  );
    48  } finally {
    49  _releaseReply ($in);
    50  }
    51  } // addMoney
    52
    53  public void subMoney (String card, double money)
    54  {
    55  org.omg.CORBA.portable.InputStream $in = null;
    56  try {
    57  org.omg.CORBA.portable.OutputStream $out = 
        _request ("subMoney", true);
    58  $out.write_string (card);
    59  $out.write_double (money);
    60  $in = _invoke ($out);
    61  return;
    62  } catch (org.omg.CORBA.portable.ApplicationException $ex) {
    63  $in = $ex.getInputStream ();
    64  String _id = $ex.getId ();
    65  throw new org.omg.CORBA.MARSHAL (_id);
    66  } catch (org.omg.CORBA.portable.RemarshalException $rm) {
    67  subMoney (card, money  );
    68  } finally {
    69  _releaseReply ($in);
    70  }
    71  } // subMoney
    72
    73  public double getCardBalance (String card)
    74  {
    75  org.omg.CORBA.portable.InputStream $in = null;
    76  try {
    77  org.omg.CORBA.portable.OutputStream $out = 
        _request ("getCardBalance", true);
    78  $out.write_string (card);
    79  $in = _invoke ($out);
    80  double $result = $in.read_double ();
    81  return $result;
    82  } catch (org.omg.CORBA.portable.ApplicationException $ex) {
    83  $in = $ex.getInputStream ();
    84  String _id = $ex.getId ();
    85  throw new org.omg.CORBA.MARSHAL (_id);
    86  } catch (org.omg.CORBA.portable.RemarshalException $rm) {
    87  return getCardBalance (card  );
    88  } finally {
    89  _releaseReply ($in);
    90  }
    91  } // getCardBalance
    92
    93  // Type-specific CORBA::Object operations
    94  private static String[]__ids = {
    95  "IDL:BillingServiceModule/BillingService:1.0"};
    96
    97  public String[] _ids ()
    98  {
    99  return (String[])__ids.clone ();
    100  }
    101
    102  private void readObject (java.io.ObjectInputStream s) 
      throws java.io.IOException
    103  {
    104  String str = s.readUTF ();
    105  String[] args = null;
    106  java.util.Properties props = null;
    107  org.omg.CORBA.Object obj = org.omg.CORBA.ORB.init (args, 
      props).string_to_object (str);
    108  org.omg.CORBA.portable.Delegate delegate = 
      ((org.omg.CORBA.portable.ObjectImpl) obj)._get_delegate ();
    109  _set_delegate (delegate);
    110  }
    111
    112  private void writeObject (java.io.ObjectOutputStream s) 
      throws java.io.IOException
    113  {
    114  String[] args = null;
    115  java.util.Properties props = null;
    116  String str = org.omg.CORBA.ORB.init (args, props).object_to_string
      (this);
    117  s.writeUTF (str);
    118  }
    119  } // class _BillingServiceStub

    И, наконец, последний файл BillingServicePOA.java (пример 6.7) содержит класс, выполняющий всю техническую работу по диспетчеризации сообщений клиента, - этот класс будет являться предком для нашего серверного класса, в котором мы переопределим методы, выполняющие содержательные операции. Класс BillingServicePOA наследует от org.omg.PortableServer.Servant и реализует CORBA -интерфейс (InvokeHandler) и интерфейс BillingServiceOperations.Интерфейс InvokeHandler объявляет метод _invoke,который является реализацией BillingServicePOA. ORB использует метод _invoke для вызова различных методов BillingService в обобщенном виде. Метод _invoke создается IDL -компилятором на основе IDL -интерфейса, объявленного в IDL -файле BillingService.idl.

    1  package com.asw.corba.ex1.BillingServiceModule;
    2
    3 /**
    4 * com/asw/corba/ex1/BillingServiceModule/BillingServicePOA.java . 
    5 * Generated by the IDL-to-Java compiler (portable), version "3.1" 
    6 * from com/asw/corba/ex1/BillingService.idl 
    7 * 16 Июль 2006 г.  14:12:21 MSD
    8 */ 
    9
    10  // определение класса BillingServicePOA
    11  public abstract class BillingServicePOA extends 
        org.omg.PortableServer.Servant
    12  implements
      com.asw.corba.ex1.BillingServiceModule.BillingServiceOperations, 
      org.omg.CORBA.portable.InvokeHandler
    13  {
    14
    15  // Constructors
    16
    17  private static java.util.Hashtable _methods = new java.util.Hashtable ();
    18  static
    19  {
    20  _methods.put ("addNewCard", new java.lang.Integer (0));
    21  _methods.put ("addMoney", new java.lang.Integer (1));
    22  _methods.put ("subMoney", new java.lang.Integer (2));
    23  _methods.put ("getCardBalance", new java.lang.Integer (3));
    24  }
    25
    26  public org.omg.CORBA.portable.OutputStream _invoke (String $method,
    27  org.omg.CORBA.portable.InputStream in,
    28  org.omg.CORBA.portable.ResponseHandler $rh)
    29  {
    30  org.omg.CORBA.portable.OutputStream out = null;
    31  java.lang.Integer __method = (java.lang.Integer)_methods.get ($method);
    32  if (__method == null)
    33  throw new org.omg.CORBA.BAD_OPERATION (0, 
      org.omg.CORBA.CompletionStatus.COMPLETED_MAYBE);
    34
    35  switch __method.intValue ())
    36  {
    37  case 0:   // BillingServiceModule/BillingService/addNewCard
    38  {
    39  String personName = in.read_string ();
    40  String card = in.read_string ();
    41  this.addNewCard (personName, card);
    42  out = $rh.createReply();
    43  break;
    44  }
    45
    46  case 1:   // BillingServiceModule/BillingService/addMoney
    47  {
    48  String card = in.read_string ();
    49  double money = in.read_double ();
    50  this.addMoney (card, money);
    51  out = $rh.createReply();
    52  break;
    53  }
    54
    55  case 2:   // BillingServiceModule/BillingService/subMoney
    56  {
    57  String card = in.read_string ();
    58  double money = in.read_double ();
    59  this.subMoney (card, money);
    60  out = $rh.createReply();
    61  break;
    62  }
    63
    64  case 3:   // BillingServiceModule/BillingService/getCardBalance
    65  {
    66  String card = in.read_string ();
    67  double $result = (double)0;
    68  $result = this.getCardBalance (card);
    69  out = $rh.createReply();
    70  out.write_double ($result);
    71  break;
    72  }
    73
    74  default:
    75  throw new org.omg.CORBA.BAD_OPERATION (0, 
      org.omg.CORBA.CompletionStatus.COMPLETED_MAYBE);
    76  }
    77  return out;
    78  } // _invoke
    79
    80  // Type-specific CORBA::Object operations
    81  private static String[] __ids = {
    82  "IDL:BillingServiceModule/BillingService:1.0"};
    83
    84  public String[] _all_interfaces 
      (org.omg.PortableServer.POA poa, byte[] objectId)
    85  {
    86  return (String[])__ids.clone ();
    87  }
    88
    89  public BillingService _this()
    90  {
    91  return BillingServiceHelper.narrow(super._this_object());
    92  }
    93
    94  public BillingService _this(org.omg.CORBA.ORB orb)
    95  {
    96  return BillingServiceHelper.narrow(super._this_object(orb));
    97  }
    98  } // class BillingServicePOA

    Файлы BillingServiceImpl.java и BillingServiceServer.java

    Следующим шагом после генерации необходимых вспомогательных файлов будет реализация серверного объекта, реализующего описанные нами методы.

    BillingServiceImpl (пример 6.8) является реализацией интерфейса BillingService.В соответствии с принятым нами ранее соглашением конкретный класс, реализующий открытый интерфейс распределенного CORBA -объекта, имеет имя <IDL interface name>Impl.Этот класс не обязан объявлять метод main, но может это сделать.

    1  // BillingServiceImpl.java
    2  package com.asw.corba.ex1;
    3
    4  // базовые пакеты Java
    5  import java.util.Hashtable;
    6
    7  import com.asw.corba.ex1.BillingServiceModule.*;
    8
    9  // пакеты OMG CORBA
    10  import org.omg.CORBA.*;
    11
    12  public class BillingServiceImpl   extends BillingServicePOA {
    13  private ORB orb;
    14  private   Hashtable hash = new Hashtable();
    15
    16  public void setORB(ORB orb_val) {
    17  orb = orb_val;
    18  }
    19
    20  public void addNewCard(String personName, String card) {
    21  hash.put(card, new Double(0.0));
    22  }
    23
    24  public void addMoney(String card, double money) {
    25  Double d = (Double)hash.get(card);
    26
    27  if (d!=null) hash.put(card,new Double(d.doubleValue()+money));
    28  }
    29
    30  public void subMoney(String card, double money) {
    31  Double d = (Double)hash.get(card);
    32
    33  if (d!=null) hash.put(card,new Double(d.doubleValue()-money));
    34  }
    35
    36  public double getCardBalance(String card) {
    37  Double d = (Double)hash.get(card);
    38
    39  if (d!=null) return d.doubleValue();
    40  else return 0;
    41  }
    42  }

    Класс BillingServiceImpl является наследником класса BillingServicePOA (который был построен автоматически) и, таким образом, уже обладает всей базовой функциональностью серверного класса. Все, что мы должны сделать, - определить реализацию методов addNewCard, addMoney, subMoney и getCardBalance.Таким образом, для реализации серверного класса нам пришлось написать всего несколько строк кода - весь остальной код был построен автоматически!

    Класс BillingServiceImpl реализует методы сервера, а запуск сервера производится классом BillingServiceServer (пример 6.9), основная задача которого - "положить" экземпляр BillingService в систему, то есть зарегистрировать его.

    Дело в том, что при создании распределенных приложений существует проблема поиска необходимых компонент. Если система распределенная, как определить, на каком узле находится тот или иной ее компонент (с тем, чтобы отправлять запросы на этот узел)? В CORBA эта проблема решается вводом специального сервиса - сервиса имен (Naming Service),который составляет список ресурсов для будущего их использования клиентами. Факт регистрации в сервисе имен не означает, что сервер может решать какие-то задачи; сервер просто предоставляет клиентам возможность доступа к нему. Сервис должен быть готов в любой момент обрабатывать входящие запросы на выполнение функций, необходимых вызывающим клиентам. Любой объект, который должен обрабатывать клиентские запросы, может зарегистрироваться в этом сервисе под каким-то именемВ нашем случае имена объектов должны быть уникальны, однако сервисы именования могут поддерживать режим, при котором на одно имя регистрируются сразу несколько объектов. При обращении пользователя по имени объекта ему возвращается ссылка на один из объектов с таким именем. Таким образом могут реализоваться механизмы обеспечения отказоустойчивости системы при отказе одного из объектов, а также механизмы балансировки нагрузки . В различных реализациях CORBA поддерживается различный набор сервисов. Минимально необходимым из них является только сервис имен. Не необходимыми, но очень полезными (и поддерживаемыми во многих реализациях) являются сервис сообщений (Event Service) и сервис уведомлений (Notification Service).Кроме перечисленных, поставщик может предоставить следующие сервисы:

  • ES позволяет создавать каналы, по которым приложения могут обмениваться сообщениями;
  • NS расширяет эту функциональность, позволяя фильтровать и типизировать сообщения;
  • сервис безопасности ( Security service ) обеспечивает аутентификацию и контроль доступа к объектам, аудит узлов;
  • сервис транзакций ( Transaction service ) TS обеспечивает поддержку системой механизма транзакций;
  • сервис контроля параллельности ( Concurrency control ) CCS обеспечивает поддержку механизма блокировок и синхронизации;
  • сервис долговременного хранения ( Persistent object service ) обеспечивает сохранение состояния объектов в постоянном хранилище (БД) и их восстановление при запуске системы.
  • Таким образом, клиенту для того, чтобы обратиться к объекту, необходимо знать его имя. Обратившись к сервису именования, клиент по имени получает ссылку на нужный ему объект.

    Класс BillingServiceServer как раз и регистрирует серверный объект BillingServiceImpl в сервисе имен.

    Рассмотрим этот класс подробнее.

    Класс BillingServiceServer (пример 6.9) содержит метод main, являющийся точкой входа в программу.

    1  // BillingServiceServer.java
    2  package com.asw.corba.ex1;
    3
    4  // пакеты OMG CORBA
    5  import  org.omg.CosNaming.*;
    6  import  org.omg.CORBA.*;
    7  import  org.omg.PortableServer.*;
    8  import  org.omg.PortableServer.POA;
    9  import  com.asw.corba.ex1.BillingServiceModule.*;
    10
    11  public class BillingServiceServer {
    12  public static void main(String args[]) {
    13  try{
    14  // create and initialize the ORB
    15  ORB orb = ORB.init(args, null);
    16
    17  // get reference to rootpoa  activate the POAManager
    18  POA rootpoa =
    19  POAHelper.narrow(orb.resolve_initial_references("RootPOA"));
    20  rootpoa.the_POAManager().activate();
    21
    22  // create servant and register it with the ORB
    23  BillingServiceImpl BSImpl = new BillingServiceImpl();
    24  BSImpl.setORB(orb); 
    25
    26  // get object reference from the servant
    27  org.omg.CORBA.Object ref = rootpoa.servant_to_reference(BSImpl);
    28  BillingService href = BillingServiceHelper.narrow(ref);
    29
    30  // get the root naming context
    31  // NameService invokes the name service
    32  org.omg.CORBA.Object objRef =
    33  orb.resolve_initial_references("NameService");
    34  // Use NamingContextExt which is part of the Interoperable
    35  // Naming Service (INS) specification.
    36  NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);
    37
    38  // bind the Object Reference in Naming
    39  String name = "BillingService";
    40  NameComponent path[] = ncRef.to_name( name );
    41  ncRef.rebind(path, href);
    42
    43  System.out.println("BillingServiceServer ready and waiting ...");
    44
    45  // wait for invocations from clients
    46  orb.run();
    47  }
    48
    49  catch (Exception e) {
    50  System.err.println("ERROR: " + e);
    51  e.printStackTrace(System.out);
    52  }
    53  System.out.println("BillingServer Exiting ...");
    54  }
    55  }

    Первое, что делает этот класс, - создает ссылку на orb (брокер объектных запросов) (строка 15), передавая в статический метод init переданные ему аргументы командной строки (что это за аргументы, мы увидим, когда перейдем к запуску приложений). Корректный входной параметр - это опция командной cтроки, указывающая брокеру объектных запросов, какой коммуникационный порт использовать. Существует версия init,не имеющая параметров, которая возвращает брокер объектных запросов, создаваемый по умолчанию, вместо брокера объектных запросов, сконфигурированного в соответствии с входными параметрами. Параметры метода init могут иметь значения null.

    Затем идут несколько строк (строки 18-20), отвечающих за получение и активизацию ссылки на корневой POA ( Portable Object Adapter )Существуют зарезервированные имена в сервисе имен, такие как RootPOA или NameService, под которыми зарегистрированы системные объекты. Соответственно, для обращения к этим системным объектам могут быть использованы объектные ссылки, полученные по этим именам . Строка "RootPOA" является стандартным именем, определенным в спецификации CORBA (вместе со списком других имен). Метод resolve_initial_ references имеет доступ к специальному списку сервисов, непосредственно доступных данному брокеру объектных запросов. Статический метод narrow класса POAHelper используется для преобразования возвращенной объектной ссылки в объект нужного типа. Метод narrow является CORBA -механизмом надежного приведения ссылки одного типа к ссылке другого типа. Метод narrow проверяет, что интерфейс, которому мы пытаемся передать целевой объект, поддерживает целевые объекты этого типа. Обычное приведение не работает с объектными ссылками CORBA, так как объектная ссылка - это посредник на пути к удаленной информации. Все классы Helper имеют метод static narrow,который дает нам возможность осуществлять приведение родительских классов к производным классам.

    В строке 23 создается экземпляр нашего класса BillingServiceImpl,и ему передается ссылка на orb (строка 24). Затем формируется объектная ссылка на наш серверный класс (сроки 27-28).

    В строках 32-33 извлекается объектная ссылка на сервис имен. Проблема обнаружения сервером сервиса имен при первом запуске решается брокером объектных запросов с помощью метода resolve_initial_references.Брокер объектных запросов имеет эффективный мини-сервис имен, с помощью которого он может выполнять обнаружение базовых сервисов. Однако чтобы метод resolve_initial_references мог работать с любым из сервисов, он возвращает объектную ссылку в виде объекта типа org.omg.CORBA.Object.Строка "NameService" является стандартным именем, определенным в спецификации CORBA. Статический метод narrow класса NamingContextExtHelper применяется для преобразования возвращенной объектной ссылки в объект нужного типа.

    Затем задается имя для нашего объекта (строка 39), которое помещается в массив объектов типа NameComponent (строка 40). Сервер не может быть зарегистрирован и использован другими объектами, если у него нет имени, - эта ошибка достаточно серьезна. Чтобы имя было правильно зарегистрировано, ресурс должен установить контекст именования. Сервер регистрируется в основном (или корневом) контексте именования, поэтому единственное, что нужно сервису имен, - это имя сервера, которое сервер посылает с помощью метода rebind (строка 41). Метод rebind вводит NameComponent, если он отсутствует в сервисе имен, или вводит его повторно, если он уже присутствует там. Однако такое использование rebind позволяет за одно обращение получить доступ только к одному экземпляру BillingService.Последним зарегистрированным BillingService является сервер, с которым связаны клиенты.

    Вызов orb.run переводит объект в состояние ожидания запросов от клиентов, более того, этот вызов является блокирующим.

    Таким образом, мы создали серверный объект, способный принимать и обрабатывать запросы пользователей и зарегистрировали его в сервисе имен (в его корневом разделе) под именем BillingService.

    Файл BillingServiceClient.java

    BillingServiceClient (пример 6.10) представляет собой клиент, который соединяется с BillingService.Клиент содержит один метод main,который добавляет денежные средства на карту с номером 1 и затем выводит текущий баланс этой карты (строки 29-30). Логика, используемая клиентами для чтения данных в сервисе имен, похожа на логику, применяемую сервером для записи в нее данных.

    1  // BillingServiceClient.java
    2  package com.asw.corba.ex1;
    3
    4  import  com.asw.corba.ex1.BillingServiceModule.*;
    5  import  org.omg.CosNaming.*;
    6  import  org.omg.CORBA.*;
    7
    8  public class BillingServiceClient {
    9  static BillingService BSImpl;
    10
    11  public static void main(String args[])
    12  {
    13  try{
    14  // create and initialize the ORB
    15  ORB orb = ORB.init(args, null);
    16
    17  // get the root naming context
    18  org.omg.CORBA.Object objRef =
    19  orb.resolve_initial_references("NameService");
    20  // Use NamingContextExt instead of NamingContext. This is
    21  // part of the Interoperable naming Service.
    22  NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);
    23
    24  // resolve the Object Reference in Naming
    25  String name = "BillingService";
    26  BSImpl = BillingServiceHelper.narrow(ncRef.resolve_str(name));
    27
    28  System.out.println("Obtained a handle on server object: " +
    BSImpl);
    29  BSImpl.addMoney("1",1234);
    30  System.out.println(BSImpl.getCardBalance("1"));
    31  } catch (Exception e) {
    32  System.out.println("ERROR : " + e) ;
    33  e.printStackTrace(System.out);
    34  }
    35  }
    36  }

    В строке 15 вызывается статический метод init класса ORB для создания брокера объектных запросов. Массив args типа String позволяет динамически настраивать брокер объектных запросов во время выполнения (массив получен из командной строки). После того как клиент получает ORB, в строках 18-19 вызывается метод resolve_initial_references этого брокера объектных запросов, чтобы получить объектную ссылку на сервис имен. В строке 22 осуществляется приведение полученной объектной ссылки к объекту типа NamingContextExt с помощью метода NamingContextExtHelper.narrow.

    Клиент должен запросить у сервиса имен объектную ссылку на сервис, который он разыскивает, - сервис BillingService,имеющий имя "BillingService" (объявлен в классе BillingServiceServer).С помощью метода resolve_str имя сервера "BillingService" передается переменной BSImpl типа BillingService.Статический метод BillingServiceHelper.narrow осуществляет приведение объектной ссылки к требуемому типу производного класса. С этого момента у клиента есть активный распределенный объект.

    Желательно сохранять имена сервисов в файлах типа .properties вместо того, чтобы "зашивать" их в исходный текст класса. Включение имен в исходный текст класса требует дополнительного сопровождения файлов классов при изменении имен. Файл свойств дает возможность настраивать уже развернутую систему, тем самым делая настройку вопросом администрирования, а не разработки.

    Выполнение примера

    Для выполнения примера требуется следующая последовательность действий:

  • скомпилировать IDL -файл с помощью idlj ;
  • реализовать и скомпилировать серверный класс;
  • реализовать и скомпилировать клиентский класс;
  • запустить сервис именования;
  • запустить сервер;
  • запустить клиент.
  • Для компиляции IDL -файла из командной строки используется компилятор idlj,входящий в состав пакета jdk фирмы Sun. Неполный список опций командной строки idlj выглядит так:

  • -f<client | server | all>
  • -pkgPrefix <имя модуля или тип IDL> <добавляемый префикс>
  • -td <выходной каталог>
  • Опция -f управляет созданием кода для заглушек и скелетов. Опция -fclient создает только клиентские файлы, -fserver - только серверные файлы, а -fall - и те, и другие. Опция -pkgPrefix генерирует имена пакетов. Эта опция используется с именем модуля. При компиляции модуля с именем modeName с опциями -pkgPrefix modeName prefix будут созданы файлы Java с именем пакета prefix.modeName.Опция -td предписывает записывать созданные файлы в указанный каталог.

    Например, если исходный код находится с каталоге C:\src,командная строка будет выглядеть следующим образом (исходя из IDL -описания, представленного в примере 6.1):

    idlj -td c:\src -pkgPrefix BillingServiceModule com.asw.corba.ex1 
       -fall BillingService.idl

    Эта командная строка создает и серверные, и клиентские файлы CORBA-Java. После реализации сервера и клиента скомпилируйте серверный и клиентский код. При компиляции никаких особенностей нет, все, что необходимо сделать - написать в командной строке

    javac *.java

    находясь в директории проекта. Необходимо проследить, чтобы пакет org.omg.*,содержащий реализацию поддержки CORBA, присутствовал в путях classpath.

    Для того чтобы запустить нашу систему, кроме запуска клиентской и серверной части нам необходимо запустить сервис имен. Мы воспользуемся сервисом имен, который входит в состав пакета jdk и называется tnameserv.exe - базовая реализация CORBA Object Service ( COS ) Naming Service. tnameserv не является готовым средством, реализующим сервис имен; скорее, оно служит средством тестирования, позволяющим проверить правильность взаимодействия клиентов и серверов. Сервис имен должен быть приведен в активное состояние до того, как будет запущен сервер. Поскольку нашей целью было создание распределенного приложения, предположим, что сервис имен будет работать на узле с именем alpha, серверный объект - на узле с именем beta,а клиент - на узле с именем gamma. Предполагаем, что на всех узлах установлен пакет jdk.

    На узле alpha запускаем сервис имен:

    H:\j2sdkL4.2_06\bin\tnameserv.exe

    Выполнение команды приведет к следующему результату:

    Initial Naming Context:
    IOR:000000000000002b49444c3a6f6d672e6f72672f436f734e616d696e672f4e 616d696e67436f
    6e746578744578743a312e30000000000001000000000000008000010200000000 0c3139322e3136
    382e312e31000384000000000035afabcb00000000202ba83e8000000001000000 00000000010000
    000d544e616d65536572766963650000000000000004000000000a000000000000 01000000010000
    00200000000000010001000000020501000100010020000101090000000100010100
    TransientNameServer: setting port for initial object references
    to: 900
    Ready.

    По умолчанию сервис ожидает обращений на 900 порте, однако иногда этот порт доступен только для администраторов. После того как сервис имен запущен, мы можем запустить на узле beta серверный объект:

    G:\BillingService >java com.asw.corba.ex1.BillingServiceServer 
      -ORBInitialHost=alpha

    Результат выполнения предыдущей команды:

    BillingServiceServer ready and waiting ...

    Мы должны каким-то образом указать, где находится сервис имен. Это делается с помощью ключей ORBInitialHost и ORBInitialPort. Именно эти параметры командной строки и передаются при инициализации orb в метод init. Поскольку мы использовали порт по умолчанию, параметр ORBInitialPort можно не указывать.

    Далее на узле gamma мы запускаем клиента:

    G:\BillingService>java com.asw.corba.ex1.BillingServiceClient 
      -ORBInitialHost=alpha

    Выполнение команды:

    Obtained a handle on server object:
    BillingServiceApp._BillingServiceStub:IOR:00000000000000174944
    4c3a48656c6c6f4170702f48656c6c6f3a312e3000000000000100000000000000
    6c000102000000
    000c3139322e3136382e312e31000bd2000000000021afabcb00000000202baba5 49000000010000
    00000000000000000004000000000a000000000000010000000100000020000000 00000100010000
    00020501000100010020000101090000000100010100 1234

    Поскольку клиент тоже нуждается в сервисе имен для получения объектной ссылки, ему также передается имя узла, на котором сервис имен запущен. Далее клиент получает объектную ссылку, распечатывает ее, вызывает метод addMoney и метод getCardBalance сервера, распечатывает строку, которую ему этот метод вернул, и, наконец, сервер выводит строку:

    BillingServer Exiting ...

    и прекращает работу.

    Таким образом, мы создали простое распределенное приложение, компоненты которого работают на разных узлах, вызывая методы друг друга. В нашем случае и клиент, и сервер были написаны с использованием языка программирования java, однако мы вполне могли написать одну из частей системы на другом языке, например, на C++.

    Вернуться к учебному плану