En esta nueva entrega del curso vamos a describir como se afronta la solución a un problema utilizando una perspectiva orientada hacia CORBA. Para ello vamos a describir las fases de anális y diseño de una aplicación, la cual utilizaremos como ejemplo a lo largo del curso. Posteriormente y a partir del diseño veremos que reflejar este diseño dentro de CORBA utilizando el lenguaje OMG/IDL. Para poder este lenguaje por completo utilizaremos el compilador de OMG/IDL a Java de una herramienta de libre distribución conocida como JavaORB, la cual implementa el estandar CORBA 2.3.
Como en todo desarrollo software en la fase de análisis debemos de capturar todo lo que se espera que nuestra aplicación haga. Para ello debemos ir buscando toda la funcionalidad requerida. Inicialmente vamos a describir el escenario de funcionamiento y despues vamos a utilizar casos de uso del sistema para capturar mas requerimientos del sistema.
De cualquier modo esta parte de A&D (Análisis y Desarrollo) no será ni mucho menos formal, ya que el objetivo del curso es aprender CORBA y no A&D de sistemas, un tema demasiado complejo y amplio como para ser cubierto en tan poco espacio. El objetivo de esta sección es dar unas pinceladas de A&D y ver como se ajustan estos procesos a CORBA.
El problema que se nos plantea es: "una organización quiere agilizar las comunicaciones internas entre sus empleados. Para ello está evaluando diferentes soluciones y entre ellas han pensado en un servicio de envío de mensajes síncrono, es decir, en un servicio al que estén de forma contínua conectados sus empleados y por el que se puedan comunicar, en comunicaciones uno a uno, uno a varios o varios a varios".
Partimos de la hipótesis de que todos los empleados tienen un ordenador con el que trabajan, por lo que en principio este va a ser la herramienta a través de la cual se comunicarán.
Al ser una organización grande hay ordenadores de muchos tipos, y según el tipo de trabajo unos empleados trabajan en un tipo de ordenador u otro. En concreto se han detectado: máquinas Linux en la zona de desarrollo software, máquinas MacOS en la zona de diseño y máquinas con Windows en departamentos como los de gestión.
Por lo tanto, la solución que demos debe de funcionar en todas estas plataformas, es decir, los binarios que se generen deben de soportar varias plataformas. Por ello todo apunta a que en los clientes vamos a utilizar Java como lenguaje de desarrollo.
El número de empleados de la empresa en bastante elevado, del orden de 10000 personas, por lo que el tráfico de comunicaciones puede ser muy elevado. Como hay que soportar comunicaciones entre varios empleados simultáneamente parece que vamos a necesitar un servidor central que coordine estas comunicaciones, ya que sino la complejidad en los clientes podría ser demasiado elevada. Dicho servidor deberá soportar una carga elevada de conexiones y nuestra experiencia nos decanta a utilizar C++ como lenguaje de implementación, aunque queremos dejar las puertas abiertas a otros lenguajes.
No se descarta la posibilidad de conexiones directas entre clientes sin tener que pasar por el servidor, para poder evitar congestiones dentro del servidor. Es una aplicación que claramente tiene su mayor complejidad en las comunicaciones. Por ello se ha pensado en el uso de CORBA como "middleware" ya que así CORBA se encargaría de gestionar dichas comunicaciones, permitiendo incluso la conexión de redes con diferentes protocolos.
El servicio ha de ser gestionable, permitiendo definir políticas de conexión y existiendo unos operadores del servicio con una funcionalidad mayor a la de los clientes.
Como resumen del escenario:
Del escenario podemos deducir que en la aplicación van a existir los siguientes objetos:
En el diseño de la solución al problema nos vamos a centrar en definir las interfaces IDL que cubran toda la funcionalidad requerida. Del análisis hemos concluido que necesitamos tres objetos dentro del sistema, por lo que existirán tres interfaces IDL dentro del mismo.
Toda la aplicación en sí pertenecerá a un módulo común, al que llamaremos "Mensajes". Dentro de este módulo se definirán las tres interfaces que hemos detectado.
// Interfaz a implementar por el servidor interface servidor { // Conexion de un operador string conectar_operador (in operador interfaz, in string clave); // Devuelve una cadena con el nombre del servidor string conectar (in cliente interfaz); // Desconecta un usuario del sistema boolean desconectar (in cliente interfaz); // Mira si un usuario esta conectado y recibe la interfaz al mismo cliente presente (in string nombre) raises (comun::ClienteNoPresente); // Devuelve una lista de los usuarios presentes en el sistema void listaUsuarios (out comun::lista_usuarios lista); };
// Interfaz a implementar por el cliente interface cliente { // Función básica de recepcion de mensajes // Devuelve "true" ante recepcion correcta boolean recibeMensaje (in string mensaje); // Mensajes de aviso especiales del servidor boolean recibeAviso (in comun::alarma mensaje); // Mensajes directos a otros usuarios void enviaMensaje (in comun::usuario destino, in string mensaje); };
// El operador es informado de accesos al servicio interface operador:cliente { boolean nuevaConexion(in string nombre); };
Para compartir estructuras de datos comunes entre los tres interfaces lo que hacemos es definirnos un interfaz nuevo llamado "comun", en el que declararemos todas las estructuras de datos compartidas por los diferentes interfaces.
interface comun { // Version de la aplicacion readonly attribute string version; // Nombre de usuario typedef string usuario; // Lista de usuarios typedef sequencelista_usuarios; // Excepcion de usuario no presente exception ClienteNoPresente {usuario nombre;}; struct alarma { unsigned short codigo; string mensaje; }; };
Vemos que gracias principalmente al lenguaje OMG/IDL, a la salida de la fase de A&D tenemos ya modularizado y especificado el sistema. A partir de este momento podemos repartir las interfaces a los distintos grupos de desarrollo. Cada grupo se puede encargar de implementar una interfaz, y si se respetan dichas interfaces, la fase de integración del sistema será poco costosa.
Por otro lado añadir nueva funcionalidad al sistema resulta sencillo. Se identifica a que modulo pertenece dicha nueva funcionalidad, y se añade a su interfaz IDL. Es más, dentro de las interfaces queda perfectamente capturado los criterios de diseño, por lo que no se introducirán cambios que deterioren la arquitectura inicial del sistema.
Resumiento podemos destacar las ventajas:
El lenguaje OMG/IDL es uno de los pilares de la plataforma OMA de OMG, y en especial de CORBA.
La especificación del lenguaje se puede consultar dentro del estándar de CORBA, en el capítulo 3. Son un total de 40 páginas cuyo índice lo podéis observar en la siguiente figura:
Figura 1: Indice del capítulo 3 del estándar de CORBA sobre OMG/IDL
Esta es la fuente definitiva que a la que el lector tiene que acudir y de ella vamos a tomar datos del lenguaje a lo largo de este apartado.
Gracias a este lenguaje descriptivo podemos especificar las interfaces de nuestros módulos CORBA, sin ligarnos a ningún lenguaje concreto.
Además es un lenguaje muy adecuado para el diseño del sistema ya que permite pensar en el diseño del sistema, sin tener que descender a detalles de implementación.
Para evitar ambigüedades a nivel de diseño es un lenguaje fuertemente tipado, al estilo de Java. Es un lenguaje cuya sintaxis está bastante cercana a la de ANSI C++ o Java, al ser orientado a objetos. Tiene facilidades como la definición de módulos e interfaces, herencia de interfaces o excepciones. Es un lenguaje lo suficientemente descriptivo como para poder detallar interfaces de objetos que van a ser distribuidos.
En la siguiente figura podemos observar las palabras claves reservadas en este lenguaje:
Figura 2: Palabras reservadas de OMG/IDL
Pasamos a describir el lenguaje con mayor detalle a continuación, así como ejemplos de los principales traducciones de OMG IDL a lenguajes como C, C++ y Java.
OMG/IDL es un lenguaje sencillo al ser declarativo. Una muestra de su sencillez es que su gramática está compuesta de 82 reglas en total, pudiéndose consultar dicha gramática dentro del capítulo 3 del estándar de CORBA.
La mejor forma de entender el lenguaje es mediante un ejemplo que iremos explicando a lo largo de este apartado. El ejemplo que utilizaremos será la interfaz IDL "Mensajes.idl" resultado de la fase de diseño de la solución que desarrollamos en el apartado anterior. Reproducimos dicha interfaz al completo para que el lector la pueda tener a mano ya que será utilizada a menudo como referencia.
1: // Módulo de intercambio de mensajes entre usuarios 2: module Mensajes { 3: interface comun { 4: // Version de la aplicacion 5: readonly attribute string version; 6: 7: // Nombre de usuario 8: typedef string usuario; 9: // Lista de usuarios 10: typedef sequencelista_usuarios; 11: 12: // Excepcion de usuario no presente 13: exception ClienteNoPresente {usuario nombre;}; 14: 15: struct alarma { 16: unsigned short codigo; 17: string mensaje; 18: }; 19: }; 20: 21: // Interfaz a implementar por el cliente 22: interface cliente { 23: // Función básica de recepcion de mensajes 24: // Devuelve "true" ante recepcion correcta 25: boolean recibeMensaje (in string mensaje); 26: // Mensajes de aviso especiales del servidor 27: boolean recibeAviso (in comun::alarma mensaje); 28: // Mensajes directos a otros usuarios 29: void enviaMensaje (in comun::usuario destino, in string mensaje); 30: }; 31: 32: // El operador es informado de accesos al servicio 33: interface operador:cliente { 34: boolean nuevaConexion(in string nombre); 35: }; 36: 37: // Interfaz a implementar por el servidor 38: interface servidor { 39: // Conexion de un operador 40: string conectar_operador (in operador interfaz, in string clave); 41: // Devuelve una cadena con el nombre del servidor 42: string conectar (in cliente interfaz); 43: // Desconecta un usuario del sistema 44: boolean desconectar (in cliente interfaz); 45: // Mira si un usuario esta conectado y recibe la interfaz al mismo 46: cliente presente (in string nombre) raises (comun::ClienteNoPresente); 47: // Devuelve una lista de los usuarios presentes en el sistema 48: void listaUsuarios (out comun::lista_usuarios lista); 49: }; 50: 51: };
Este ejemplo es bastante completo y nos va a permitir profundizar en diferentes aspectos de OMG IDL.
La primero que observamos es la forma de encapsular en módulos las interfaces. Un módulo debe de ser un conjunto de interfaces que proporcionan una funcionalidad concreta dentro del sistema. En nuestro caso este módulo es el encargado de definir el módulo de Mensajes de una herramienta de trabajo cooperativo.
Dentro del módulo hemos definido dos interfaces: una para los clientes y otra para el servidor central. La arquitectura sigue los esquemas centralizados, es decir, que un servidor central controla la forma de interactuar entre los diferentes clientes. Pero también se pueden obtener las interfaces a otros clientes y comunicarnos con ellos de forma directa.
Comprobamos aquí que CORBA se adapta sin problemas a paradigmas como el de cliente/servidor o de igual a igual sin ningún tipo de problema.
Las funciones se agrupan en interfaces cuando tienen un propósito común. Dichas funciones tienen parámetros de entrada, con sus tipos correspondientes y parámetros de salida. Es necesario especificar el parámetro de retorno, aunque este sea "void".
Es característicos de OMG/IDL que los parámetros que se pasan a la función pueden ser de tres tipos:
Esta característica de CORBA en el paso de parámetros complica la gestión de memoria dentro de la implementación. En los tipos de parámetros "in" la gestión de memoria del parámetro es sencilla: el cliente se encarga de la reserva y liberación de memoria.
Pero en los parámetros "out" y "inout" aparece el problema de que, el cliente puede reservar una cantidad de memoria para el parámetro, cantidad que puede ser modificada por el objeto para que entre en el parámetro la respuesta.
En el estándar se han dividido 20 tipos de parámetros en 6 tipos de paso de parámetros. Por ejemplo, para parámetros de longitud fija (incluyendo las estructuras), el llamante reserva y libera el espacio excepto en el caso de "any".
Las referencias a objetos también son gestionadas en el cliente. Pero por ejemplo, si el parámetro es "inout", la implementación del objeto va a invocar la operación CORBA::Object_release en el valor original para reasignar el parámetro (algo que también afecta en el lado del cliente). Para guardar el valor original de la referencia al objeto, deberemos utilizar CORBA::duplicate antes de invocar la operación.
Hay casos en los parámetros "out" y "inout" en los que la implementación debe de reservar la memoria, pero debe ser liberada en el cliente.
Dentro del estándar estas detallados todos los casos y hay que ser cuidadoso para evitar agujeros de memoria en nuestras aplicaciones.
Los tipos de estos datos son los que solemos encontrarnos en cualquier lenguaje de programación: integer (signed/unsigned long, short), float, double, boolean, octet, any.
También tenemos tipos estructurados como: struct, discriminated union, enumerations, sequence, string y array.
Un tipo especial de datos son los atributos. Un ejemplo de su uso lo encontramos en la línea 5. Los atributos al ser procesados por el compilador de IDL generan una función (get) si son de solo lectura (como en nuestro caso), o dos funciones (get y set) si son de lectura y escritura. El acceso al atributo se ha de realizar de forma obligatoria a través de estas operaciones.
De todos estos tipos de datos quizás merezca mención especial el tipo "any" en el que podemos meter cualquier otro tipo de dato de OMG/IDL. Un tipo de datos muy flexible que no solemos encontrar en los lenguajes usuales.
Por último comentar que existe un tipo especial de operación, aquellas del tipo "oneway". Son operaciones que se invocan sin esperar ningún valor de retorno, no siendo bloqueantes. Por ello en este tipo de operaciones el tipo de retorno ha de ser "void" y todos los parámetros han de ser del tipo "in". Un ejemplo de este tipo de operación lo observamos en la línea 27, donde el servidor envía las alarmas a los clientes, sin esperar que estos le respondan nada.
Algo importante en la invocación de operaciones sobre objetos CORBA es que, aunque para el desarrollador sean invocaciones comunes sobre objetos, el mecanismo para sus ejecución es complejo: han de pasar por los cabos del cliente, por el ORB, por el adaptador de objetos, encontrar el objeto adecuado, viajar por los cabos del servidor, realizar la invocación sobre el objeto y recorrer el mismo viaje de vuelta.
Es sencillo que en todo este trasiego puedan aparecer problemas. Y para informar al cliente de dichos problemas aparecen las excepciones. Ante problemas en la invocación de estas operaciones, el ORB nos puede devolver excepciones de diferentes tipo, según el problema aparecido.
Pero es más dentro de nuestro código también podemos crear excepciones para la gestión de errores, o para comunicar situaciones excepcionales. Tal es el caso de la función de la línea 39, que en el caso de que un usuario no este conectado lo devuelve como una excepción.
La herencia es un mecanismo de reutilización de funcionalidad. En nuestro caso la utilizamos para definir la interfaz de un operador, que es un cliente con una operación más, la de recibir información de conexiones.
Al no tener el lenguaje C objetos ni excepciones, en el mappping de OMG/IDL a C en todas las operaciones aparecen el objeto sobre el que se va a invocar, y una variable de contexto para recoger información sobre las excepciones. La carencia en C de espacios de nombres obliga a nombres de la forma "CORBA_object" y con ello, aparece el problema de posibles colisiones de nombres. Un ejemplo de mapping sencillo podría ser:
interface ejemplo { long operacion (in string arg); };
Este interfaz en IDL genera en C (partes relevantes):
typedef CORBA_Object ejemplo; extern CORBA_long operacion ( ejemplo o, CORBA_string arg, CORBA_Enviroment *ev);
Queda patente del ejemplo como en cada operación, hay que especificar el objeto sobre el que se va a ejecutar la operación, así como el entorno por el que se pasarán las excepciones.
Los tipos básicos no se traducen directamente a tipos en C, ya que los tipos en C pueden variar de una arquitectura a otra (número de bits, forma de ordenación de los bytes ...). Por ello por ejemplo el tipo IDL "long" se mapea a "CORBA_long" en C, y no a un long directamente.
Para traducir el mecanismo de herencia lo que se hace, como en C no existe la herencia, es incluir dentro de la traducción todos los elementos de la interfaz, y todos los de las interfaces de los que hereda.
Como ya dijimos el manejo de excepciones se realiza utilizando variables de entorno "CORBA_Enviroment". Este mismo mecanismo es el que se utiliza en el caso de los compiladores de C++ que aún no tuvieran soporte para excepciones.
La traducción de OMG/IDL es más directa a C++ que a C, a pesar de que en cuando se publicó el mapping de OMG/IDL a C++ aún no estaba aprobado el estándar ANSI C++, lo que obligó a dar rodeos para aspectos (p.e. string) que hoy serían mucho más sencillo.
Siguiendo con el mismo ejemplo anterior:
interface ejemplo { long operacion (in string arg); };
Este interfaz en IDL genera en C++ (partes relevantes):
class ejemplo : virtual public CORBA::Object { virtual CORBA::Long operacion( const char* arg ) = 0; }
Todos las interfaces se convierten en objetos CORBA por el mecanismo de la herencia, hecho que muestra que todos los objetos CORBA tienen una funcionalidad común, con operaciones como:
Recordemos que los objetos CORBA en nuestra aplicación son tipos abstractos, es decir, no podemos interpretar su representación. Estas operaciones comunes nos permiten copiar referencias, ver si una referencia apunta a un objeto válido, o hacer un "casting" de particularización.
Normalmente C++ mos proporciona el "casting" de generalización. Con la operación de _narrow podemos transformar un "CORBA::Object" en un objeto concreto.
Como vemos la operación de la interfaz es definida como una operación abstracta pura, es decir, que para implementar este objeto estamos obligados a implementar esta operación. Normalmente para implementar la interfaz lo que se hace es heredar de la clase ejemplo (class ejemploImp:virtual public ejemplo) e implementar la operación.
Como comentamos en la introducción a IDL, uno de los problemas a la hora de desarrollar con CORBA era la gestión de memoria, en concreto con los parámetros "inout" y "out".
En C++ este problema se alivia un poco gracias a que los tipos de datos que no son básicos (long, short, float, double) reciben un tratamiento especial. Un tipo "T" reciben traducción sobre "T" y "T_var". El tipo "T_var" incluye mecanismos de gestión automática de memoria, por lo que si utilizamos estos tipos, nos vemos liberados de reservar y liberar memoria.
Dentro de los ejemplos profundizaremos más en el uso de IDL en C++, como se implementan los interfaces y como se usan los objetos.
El mapping a Java es el último que se produjo, finales de 1997, y es quizás el más directo de todos, debido al soporte que da Java a la programación orientada a objetos, el uso de interfaces, ...
Tomando el ejemplo anterior tenemos:
interface ejemplo { long operacion (in string arg); };Este interfaz en IDL genera en Java (partes relevantes):
public interface ejemplo extends org.omg.CORBA.CORBject { int operacion(java.lang.String arg); }Este ejemplo es muy parecido al de C++, pero como Java tiene la construcción sintáctica "interface" la traducción es todavía más inmediata.
Uno de los problemas que presenta Java es que no soporta la herencia múltiple. Esto obliga a que por ejemplo, si la clase que implementa la interfaz ya hereda de alguna otra, no podemos heredar de nuevo de la clase ejemplo (no heredamos directamente de la interfaz "ejemplo" si no de la clase "ejemploStub").
Cuando OMG definió como se pasaba de IDL a Java, Java estaba ya prácticamente estandarizado por lo que el "mapping" fue sencillo.
Los problemas de gestión de memoria aquí no aparecen ya que, Java gestiona de forma automática la memoria con el recolector de basura.
Hasta el momento hemos hecho una introducción a como se traduce de OMG/IDL a C, C++ o Java. Hemos visto que el soporte de objetos de C++ y Java facilita su traducción.
A continuación pasamos a describir las herramientas básicas que utilizaremos para desarrollar los ejemplos, para a continuación pasar a programar utilizando Java.
Figura 3: Distributed Object Group
JavaORB, desarrollado por el DOG, es una herramienta que nos permite desarrollar utilizando CORBA 2.2, por lo que tiene implementados sistemas como POA.
Lo primero que debemos tener en nuestra máquina GNU/Linux es tener instalada la versión de JDK 1.1.x, siendo recomendable la JDK 1.1.7.
El JDK para GNU/Linux se puede obtener de http://www.blackdown.org. Es importante no utilizar la versión JDK 1.2 ya que esta incluye su propio ORB y hay que configurar JavaORB de una forma especial para que no colisionen ambos ORBs. Esto es posible y se detalla dentro de la página web de JavaORB, pero no se va a cubrir en este artículo.
Una vez instalado el JDK 1.1 en nuestro sistema la instalación de JavaORB es muy sencilla. Lo primero que debemos hacer es ir al servidor de web del grupo que lo ha desarrollado (http://www.multimania.com/dogweb) y obtener el fichero "JavaORB_1.2.4.zip" que constituye la implementación de CORBA. Atención que cuando el lector acceda a este web pueden existir versiones nuevas, y su instalación aunque muy parecida a la que aquí se describe, puede diferir.
Tras ello ejecutamos la orden:
unzip JavaORB_1.2.4.zip
Una vez descomprimido el fichero nos vamos al directorio "Release/JavaORB" y dentro de él tenemos en el fichero "README.txt" las instrucciones de instalación. Básicamente, si no utilizamos JDK 1.2, en cuyo caso el lector debe leer dicho fichero, lo único que hay que hacer es añadir al CLASSPATH el fichero "JavaORBv1_2_4.jar". El CLASSPATH es una variable de entorno que utiliza el JDK para encontrar las clases.
Una vez hecho esto ya disponemos de todo lo necesario para desarrollar aplicaciones CORBA basadas en Java.
En nuestro caso lo único que vamos a hacer de momento es utilizar el compilador de IDL que proporciona la herramienta, para que el lector pueda empezar a trabajar con el lenguaje IDL y comprobar que utiliza de forma correcta el lenguaje.
Para utilizar dicho compilador encontramos dentro del directorio "Release/JavaORB/bin" el ejecutable "idl2java" (al ser una distribución para entornos MSDOS estos ficheros no tienen permiso de ejecución. El lector debe modificar esto con la orden "chmod 755 idl2java").
Este ejecutable lo que hace es lanzar un programa Java que es el que pasa del lenguaje IDL a Java. El compilador genera tanto los cabos (stubs) del cliente como los esqueletos (skeletons) del servidor. También genera las interfaces que debe implementar el servidor.
Pero de momento no vamos a entrar en estos detalles y nos vamos a centrar en el compilador "idl2java". Para su uso, definimos nuestras interfaces en un fichero ".idl", en nuestro caso "Mensajes.idl", y ejecutamos:
idl2java Mensajes.idl
Para ello puede el lector crearse un directorio "idl" dentro de su cuenta, copiar allí el fichero Mensajes.idl y poner en el PATH el directorio donde se encuentra el ejecutable "idl2java".
Si no utilizamos ningún parámetro del compilador, el resultado de dicha invocación es la creación de un directorio "corba_pkg" con todas las clases necesarias para los clientes y servidores CORBA.
Todas las opciones disponibles del compilador son:
bash-2.01$ idl2java ################################################# # Java ORB # # ----------------- # # (c) 1997, 1998, 1999 # ################################################# # Java IDL Compiler, Release 1.6 # ################################################# Options ------- -release Show version number -nopackage Don't use corba_pkg directory as a package -outdir: Provide a way to specify the ouput dir. This option will not use the corba_pkg directory. For example : idl demo.idl -outdir:/home/me/ -package: package_name Generate files in package_name Example: idl demo.idl -package:exemple -I Allow specification of include directory Example: idl demo.idl -I/home/me/idl -I../autre -D Define a symbole. It is equivalent to #define -nostub Don't generate stub. -noskeleton Don't generate skeleton. -tie Generate TIE files to delegation mode. -user Generate code for user -pidl Consider the IDL file as PIDL description. It generates mapping but no stub, no skeleton. -native: native name = native mapping Define native type mapping. For example : idl demo.idl -native:cookie=java.lang.Object this command implies the mapping of cookie in java.lang.Object. -poa Generate skeleton for POA
De ellas destacar el parametro "-poa" que generará los esqueletos necesarios para utilizar POA como adaptador de objetos de CORBA. Las demás opciones serán descritas a lo largo de las próximas entregas del curso.
Quizás el lector quiera comenzar a utilizar un interfaz más sencillo e ir poco a poco complicándolo. Un buen punto de partida es:
interface echo { string repite (in string mensaje); };
Para el lector avanzado podemos indicarle que consulte el fichero "corba_pkg/echoOperations.java" donde encontrará la interfaz Java que es traducción directa de la interfaz OMG/IDL.
A lo largo de este artículo hemos podido ver la importancia del lenguaje OMG/IDL.
Todo proceso de diseño software orientado a objetos debe acabar definiendo de forma clara las interfaces de comunicación de los objetos de la aplicación. Y estas se describen en el mundo CORBA utilizando el leguaje OMG/IDL.
OMG/IDL es un lenguaje sencillo, basado fundamentalmente en Java, y con algunas extensiones para reflejar mecanismos de comunicación. A partir de él se obtiene de forma automática los cabos (stubs) y esqueletos (skeletons) que permitirán a nuestros clientes y servidores comunicarse a través del ORB.
Hemos visto por último la herramienta JavaORB, una implementación de CORBA 2.2 que incluye un compilador de IDL a Java con el que podemos empezar a trabajar.
Por fin en la próxima entrega del curso empezaremos a desarrollar una aplicación CORBA, una vez sentados los principios de CORBA. Será un ejemplo sencillo pero que demostrará la potencia de esta arquitectura para desarrollo distribuido de objetos.