|
Es de suma importancia que antes de continuar, echen un vistazo al primer tutorial de CORBA-omniORB, en el encontraran todo lo necesario para la instalacion y puesta en marcha.
CORBA - omniORB - Tutorial 01
Ok, continuemos.
En ese primer tutorial hablamos sobre como compilar,
instalar y ejecutar omniORB, incluimos un pequeño ejemplo.
Esta vez explicaremos, el ejemplo linea por linea, o al
menos las secciones que se deben de mencionar; ademas de que mostraremos la
interoperabilidad de CROBA, usando C++ en el cliente y Python en el servidor.
Lo primero que debemos hacer es crear un archivo .idl,
conocido como interface description lenguage. Lo llamamos SQL.idl.
module SQL
{
interface SQLQuery
{
string Query(in string query);
};
};
Aqui definimos una interfaz comun que usaran los
programas, osea, que se ponen de acuerdo como seran los metodos que se
utilizaran, si repasamos el codigo vemos que se activa un modulo el cual tiene
una interfaz con un solo metodo que como parametro recibe un string
de nombre query y regresa un string.
Ya que tenemos este archivo, necesitamos generar los
archivos "back-end", que son los que se encargan del trabajo rudo y a
nosotros solo nos toca implementar y usar el metodo que declaramos.
$omniidl -bpython -CPyhton SQL.idl
$omniidl -bcxx -CC++ SQL.idl
Los parametros -bpyhton o -bcxx
sirven para generar los "back-ends" para dicho lenguaje, la parte de -CPython
y -CC++ es para cambiar de directorio antes de generar los
archivos (los directorios deben de existir), esto sirve para tener bien
organizado el codigo; y el ultimo parametro es el archivo.idl
Antes de continuar expliquemos algo de simbologia:
La explicacion del codigo esta
en verde, este texto quitalo del codigo fuente para que funcione.
El
codigo usa los colores de resaltado de vim y la letra es de diferente tamaño y
fuente (Tahoma).
Los nombres y variables a las que hay que ponerle
atencion estan en negritas y cursiva
Ya que tenemos los archivos basicos para C++ y Python nos
pasamos al directorio de Python y creamos un archivo llamado Servidor.py
Dentro de el colocamos el siguiente codigo:
#!/usr/bin/env python
Comenzamos agregando los imports
necesarios.
import sys
import CosNaming
import SQL ,SQL__POA
import MySQLdb
from omniORB
import
CORBA, PortableServer
Definimos una clase con el nombre
SQLQuery_i, que es el que definimos en nuestro archivo .idl, mas _i.
class SQLQuery_i(SQL__POA.SQLQuery):
Dentro de el debemos implementar
el metodo Query, que definimos para nuestra interfaz en el archivo .idl, asi
cualquier cliente podra solicitar los servicios, debemos recordar que definimos
que recibia un string (qry) y regresaba un string (res).
def Query(self,
qry):
Mostramos el saludo que nos envio
el cliente y hacemos lo mismo.
print "Saludo recibido -> " , qry
return "Hola, Soy Python"
Una vez definida la clase y como
se implementan los metodos debemos de comenzar a trabajar con CORBA. Antes que
nada debemos crear un objeto, al cual le pasamos los argumentos que recibimos
de la linea de comandos por medio de la variable sys.argv, ademas del
identificador de la implementacion de CORBA que estamos usando.
orb = CORBA.ORB_init(sys.argv, CORBA.ORB_ID)
poa = orb.resolve_initial_references("RootPOA")
A continuacion creamos un objeto
del tipo SQLQuery_i, que definimos al inicio del programa.
implemetacionSQLQuery = SQLQuery_i()
objetoSQLQuery = implemetacionSQLQuery._this()
La siguiente parte explica como
dar de alta un servicio en el servidor de nombres de omniNames, para que los
clientes puedan invocar los objetos con el nombre del servicio, estos nombres
deben de ser consistentes tanto en el servidor como el cliente.
Primero buscamos una referencia
con el nombre ServicioSQL y creamos un contexto raiz. Nota: esto cobrara mayor
sentido cuando discutamos como se ejecuta el programa.
obj = orb.resolve_initial_references("ServicioSQL")
rootContext = obj._narrow(CosNaming.NamingContext)
if rootContext is None:
print "No se pudo inicar el Contexto \"Root\""
sys.exit(1)
Ya que tenemos un contexto raiz,
creamos un nombre para este.
nombre
= [CosNaming.NameComponent("SD",
"Contexto")]
try:
Asociamos el contexto raiz con el
que usaremos, que llamaremos SQLContext
SQLContext = rootContext.bind_new_context(nombre)
print "Nuevo contexto creado"
except
CosNaming.NamingContext.AlreadyBound, ex:
Esta excepcion se dispara si el
contexto raiz ya existe, en ese caso, busca que el nombre que le asignamos
("SD"-"Contexto"), para re-utilizarlo.
print "El contexto ya existe"
obj = rootContext.resolve(nombre)
SQLContext = obj._narrow(CosNaming.NamingContext)
if SQLContext is None:
print "EjemploEcho.Objeto existe pero no es un Nombre de
Contexto"
sys.exit(1)
Despues de nombrar el contexto raiz,
debemos agregar el componente y el nombre con el que los identificaremos.
nombre
= [CosNaming.NameComponent("SQL",
"SQLQuery")]
try:
Ahora lo asociamos con el contexto
raiz que creamos antes.
SQLContext.bind(nombre, objetoSQLQuery)
print "Bind"
except
CosNaming.NamingContext.AlreadyBound:
Una vez mas, si el contexto ya
existe, solo lo volvemos a agregar y listo.
SQLContext.rebind(nombre, objetoSQLQuery)
print "Rebind"
Iniciamos el manejador de objetos
e iniciamos el orb.
poaManager = poa._get_the_POAManager()
poaManager.activate()
orb.run()
Ya hemos terminado con el servidor, vamos a hacer el
cliente, recordemos que este se hara en C++; asi que nos pasamos
a la carpeta de C++ y creamos un archivo llamado Cliente.cpp y
agregamos el siguiente codigo:
Agregamos las cabeceras y
declaramos los espacios de nombres que usaremos.
#include <iostream>
#include <string>
#include "SQL.hh"
using namespace
std;
using namespace
SQL;
Declaramos las funciones
prototipo, la primera sera con la que obtendremos la referencia al objeto que
creamos en servidor con Python, usando los nombres que declaramos en dicho
programa.
La segunda sera la funcion que
llamara a un metodo del objeto en el servidor, es por eso que pasamos una
referencia al objeto y el string con el saludo.
CORBA::Object_ptr getReferenciaObjeto(CORBA::ORB_ptr orb);
void query(SQLQuery_ptr refSQLQuery,
const string qry);
int main(int
argc, char** argv)
{
try
{
Iniciamos el orb, con los parametros
que recibimos en la linea de comandos.
CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);
Llamamos ala funcion
getReferencia, para obtener el apuntador al objeto del servidor.
CORBA::Object_var obj = getReferenciaObjeto(orb);
Ya que tenemos el puntero, debemos
convertirlo a uno del tipo SQLQuery, que es el nombre de la clase que
declaramos en Pyhton para asi poder llamar a los metodos que tiene definidos.
SQLQuery_var referenciaSQLQuery = SQLQuery::_narrow(obj);
Creamos el saludo.
string qry = "Hola, Soy
C++";
Llamamos a la funcion query para
que haga la peticion, recordemos que debemos de pasar la referencia al objeto
SQLQuery.
query(referenciaSQLQuery, qry);
Destruimos el objeto, para
finalizar.
orb->destroy();
}
El resto es manejo de excepciones.
catch (CORBA::TRANSIENT&)
{
cerr << "Exception
del Sistema TRANSIENT -> no se pudo contactar el servidor."
<< endl;
}
catch
(CORBA::SystemException& ex)
{
cerr << "Excepcion
del Sistema CORBA::" << ex._name() << endl;
}
catch (CORBA::Exception&
ex)
{
cerr << "CORBA::Exception:
" << ex._name() << endl;
}
catch
(omniORB::fatalException& fe)
{
cerr << "Excepcion
omniORB::fatalException:" << endl;
cerr << " Archivo:
" << fe.file() << endl;
cerr << " Linea:
" << fe.line() << endl;
cerr << " Mensaje:
" << fe.errmsg() << endl;
}
return 0;
}
Ahora implementamos el metodo que
llamara al metodo Query del objeto remoto.
void query(SQLQuery_ptr refSQLQuery,
const string qry)
{
Verificamos que en verdad exista
la referencia hacia el objeto.
if (CORBA::is_nil(refSQLQuery))
{
cerr << "La
referencia del objeto es nula" << endl;
return;
}
Aqui es donde viene lo
interesante.
Hasta este momento hemos
utilizados el tipo de variable std::string (standard C++), pero para que el
texto que mandamos al metodo en el objeto remoro sea compatible, debemos de
utilizar el tipo CORBA::String_var (omniORB) para que Python reciba la
informacion como se debe.
CORBA::String_var mensaje = qry.c_str();
Y mandamos llamar el metodo Query,
del objeto remoto y el resultado, lo guardamos en una variable del tipo
CORBA::String_var.
CORBA::String_var res = refSQLQuery->Query(mensaje);
Finalmente mostramos el resultado.
Notemos que la variable del tipo CORBA::String_var, contiene una sobre carga
del operado << para que std::cout funcione.
cout << "Mensaje enviado ->
" << mensaje << endl;
cout << res << endl;
}
Hagamos el ultimo metodo, el cual
se encarga de resolver las referencias y los nombres de los objetos y componentes
remotos. Mucho de este trabajo, se asemeja al que ya hicimos en Pyhton, solo
hay que traducirlo a C++.
CORBA::Object_ptr getReferenciaObjeto(CORBA::ORB_ptr orb)
{
Primero creamos un contexto raiz.
CosNaming::NamingContext_var rootContext;
try
{
Obtenemos la referencia al servicio
"ServicioSQL". Nota: como lo dijimos en la seccion de Python, esto cobrara
mayor sentido cuando discutamos como se ejecuta el programa.
CORBA::Object_var obj;
obj = orb->resolve_initial_references("ServicioSQL");
Y nuestro contexto raiz lo
asociamos con la referencia a "ServicioSQL".
rootContext = CosNaming::NamingContext::_narrow(obj);
El resto es solo manejo de
errores.
if(CORBA::is_nil(rootContext))
{
cerr << "Fallo
en narrow" << endl;
return
CORBA::Object::_nil();
}
}
catch
(CORBA::NO_RESOURCES&)
{
cerr << "Excepcion
NO_RESOURCES. Quizas debas configurar omniORB con el path del servicio de
Nombres" << endl;
return 0;
}
catch(CORBA::ORB::InvalidName&
ex)
{
cerr << "El servicio
solicitado es invalido [nt existe]." << endl;
return
CORBA::Object::_nil();
}
¿Recuerdan los nombres que usamos
en el servidor?
Debemos de usar los mismos, para
que se puedan comunicar, imaginemos lo siguiente como un pequeño arbol con una
raiz, del cual se desprenden componentes, de diferentes tipos.
Creamos el nombre que se usara por
el servidor de nombres (omniNames, ver Tutorial 01).
CosNaming::Name
nombre;
nombre.length(2);
nombre[0].id = (const char*)
"SD";
nombre[0].kind = (const char*)
"Contexto";
nombre[1].id = (const char*)
"SQL";
nombre[1].kind = (const char*)
"SQLQuery";
try
{
Y en nuestro contexto raiz, que
contiene la informacion del servicio ("ServicioSQL" y una IP), tratamos de
resolver los nombres de los servicios. Y regresamos un puntero al objeto encontrado con las caracteristicas especificadas.
return
rootContext->resolve(nombre);
}
Y verificamos errores.
catch(CosNaming::NamingContext::NotFound&
ex)
{
cerr << "No se
encontro el Contexto." << endl;
}
catch(CORBA::TRANSIENT&
ex)
{
cerr
<< "Excepcion del Sistema, no se pudo
contactar el servidor de nombres, asegurate que esta corriendo y omniORB esta
bien configurado" << endl;
}
catch(CORBA::SystemException&
ex)
{
cerr << "Excepcion de
CORBA::" << ex._name() << "
al usar el servidor de nombres." << endl;
return 0;
}
return CORBA::Object::_nil();
}
Ya tenemos el cliente
listo solo nos basta compilarlo, para poder comenzar la prueba, para esta tarea
nos ayudaremos de la famosa herramienta, make; asi que debemos de
crear dentro de la misma carpeta (C++) el archivo Makefile y
colocar el siguiente texto:
LIBS
+=
-lomniORB4 -lomnithread -lomniDynamic4
OPS += -Wall -pipe -O2
CXX = g++
all: Cliente
Cliente: SQLSK.o Cliente.o
$(CXX)
$(LIBS) $^ -o $@
SQLSK.o: SQLSK.cc
$(CXX)
-c $(OPS)
$^ -o $@
Cliente.o: Cliente.cpp
$(CXX)
-c $(OPS)
$^ -o $@
clean:
rm -f *.o Cliente
Ejecutamos el make.
$make
Pues ya tenemos todo listo, Servidor, Cliente
y si seguimos el primer tutorial, omniORB esta listo, ademas de
que el servidor de nombres, omniNames, tambien esta funcionando, asi
que solo debemos de arrancar cada uno de los programas.
Comencemos con el servidor
$ python Servidor.py -ORBInitRef
ServicioSQL=corbaname::localhost
Lo que hacemos es iniciar el codigo Python,
pasandole la referencia inicial, que en este caso es un servicio llamado "ServicioSQL"
y que se encuentra en localhost, osea en nuestra maquina.
Recordemos que al iniciar el ORB, en Python
como en C++ le deciamos que resolviera la referencia inicial y le decimas el
nombre del servicio (en los 2 fue ServicioSQL), pues ese nombre
es el que pasamos como parametro para que el programa y el servidor de nombres
(omniNames) sepan sobre que o quien estamos vamos a trabajar.
La salida sera:
Nuevo contexto creado
Bind
Y despues de ejecutar el cliente
Saludo recibido -> Hola, soy C++
Ahora el cliente.
$ ./Cliente -ORBInitRef
ServicioSQL=corbaname::196.168.10.5
Es muy parecido al anterior, solo que esta vez le decimos
que el "ServicioSQL" lo encuentra en la direccion IP
especificada.
Nota: Esta es una IP de ejemplo, para ejecutarlo
en una sola maquina tambiin hay que ponerle localhost, pero si lo
hacemos en una red, ponemos la IP de la computadora
que esta ejecutando el servidor.
La salida sera:
Mensaje enviado -> Hola, soy C++
Hola, soy Python
Pues ya esta, recuerda consultar la documentacion que
viene con los codigos fuente, esta muy buena y este ejemplo se baso en ella.
|