3.1 El lenguaje de especificación IDL El lenguaje de especificación IDL n n n Permite especificar interfaces remotas en un lenguaje neutral El compilador (preprocesador) de IDL genera código para el lenguaje(s) destino(s) (para poder invocar las operaciones e implementar las interfaces) Mappings estandarizados para un buen número de lenguajes (C, C++, Java, COBOL, Smalltalk, Ada, etc.) n n Cada vez se van añadiendo más ... Actualmente IDL es un lenguaje relativamente extenso n Veremos lo esencial ... Reglas léxicas (1) n Ficheros fuente IDL n n Deben tener la extensión .idl Son preprocesados por el preprocesador de C++ n n Se puede usar #define, #include, #ifdef, #ifndef, #endif, etc. Comentarios // Esto es un comentario de una línea. /* Esto es un comentario de varias líneas. */ n Palabras reservadas n n Casi todas en minúsculas Algunas excepciones: TRUE, FALSE y Object Reglas léxicas (y 2) n Identificadores n n Deben comenzar con un carácter alfabético seguido por cualquier número de caracteres alfabéticos, dígitos o “_” (subrayado) No se distingue entre mayúsculas y minúsculas, pero deben utilizarse consistentemente n n n Si definimos el identificador MAX, a partir de ese momento siempre debemos utilizar MAX y no Max, max, etc. Objetivo: permitir mappings de IDL tanto a lenguajes que distinguen entre mayúsculas y minúsculas en identificadores (ej.: C++, Java) como a otros que no (ej.: Ada) Es importante evitar nombres de identificadores que sean palabras reservadas en algún de lenguaje de implementación (ej.: package, self) Tipos predefinidos (1) n Numéricos Tipo short Rango Tamaño -215.. 215-1 >= 16 bits long -231.. 231-1 >= 32 bits long long -263.. 263-1 >= 64 bits unsigned short 0.. 216-1 >= 16 bits unsigned long 0.. 232-1 >= 32 bits unsigned long long 0.. 264-1 float IEEE Precisión simple double IEEE Precisión doble long double IEEE Precisión extendida >= 64 bits >= 32 bits >= 64 bits >= 79 bits Tipos predefinidos (2) n Numéricos (cont) n n El mapping de IDL a un lenguaje de implementación debe preservar el tamaño, pero no necesita mantener el rango ¿ Por qué los tamaños pueden ser mayores ? n n ¿ Por qué algunos mappings no mantienen los rangos n n Ej.: algunas arquitecturas (CPUs) no disponen de caracteres de 8 bits o enteros de 16 bits, y se mapean a tipos con tamaños mayores Ej.: Java no tiene enteros sin signo, y por tanto, mapea los tipos IDL long y unsigned long al tipo Java int long long, unsigned long long, long double se añadieron en CORBA 2.1, por lo que los ORBs antiguos no los soportarán Tipos predefinidos (3) n Caracteres n n n n char: 8 bits wchar: tamaño dependiente de la implementación Codificación: dependiente de la implementación en ambos casos, pero el ORB se preocupará de realizar la conversión (si puede) cuando cliente y servidor utilizan distintos sistemas de codificación Cadenas de caracteres n n string y wstring. No pueden contener carácter 0 Ejemplos string<10> // string acotado (10 char como máximo) string // string no acotado wstring<10> // wstring acotado (10 wchar como máximo) wstring // wstring no acotado n n string<x> y wstring<x> son tipos propios (distintos de string y wstring) wchar y wstring se añadieron en CORBA 2.1, por lo que los ORBs antiguos no los soportarán Tipos predefinidos (y 4) n octet n n n n boolean n n 8 bits No se realiza ninguna conversión entre cliente y servidor Utilidad: transferir datos binarios Proporciona los valores TRUE y FALSE any n n n En un tipo “contenedor”: un valor de tipo “any” puede contener un valor de cualquier tipo (predefinido o definido por el usuario) Es seguro (type-safe) y permite instrospección Utilidad: cuando en una operación no se conoce el tipo de uno de sus parámetros en tiempo de compilación (ej.: servicio de eventos) typedef n Permiten crear un nuevo nombre para un tipo typedef short Year; typedef short Temperature; typdef Temperature Temp; // ¡ Mal estilo ! n n Permiten realizar especificaciones más claras Se deben evitar alias innecesarios (ej.: la tercera definición) n n Es confuso Puede causar problemas en los mappings que utilicen reglas estrictas de equivalencia de tipos Enumeraciones n Ejemplo enum Color {red, green, blue, yellow, white}; n A diferencia de C, Color es un tipo y el nombre del tipo es obligatorio typedef enum {red, green, blue} Color; // ¡ Error ! n Los valores de un tipo deben ser únicos dentro de su ámbito enum InteriorColor {red, green, blue}; enum ExteriorColor {blue, yellow, white}; // ¡ Error ! n A diferencia de C++, no se les puede dar valores enum Color {red=0, green=8, blue=16}; // ¡ Error ! Estructuras n Ejemplo struct Date { short year; short month; // 1-12 short day; // 1-31 }; n A diferencia de C, Date es un tipo y el nombre del tipo es obligatorio typedef struct { short year; short month; // 1-12 short day; // 1-31 } Date; // ¡ Error ! Union (1) n Ejemplo union Token switch(Color) { case blue: float floatValue; case red: case green: long longValue; default: string stringValue; }; n n El discriminador tiene que ser un tipo entero, char, boolean o una enumeración A diferencia de C n n Token es un nombre de tipo y el nombre del tipo es obligatorio Utilizan discriminador con case y quizás un default Union (2) n No se debe utilizar default ni más de un case por campo n n Complica algunos mappings (ej.: C++) No se debe utilizar una union para simular sobrecarga ... enum OrderDetailInformationKind {text, numeric, none}; union OrderDetailInformation switch(OrderDetailInformationKind) { case text: string description; case numeric: long index; }; interface Order { void setOrderDetailInformation(in OrderDetailInformation info); }; Union (y 3) n ... es mejor estilo interface Order { void setDescription(in string description); void setIndex(in long index); }; n Uso idiomático: simular operaciones con parámetros opcionales union AgeOptional switch(boolean) { case TRUE: unsigned short age; }; n Consejo general n No usar union Vectores n Se pueden definir vectores de una o varias dimensiones typedef Date DateVector[10]; // Tipo “DateVector” typedef string StringMatrix[10][20]; // Tipo “StringMatrix” n n Se debe usar typedef A diferencia de otros lenguajes, no soporta vectores abiertos (“open arrays”) typedef string StringMatrix[][20]; // ¡ Error ! n Hay que tener cuidado cuando se pasan índices de arrays en invocaciones remotas n Es preciso definir una convención Secuencias n Las secuencias son vectores de longitud variable typedef sequence<Color> Colors; // Secuencia no acotada typedef sequence<Date, 10> Dates10; // Secuencia // acotada (10 fechas como máximo) n n Se debe usar typedef ¿ Cuándo usar secuencias y cuándo usar vectores ? n n n n n Una lista de cosas con número fijo de elementos => vector Una lista de cosas con número variable de elementos => secuencia Cadenas de caracteres de tamaño fijo => vector de char Vectores dispersos (“sparse arrays”) => secuencias Estructuras de datos recursivas => secuencias Tipos de datos recursivos n IDL no tiene punteros, pero permite definir estructuras recursivas struct Node { long value; sequence<Node, 2> children; }; // Un árbol binario n Restricciones n n n Sólo para struct y union La recursividad se expresa con una secuencia anónima del tipo incompleto (recursivo) No se permite recursividad mutua Constantes y literales n Se pueden definir de cualquier tipo predefinido (excepto any) o de un enumerado const const const const const const const const const const const n short MAX_TEMP = 35; short MIN_TEMP = -10; short AVG_TEMP = (MAX_TEMP+MIN_TEMP)/2; float PI = 3.1415926; double MAXIMUM = 1E10; char FIRST_LOWER_CASE_LETTER = ‘a’; char NULL = ‘\0’; string LAST_WORDS = “My God, it’s full of stars !”; octet MSB_MASK = 0x10; // Hexadecimal (tb. en enteros) octet MSB_MASK2 = 0100; // Octal (tb. en enteros) Color FAVORITE_COLOR = blue; Las expresiones pueden utilizar n n +, -, *, /, % (con enteros y reales, excepto %) |, &, ^, <<, >>, ~ (con enteros) Interfaces (1) n Ejemplo interface Thermostat { typedef short Temperature; typedef string Location; exception InvalidTemperature { Temperature selectedTemperature; Temperature minimumPermitedTemperature; Temperature maximumPermitedTemperature; }; Tempeature getTemperature(); void setTemperature(in Temperature value) raises(InvalidTemperature) Location getLocation(); }; Interfaces (y 2) n Un interfaz es un tipo interface Controller { typedef sequence<Thermostat> ThermostatList; ThermostatList findAllThermostats(); Thermostat findThermostatByLocation( in Thermoter::Location loc); }; n Los objetos remotos se pasan por referencia n n En el anterior ejemplo, el cliente obtiene una referencia a un proxy del objeto remoto. ¡¡¡ Los objetos remotos no se mueven !!! Existe la referencia especial “nil” n Utilidad: indicar opcionalidad (pasar “nil” en un parámetro que admita un objeto remoto) o indicar “no encontrado” (findThermostatByLocation) Operaciones (1) n n n Pueden devolver excepciones No se pueden sobrecargar Atributos direccionales n n n n in: el valor del parámetro se lo envía el cliente al servidor out: el valor del parámetro se lo envía el servidor al cliente inout: el valor del parámetro se lo envía el cliente al servidor, quien posiblemente lo modifica, y se lo envía otra vez al cliente (machacando el valor anterior) Estilo n n Si una operación acepta uno o más parámetros in y devuelve un solo resultado => el resultado se debería devolver como valor de retorno Si una operación tiene varios valores de retorno con la misma importancia => usar out para los correspondientes parámetros y void como tipo de retorno Operaciones (y 2) n Estilo n Si una operación retorna varios valores, pero uno de ellos tiene más importancia => devolver este último como valor de retorno y el resto como out boolean getNext(out ValueType value); n Usar los parámetros inout con precaución n n n Se asume que el llamador no quiere conservar el valor Eficientes cuando se pasan valores grandes Ejemplo invertMatrix (inout Matrix aMatrix); Excepciones definidas por el usuario (1) n Ejemplo exception InvalidTemperature { Temperature selectedTemperature; Temperature minimumPermitedTemperature; Temperature maximumPermitedTemperature; }; n n Pueden no tener campos No admiten herencia n n n Complicaría algunos mappings No se pueden usar como campos de tipos definidos por el usuario Si una operación puede levantar varias excepciones, se separan por comas en la cláusula raises void foo() raises(Exception1, Exception2); Excepciones definidas por el usuario (2) n Mal estilo interface DataBase { typedef sequence<Row> RowList; typedef string Query; exception NoRow { Query failedQuery; }; RowList lookup(in Query q) raises(NoRow); }; n n n Puede ser normal que no exista ningún registro que cumpla la query El campo failedQuery no aporta nada ¿ Por qué falló la query ? n ¿ no había filas que cumplieran la condición ?, ¿ no era sintácticamente correcta ?, ¿ falló la base de datos ? Excepciones definidas por el usuario (y 3) n Buen estilo interface DataBase { typedef sequence<Row> RowList; typedef string Query; exception SyntaxError { unsigned short position; }; exception DataBaseInternalError { string explanation; }; RowList lookup(in Query q) raises(SyntaxException, DataBaseInternalError); }; Excepciones del sistema n CORBA define un buen número de excepciones del sistema n n n Cualquier operación puede levantar una excepción del sistema Nunca debe levantarlas el programador Todas las excepciones tienen la siguiente definición enum completion_status { COMPLETED_YES, COMPLETED_NO, COMPLETED_MAYBE }; exception <SystemExceptionName> { unsigned long minor; completion_status completed; }; n Ej.: COMM_FAILURE, NO_MEMORY, INTERNAL, OBJ_ADAPTER, INV_OBJREF, etc. Atributos n Ejemplo interface Thermostat { typedef short TemperatureType; typedef string LocationType; attribute TemperatureType temperature; readonly attribute LocationType location; }; n Los atributos no son el estado del objeto que implemente el interfaz n n n n Sólo definen operaciones para leer y escribir valores Los atributos readonly sólo definen operaciones de lectura Inconveniente: no pueden tener cláusula raises Consejo: no usuarlos Declaraciones incompletas n Ejemplo interface Husband; interface Wife { Husband getSpouse(); }; interface Husband { Wife getSpouse(); }; Herencia (1) n Se permite herencia de interfaz interface Thermometer { typedef short Temperature; typedef string Location; Temperature getTemperature(); Location getLocation(); }; interface Thermostat : Thermometer { void setTemperature(in Temperature t); }; n .. y en consecuencia se soporta el polimorfismo interface Controller { Thermometer findByLocation(in Location l); void add(in Thermometer t); }; Herencia (y 2) n Todos los interfaces derivan implícitamente de Object n n n Un interfaz que deriva de otro no puede volver a declarar (redefinir) una operación del padre n n En un parámetro de tipo Object se puede pasar una referencia a un objeto remoto de cualquier tipo La sintaxis de IDL no permite que un interfaz derive explícitamente de Object No tiene sentido en herencia de interfaz Se permite herencia múltiple interface AmphibiousVehicle : LandVehile, WaterVehicle // ... }; n { No se puede heredar un mismo atributo u operación de más de un interfaz base Módulos n Similares a los namespaces de C++ module es { module udc { module fbellas { module corba { module clock { module idl { struct TimeOfDay { // ... }; interface Clock { TimeOfDay getTimeOfDay(); }; }; }; }; }; }; }; n n Evitan conflictos de nombres Se pueden cerrar y volver a abrir Tipos anónimos (1) n Con CORBA 2.4, los tipos anónimos se consideran desaprobados n n n n Complican mappings Se consideran desaprobados (“deprecated”) En consecuencia, no se deben usar Una mala definición ... struct Order { long identifier; string<100> description; // ¡ deprecated ! }; n Una buena definición ... typedef string<100> OrderDescription; struct Order { long identifier; OrderDescription description; }; Tipos anónimos (y 2) n n Hasta CORBA 2.3 (inclusive) no queda más remedio que usar secuencias anónimas para los tipos recursivos CORBA 2.4 añade declaraciones incompletas para struct y union n Ya no es preciso usar tipos anónimos en ningún caso typedef struct Node; typedef sequence<Node, 2> ChildSequence; struct Node { long value; ChildSequence children; }; Identificadores de repositorio (1) n n n n El compilador de IDL genera un identificador de repositorio por cada identificador Con el identificador se puede acceder a su definición (introspección) por medio del Repositorio de Interfaces ¡ El identificador debe ser único ! Existen tres tipos de formatos de identificadores n n n n IDL DCE UUID (Universally Unique IDentifier) LOCAL El formato IDL es el formato que utiliza el compilador de IDL por defecto y es el usado normalmente Identificadores de repositorio (y 2) n Ejemplo module TCS { // IDL:TCS:1.0 typedef short Temperature; // IDL:TCS/Temperature:1.0 interface Thermometer { // IDL:TCS/Thermometer:1.0 Temperature getTemperature(); // IDL:TCS/Thermometer/getTemperature:1.0 }; }; n ¿ Y qué ocurre si alguien más ha elegido el nombre TCS para un módulo ? #pragma prefix “es.udc” module TCS { // IDL:TCS:1.0 typedef short Temperature; // IDL:es.udc/TCS/Temperature:1.0 interface Thermometer { // IDL:es.udc/TCS/Thermometer:1.0 Temperature getTemperature(); // IDL:es.udc/TCS/Thermometer/getTemperature:1.0 }; }; n Otra posibilidad es usar nombres de módulos del estilo “es.udc....” Ámbito (1) n Crean ámbito (scope) n n n módulos, interfaces, estructuras, unions, excepciones y listas de parámetros Dentro de un ámbito los nombres de identificadores deben ser únicos Ejemplo module M1 { typdef short T1; interface I1 { typedef long T2; // ... }; }; module M2 { interface I2 { void foo(in M1::T1 par1, in M1::I1::T2 par2); }; }; Ámbito (y 2) n Error típico interface Thermostat { typedef short Temperature; exception InvalidTemperature { // ... }; void setTemperature(in Temperature temperature) // ¡ Error ! raises(InvalidTemperature) };