Статья1) продолжает серию публикаций в журнале [2, 3], посвященных обсуждению ключевых аспектов новой, быстро развивающейся и интенсивно применяемой технологии проектирования и создания интероперабельных информационных систем. Основное внимание в ней уделяется Языку определения интерфейсов- важному компоненту стандарта CORBA, направленного на поддержку разработки систем указанного класса. Возможности языка рассматриваются в контексте обсуждения основных принципов архитектуры CORBA и концепций объектной модели, на которых этот язык базируется. Один из ключевых принципов архитектуры CORBA, обеспечивающий интероперабельность приложений, заключается в независимости спецификации интерфейсов объектов от их реализации. Именно для решения этой задачи в комплексе стандартов CORBA предусматривается Язык определения интерфейсов.
Компонентами такого глобального пространства являются произвольные информационные ресурсы - программные компоненты, базы данных, базы знаний, файлы данных, в том числе содержащие мультимедийную информацию, компоненты существующих информационных систем и др., - представляемые коллекциями объектов, независимо от аппаратурно/программных платформ их реализации и размещения в пространстве. Создание интероперабельных сред информационных ресурсов обеспечивает как динамическое образование композиций ресурсов, организуемых для решения задачи (реализации информационной системы, реализации проекта, поддержки групповой деятельности и т.д.), так и мегапрограммирование - программирование в среде готовых компонентов [15], позволяющее крупномасштабно применять технологию повторного использования информационных ресурсов, например в Internet.
Спецификации системы де-факто стандартов архитектуры промежуточного слоя разрабатываются Object Management Group (OMG) - крупнейшим в мире консорциумом разработки программного обеспечения, включающим теперь уже свыше 600 членов - компаний-производителей программных продуктов, компьютеров, телекоммуникационных систем, разработчиков прикладных систем и конечных пользователей. Эта система стандартов разрабатывается компаниями-участниками OMG согласованно с тем, чтобы обеспечивалась интероперабельность компонентов распределенных систем, несмотря на различие фирм, разрабатывавших компоненты промежуточного слоя. Деятельность OMG является также важным консолидирующим фактором, противодействующим эгоистическим стремлениям отдельных компаний навязать собственные, недостаточно обоснованные и неполностью специфицированные стандарты пользователям и, таким образом, монополизировать рынок.
Основными компонентами архитектуры промежуточного слоя, развиваемой OMG, являются: Common Object Request Broker Architecture (CORBA - Общая архитектура Брокера Объектных Заявок), архитектура Общих Объектных Служб и Общих Объектных Средств [2]. CORBA образует нижний слой архитектуры промежуточного слоя, обеспечивающий технологическую платформу интероперабельности. Семантика объектов на этом уровне не принимается во внимание.
Технология Брокера Объектных Заявок (Object Request Broker - ORB), играющего роль "общей шины" в глобальном пространстве объектов, посредством которой объекты этого пространства могут взаимодействовать друг с другом, широко поддерживается в настоящее время рядом продуктов, распространяемых Digital, IBM, SunSoft, IONA Technologies и другими для разнообразных платформ. Используя концепцию "клиент - сервер", технология Брокера Объектных Заявок обеспечивает интероперабельность прикладных систем, размещенных в различных компьютерах в неоднородной распределенной среде. CORBA [10] определяет архитектуру, в которой различные реализации ORB обеспечивают общий сервис и интерфейсы для достижения мобильности (portability) клиентов и реализаций объектов (Object Implementation) по отношению к брокерам, реализованным различными компаниями.
Важным компонентом CORBA является Язык определения интерфейсов (IDL - Interface Definition Language). В версии стандарта CORBA 2.0 этот язык стал называться OMG IDL. Однако мы будем здесь иногда для краткости использовать далее и его прежнее название - IDL.
OMG IDL является средством, при помощи которого потенциальным клиентам объекта-сервера сообщается, какие операции доступны на его интерфейсе и как их следует вызывать. IDL - язык однородной спецификации интерфейсов разнообразных информационных ресурсов, инкапсулируемых посредством CORBA. Это - чисто описательный язык. Определения на IDL могут быть отображены стандартным образом в конкретные языки программирования, такие как C, C++, Smalltalk. Репозитарий Интерфейсов, содержащий определения интерфейсов на IDL, позволяет видеть интерфейсы доступных сервисов в сети и программировать их использование в программах-клиентах с учетом возможностей Брокера. Содержащиеся в Репозитарии Интерфейсов спецификации могут быть также использованы в период выполнения клиента. Роли "клиент" и "сервер" следует рассматривать как относительные: клиент (сервер) в архитектуре CORBA может быть сервером (клиентом) по отношению к другим клиентам (серверам).
Операция интерфейса обозначает сервис, который можно вызвать посредством этой операции. Операции имеют сигнатуру, определяющую типы параметров вызова операции и возвращаемых результатов. Операции являются родовыми в том смысле, что операция с одной сигнатурой может быть вызвана у объектов, имеющих различную реализацию. Это приводит, возможно, к различному наблюдаемому поведению объектов. Реализации объектов полностью отделены от спецификаций интерфейсов в IDL. Это означает, что для каждого объекта можно выбрать его собственную реализацию, поддерживаемую одним и тем же интерфейсом.
В настоящей статье мы предлагаем обзор особенностей языка OMG IDL и его места в архитектуре CORBA. Однако необходимо предварительно рассмотреть объектную модель, на которой он основан, а также функции Брокера Объектных Заявок.
CORBA определяет среду для различных реализаций ORB, поддерживающих общие сервисы и интерфейсы (Рис. 1). Это обеспечивает мобильность клиентов и реализаций объектов по отношению к различным реализациям ORB. ORB обеспечивает интероперабельность компонентов глобального объектного пространства.
Определения интерфейсов объектов могут быть помещены в Репозитарий Интерфейсов (Interface Repository) двумя способами: статически - в результате спецификации на IDL, или динамически. Репозитарий представляет компоненты интерфейса как объекты и обеспечивает доступ к ним в период выполнения.
При формировании заявки клиент может использовать интерфейс динамического вызова или генерируемый компилятором IDL стаб (stub) - локальную процедуру вызова заданной операции при обращении к ней.
Клиент может непосредственно взаимодействовать с ORB. В этом случае ORB ищет соответствующий код реализации объекта, пересылает ему параметры заявки и передает управление. Реализация объекта принимает параметры заявки через сгенерированный компилятором IDL скелетон (Skeleton) и при этом может обращаться к Объектному Адаптеру (Object Adaptor) и ORB. Скелетон - это специфический для каждого интерфейса компонент ORB, служащий для передачи параметров заявок конкретным методам. Когда обработка заявки завершена, выходные значения возвращаются клиенту. Объектный Адаптер является основным средством взаимодействия ORB с реализацией объекта. Это взаимодействие обычно включает: генерацию и интерпретацию объектных ссылок, вызов методов, обеспечение секретности взаимодействия, активизацию/деактивизацию объекта и реализации, отображение объектных ссылок в реализации объектов и регистрацию реализаций. Предполагается наличие нескольких широко доступных Объектных Адаптеров с интерфейсами, соответствующими определенным видам объектов.
Различные ORB могут иметь разную реализацию и поддерживать различные объектные механизмы. В структуре ORB выделяется ядро, обеспечивающее внутреннее представление объектов и передачу заявок, и набор надстраиваемых компонентов, интерфейсы которых маскируют различия в реализации ORB. Клиенты максимально мобильны и должны работать без изменения исходного кода в среде любого ORB, который поддерживает отображение IDL в соответствующий язык программирования.
Мобильны также и реализации объектов для разных ORB при условии, что последние поддерживают нужное языковое отображение и имеют требуемые Объектные Адаптеры.
Языковое отображение включает определение характерных для IDL типов данных и интерфейсов доступа к объектам средствами соответствующего языка программирования. Отображение определяет структуру интерфейса стаба клиента, интерфейса динамического вызова, скелетона реализации объекта, объектных адаптеров и прямые интерфейсы ORB.
Для определенного языкового отображения обеспечивается программный интерфейс к стабу для каждого типа интерфейса. Стабы осуществляют обращения к ORB, используя скрытые и, возможно, оптимизированные для определенного ядра ORB интерфейсы. Для определенного языкового отображения и, возможно, в зависимости от используемого Объектного Адаптера будет обеспечиваться доступ к методам, реализующим каждый объектный тип. Вызов этих методов осуществляется через скелетон. Наличие скелетона не подразумевает существование соответствующего стаба клиента. Возможно, и обратное. Можно написать Объектный Адаптер, который не использует скелетоны для вызова методов реализации объектов.
В настоящее время существует ряд промышленных реализаций ORB, соответствующих стандарту CORBA. OMG непрерывно совершенствует CORBA. Текущий уровень стандарта - CORBA 2.0 [12].
Объектная модель OMG определяется в виде объектной модели-ядра (Core Object Model, далее для краткости - Ядро) и совокупности расширений. Объектная модель-ядро использует минимальный набор базовых понятий. Примерами понятий Ядра являются объекты, операции, типы, отношение тип/подтип, наследование, интерфейс типа. Каждое расширение вводит дополнительный набор понятий. Расширяться могут либо Ядро, либо уже существующие и согласованные расширения. При этом вводится понятие профиля (profile) как некоторой комбинации Ядра и одного или нескольких расширений, необходимых для определенной целевой архитектуры.
Задачей Ядра является обеспечение мобильности программ и спецификаций типов, а также достижение интероперабельности компонентов в распределенной неоднородной среде. OMG различает три уровня рассмотрения мобильности: уровень проекта, уровень исходного кода, уровень объектного кода.
Ядро поддерживает мобильность на уровне проекта. Определяя согласованную семантику объектной модели, Ядро обеспечивает мобильность базовых проектных решений, фиксированных в спецификациях применений и типов, в среде согласованных с OMG компонентов. Очевидно, что определение согласованной семантики объектной модели является необходимой предпосылкой для поддержки мобильности на уровне исходного и объектного кодов.
Интероперабельность подразумевает операционную совместимость применений, библиотек классов и других продуктов, поставляемых различными компаниями для разных платформ. Ядро фиксирует для этого минимальную согласованную объектную семантику. Вместе с тем, как предупреждает OMG:
Понятия объектной модели-ядра
Ядро базируется на понятиях: Объекта, Операции (сигнатура, параметры, возвращаемые значения), Необъектного типа, Объектного типа, Отношений подтипа и наследования, Интерфейса типа.
Объекты Объект может моделировать любую сущность реального мира (личность, корабль, документ и т. д.). Базовой характеристикой объекта является идентификатор, который неизменчив, существует в течение жизни объекта и не зависит от свойств и поведения объекта.
Операции Операции применяются к объектам. Например, для того, чтобы определить дату рождения некоторого лица, необходимо применить к соответствующему объекту операцию date_of_birth. Каждая операция имеет сигнатуру, которая состоит из имени операции, а также, возможно, из имен параметров и результатов вместе с соответствующими типами.
Вследствие вызова операции, называемого заявкой, могут быть порождены: некоторое множество результатов; побочные эффекты, связанные с изменениями состояний объектов; исключительные ситуации, в настоящий момент не рассматриваемые как часть Ядра. Ядро не фиксирует конкретный синтаксис для спецификации операций. В Ядре поддерживается классическая объектная модель: среди параметров операции первым задается параметр, определяющий объект, которому будет отправлено сообщение. Операция является частью интерфейса типа этого управляющего параметра. Всякая операция определена на каком-либо объектном типе, причем только на одном. Все операции, определенные на заданном типе, имеют уникальные в рамках данного типа имена. Сами операции так же, как и заявки, объектами не являются.
Необъектные типы В Ядре различаются объектные значения (объекты) и необъектные значения, которые вместе образуют множество обозначаемых значений (denotable values). Необъектные значения не обладают уникальными идентификаторами. Операции не могут быть определены на необъектных типах. Ядро не специфицирует множество необъектных типов. Эти типы должны быть определены в соответствующих профилях.
Объектные типы, иерархия типов Набор операций, которые могут быть применены к заданному объекту, определяет его тип. Объекты создаются как экземпляры типов. Типы могут соотноситься друг с другом посредством отношения подтипа. Множество сигнатур операций, определенных на некотором типе, образует интерфейс данного типа. Интерфейс типа включает сигнатуры операций всех его супертипов. Иерархия типов организована в виде направленного ациклического графа, корнем которого является тип Object. Необъектные типы не включаются в иерархию объектных типов и не являются подтипами типа Object. Все рассматриваемые ниже правила для отношений подтипа и наследования относятся только к объектным типам. Типы не отождествляются со своими интерфейсами и множествами экземпляров (экстенсионалами). Как интерфейсы, так и экстенсионалы, могут изменяться, при этом идентификатор типа остается неизменным.
Отношения подтипа и наследования В Ядре различаются отношения тип/подтип и наследования. Отношение подтипа базируется на интерфейсах типов. Оно определяет правила, по которым значения одного типа могут использоваться в контексте, предполагающем использование значений другого типа. Наследование - это механизм для повторного использования, который позволяет определить тип в терминах другого типа.
Отношение подтипа Тип S является подтипом типа T, если S есть специализация типа T. При этом объект типа S является также и объектом типа T и, следовательно, может использоваться везде, где применяются объекты типа T. Отношение подтипа является множественным. В Ядре необходимо явно указывать, что один тип является подтипом другого. Для каждой операции типа должна иметься соответствующая операция подтипа, такая, что:
Эти жесткие правила, однако, могут быть ослаблены при определении профилей.
Отношение наследования При наследовании тип S наследует все операции типа T и, кроме того, может иметь собственные операции. В отличие от отношения подтипа, наследование базируется как на интерфейсах типов, так и на их реализациях: и те и другие могут наследоваться. Однако в Ядре не рассматриваются вопросы, связанные с наследованием реализаций, такие, например, как перекрытие операций супертипа операциями подтипа. При этом если S является подтипом типа T, то S также наследует T.
Ядро не поддерживает механизмов для разрешения конфликтов и не допускает переопределений сигнатур операций в подтипах. Эти ограничения ослабляются в компонентах-расширениях.
Совместимость расширений Ядра Правильные расширения Ядра должны определять минимальный и достаточный набор понятий, так чтобы понятия Ядра не были удалены и заменены. В идеале расширения должны быть ортогональными по отношению друг к другу. При расширении допускается конкретизация исходной модели, наполнение расширенной модели конкретными экземплярами сущностей, определяемых исходной моделью, ограничение исходной модели удалением сущностей или введением дополнительных ограничений на их использование.
Первичным в модели является интуитивное понятие объектной системы, призванной обеспечивать услуги (сервисы) клиентам. Сущности, образующие объектную систему, называются объектами. Предполагается, что объекты идентифицируемы и что они инкапсулированы. Объекты могут предоставлять сервисы клиентам по их заявкам.
Заявка (request) при этом определятся как событие, имеющее место в определенный момент времени, и она может быть параметрической. Заявка содержит информацию, указывающую требуемый сервис, объект, его предоставляющий, фактические параметры, передаваемые сервису, а также, возможно, контекст данной заявки, являющийся источником дополнительной информации о заявке для выполняющего ее сервиса. Различаются входные, выходные и смешанные (входные-выходные) параметры заявки. Заявка может возвращать единственное результирующее значение так же, как и значения произвольного числа выходных параметров.
Значение (value) есть то, что может служить допустимым фактическим параметром заявки. Более точно, значение является экземпляром типа данных IDL. Если значение служит для идентификации объекта, его называют именем объекта. С именами объектов связано понятие объектной ссылки, которое трактуется как имя объекта, обозначающее некоторый конкретный объект.
Важное место в модели занимает концепция типа. Тип (type) понимается как сущность, с которой связан некоторый одноместный предикат с аргументами-значениями. Если предикат является истинным для некоторого заданного значения, то говорят, что это значение удовлетворяет рассматриваемому типу и называют его членом данного типа. Экстенсионалом типа называют множество значений, удовлетворяющих типу в заданный момент времени.
Концепция типа ассоциируется в модели с объектами. Объектный тип - это такой тип, членами которого являются некоторые объекты, точнее говоря, значения, их идентифицирующие.
В описании объектной модели OMG особо выделяются типы данных. Эти типы подразделяются на базовые (basic types), конструируемые (constructed types) и шаблонные (template types).
К числу базовых типов отнесены типы данных, традиционно используемые в популярных языках программирования, - тип целых чисел в различных представлениях, тип чисел с плавающей точкой, литерный и строковый типы, тип логических значений, 8-битовый тип, значения которого не подвергаются каким-либо преобразованиям при передаче данных, и, наконец, тип "any" (любой), обозначающий любой базовый или конструируемый тип.
Конструируемые типы включают: тип "struct" (структура), определяемый как упорядоченное множество пар (имя, значение), тип "union" (размеченное объединение - предложенная в [5] структура данных, аналогичная структуре вариантной записи в Паскале), а также перечислимый тип "enum", определяющий упорядоченную последовательность идентификаторов. Для удобства введены также шаблонные типы, к числу которых отнесены тип "sequence" (последовательность), определяющий переменной длины массивы значений одного типа с заданным или неопределенным числом элементов, а также тип "string" (строка), определяющий последовательности символов алфавита языка заданной или неопределенной длины.
Поведенческие аспекты объектной модели CORBA связаны прежде всего с уже упоминавшимися ранее сервисами. Важными частными случаями сервисов являются сервисы, обеспечивающие создание и уничтожение указываемых в заявках объектов.
Для указания нужных сервисов используются некоторые идентифицируемые сущности, называемые операциями. Каждая операция обладает идентификатором и сигнатурой. Сигнатура операции представляет собой совокупность спецификаций параметров заявок, соответствующих этой операции, и ее результатов. Определение операции расширено спецификацией исключительной ситуации, трактуемой как указание на неудачное выполнение заявки, и контекстом заявки, обозначающим специфическую для данной операции информацию, которая оказывает влияние на процесс выполнения заявки. Предусматривается две модели выполнения заявки. Первая предполагает синхронизацию клиента с выполнением заявки объектом (такая синхронизация может быть как непосредственной - клиент останавливается и ждет результатов заявки, так и отсроченной). Вторая модель не предполагает возвращения каких-либо результатов объектом и синхронизации клиента с выполнением заявки.
Заметим, что в сигнатуре операции используются только позиционные параметры. Для каждого параметра предусматривается две характеристики - режим и тип. Режим указывает способ использования параметров - входной (in), выходной (out) и комбинированный (inout). Задание типов позволяет ограничить возможные значения входных параметров, а также указать свойства вырабатываемых результатов.
Интерфейс трактуется как набор всех потенциально возможных операций, которые могут быть выполнены объектом по заявкам клиентов. Вводится отношение "объект удовлетворяет интерфейсу". Оно имеет место, если рассматриваемый объект может быть указан как целевой в любой возможной заявке, которая адекватна данному интерфейсу. Интерфейсы могут иметь атрибуты. Наличие атрибута логически эквивалентно объявлению пары функций доступа: для извлечения и для установки значения атрибута, соответственно.
Интерфейсы в модели типизируются. Под типом данного интерфейса понимается такой тип, которому удовлетворяет любой объект, удовлетворяющий рассматриваемому конкретному интерфейсу.
На интерфейсы распространяется действие механизмов наследования. Интерфейс, наследующий свойства другого интерфейса, называется производным от этого другого интерфейса, который, в свою очередь, называется базовым по отношению к нему. В производном интерфейсе могут объявляться новые элементы или переопределяться наследуемые. Интерфейс может быть производным от нескольких базовых интерфейсов. На множестве определенных интерфейсов могут, таким образом, поддерживаться иерархии отношений наследования. Наследование интерфейсов есть композиционный механизм, позволяющий объекту поддерживать множественные интерфейсы. Главный (principal) интерфейс является наиболее специализированным интерфейсом, поддерживаемым объектом. Этот интерфейс состоит из всех операций в транзитивном замыкании графа наследования интерфейсов.
Введено понятие реализации объекта, которое трактуется следующим образом. Метод представляет собой программу, исполняемую для реализации сервиса. С методом связаны формат метода, определяющий набор исполнительных механизмов для интерпретации метода, и исполнительный механизм - абстрактная машина, интерпретирующая методы определенных форматов. Реализация объекта представляет собой определение, содержащее информацию, необходимую для создания объекта и для участия объекта в обеспечении надлежащего набора сервисов. Реализация обычно включает определения методов, оперирующих состоянием объекта.
Завершая рассмотрение объектной модели CORBA, нужно заметить, что обеспечение независимости ("изоляции") клиентов, обращающихся с заявками к сервисам, от предоставляющих эти сервисы объектов благодаря инкапсулирующим их интерфейсам, - это один из основополагающих принципов рассматриваемой модели.
Определение интерфейса объекта средствами OMG IDL полностью характеризует все операции, которые могут выполняться данным объектом по заявкам клиентов. Это определение служит источником информации для разработки программ-клиентов, обращающихся к объектам с заявками на выполнение операций, предусмотренных определениями их интерфейсов. Поскольку определение используемого клиентом интерфейса должно быть доступно его реализации, необходимо осуществлять отображение спецификаций, заданных в языке OMG IDL, в язык реализации клиента. Характер, способы и конкретная техника такого отображения существенно зависят от функциональных возможностей языка клиента. В текущей версии стандарта представлены спецификации отображения OMG IDL в языки C, C++ и Smalltalk. Проблемы и подходы, связанные с такого рода отображениями, кратко обсуждаются далее в разд. "Отображение IDL в языки программирования."
Мы будем рассматривать здесь возможности языка OMG IDL, соответствующие версии стандарта CORBA 2.0 [12]. Нужно заметить, впрочем, что они весьма незначительно отличаются от спецификаций IDL в предыдущих версиях стандарта CORBA 1.1 [10] и CORBA 1.2 [11].
Именно благодаря такой близости языков специфицированное в CORBA 2.0 отображение OMG IDL в C++ является весьма естественным.
Вместе с тем, в отличие от C++, OMG IDL является декларативным языком. В нем нет средств описания алгоритмов и переменных. Налагаются также некоторые ограничения на синтаксические конструкции, общие для обоих языков. Так, в OMG IDL обязательно должен специфицироваться тип возвращаемого функцией значения. Для каждого формального параметра в объявлении операции должно специфицироваться имя. Исключаются спецификации типа "char" с ключевыми словами "signed" или "unsigned" и т.д.
Для описания синтаксиса языка в спецификациях стандарта CORBA используется нотация, аналогичная EBNF (Extended Backus-Naur Format - Расширенный формат Бэкуса-Наура). Символы этой нотации имеют следующий смысл:
::= - является по определению
| - или
- нетерминальный символ, представляемый заключенным в скобки понятием
"текст" - литерал
* - возможность повторения предшествующей синтаксической конструкции нуль или более раз
+ - возможность повторения предшествующей синтаксической конструкции один или более раз
{ } - заключенные в скобки синтаксические конструкции рассматриваются как единая конструкция
[ ] - заключенная в скобки синтаксическая конструкция является необязательной.
Идентификатор определяется традиционным образом как произвольной длины последовательность букв, цифр и символов подчеркивания "_", начинающаяся, как обычно, буквенным символом, причем строчные и прописные буквы не различаются.
Ключевые слова в языке подчиняются правилам построения идентификаторов. Их немного более трех десятков, и ряд из них заимствован из C++. Литералы в OMG IDL также подобны литералам в C и C++. Они могут быть целыми, символьными, с плавающей точкой и строковыми. Целые литералы могут быть десятичными, восьмеричными (начинаются цифрой "0") и шестнадцатиричными (начинаются символами "0x" или "0X") числами. Символьные литералы имеют тип "char" и представляют собой последовательности из одного или более символов, заключенные в одиночные кавычки. В них могут использоваться также управляющие последовательности (escape-sequence). Для записи литералов с плавающей точкой используется традиционное их представление, включающее десятичные мантиссу и порядок. Строковые литералы также имеют обычный смысл и заключаются в двойные кавычки.
Спецификации на языке OMG IDL могут содержать комментарии. Правила их записи также заимствованы из C++. За символами "//" следует комментарий, продолжающийся до конца строки, где указана эта комбинация символов. Последовательность символов в последовательных строках спецификаций, заключенная между комбинациями символов "/*" и "*/", также рассматривается как комментарий.
Семантически законченное исходное описание на языке IDL, пригодное для конкретного применения, называется OMG IDL спецификацией. Она представляют собой совокупность определений модулей, интерфейсов, констант, типов и исключительных ситуаций. Рассмотрим кратко каждую из этих языковых конструкций.
Модули Синтаксическое определение модуля имеет вид:
модуль::= "module" идентификатор "{" определение+ "}"Здесь и далее определение понимается как определение модуля, интерфейса, исключительной ситуации, константы или типа.
Модули используются для того, чтобы избежать коллизий между именами, определенными в спецификации OMG IDL, и именами в языках программирования и других программных системах, совместно с которыми эти спецификации применяются.
С помощью модулей устанавливается область определения имен в спецификациях для последующего управления разрешением видимости. При ссылках в спецификациях или вне их на имена, определенные в некотором IDL-модуле, следует квалифицировать эти имена именем модуля, задавая его как префикс с разделителем - символом операции разрешения видимости ("::"). Например, ссылка CORBA::Trans означает языковый объект с именем Trans, определенный в модуле CORBA. Заметим, что в OMG IDL сохраняются и обычные для C++ возможности ссылки на глобальные переменные в области действия одноименной локальной переменной с помощью операции "::".
Интерфейсы Интерфейс - главный объект языка. Синтаксически он определяется следующим образом:
интерфейс::=заголовок_интерфейса "{" тело_интерфейса "}"Заголовок интерфейса состоит из ключевого слова "interface", за которым следуют имя интерфейса и необязательная спецификация наследования.
Например, спецификация:
interface optim_model:link_model;представляет собой заголовок интерфейса optim_model, который является производным от базового интерфейса link_model. Синтаксис языка располагает средствами, позволяющими описывать в заголовке интерфейса сложные структуры наследования. Имена базовых интерфейсов при этом могут квалифицироваться символом "::" или префиксом - именем блока с последующим символом операции "::".
Тело интерфейса может включать произвольное число объявлений типов, констант, исключительных ситуаций, атрибутов и операций. Допускаются, в частности, и пустые интерфейсы, тело которых не содержит никаких объявлений. Все указанные виды объявлений будут рассмотрены далее.
Заметим, что, в отличие от объявления интерфейса, непосредственно описывающего его свойства, предусматривается также возможность упреждающего объявления:
"interface" идентификатор.при этом объявляется имя интерфейса, но не специфицируется его определение. Предполагается, что определение последует позднее в данной OMG IDL-спецификации. Упреждающие объявления позволяют, в частности, задавать определения интерфейсов, которые ссылаются друг на друга.
Константы Объявления типизированных констант, т.е. таких переменных, значения которых не могут изменяться, полностью аналогичны с точностью до возможных типов таким декларациям в C++:
константа ::= "const" тип идентификатор" = константное_выражениеЗдесь тип определяет тип объявляемой константы, идентификатор указывает ее имя, а значение константного выражения рассматривается как значение константы. Это значение может быть числовым, строковым, символьным, булевским или представляет собой какое-либо имя.
Пример спецификации константы:
const float pi = 0.314159265358979323E1;Типы В языке OMG IDL предусмотрены синтаксические средства для объявления базовых, конструируемых и шаблонных типов данных объектной модели OMG.
Базовые типы обозначаются ключевыми словами: "any", "boolean", "char", "double", "float", "long", "octet", "short", "unsigned short", "unsigned long". Синтаксис объявлений таких типов приведен ниже и не требует дополнительных пояснений:
спецификация_базового_типа::= ключевое_слово_базового_типа декларатор {","декларатор}*где:
декларатор::= идентификатор | идентификатор "[" положительная_ целая_константа "]"Второй вариант декларатора в приведенной спецификации представляет собой массив заданной длины.
Примеры объявлений базовых типов:
float input_stream, output_stream; short green_array [25], blue_vari;Ключевые слова конструируемых типов - "struct", "union", "enum".
Тип "struct" представляет собой совокупность других заданных типов, рассматриваемую как единое целое. Его синтаксическая спецификация имеет вид:
спецификация_типа_struct::= "struct"
идентификатор "{" список_членов "}"
список_членов::= член+ член::= спецификация_типа
декларатор {","декларатор}*Спецификация типа означает здесь и далее ключевое слово любого базового или конструируемого типа.
Пример спецификации типа struct:
struct model {
string title; char modtype; string institute; short dimension; boolean implementation;
};Размеченное объединение ("union") - это такое объединение других типов, в заголовке которого указывается тип величины, определяющей при конкретном вызове, какой именно член объединения должен быть использован. Тип размеченного объединения специфицируется следующим образом:
спецификация_типа_union::= "union" идентификатор "switch" "("тип_ переключателя")" "{" тело_переключателя "}"
тип_переключателя::= short | long | unsigned short | unsigned long | char | boolean | enum
тело_переключателя::= вариант+
вариант ::= метка_варианта+ спецификация_элемента ";"
метка_варианта::= "case" константное_выражение ":" |"default" ":"
спецификация_элемента::= спецификация_типа деклараторПример спецификации типа размеченного объединения:
union variant switch(char){
1: char symbol; 2: float min_price; 3: struct new_occurence{ long x; float y; char z; }; default: string line
};Перечислимый тип ("enum") по традиции определяет упорядоченное множество идентификаторов и специфицируется следующим образом:
спецификация_типа_enum::= "enum" идентификатор "{"идентификатор {"," идентификатор}* "}"Пример такой спецификации:
enum color {black, white, blue, red, yellow};Наряду с базовыми и конструируемыми типами, в OMG IDL используются шаблонные типы. К числу таких типов, как уже отмечалось, относятся типы "sequence" (последовательность) и "string" (строка). Эти типы имеют вспомогательный характер и введены из соображений удобства. Они используются, главным образом, в объявлениях переименования типов "typedef".
Последовательность понимается как одномерный массив, обладающий максимальным размером, фиксируемым на стадии компиляции, и длиной, которая определяется в период исполнения. Спецификация типа "sequence" имеет вид:
спецификация_типа_sequence ::= "sequence" "" {спецификация_базового_типа | спецификация_шаблонного_типа} ["," положительная_целая_константа] "" идентификаторПоложительная целая константа, если она задана, указывает максимальный размер последовательности, которая в таком случае называется ограниченной. В противном случае она называется неограниченной.
Примеры спецификаций последовательностей:
typedef sequence short, 25 bound_sequence;
typedef sequence char .unbound_sequence;Строка трактуется как последовательность любых символов алфавита, за исключением символа "/0". Для строки может быть факультативно задан ее максимальный размер - положительное целое. Если он задан, строка называется ограниченной. В противном случае она называется неограниченной. Спецификация типа "string" выглядит таким образом:
спецификация_типа_string ::= "string" [""положительная_целая_константа""] идентификаторПримеры спецификаций строк:
typedef string 25 bound_string;
typedef string unbound_string;Исключительные ситуации В языке OMG IDL предусмотрены возможности для объявления исключительных ситуаций, которые позволяют установить, какая исключительная ситуация имела место при выполнении заявки. Спецификация исключительной ситуации включает конструкцию, похожую на структуру ("struct"), которая описывает возвращаемое значение, и она имеет вид:
спецификация_исключительной_ситуации ::= "exception" идентификатор "{" член* "}"Здесь синтаксический терм член имеет тот же смысл, что и в определении типа ("struct"). Терм идентификатор - это идентификатор данного типа исключительной ситуации. Если при выполнении заявки возникает исключительная ситуация, то значение этого идентификатора доступно для анализа с тем, чтобы установить, какая именно исключительная ситуация имеет место.
В стандарте CORBA 2.0 определен некоторый перечень стандартных исключительных ситуаций, которые могут возникать при выполнении заявок.
Для примера приведем объявление исключительной ситуации, возникающей при передаче неправильного параметра:
#define ex_body {unsigned long minor;
completion_status completed;}
enum completion_status {COMPLETED_YES,
COMPLETED_NO, COMPLETED_MAYBE};
enum exception_type {NO_EXCEPTION,USER_EXCEPTION, SYSTEM_EXCEPTION}
exception BAD_PARAM ex_body;Операции интерфейса Для обеспечения полноты рассмотрения спецификаций интерфейсов рассмотрим теперь, каким образом специфицируются операции в языке OMG IDL.
Приведем синтаксис объявления операций:
объявление_операции::=
[атрибут_операции] тип_результата идентификатор объявления_параметров [исключительные_ситуации] [контекстное_выражение]Рассмотрим семантику элементов этой спецификации.
Факультативный атрибут операции, указываемый ключевым словом "oneway"(односторонний), описывает требования к коммуникационному сервису, в задачу которого входит вызов запрашиваемой операции. Если этот атрибут специфицирован, операция будет вызываться не более, чем однократно, и такой вызов осуществляется без обратной связи - операция не может содержать выходных параметров, возвращаемое значение имеет тип "void", не могут специфицироваться никакие исключительные ситуации. Если атрибут не специфицируется, операция вызывается самое большее один раз, если возникает исключительная ситуация, и в точности один раз при успешном ее завершении.
Идентификатор означает идентификатор вызываемой операции, а тип результата - тип возвращаемого значения. Допускается любой тип, определенный в языке. Если операция не возвращает никакого результирующего значения, специфицируется тип "void".
Объявления параметров - это заключенный в круглые скобки список объявлений параметров, может быть, пустой.
Объявление отдельного параметра имеет вид:
объявление_параметра ::= атрибут_параметра тип_параметра идентификатор_параметраАтрибут параметра характеризует здесь направление его передачи в коммуникации между клиентом и сервером. Вариант "in" указывает, что параметр передается от клиента к серверу, "out" - от сервера клиенту и, наконец, "inout" - передается в обоих направлениях.
Тип параметра может быть любым базовым типом или типом "string".
Идентификатор параметра - это обычный идентификатор.
В объявлении операции факультативная спецификация исключительных ситуаций имеет следующий вид:
исключительные_ситуации ::= "raises" "(" идентификатор {","идентификатор}* ")"Здесь идентификатор - это идентификатор ранее определенной, в соответствии с приведенным выше синтаксисом, исключительной ситуации.
Наконец, оставшийся факультативный элемент спецификации операции - контекстное выражение - служит для задания дополнительной информации, которая может оказать влияние на выполнение заявки клиента. Здесь используется следующий синтаксис:
контекстное_выражение ::= "context" "(" строковый_литерал {"," строковый_литерал}* ")"Указанная выше дополнительная информация представляется здесь в форме строковых литералов.
Примеры спецификаций операций:
float measuring(in float length, in float width, out float space) context "***";
void set_high(in short x);Атрибуты интерфейсов Как уже указывалось выше, спецификация интерфейса может содержать атрибуты. Определение каждого атрибута равносильно заданию пары функций - функции выборки значения атрибута и функции установки значения атрибута. Конкретные имена этих функций зависят от спецификаций отображения IDL в конкретные языки программирования. Атрибуты интерфейсов являются субъектом наследования. Для спецификации атрибутов интерфейсов используется следующий синтаксис:
объявление_атрибута::= ["readonly"] "attribute" тип_параметра идентификатор {","идентификатор}*Если указывается факультативное ключевое слово "readonly", то определение данного атрибута равносильно единственной функции, осуществляющей выборку значения атрибута. Смысл типа параметра пояснялся выше, а идентификаторы - это идентификаторы специфицируемых атрибутов данного интерфейса.
Примеры спецификаций атрибутов:
attribute short count; readonly attribute long time;
Итак, имеем:
// OMG IDL specification example: myBank.idl module BANK {
interface BankAccount {
// types enum account_kind {checking, saving}; // exceptions exception account_not_available {string reason;}; exception incorrect_pin {}; // attributes readonly attribute float balance; attribute account_kind what_kind_of_account; // operations void access (in string account, in string pin) raises (account_not_available, incorrect_pin); void deposit (in float f, out float new_balance) raises (account_not_available); void withdraw (in float f, out float new_balance) raises (account_not_available);
}; // end of interface BankAccount
} // end of module BANK
Поддержка IDL в этих реализациях достаточно проста. Компилятор IDL, прежде всего, осуществляет контроль заданных спецификаций, а затем отображает их в соответствующий язык программирования, например в C++. При этом порождаются три исходных файла: заголовочный файл, который должен быть включен в реализацию интерфейса и в программы всех клиентов, исходный файл, который компилируется далее для получения скелетона, и исходный файл, который компилируется и используется как стаб клиента. Спецификации интерфейсов на IDL могут быть при необходимости помещены также в Репозитарий Интерфейсов.
Основные вопросы, вызывающие трудности при разработке стандартов отображения IDL, включают выбор представления объекта ORB в конкретном языке программирования (следует различать, как объект представляется в программе, как он передается в качестве параметра, как осуществляется вызов операций на его интерфейсе); представление исключительных ситуаций в конкретном языке; представление интерфейсов ORB.
К настоящему времени OMG определены отображения IDL в языки C, C++ и Smalltalk. Завершается разработка стандарта отображения IDL в язык Ada. Эта работа не проста. Так, только обсуждение и принятие отображения IDL в С++ заняло более двух лет напряженной работы, подтвердившей важность технологии принятия стандартов, используемой OMG. С++ используется миллионами программистов в качестве объектно-ориентированного языка программирования. Поэтому вопрос о том, как CORBA должна быть представлена для этого сообщества, является жизненно важным. В конечном счете группа фирм в составе SunSoft, IBM, IONA Techno-logies, Digital, Hewlett-Packard и Expersoft выработала предложение, которое было принято. Далее рассматриваются основные положения этого стандарта отображения.
Отображение обеспечивается предопределенными типами, классами и функциями, определения которых сосредоточены в модуле с именем CORBA. Этот модуль является доступным для единиц компиляции C++ как пространство имен CORBA. Поэтому тип IDL Object имеет имя CORBA::Object.
Согласно стандарту, OMG IDL-модули отображаются в пространства имен C++, OMG IDL интерфейсы отображаются в классы C++, конструкции OMG IDL в области действия интерфейса оказываются доступными посредством квалифицированных имен C++. При отображении интерфейсов в классы действует ряд ограничений. Программа, совместимая с CORBA-C++, не может создавать экземпляры интерфейсного класса, не может вводить его подклассы, использовать указатель (A*) или ссылку (A) на интерфейсный класс. Эти ограничения введены, чтобы допускались произвольные реализации интерфейсов.
Использование интерфейсного типа в OMG IDL обозначает объектную ссылку. Из-за различных способов употребления объектных ссылок и различных способов их реализации в C++, объектные ссылки отображаются в два типа C++. Для интерфейса A эти типы именуются A_var и A_ptr. Переменная типа объектной ссылки A_var является удобной, поскольку переменная автоматически освобождает объектную ссылку при ее удалении или при присваивании ей новой ссылки. Тип указателя A_ptr обеспечивает более примитивную объектную ссылку, семантика которой подобна указателю C++ (полной совместимости здесь нет). Для объектных ссылок определен ряд функций. Например, функция CORBA:: release(), примененная к указателю на объект, уменьшает счетчик ссылок на единицу и удаляет объект, если счетчик ссылок становится равным нулю. Счетчики ссылок поддерживаются одновременно в клиентах и серверах. CORBA::release(), вызываемая клиентом, не оказывает воздействия на счетчик ссылок целевого объекта в сервере.
Вместо этого каждый представитель (proxy) объекта клиента имеет свой собственный счетчик ссылок. Удаление представителя объекта при достижении нуля его счетчиком ссылок не оказывает воздействия на счетчик ссылок целевого объекта. Последний может изменяться объектами самого сервера. Существенно, что реальный объект может быть удален сервером, если даже существуют клиенты, в которых образованы представители для данного объекта. Последующие попытки вызова функций реального объета при помощи их представителей приводят к образованию исключительных ситуаций.
Константы OMG IDL отображаются непосредственно в определения констант C++. Базовые типы данных OMG IDL имеют естественное отображение в типы данных C++. Структурированные типы данных "struct", "union", "sequence" отображаются в тип структуры или в класс C++. Массивы отображаются в определения массивов C++.
Операции отображаются в виртуальные функции-члены C++, имеющие то же имя, что и операции. Каждый атрибут отображается в пару функций C++ - одну для установки значений атрибута, а другую - для выборки его значений.
Приведеный ниже простой пример отображения спецификаций интерфейсов IDL в C++ демонстрирует употребление имен операций и наследования. Пусть в IDL определены следующие интерфейсы:
// IDL interface myaccount {
void deposit (in float sum); float readBalance (out string nameadr);
}; interface checkCredit: myaccount {
void setCreditLimit (in float limit);
}; interface mybank {
myaccount newaccount (in string name);
};Классы C++, образующиеся в соответствии с правилами отображения:
// C++ class myaccount: public virtual CORBA::Object {
virtual void deposit (CORBA:Float sum); virtual CORBA:Float readBalance (char *nameadr);
} class mybank: public virtual CORBA::Object {
virtual account_ptr newaccount (const char* name);
}; class checkCredit: public virtual myaccount {
public: virtual void setCreditLimit (CORBA::Float limit);
};Объект C++, использующий интерфейс checkCredit, может вызвать наследуемую функцию deposit():
checkCredit_ptr checkingAc; // операторы, устанавливающие значение checkingAc, опущены checkingAc - deposit (90.9);
IDL играет роль общего, "канонического" языка, обеспечивающего интероперабельность объектов, имеющих программно-несовместимые реализации. Для n различных языков программирования n стандартных отображений IDL решают проблему несовместимости языков.
Поскольку IDL является воплощением объектной модели CORBA, обеспечивающей лишь минимальный набор средств спецификации интерфейсов, важное значение для спецификации и реализации интероперабельных информационных систем имеет расширение состава этих средств. Эта задача решается, прежде всего, путем создания различных профилей. Известным расширением IDL является язык ODL (Object Definition Language), являющийся частью стандарта архитектуры объектных СУБД [3]. Подобные ODL-расширения - тип связей (Relationship) и тип коллекции (Collection) определены OMG в спецификации Общих Объектных Служб. Эти расширения играют существенную роль в применении архитектуры OMG.
Другое важное направление работ, связанных с использованием IDL, - расширение сферы его применения за счет пополнения спектра языков программирования, в которые стандартным образом могут отображаться спецификации IDL. Деятельность в этом направлении продолжается и после выпуска стандарта CORBA 2.0. В дополнение к предусмотренным текущей версией стандарта спецификациям отображения IDL в языки C, C++ и Smalltalk завершается работа над аналогичным стандартом под эгидой OMG для языка Ada [13].
Различными группами специалистов ведутся также работы по созданию таких спецификаций и реализующего их инструментария для ряда других широко распространенных языков программирования, которые благодаря этому также вовлекаются в сферу применения стандартов CORBA. Известны, в частности, такие попытки, связанные с языками FORTRAN, COBOL, Eiffel, Java, Modula-3 [6, 8, 14]. Для некоторых из них не только разработаны спецификации отображения, но и уже созданы соответствующие компиляторы.
Для достижения указанных целей предполагается также расширить систему типов языка OMG IDL [7]. Авторы языка намерены вместе с тем отслеживать изменения в стандарте языка-прототипа IDL - ANSI C++ - и поддерживать совместимость с ним.
2. Брюхов Д.О., Задорожный В.И., Калиниченко Л.А., Курошев М.Ю., Шумилов С.С. Интероперабельные информационные системы: архитектуры и технологии. - СУБД, #4, 1995.
3. Калиниченко Л.А. Стандарт систем управления объектными базами данных ODMG-93: краткий обзор и оценка состояния. - СУБД, #1, 1996.
4. Строустрап Б. Язык программирования Си++. Радио и связь. - М., 1991.
5. Хоор К. О структурной организации данных. В сб. "Дал У., Дейкстра Э., Хоор К. Структурное программирование". /Пер. с англ. - М.: Мир, 1975.
6. The CORBA IDL Compiler for Java. http://herzberg.ca.sandia.gov/jidl.
7. IDL Type Extension RFP. OMG TC Document 95-1-35. August 22, 1995.
8. Nayeri Farshad. idlm3: An OMG IDL to Modula-3 Translator. http://vim.ecs.soton.ac.uk/modula-3 /modula-3/html/idlm3.html.
9. Object Management Group, "Object Management Architecture Guide", OMG Document Number 92.11.1, September 1, 1992.
10. Object Management Group, "The Common Object Request Broker: Architecture and Specification", OMG Document Number 91.12.1, December 1991.
11. Object Management Group, "The Common Object Request Broker: Architecture and Specification", OMG Document Number 93.xx.yy, Revision 1.2. Draft 29 December 1993.
12. Object Management Group, "The Common Object Request Broker: Architecture and Specification". Revision 2.0. July 1995.
13. OC Systems Supports OMG"s IDL to Ada95 Mapping. http://ocsystems.com/company/press_releases/ omgpress.html.
14. The Spring-Java IDL system. http://www.sun.com/tech/projects/spring/spring-java.html.
15. Wiederhold G., Wegner P., Ceri S. Toward megaprogramming. CACM, v. 35, no. 11, November 1992.
16. Zhonghua Yang. Example IDL Definition. http://hume.nml.nih.gov/~kls/corba/idl-ex.html.