Здравствуйте, подскажите пожалуйста где можно достать материалы по курсу Кросс-платформенные и многозвенные технологии, о которых говориться, к примеру, в Лекции 2. Пример "Служба мгновенных сообщений" |
Технология CORBA
Связывание с IDL
Спецификация CORBA регламентирует, во что должен превратиться каждый элемент языка IDL в процессе его трансляции в исходные тексты на языке программирования высокого уровня. Версия 2.2 спецификации CORBA расписывает подобные соответствия для C, C++, Smalltalk, Cobol, Ada и Java.Для примера рассмотрим подробнее трансляцию в Java.
Комментарии
Комментарии IDL никак не отражаются на сгенерированном Java-коде.
Имена
Результаты работы различных компиляторов могут различаться. В основном это касается добавления символов подчеркивания перед сгенерированными именами. Поскольку трансляция зависит от того, что за элемент языка IDL подвергается обработке, различается и количество файлов, получаемых в процессе генерации. Для пользовательских типов, например, будут созданы специальные файлы, имена которых заканчиваются суффиксами Helper и Holder.По спецификации, компилятор резервирует за собой следующие имена:
<тип> Helper, где <тип> - имя пользовательского типа;
<тип> Holder, где <тип> - имя пользовательского типа;
<базовыйТипJava>Holder, где <базовыйТипJava> - один из примитивных типов языка Java;
<интерфейс>Package, где <интерфейс> - имя интерфейса IDL.
Вспомогательные классы
Если программист описывает собственные типы, в результате их трансляции появляются два вспомогательных класса, имена которых состоят из имени типа с добавлением суффиксов Helper и Holder. Они необходимы для корректной работы с объектами. Helper содержит набор статических методов, выполняющих одни и те же рутинные действия. Класс с суффиксом Holder работает "оболочкой" для пользовательского типа, когда его нужно передать в качестве параметров операции объекта. Его генерация происходит не во всех случаях.
Класс Helper всегда имеет статические методы для чтения и записи данных в поток read () и write (), упаковки данных в тип Any и распаковки (методы insert () и extract() ), а также методы определения типа type() и его идентификатора в репозитарии id().
Класс Holder должен не только уметь записывать данные в поток методом _write(), читать их оттуда методом _ read () и возвращать код типа методом _ typecode (). В нем должна быть предусмотрена открытая переменная value, хранящая значение, и два конструктора: один - по умолчанию, т.е. без параметров, и второй - с параметром, инициализирующим переменную value.
Модули
Во время трансляции описания модулей превращаются в пакеты с теми же самыми именами, что и сами модули. Соответственно все описания типов внутри модулей после трансляции в классы и интерфейсы Java приобретают область видимости внутри сгенерированных пакетов. Если описания типов находятся за пределами модулей, то они транслируются в глобальный пакет Java,т. е. не включаются ни в один пакет. Для примера опишем следующий модуль:
module UserModule { typedef string UserType; };
После его трансляции в выходном каталоге появится подкаталог с именем модуля, и в нем будут сохранены файлы, появившиеся в результате генерации исходных текстов для типа UserType. А в самих этих текстах появится строка принадлежности к пакету UserModule:
package UserModule;
Интерфейсы
В первую очередь создаются описания общедоступных интерфейсов Java,наследуемые от базового CORBA -интерфейса org.omg.CORBA.Object. Возьмем следующее описание на
IDL:
interface UserInterface { };
После компиляции создается следующий интерфейс на языке Java:
public interface UserInterface extends org.omg.CORBA.Object { }
Внутри сгенерированного интерфейса описываются операции, которые компилятор обнаружит в IDL -файле. Для каждого из атрибутов интерфейса в них создаются описания методов чтения и записи. Если атрибут объявлен как readonly,для него генерируется лишь метод чтения. Исходный текст на IDL:
attribute float UserAttribute;
будет транслирован в следующие описания методов:
float UserAttribute();
void UserAttribute(float arg);
Если IDL -интерфейс наследуется от другого интерфейса, то в его описании на Java также будет присутствовать наследование.
Любой параметр операции с модификатором in транслируется в аргумент метода, имеющий соответствующий тип на языке Java.То же самое и с возвращаемым операцией значением. Параметры inout и out не могут транслироваться непосредственно в параметры методов на Java. Поэтому приходится пользоваться Holder классами. Программа-клиент подставляет в качестве параметра экземпляр подобного класса, в котором, как в контейнере, находится передаваемое значение. После передачи параметра по значению хранимые данные изменяются на серверной стороне и возвращаются клиенту, который "вскрывает контейнер" и извлекает новое значение аргумента. Например, показанная операция имеет параметр, объявленный как inout:
void userOperation(inout double param);
Компилятор IDL сделает из этого следующий метод на языке Java:
void userOperation(org.omg.CORBA.DoubleHolder param);
Такой подход, конечно, создает дополнительные сложности программиста: придется создать экземпляр Holder класса вручную.
Для интерфейса также создаются класс Helper и класс Holder. В первом из них дополнительно к методам, описанным в разделе "Вспомогательные классы", генерируется метод narrow(), с помощью которого делается приведение к оригинальному типу интерфейса. Дело в том, что программе при запросе ссылки на объект возвращается ссылка типа org.omg.CORBA.Object, которую необходимо привести к запрошенному типу перед использованием, что и делает narrow(). При невозможности произвести эту операцию происходит исключение CORBA::BAD PARAM.
Некоторые компиляторы (в том числе, idl2java из Visibroker) генерируют еще один метод. Он называется bind() и служит для получения ссылки на запрашиваемый объект. Этот метод не является частью спецификации CORBA.
Часто у программистов возникают сложности с пониманием того, как транслируются вложенные в описания интерфейсов конструкции. Кажется, что достаточно сгенерировать новый пакет с его именем и поместить в него внутреннее содержимое интерфейса. Однако, по спецификации CORBA,во избежание конфликтов имен нельзя создавать пакеты, имена которых совпадают с уже имеющимися именами. Поэтому решено задавать имена пакетов, добавляя к имени интерфейсов суффикс Package.
Опишем интерфейс, внутри которого объявляется пользовательский тип:
interface UserInterface { typedef any UserType; };
В результате трансляции получится пакет UserInterfacePackage, в который и будут помещены все сгенерированные для пользовательского типа файлы, и все они будут начинаться с директивы:
package UserInterfacePackage;
Простые типы
Трансляция простых типов IDL приводит к появлению соответствующих идентификаторов, но уже имеющих Java -типы. В табл. 2.4 показано соответствие между ними: если данный тип может привести к возникновению исключительной ситуации, то она отмечена в графе "Исключения".
Программист должен быть осторожен, когда работает с целочисленными типами IDL,объявленными как unsigned. Как известно, в Java нет беззнаковых типов, и это может стать причиной ошибок. Следовательно, требуется позаботиться о соблюдении знаковости транслированного числа.
Holder классы для простых типов IDL определены в библиотеках, отвечающих за поддержку CORBA в пакете org.omg.CORBA. Имена этих классов начинаются с имени IDL -типа, написанного с заглавной буквы, и заканчиваются суффиксом Holder.
Константы
Константы внутри интерфейса.
Константы, декларируемые внутри интерфейса, транслируются в поля, описанные как public final static, т. е. константы Java.Например, строки:
interface UserInterface { const string constIntoInterface = "Hello!"; };
будут превращены компилятором java2idl в следующий исходный текст на языке Java:
public interface UserInterface extends com.inprise.vbroker.CORBA.Object { final public static java.lang.String constIntoInterface = (java.lang.String) "Hello!"; }
Константы вне интерфейса
Константы, не включенные ни в один интерфейс, превращаются в интерфейс с тем же самым именем, что и константа. Внутри этого интерфейса помещается public static final поле с именем value. Например, следующий исходный текст:
module UserModule { const string constIntoInterface = "Hello!"; };
будет транслирован следующим образом:
package UserModule; public interface constIntoInterface { final public static java.lang.String value = (java.lang.String)"Hello!"; }
Конструируемые типы
Перечислимые типы
В результате трансляции перечисления получается класс с модификаторами public final и именем, соответствующим имени, описанному в IDL-файле.Для каждого элемента выбора внутри класса создаются два статических члена. Первый является уникальной целочисленной константой, а второй - ссылкой на экземпляр перечисления, инициализированный константным значением для данного выбора. Заодно генерируется закрытый конструктор, инициализируемый целочисленным значением. Еще один метод, value (), возвращает целое число, которым инициализировано перечисление, а в дополнение к нему имеется метод получения элемента перечисления по заданному числу from_int (). При недопустимом значении параметра этого метода возникает исключение CORBA::BAD_ PARAM. Например, исходный текст:
enum UserEnum { labelOne, labelTwo, labelThree };
будет транслирован следующим образом:
public final class UserEnum { public static final int labelOne = 0, labelTwo = 1, labelThree = 2; public static final UserEnum labelOne = new UserEnum(labelOne); public static final UserEnum labelTwo = new UserEnum(labelTwo); public static final UserEnum labelThree = new UserEnum(labelThree); public int value() { return value; } public static final UserEnum from int(int i) throws org.omg.CORBA.BAD_PARAM { switch (i) { case _labelOne: return labelOne; case _labelTwo: return labelTwo; case _labelThree: return labelThree; default: throw new org.omg.CORBA.BAD_PARAM(); } } private UserEnum(int _value) { this.value = value; } private int _value; }
Дискриминируемые объединения
Объединение, описанное на языке IDL, транслируется в Java -класс тем же самым именем и с модификаторами public final. Внутри можно также найти:
- конструктор по умолчанию (без параметров);
- метод чтения дискриминатора discriminator() ;
- метод чтения для каждого варианта с именем, заимствованным из декларируемого варианта;
- методы модификации значения для каждого декларируемого варианта;
- методы модификации значения для каждого декларируемого варианта, который объявляется для нескольких меток case ;
- метод default (), если в нем есть необходимость.
В качестве примера рассмотрим следующее дискриминируемое объединение:
enum UserEnum {Single, Double, Any}; union UserUnion switch (UserEnum) { case Single: case Double: wchar anySymbol; default: any other; };
и полученный в результате трансляции Java-код:
final public class UserUnion { private java.lang.Object _object; private UserModule.UserEnum _disc; private UserModule.UserEnum _defdisc = UserModule.UserEnum.Any; public UserUnion() { } public UserModule.UserEnum discriminator() { return _disc; } public char anySymbol() {... } public org.omg.CORBA.Any other() {... } public void anySymbol(char value) {... } public void anySymbol(UserModule.UserEnum disc, char value) {... } public void other(org.omg.CORBA.Any value) {... } }
Все методы чтения генерируют исключительную ситуацию CORBA:: BAD_OPERATION, если читаемое значение не установлено. Поэтому желательно сначала вызывать метод descriminator (), чтобы ознакомиться с текущим типом хранимого значения. Если не указать в объединении метку default, компилятор сверит все имеющиеся метки со всеми возможными значениями дискриминанта. Если таких значений больше, чем ветвей case, будет сгенерирован еще один метод default () (или _ default () в случае конфликта имен), в котором хранимое значение будет установлено так, чтобы оно было за пределами дискриминанта. Если в предыдущем примере удалить строку с меткой default, то сгенерируется следующий метод:
public void _default() { disc = defdisc; _object = null; }