5. Servicio de Nombres

El estándar CORBA, además de la arquitectura básica de objetos remotos y la sintaxis de IDL, incluye especificaciones para varios servicios distribuidos de objetos, como por ejemplo el servicio de nombrado de objetos, el servicio de política de seguridad, y los servicios de objetos persistentes. El servicio de nombrado de objetos es fundamental para las aplicaciones CORBA debido a que proporciona una forma estándar de encontrar objetos CORBA remotos en la red. Se proporcionan dos servicios CORBA para localizar objetos: el Naming service, que encuentra objetos mediante el nombre, y el Trading service, que encuentra objetos por tipo y propiedades.

El Naming service funciona como una guía de teléfonos y permite encontrar una única referencia usando un identificador que funciona como una clave. El Trading service, por otro lado, permite recuperar un conjunto de referencias que cumplen un criterio de búsqueda, con un funcionamiento muy parecido al de las páginas amarillas.

5.1. Localización de objetos

Antes de invocar operaciones sobre un objeto CORBA, el cliente debe obtener primero la referencia al objeto CORBA. La arquitectura CORBA proporciona varios mecanismos para que los objetos CORBA y sus aplicaciones servidor pueden usar para que sus referencias a objetos puedan estar disponibles para potenciales clientes:

En este tema nos vamos a referir al primero de ellos.

5.2. Servicio Naming

El servicio Naming proporciona una correspondencia entre un nombre y una referencia a un objeto. El almacenamiento de dicha correspondencia se denomina como enlace (binding) de una objeto a un nombre, y la eliminación de la misma se denomina desenlazar (unbinding) el nombre. Por otro lado, obtener una referencia a objeto que se ha enlazado con un nombre se conoce como resolver (resolve) el nombre.

Los nombres pueden estructurarse jerárquicamente unsando contexts. Los contexts son similiares a los directorios de los sistemas de ficheros, y pueden contener enclaces a nombres, así como subcontexts.

El uso únicamente de referencias a objetos para identificar objetos tiene dos problemas para los usuarios humanos. Primero, las referencias a objetos son tipos de datos opacos, y segundo, su forma String es una secuencia muy larga de números. Cuando se reinicia un servidor, sus objetos típicamtne tienen nuevas referencias de objetos. En muchos casos, sin embargto, los clientes queiren usar el servidor de forma repetida sin necesidad de tener en cuenta que el servidor se ha vuelto a iniciar.

El servicio Naming soluciona estos problemas proporcionando una capa de abstracción extra para la identificación de los objetos. Proporciona identificadores de objetos legibles para los usuarios humanos; los usuarios pueden asignar nombres que se asemejan a nombres estructurados de ficheros, un mecanismo de identificación persistente, y los objetos pueden enlazarse ellos mismos con el mismo nombre con independencia de su referencia. Podemos decir que el servicio Naming es un repositorio para almacenar referencias a objetos CORBA.

El servicio Naming usa una estructura de nombres definida en IDL para representar nombres, pero también define un formato de cadenas de caracteres estándar para nombres y conversión de operaciones entre los formatos de nombres internos y externos. De esta forma los nombres, así como las referencias a objetos, pueden ser convertidas en cadenas de caracteres y exportadas.

Cuando una aplicación servidor inserta una referencia a objeto en el servicio Naming, es necesario proporcionar un nombre que usará para identificar dichar referencia a objeto. Esta asociación referencia a objeto-nombre se denomina binding.

El uso típico del servicio Naming consiste en enlazar (bind) implementaciones de objetos con nombres del servicio Naming y desenlazarlos (unbind) cuando éstos terminan. Los clientes "interrogan" al servicio Naming, proporcionando un nombre, y reciben la referencia al objeto asociada, si existe. Una vez obtenida dicha referencia, invocan sobre ella las operaciones. La figura 5.1 ilustra este escenario típico.

Figura 5.1. Uso típico del servicio de nombres CORBA.

El servicio Naming almacena los enlaces a referencias a objetos en naming contexts. Un naming context es un objeto CORBA que mantiene una colección de cero o más enlaces a referencias de objetos. La naturaleza recursiva de los naming contexts permite la creación de una jerarquía de naming contexts, similar a la de una estructura de directorios de un sistema de ficheros. Mediante la creación de nuevos naming contexts dentro de otros ya existentes se consigue segmentar el espacio de nombres de objetos global, y paliar el problema de la colisión de nombres. El Naming service contiene un naming context inicial, denominado raiz, o initial context. Para insertar una referencia en el servicio Naming, la aplicación servidor debe primero obtener la referencia al objeto que constituye la raiz o initial context. Para ello utiliza el método resolve_initial_references().

//Conexión con el Servicio de Nombres
org.omgCORBA.Object contextObj = orb.resolve_initial_references("NameService");
NamingContext rootContext = NamingContextHelper.narrow(contextObj);

El módulo CosNaming proporciona definiciones de tipo usadas para identificar objetos por nombre. La interfaz principal se denomina NamingContext, y contiene operaciones para enlazar nombres a referencias a objetos y para crear subcontextos (new_context() y bind_new_context(), este último crea un subcontexto y lo enlaza en una única operación). Hay dos operaciones para enlazar un objeto a un nombre en un contexto, y otras dos para enlazar otro contexto a un nombre. Las operaciones bind() y bind_context() asocian un nuevo nombre con un objeto. En el caso de bind_context() el objeto debe ser del tipo NamingContext. Las operaciones rebind() y rebind_context() funcionan de forma similar a las anteriores pero en lugar de provocar una excepción si el nombre ya existe, simplemente reemplazan la referencia al objeto existente. La operación unbind() eliminará un nombre y su referencia a objeto asociada de un contexto o uno de sus subcontextos.

Los nombres son secuencias de NameComponents. Un NameComponent tiene dos campos: id contiene el string con el que se hará matching cuando se resuelva un nombre; y kind, disponible par propósitos específicos de la aplicación. Ambos campos se interpretan por el servicio de nombres, de forma que nombres con campos id idénticos, pero con valores diferentes en el campo kind se consideran distintos. El siguiente código inserta en el servicio de nombres la referencia al objeto Muestra, dentro del contexto Modulos, que a su vez está dentro del contexto raíz.

NameComponent mod = new NameComponent("Modulos","");
NameComponent mue = new NameComponent("Muestra","");
NameComponent path[] = {mod,mue};
rootContext.rebind(path,obj); //obj es una instancia del objeto remoto

Los NamingContexts pueden resolver nombres con una única componente y devolver una referencia a un objeto. El efecto de resolver nombres con más de una componente es el mismo que el de resolver la primera componente en un subcontexto y dejar el resto del nombre a dicho subcontexto para su resolución.

//recuperamos la referencia al objeto CORBA del servicio de nombres correspondiente a "path"
org.omg.CORBA.object obj = rootContext.resolve(path);

El servicio de nombres se lanza desde línea de comandos mediante la orden:

tnameserv -ORBInitialPort x

En donde x es el puerto que se quiere usar. Si no se especifica un número de puerto, por defecto, el servicio de nombres se ejecuta sobre el puerto 900.

El siguiente ejemplo muestra el uso del Naming service desde un cliente Java. Implementamos la clase ContextLister que permite imprimir todos los enlaces (bindings) de un NamingContext en un stream Java.

import org.omg.cosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*;
import java.io.*;
import java.util.*;
 
public class ContextLister {
  private NamingContextExt root_context;
 
  public contextLister(org.omg.CORBA.ORB orb) {
     //inicializa el servicio Naming mediante el ORB
    try {
     System.out.println("Servicios iniciales: ");
     String[] services = orb.list_initial_services();
 
     if (services.length==0)
       System.out.println("No hay servicios disponibles");
 
     for (int i=0; i< services.length; i++)
       System.out.println(services[i]);

     org.omg.CORBA.object obj =
       orb.resolve_initial_references("NameService");
     root_context= NamingContextExtHelper.narrow(obj);     
    }
    catch(org.omg.CORBA.ORBPackage.InvalidName inex) {
      inex.printStackTrace();
    }
    catch(org.omg.CORBA.SystemException corba_exception) {
      System.err.println(corba_exception);
    }
    if (root_context== null) {
      System.err.println("No se encuentra el servicio Naming");
      System.exit(1); 
    }
  }
}

De forma alternativa se puede utilizar un constructor que inicializa el objeto ContextLister con una referencia a objeto como cadena de caracteres (stringfied) para el root context.

public ContextLister(org.omg.CORBA.ORB orb, String str) {
  //inicializa el servicio Naming mediante un stringfied ORB
  try {
    org.omb.CORBA.Object obj = orb.string_to_object(str);
    root_context = NamingContextExtHelper.narrow(obj);
  }
  catch(org.omg.CORBA.SystemException corba_exception) {
      System.err.println(corba_exception);
  }
  if (root_context== null) {
      System.err.println("No se encuentra el servicio Naming");
      System.exit(1); 
  }
}

Para ejecutar el código cliente se utiliza la orden:

java Cliente [ -ORBInitialPort x] [-ORBInitialHost host]

En donde x es el puerto y host es la máquina en la que se está ejecutando el servicio de nombres.

Para ejecutar el código de la aplicacion servidor, se utiliza la orden:

java Servidor [ -ORBInitialPort x] [-ORBInitialHost host]

En donde x es el puerto y host es la máquina en la que se está ejecutando el servicio de nombres.

5.3. Servicio Trading

El servicio Trading tiene su base en los estándares ISO Open Distributed Processing (ODP). En este caso simplemente vamos a ilustrar la idea general, sin proporcionar ningún ejemplo práctico de uso.

Los traders son repositorios de referencias a objetos que son descritas por un tipo de interfaz y por un conjunto de valores de propiedades. Tal descripción de la interfaz se conoce como oferta de servicio (service offer). Cada oferta de servicio tiene un tipo de servicio (service type), consistente en una combinación del tipo de interfaz del objeto correspondiente y una lista de propiedades para las que la oferta de servicio debería proporcionar valores.

Un exporter es un servicio que actúa como un agente que proporciona un service offer en un trader.

Un cliente que requiera de un trader para encontrar un servicio se denomina importer. Un importer proporciona una especificación al trader de un tipo de servicio y unas restricciones sobre las propiedades que oferta dicho tipo. Las restricciones se indican mediante una expresión que describe los requerimientos del importer. Un uso típico del trader service se ilustra en la Figura 5.2

Figura 5.2. Uso de un Trading service de CORBA.

Un servidor, el exporter, puede registrar un servicio utilizando el interfaz Register. Los clientes que pueden ser applets o aplicaciones, pueden actuar como importers y buscar servicios usando la interfaz Lookup.