Alvaro del Castillo San Félix
Distribuido bajo licencia FDL
Contribuciones

CURSO PRÁCTICO DE CORBA EN GNU/LINUX

Introducción

En al anterior número de la serie del curso de CORBA se anunciaba para esta entrega un ejemplo en C++ de una aplicación CORBA y la presentación de nuevos servicios de CORBA. Sin embargo, la publicación del primer libro para el desarrollo de aplicaciones para GNOME utilizando CORBA ( noticia aparecida en Barrapunto [3]), me ha parecido tan interesante que he creído más conveniente mostrar en este artículo como se utiliza CORBA en GNOME para desarrollar aplicaciones, y cómo se utiliza CORBA dentro del mundo de los componentes.

Este artículo está basado en el libro de Mathieu Lacage y Dirk-Jan C. Binnema titulado "GNOME y CORBA" que el lector podrá obtener en la referencia [1]. Se hará menos incapié en CORBA de lo que se hace en el libro, y nos centraremos en el uso de CORBA dentro de GNOME, en concreto en cómo CORBA lográ que todas las aplicaciones de GNOME se puedan comunicar entre ellas y, gracias a Bonobo, incluso una aplicación puede contener a otra y controlarla.

Este ejemplo práctico del uso de CORBA en GNOME cubre casi todos los aspectos de la arquitectura CORBA y abre puertas a una metodología de desarrollo nueva, abierta y multiplataforma, gracias a las interfaces IDL que definen todo el sistema. En cierto modo se va dejando e mundo de las cabeceras de C para pasar al mundo de las interfaces IDL, que son en la mayoría de los casos mucho más fáciles de entender.

Para desarrollar con GNOME vamos a necesitar lo más nuevo en cuestión de software, ya que hasta hace muy poco este desarrollo era complejo y las interfaces de programación cambiaban de forma incompatible. Por ello se recomienda al lector que intente seguir el artículo desde un distribución como Linux Mandrake 6.1, que se entregó en Linux Actual.

CORBA en GNOME

Ya comentamos en anteriores entregas de la serie la exitencia de ORBit, una implementación de CORBA 2.2 muy ligera y desarrollada en C. Esta implementación se desarrollo en exclusiva para GNOME ya que las necesidades de GNOME eran muy específicas: necesitaba una implementación de CORBA reducida y muy rápida ya que se incluye dentro de la mayoría de aplicaciones de GNOME. Cuando se comenzó a desarrollar GNOME se disponía de MICO pero su lentitud a la hora de ser utilizado dentro de las aplicaciones y la cantidad de recursos que necesitaba, forzaron a los desarrolladores de GNOME, muchos de ellos con máquinas poco potentes, a abondonar su uso y desarrollar su propia implementación.

El principal problema de ORBit a la hora de empezar a manejarlo es que utiliza C orientado a objetos, al igual que lo hace GTK, y la sintaxis inicialmente es un tanto engorrosa. Esta sensación inicial se vence tras los primeros programas, y ORBit incluye facilidades para ahorrarnos el tener que teclear más de los estríctamente necesario, siendo capaz de generar código de ejemplo al que lo único que le falta es rellenar la implementación de las funciones de las interfaces IDL.

A parte de las funcionalidades del propio ORBit, se ha desarrollado una librería para facilitar el uso de CORBA dentro de GNOME, conocida como "libgnorba" y que describimos en el siguiente apartado.

La librería de CORBA de GNOME: libgnorba

Para facilitar el uso de CORBA dentro de GNOME se ha desarrollado una librería que intenta encapsular la mayor parte posible de CORBA, con el objetivo de facilitar la vida del desarrollador. Esta librería es "gnorba" y recubre principalmente las llamadas de inicialización de CORBA que se encargan de inicializar el ORB y de resolver las referencias a los servicios predefinidos dentro del ORB.

Inicialización de CORBA en GNOME

Quizás este es un buen momento para que el lector acuda a la anterior entrega de este curso con el objetivo de no perderse. Vamos a recordar estos conceptos de inicialización: Unas reglas inciales del uso de C orientado a objetos dentro de CORBA podrían ser: ORB es un objeto de la clase CORBA y la sintaxis para llamar a funciones de este objeto es "CORBA_ORB_nombre_funcion". Cuando queramos aplicar una función sobre un objeto este objeto se le pasará como parámetro a la función. Y para el tratamiento de las excepciones habrá que pasar en toda llamada a función CORBA un parámetro en dónde se guararán las posibles excepciones. De cualquier forma en los ejemplos tendremos tiempo de analizar en concreto esta sintaxis.

Prosigamos con la librería "gnorba" de GNOME. Cuando queríamos que un servidor CORBA se quedara a la espera de los clientes, finalizabamos al servidor en un bucle infinito (CORBA_ORB_run()) desde el que se trataban todos los accesos al servidor CORBA.

La librería GTK, con la que se crean las interfaces gráficas en GNOME, también acaba sus programas en un bucle final, en el que se reciben los eventos del usuario sobre la interfaz gráfica (pulsaciones de un botón, arrastre de una ventana ...).

Está claro pues que un programa que utilice GNOME debería de terminar en dos bucles infinitos, algo que no es posible. La librería "gnorba" se encarga de encapsular estos dos bucles e integrar el bucle de CORBA dentro del de GTK en la llamada a la función "gnome_CORBA_init". Veamos un ejemplo de su uso:

#include 
#include 
#include 

 int main (int argc, char **argv)
 {
   CORBA_ORB orb;
   CORBA_Environment ev;

   CORBA_exception_init (&ev);
   orb = gnome_CORBA_init ("ejemplo de inicialización de ORB y GTK", 
                           "v 1.0 Barrapunto - acs", 
                           &argc, 
                           argv,
                           GNORBA_INIT_SERVER_FUNC, 
                           &ev);

   return 0;
 }
Vemos que se utiliza la librería "orbit", "gnorba" y "gnome" la cual a su vez utiliza GTK. La llamada a la función "gnome_CORBA_init" nos devuelve el ORB con el que vamos a trabajar a partir de este momento.

Para poder compilar los ejemplos de este artículo es indispensable tener instalada una distribución con un GNOME actualizado y con los paquetes de desarrollo instalados. Para compilar estos ejemplos se va a utilizar Mandrake 6.1 que se publicó en Linux Actual. Para compilar este pequeño programa se puede utilizar la línea:

gcc `gnome-config --cflags gnorba` ej1.c
Aquí vemos una utilidad muy potente a la hora de compilar que nos evita el uso de complejos Makefiles. Con "gnome-config" podemos obtener los distintos "flags" necesarios a la hora de compilar y enlazar con las distintas librerías de gnome. En este caso, como sólo compilamos y no enlazamos, le decimos que nos de los "flags" necesarios para poder compilar un programa que utiliza la librería "gnorba", cuyo resultado es: "-I/usr/include -DNEED_GNOMESUPPORT_H -I/usr/lib/gnome-libs/include -I/usr/lib/glib/include". Las librerías GTK y GLIB también nos proporcionan utilidades similares.

Servidor de Nombres en GNOME

Como ya vimos en anteriores entregas el servidor de nombre CORBA nos permite acceder a las referencias a los objetos por medio de un nombre. Es una especie de DNS pero para los IOR ( identificador de objeto interoperable). Un problema que tiene ORBit es que no se puede acceder actualmente al servidor de nombres a través de la función del ORB "CORBA_ORB_resolve_initial_reference". Y un segundo problema es cómo se arranca el servidor de nombres CORBA de forma automática cuando se necesite.

De nuevo "libgnorba" viene en nuestro auxilio y se encarga de resolver ambos problemas. Utilizando la llamada "gnome_name_service_get" obtenemos la referencia al servidor de nombres en el caso de que el servidor esté arrancado. En el caso de que no lo esté, esta llamada lo arranca de forma automática y nos devuelve el IOR del servidor de nombres arrancado.

Por lo tanto el programador es liberado de ambas labores y para el casi transparente el cómo se gestiona el servidor de nombres. El sabe que a esta referencia puede pedirle que le devuelva la IOR de otros objetos a partir de un nombre.

GOAD: Demonio de Activación de Objetos en GNOME

Dentro de "libgnorba" también tenemos unos API que nos permiten obtener un listado de todos los servidores CORBA disponibles en el sistema, y podemos activarlos y desactivarlo en función de nuestras necesidades.

Con la propia librería viene una utilidad conocida como "goad-browser" que permite acceder al conjunto de servidores registrados dentro del sistema. En la siguiente figura vemos la información que nos proporciona dicha utilidad.

Figura 1: El visualizador de servidores GNOME

En la primera columna vemos el identificador del servidor, si está activo o no lo está (desde aquí lo podemos activar) y el identificador de la interfaz IDL que describe al servidor. Por ejemplo, si activamos el "control_center" nos aparecerá la ventana que nos permite configurar todo el entorno gnome. En cierto modo el "goad-browser" lo podemos ver como un lanzador de aplicaciones.

Figura 2: Centro de Control de GNOME

Pero la verdadera potencia está en la API de acceso a GOAD, muy sencilla. De ella presentamos las dos funciones principales de la misma (dentro de gnorba.h está la API completa).

Tabla 1. API de GOAD
Nombre funciónUso de función
goad_server_list_getLista de servidores registrados en GOAD
goad_server_activateActiva un servidor CORBA de GOAD

Cuando desarrollemos un programa en CORBA, si necesitamos la funcionalidad de otro que esté presente en el sistema y registrado dentro de GOAD, podremos activarlo a través de esta interfaz de forma muy sencilla. Y además, podremos acceder a todas la funcionalidad que exporte por su interfaz IDL, tal y como se haría en cualquier servidor CORBA en otro entorno. De esta forma empezamos a ver la potencia y sencillez que aporta CORBA a un entorno gráfico como GNOME, y esto aún es sólo el principio, la verdadera potencia llegará con el mundo de los componentes y Bonobo.

Si una vez que hemos desarrollado nuestro programa creemos que puede ser útil registrarlo como un servidor CORBA dentro de GOAD, lo debemos de dar de alta dentro del directorio "/etc/CORBA/servers" (en Linux Mandrake 6.1). Para ello añadimos en este directorio un nuevo fichero con el nombre "nombre_servidor.gnorba". Por ejemplo, si nos fijamos de nuevo en el Centro de Control el fichero que lo describe es "gnomecc.gnorba" y su contenido es:

[control_center]
type=exe
repo_id=IDL:GNOME/control_center:1.0
description=GNOME Control Center
location_info=gnomecc
El fichero como vemos es autodescriptivo: gnomecc es un ejecutable cuya interfaz IDL es "IDL:GNOME/control_center:1.0".

Conclusiones de libgnorba

Gracias a esta librería podemos evitar temas siempre espinosos y de poco interés para nuestra aplicación final: la inicialización de CORBA, la activación y acceso al servidor de nombres de CORBA y el control de registro de servidores CORBA y su activación. El lector que tenga más presente POA posiblemente piense que el GOAD es algo que se podría implementar con POA y los "servant manager". Es cierto, pero los desarrolladores de GNOME entienden que esta solución es más sencilla. Quizás como no tienen el problema de la portabilidad entre ORBs, este tipo de aspectos los prefieren solucionar "ad-hoc" (a medida) para su caso concreto.

El escritorio GNOME

Ha llegado el momento de ver cómo todas las piezas del sistema se combinan entre sí. Y son todas esta piezas coordinadas lo que se conoce como el escritorio de GNOME, un sistema en el que varias aplicaciones se unen entre sí con el fin de dar un conjunto de funcionalidades de forma gráfica al usuario del sistema.

La mayoría de las definiciones de las interfaces de las aplicaciones GNOME se encuentran en el directorio "/usr/share/idl".

Entre las interfaces IDL que se definen destacan:

Por ejemplo, si quisieramos crear un entorno de desarrollo para GNOME, lo que hacemos es crear el núcleo que interacciona con el editor de textos, el compilador, el depurador etc. a través de las respectivas interfaces IDL de cada módulo. Nuestro entorno a partir de este desarrollo podrá trabajar con diferentes editores y compiladores de diferentes lenguajes sin mas que cambiar el objeto al que accedemos para que haga cada uno de los papeles. Es la flexibilidad llevada a su máxima expresión.

Desde cualquier aplicación nuestra dentro del entorno de GNOME podemos acceder a todos los servicios que nos ofrecen estas interfaces. No hay que olvidar que el tener las interfaces IDL nos permite poder acceder a los objetos que las implementan. Vemos que la unión entre todos los elementos de GNOME se consigue en gran medida gracias a CORBA.

Para que el lector se pueda hacer una idea de cómo están definidas estas interfaces en el siguiente cuadro se puede ver la de "help-browser.idl".

module help_browser {
    interface simple_browser {
        void           fetch_url(in string URL); // muestra URL en el navegador
actual 
        simple_browser show_url (in string URL); // abre navegador y muestra
URL 
    };
};
Interfaz help-browser.idl en GNOME

El lector, si ha seguido la serie, no tendrá ningún problema en entender con detalle la interfaz. Recordemos que si quisieramos acceder a la interfaz de ayuda, a partir de este IDL y utilizando el compilador IDL de ORBit (orbit-idl), generamos los cabos (en lenguaje C) en este caso, los cuales incluimos en nuestro programa y ya podemos utilizar el sistema de ayuda integrado de GNOME. Para localizar el objeto que implementa esta interfaz, utilizamos el servidor de nombres y buscamos la IOR del objeto cuyo nombre sea "gnome-help-browser". Una vez que tenemos la referencia al objeto ya sabemos lo sencillo que es utilizar el objeto con simples llamadas sobre esta referencia.

Realmente todo este proceso se simplifica algo gracias a las librerías de GNOME, pero las ideas que están detrás siguen siendo las mismas.

Bonobo: El modelo de componentes

Si lo que hemos visto hasta ahora puede haber dejado gratamente sorprendido al lector por la facilidad del uso de los programas existentes en GNOME, gracias a CORBA, el mundo de Bonobo es mucho más espectacular y gran parte del futuro del software libre, y su equiparación al software cerrado, está en la creación y uso de componentes de la forma más sencilla y flexible posible.

Miguel de Icaza, lider del proyecto Bonobo, cuando buscaba que modelo de componentes a utilizar en GNOME, encontró en OLE2 de Microsoft una especificación documentada de un modelo de componentes como el que quería, por lo que decidió adoptarlo dentro de GNOME. Miguel ha defendido está arquitectura dentro de la comunidad del software libre con éxito, a pesar de estar realizada por Microsoft.

No deja de ser curiosa la combinación de utilizar como "middleware" CORBA y como modelo de componentes OLE2. Recordemos que la plataforma de software distribuido de Microsoft también utiliza OLE2 como modelo de componentes, aunque como "middleware" utiliza COM, el principal competidor de CORBA.

La idea es que un desarrollador disponga entre sus herramientas de trabajo de una paleta de componentes lo más amplía posible, algo que ya es una realidad dentro de Visual Basic o en el mundo de Java con los beans.

Una vez que dispongamos de esos componentes, organizados por paletas de diferente funcionalidad, el desarrollador tan sólo tendrá que crear de cero aquella funcionalidad que no encuentre en los componentes. De esta forma, las aplicaciones pasan a sr un grupo de componentes que colaboran entre sí. Y, ¿cómo sabemos que nos ofrece un componente? Muchas veces esta información nos la da la documentación, pero si dichos componentes tienen interfaces IDL, estas interfaces son la documentación más exacta y útil para el desarrollador, aparte de ser independientes del lenguaje y plataforma de desarrollo. Y esta información puede ser utilizada por herramientas de generación automática de código o para mostrar las funciones dentro de las propiedades de un componente.

Dentro de GNOME parece que el entorno de desarrollo basado en componentes va a ser GLADE, cuya interfaz gráfica la podemos observar en la siguiente figura.

Figura 2: Paleta de componentes de GLADE

La idea es ir tomando estos componentes y de forma visual y construyendo la interfaz gráfica de nuestra aplicación. Una vez construida la interfaz gráfica se genera el código, que en muchos casos será compilar las interfaces IDL de los componentes y generar los cabos para que podamos acceder a la funcionalidad del componente desde nuestro programa. Este código se podrá generar fácilmente en diferentes lenguajes. Sólo hace falta que ORBit tenga un compilador al lenguaje deseado, ya que CORBA se encarga de que lo demás funciones. De esta forma, la herramienta de desarrollo no nos obliga a utilizar ningún lenguaje y cada uno puede trabajar con el que se sienta más cómodo y sea más adecuado en su proyecto. En la actualidad aún en GLADE no está disponible esta funcionalidad, pero seguro que en los próximos meses la veremos aparecer.

Este modelo de desarrollo es muy parecido al mundo de los componentes de Microsoft, donde los componentes al instalarse en el sistema se registran dentro del Registro del sistema (en GNOME es el GOAD) y desde las herramientas de desarrollo visuales, se pueden utilizar estos componentes, cuya interfaz se define en el IDL de Microsoft, muy similar al de CORBA.

Dentro de GLADE el aspecto que puede tener una interfaz gráfica en desarrollo lo podemos observar en la figura 3.

Figura 3: Ventana desarrollo de interfaz gráfica en GLADE

Bonobo: Desarrollo de componentes

Una de las ideas de los componentes es crear un entorno de trabajo centrado en los documentos. Cuando trabajas en un informe y necesitas insertar una hoja de cálculo, el entorno nos da la posibilidad de introducir dentro de nuestro procesador de textos (un contenedor de componentes) una hoja de cálculo, un componente, y utilizar todas las funcionalidades de la aplicación de hoja de cálculo dentro de nuestro procesador de textos. Un ejemplo de este modelo es como Word incluye hojas de cálculo Excel.

De cara al usuario todo es transparente pero el hacer posible este modelo requiere de unas infraestruturas bastante potentes, que en GNOME las proporciona Bonobo.

Bonobo es un conjunto de interfaces CORBA (definidas en OMG/IDL) que definen lo que es un componente empotrable dentro de un contenedor (contenido) y lo que es un contenedor. Además, Bonobo incluye una implementación por defecto, en C, de estas interfaces para evitar al programador el trabajo de tener que escribir en cada nuevo componente o contenedor el mismo código.

Todas las interfaces de CORBA heredan de "GNOME::Unknown", una interfaz que nos permite preguntar a un componente cuáles son las interfaces que implementa y permite llevar cuentas de cuantas veces ha sido referenciado un componente para poder controlar el momento de su destrucción. Aquí vemos la definición en IDL de dicha interfaz.

module GNOME {
         interface Unknown {
                 void ref ();

                 void unref ();
                 
                 Object query_interface (in string repoid);
         };
 };
Es muy importante que cada vez que obtengamos una referencia a un objeto Bonobo llamemos al método "ref" de ese objeto. De esta forma la implementación del objeto sabe cuantas veces ha sido referenciado, y hasta que este número no llegue a cero, sabe que no se puede eliminar dicho objeto. Dentro de CORBA no hay ningún mecanismo para llevar este control sobre el ciclo de vida de un objeto, por lo que hay que descargar en el programador esta responsabilidad. De igual modo, cuando no vayamos a utilizar más esta referencia, debemos de llamar al método "unref", que puede llegar a provocar la liberación del objeto y que nuestra aplicaciónn ocupe menos recursos.

Un objeto Bonobo puede implementar varias interfaces, y por lo tanto, puede ser visto de distintas formas dentro de las aplicaciones, dependiendo de por la interfaz por la que se acceda a él. Cuando obtenemos una referencia a un objeto Bonobo a través del método "query_interface" podemos pasarle una interfaz. El objeto Bonobo, si la implementa, nos devolverá una referencia CORBA para poder acceder a la implementación de dicha interfaz. En el caso de que no la implemente nos devuelve el objeto nulo.Veamos un pequeño ejemplo de su uso.

CORBA_Object obj_unknown;
CORBA_Object obj_embeddable;

obj_unknown = CosNaming_NameService_resolve (orb, "Word97", &ev);

obj_embeddable = GNOME_Unknown_query_interface 
                   (obj_unknown, "IDL:GNOME/embeddable:1.0", &ev);
Lo primero que hacemos es acceder al servicio de nombrado y obtener el IOR del objeto cuyo nombre es "Word97". Como puede observar el lector, a esta función se le pasa el objeto orb y se utiliza la variable "ev" para almacenar las posibles excepciones que puedan aparecer en esta llamada. El lector que quiera comprender en profundidad esta sintaxis puede acudir [1] donde se explica con bastante detalle la traducción de OMG/IDL a C.

Una vez que ya tenemos una referencia a un objeto Bonobo, invocamos sobre él el método "query_interface" para comprobar si implementa la interfaz "IDL:GNOME/embeddable:1.0", es decir, si es un objeto empotrable en un contenedor. Si lo implementa, obtenemos una referencia a la implementación de esta interfaz por el objeto y la podemos utilizar para controlar la inserción del objeto dentro del contenedor de nuestra aplicación.

Uno de los usos más flexibles de esta interfaz "Unknown" es que permite que un objeto sea actualizado sin que afecte para nada a todos los clientes que ya existían de dicho objeto. Basta con mantener en el objeto las interfaces antiguas junto a las nuevas. De esta forma la actualización de objetos siempre asegura la compatibilidad hacia atrás aunque eso sí, hay que tener cuidado de no acabar con interfaces enormes y poco manejables.

Dentro de Bonobo se ha hecho todo lo posible para evitar que el desarrollador tenga que aprender CORBA en profundidad, aunque los conocimientos básicos son necesarios para entender lo que estamos haciendo. Buscando esta facilidad se creó el objeto "GnomeObject", que está basado en el objeto raíz GTK, y que implementa por defecto la parte de funcionalidad de un componente Bonobo que es siempre muy parecida. Este objeto "GnomeObject" es básicamente un recubrimiento de un "servant" de POA al que se le une la implementación de la interfaz "Unknown". Recordar al lector que un "servat" en CORBA era el código encargado de implementar una interfaz IDL, y que es un concepto perteneciente a POA, al adaptador de objetos portable de CORBA.

Hasta aquí hemos llegado en esta presentación del uso de CORBA pero en el siguiente apartado damos al lector algunas pistas de por dónde continuar para profundizar en el desarrollo de aplicaciones GNOME.

Por dónde seguir

Debido a la extensión que nos llevaría una descripción completa de un contenedor y un componente empotrable no podemos completarla en el presente artículo, y quizás sería ya necesario dedicar una serie completa a este tema.

Si el lector se siente con ganas de seguir avanzando en el desarrollo de aplicaciones GNOME, conociendo las tripas del sistema, algo siempre recomendable, los pasos a seguir son leer los capítulos 7 y 8 de la referencia [1] donde se explica cómo se construye un componente embebible desde cero, y como se construye un contenedor.

Una vez que tengamos claro esto podemos pasar a ver cómo se guarda el estado de una aplicación que trabaja con componentes, tema delicado ya que hay que ir componente por componente guardando su estado, de tal forma que cuando arranquemos de nuevo la aplicación y volvamos a la versión guardada del mismo, todo quede en el mismo estado en el que lo habíamos dejado. Lo que se hace básicamente es definir unas interfaces para dar soporte de persistencia, es decir, que el objeto que implemente estas interfaces es capaz almacenar y recuperar su estado. Utilizando sobre todos los componentes que componente nuestra aplicación esta interfaz, podemos almacenar el estado del conjunto de la aplicación de una forma estándar.

Conclusiones

El mundo del software libre estaba muy por detrás del mundo de software cerrado en el mundo de los componentes. Esta distancia se ha acortado de forma espectacular con GNOME y su modelo de componentes Bonobo.

La combinación de CORBA y OLE2 lográn un entorno de desarrollo de componentes potente y sencillo, en el que el programador puede comprender y seguir todos los detalles de la arquitectura, y para el que no se necesitan complejas herramientas de desarrollo.

En el mundo del software libre se avecina una próxima revolución, la de los componentes libres. En cuanto el conjunto de desarrolladores comprendan el modelo de desarrollo basado en componentes, y dispongan de las herramientas adecuadas, veremos surgir una gran variedad de paletas de componentes libres, que podremos utilizar para desarrollar nuestras aplicaciones de forma muy rápida y robusta.

GNOME es un ejemplo práctico del uso de CORBA muy completo e interesante, que puede servir al lector para profundizar en sus conocimientos de CORBA y aprender a desarrollar aplicaciones sencillas y potentes con muy pocas líneas de código.

Referencias

  1. Libro GNOME y CORBA: http://developer.gnome.org/doc/guides/corba
  2. GNOME: http://www.gnome.org
  3. Noticias del software libre: http://barrapunto.com
  4. Noticias de GNOME: http://news.gnome.org