Implementación de interfaces CORBA con Bonobo

Una de las cosas en las que más nos va a ayudar Bonobo es, sin duda, en la facilidad que nos da para la implementación de interfaces CORBA. Como se pudo comprobar en el capítulo dedicado a ORBit, la lista de cosas a hacer, si bien no muy complicada, hace que el acceso al uso de CORBA se limite un poco más. Por ello, en la implementación de Bonobo para el proyecto GNOME se ha puesto un cuidado especial en ocultar todo el código generado por el compilador IDL, de forma que su uso sea mucho más sencillo. Así, Bonobo, aparte de los interfaces CORBA IDL anteriormente comentados, incluye multitud de funciones que nos van a ayudar enormemente en el uso de CORBA en nuestras aplicaciones, y todo ello probablemente sin enterarnos siquiera de que lo que estamos usando, por debajo, es CORBA.

Una de las cosas más útiles que tenemos en Bonobo es el objeto BonoboObject (o BonoboXObject), que nos ayuda enormemente a la hora de implementar nuestros propios interfaces IDL, eso si, basados en Bonobo::Unknown.

El uso de BonoboObject (o BonoboXObject) es muy sencillo. Para empezar, lo primero que tenemos que hacer es definir, en un fichero IDL, nuestros interfaces. Como hemos comentado, estos interfaces estaran basados en Bonobo::Unknown, de forma que, sin ningun esfuerzo añadido, nuestros objetos CORBA se conviertan en componentes Bonobo.

Así, definiriamos el siguiente interfaz:

      #include <Bonobo.idl>
      module GNOME {
        module Libro {
          interface String : Bonobo::Unknown {
            string toUpper (in string str);
            string toLower (in string str);
          };
        };
      };
    

Como se puede observar, estamos definiendo un interfaz (GNOME::Libro::String), que deriva de Bonobo::Unknown, y que contiene dos sencillos métodos para convertir cadenas a mayúsculas (toUpper) y a minúsculas (toLower).

Implementación del componente con BonoboObject

Una vez que hemos definido nuestro(s) interfaz(es) IDL, llega el momento de pasar a la implementación. Para ello, usaremos el sistema de objetos de GLib, de forma que crearemos una nueva clase, que llamaremos LibroString. Esta clase estará basada en BonoboObject, de forma que todo lo necesario para que nuestro interfaz implemente el interfaz CORBA Bonobo::Unknown sea añadido a nuestra nueva clase.

Así pues, crearemos primero el fichero de cabecera para nuestra clase, que llamaremos libro-string.h. Este fichero contendrá todas las declaraciones típicas de un fichero de cabecera para un objeto GObject, con algunas pequeñas diferencias, que comentaremos a continuación.

En primer lugar, una de las diferencias es que tendremos que incluir, en este fichero de cabecera, el fichero de cabecera generado por el compilador orbit-idl a partir de nuestro fichero IDL comentado anteriormente. Así, al principio de este fichero, junto con el resto de #include's, añadiremos:

	#include <GNOME_Libro.h>
      

En la definición de los tipos para las instancias y clases de nuestro nuevo objeto, haremos que éste derive de BonoboObject. En la estructura de clase, además, añadiremos algo nuevo:

	struct _LibroString {
	  BonoboObject object;
	};

	struct _LibroStringClass {
	  BonoboObjectClass parent_class;
	  POA_GNOME_Libro_String__epv epv;
	};
      

Como se puede observar, hemos añadido la estructura epv (comentada en el capítulo sobre CORBA) a la estructura de clase de nuestro objeto LibroString. Esto se debe a que BonoboObject necesita que la epv esté disponible en la estructura de clase, de forma que pueda inicializar el objeto CORBA.

El siguiente paso es escribir la implementación de nuestro objeto, lo cual haremos en el fichero libro-string.c. Aquí es donde realmente notaremos una enorme diferencia con respecto a la implementación de objetos CORBA comentada en el capítulo sobre CORBA/ORBit, donde el código que tenemos, más o menos, que mantener/escribir, es realmente extenso, lo cual, por supuesto, nos hace más propensos a posibles errores. Gracias a Bonobo en general, y a BonoboObject en particular, todo esto se facilita enormemente.

Para empezar, como cualquier clase nueva que creamos, tenemos que implementar la función _get_type para dicha clase. En el caso de clases derivadas de BonoboObject, la forma de registrar una nueva clase es algo distinta, pero, de lejos, mucho más sencilla. Sólo tenemos que usar una macro (definida con #define) disponible en el fichero bonobo-object.h:

	#define BONOBO_TYPE_FUNC_FULL(class_name, corba_name, parent, prefix)
      

Esta macro, como podremos observar si editamos el fichero bonobo-object.h, simplemente genera una función típica _get_type usando los parámetros que le pasamos para generar los nombres de las distintas estructuras, funciones, etc. Así, en el caso de nuestro interfaz GNOME::Libro::String, quedaría de la siguiente manera:

	BONOBO_TYPE_FUNC_FULL (LibroString,
	                       GNOME_Libro_String,
	                       BONOBO_TYPE_OBJECT,
	                       libro_string)
      

Incluyendo esto en nuestro fichero libro-string.c, tendriamos ya creada la función libro_string_get_type, que será la encargada de registrar la nueva clase cuando así sea requerido.

La siguiente diferencia con respecto a una clase "normal" reside en la implementación de la función de inicialización de la clase (libro_string_class_init en este caso), en la que, como comentábamos anteriormente, tendremos que inicialiar la estructura epv de nuestra clase:

	static void
	libro_string_class_init (LibroStringClass *klass)
	{
	  POA_GNOME_Libro_String__epv *epv;

	  epv = &klass->epv;
	  epv->toUpper = impl_String_toUpper;
	  epv->toLower = impl_String_toLower;
	}
      

BonoboObject se encarga de todo lo necesario para inicializar nuestro objeto CORBA, nosotros sólo tenemos que indicarle la única y exclusivamente la información que necesita para ello. En esta función, lo que estamos haciendo es especificar dónde está la implementación de los métodos de nuestro objeto (toUpper, toLower), para lo que usamos la estructura epv generada por el compilador orbit-idl, en la que se especifican los punteros a las funciones que implementan los distintos métodos de los objetos CORBA. Con esto, añadiendo además las demás funciones típicas de una clase (libro_string_init, libro_string_new, etc), tenemos ya todo lo necesario para nuestro objeto, basado en Bonobo::Unknown. Sólo nos falta añadir la implementación de los métodos. En ellos, que tienen la misma forma que los generados por orbit-idl, usaremos la función bonobo_object, que nos devuelve una referencia a nuestro BonoboObject dado un objeto CORBA:

	/* Detects the pointer type and returns the object reference - magic. */
	BonoboObject *bonobo_object (gpointer p);
      

Como dice el comentario (sacado del fichero bonobo-object.h), esta función lo que hace es magia, pues mediante aritmética de punteros y demás artes esotéricas, accede, dado un objeto CORBA (CORBA_Object) al objeto BonoboObject asociado. Por tanto, nuestros métodos quedaran de la siguiente manera:

	static CORBA_char *
	impl_String_toUpper (PortableServer_Servant servant,
	                     const CORBA_char *str,
	                     CORBA_Environment *ev)
	{
	  gint n;
	  CORBA_char *corba_str;
	  LibroString *libstr = bonobo_object (servant);

	  bonobo_return_val_if_fail (LIBRO_IS_STRING (libstr), NULL, ev);

	  corba_str = CORBA_string_dup (str);
	  for (n = 0; n < strlen (corba_str); n++) {
	    corba_str[n] = toupper (corba_str[n]);
	  }

	  return corba_str;
	}

	static CORBA_char *
	impl_String_toLower (PortableServer_Servant servant,
	                     const CORBA_char *str,
	                     CORBA_Environment *ev)
	{
	  gint n;
	  CORBA_char *corba_str;
	  LibroString *libstr = bonobo_object (servant);

	  bonobo_return_val_if_fail (LIBRO_IS_STRING (libstr), NULL, ev);

	  corba_str = CORBA_string_dup (str);
	  for (n = 0; n < strlen (corba_str); n++) {
	    corba_str[n] = tolower (corba_str[n]);
	  }

	  return corba_str;
	}