March 2nd, 2009 por CaDs

XMPP es uno de los protocolos de comunicación más utilizados hoy en día. Probablemente lo conozcáis como Jabber, y es un protocolo estándar para la mensajería instantánea.
XMPP está optimizado para la comunicación entre humanos, pero hay una creciente tendencia a utilizarlo como protocolo de comunicación entre máquinas.
Habitualmente, cuando tenemos un sistema distribuido necesitamos comunicar los diversos eventos que nuestra lógica genera entre diversas máquinas. Tecnologías como JMS, en el caso de Java, proporcionan un framework orientado a mensajes para integrar con tus aplicaciones.
Pero XMPP ofrece otra manera de trabajar un tanto distinta y un poco más estándar.
Para el ejemplo de este post usaré Java como lenguaje de programación y Smack, una librería de la gente de Jive Software (pueden encontrarse librerías similares como XMPP4R para rails, o XMPPhp para php).

Lo primero que necesitamos para enviar y recibir mensajes es un servidor XMPP. Openfire es un buen ejemplo de lo que debe ser un servidor XMPP. Ligero, fácil de configurar y rápido de instalar. Por supuesto podéis elegir cualquier otro servidor, a fin de cuentas lo único que nos interesa es que despache los mensajes.
Configurar Openfire es relativamente sencillo, así que me saltaré esa parte. De todas formas podéis ver la guía de instalación aquí.

Una vez instalado y configurado nuestro servidor es cuestión de comenzar a enviar y a procesar mensajes.
Supongamos que hemos creado dos cuentas para nuestras máquinas. Una llamada cliente y otra llamada servidor (original no?).
Así pues podemos crear nuestra aplicación cliente y generar una clase que se encargue de establecer la conexión con el servidor.
Usando la clase org.jivesoftware.smack.XMPPConnection de Smack es algo como:


XMPPConnector connector = new XMPPConnector("url.miserver.com") ;
connector.login("cliente","password");

De este modo tenemos a nuestra máquina cliente “logueada” en nuestro servidor Jabber.
Siguiendo el mismo procedimiento podemos “loguear” nuestra máquina servidor en otra clase que se encargue de levantar la conexión para la aplicación del servidor.


XMPPConnector connector = new XMPPConnector("url.miserver.com") ;
connector.login("servidor","password");

Teniendo ambas máquinas levantadas es hora de que nuestro cliente envíe el primer mensaje al servidor. Para ello usaremos la clase org.jivesoftware.smack.packet.Message


Message message = new Message() ;
message.setTo("servidor@url.miserver.com") ;
message.setSubject("message test") ;
message.setBody("Hello world") ;
connection.sendPacket(message) ;

Y eso es todo, nuestro conector, el que hemos creado previamente, se encarga del resto.
Esto como veis no tiene utilidad alguna. Sería distinto en el caso de que estuvierais escribiendo un código para que vuestra aplicación os avisara de determinados eventos, pero en nuestro caso, enviar un mensaje del tipo “Hello World” al servidor no tendrá mucho efecto.

La cosa cambia cuando configuramos un listener que se encargue de procesar ciertos mensajes. Un listener es algo así como un filtro que, siempre que se cumplan las condiciones que nosotros escribamos, actuará sobre el mensaje recibido.
Para ello usaremos la clase org.jivesoftware.smack.PacketFilter para crear un filtro


PacketFilter filter = new AndFilter(new PacketTypeFilter(Message.class), new FromContainsFilter("cliente@url.miserver.com"));

En este caso estamos creando un filtro que valida que lo recibido sea un objeto de la clase Message y que provenga de “cliente@url.miserver.com”.
Y ahora registramos nuestro listener usando la clase org.jivesoftware.smack.PacketListener


PacketListener myListener = new PacketListener() {
        public void processPacket(Packet packet) {
            if (packet instanceof Message) {
                Message msg = (Message) packet;
                // Process message
                DoWhatYouWantWithTheMessage();
                ...
            }
        }
    };
connector.addPacketListener(myListener, filter);

En este ejemplo se crea un nuevo PacketListener y se define el método processPacket que es el encargado de ejecutar la lógica de nuestra aplicación cuando se reciba un mensaje que cumpla las condiciones definidas en nuestro filtro.
Como esto ha quedado algo abstracto pongamos un ejemplo algo más práctico.
Supongamos que tenemos una clase llamada BeanUsuario que tiene como atributos un String email y un String password.
Ahora vamos a enviar un mensaje a nuestro servidor, donde reside la base de datos de nuestra aplicación, para que almacene un usuario. Para ello usaremos el método setProperty que nos permite enviar cualquier objeto Java (que sea serializable) dentro del mensaje.


//Código del Cliente
//Creamos el conector
XMPPConnector connector = new XMPPConnector("url.miserver.com") ;
//Nos logueamos como cliente
connector.login("cliente","password");
//Creamos el bean de usuario
BeanUsuario bean = new BeanUsuario("carlos@hola.com", "mipassword") ;
//Creamos un nuevo mensaje
Message message = new Message() ;
//Ponemos como destinatario nuestro servidor
message.setTo("servidor@url.miserver.com") ;
//Metemos el bean de nuestro usuario en el mensaje
message.setProperty("ususario", bean) ;
//Enviamos el mensaje
connector.sendPacket(message) ;

//Código del Servidor
//Creamos la conexión
XMPPConnector connector = new XMPPConnector("url.miserver.com") ;
//Nos logueamos como servidor
connector.login("servidor","password");
//Creamos un filtro para recibir los mensajes que vengan del cliente
PacketFilter filter = new AndFilter(new PacketTypeFilter(Message.class), new FromContainsFilter("cliente@url.miserver.com"));
//Creamos el listener y el método donde se procesará el mensaje
PacketListener myListener = new PacketListener() {
        public void processPacket(Packet packet) {
            if (packet instanceof Message) {
                Message msg = (Message) packet;
                //Obtenemos el bean del usuario
                BeanUsuario bean = (BeanUsuario)msg.getProperty("usuario") ;
               //Almacenamos el usuario en nuestra base de datos
                gestorBaseDatos.store(bean) ;
            }
        }
    }; 
// Registramos el listener.
connector.addPacketListener(myListener, filter);

Y listo, de esta forma tenemos nuestra mini aplicación distribuida perfectamente comunicada y coordinada.
Claro que en un entorno del Mundo Real ™ la cosa sería algo más compleja. Deberíamos enviar un mensaje de confirmación al usuario, tal vez meter esto dentro de un thread… en fin las posibilidades son muchas. Pero la base es esta.

Qué os parece? pensáis que XMPP puede terminar imponiéndose como estándar de comunicación entre máquinas? Qué soléis usar para comunicar vuestras aplicaciones distribuídas?

  • Share/Bookmark

Deja un comentario