3/28/2010

Desarrollo rápido de aplicaciones de red: Apache Mina (II) - Nociones básicas

En el artículo anterior explicaba como preparar el entorno de trabajo necesario para desarrollar aplicaciones de red utilizando los framework J2EE, Eclipse y Apache MINA. Un buen punto de partida es como siempre la "Quick start guide" del proyecto, en ella se explican los pasos básicos para la construcción de una aplicación con MINA, en este caso a través del ejemplo del servidor de hora probado con anterioridad, no tiene sentido explicar ese mismo ejemplo así que me centraré en el servidor reverser que es muy similar. No entraré en los parámetros de los métodos de las clases, para eso tenemos la API, ni en el funcionamiento interno del framework, en la guía de usuario que aunque no está completa todavía, sí aborda algo el tema en el apartado 2. Como comenté en la entrada anterior solo daré algunas nociones que puedan ayudar a alguien intentando que esto sea un complemento a la documentación oficial del proyecto ya que solo se explica el código de algún ejemplo y no existe demasiado en castellano sobre MINA.


El paquete del reverser(org.apache.mina.example.reverser) tiene 2 clases y una archivo .html con la descripción del mismo. La clase Main.java es la principal del proyecto como su nombre indica y ReverserProtocolHandler.java tampoco es muy difícil de suponer que va a ser el manejador de los eventos que producen las conexiones entrantes.
Comenzamos la inspección del código por la clase Main.java:



Esta clase es prácticamente igual que en el caso del servidor de hora del ejemplo de la documentación, solo cambia la línea 4 que ahora comentaremos:

1 - La clase NioSocketAcceptor.java es el objeto que se encarga de escuchar las conexiones entrantes.
2 y 3 - Los filtros funcionan como muestra la siguiente imagen enlazada desde éste artículo de la documentación oficial donde se explica todo esto en profundidad. La idea se resume en que el mensaje lo recibe la aplicación por el acceptor y va "subiendo" desde el nivel más bajo de la cadena de responsabilidad a medida que se le van aplicando filtros de la cadena(ioFilterChain) definida sobre ese aceptor cuya misión es tratar las tramas de bits recibidos. Como podemos deducir de lo anterior el orden en que se le aplican es importante ya que, como acabo de decir, la trama recibida se trata secuencialmente.


En nuestro ejemplo se aplican dos filtros(se pueden aplicar todos los que se necesiten en función de nuestras necesidades):
- logger: Crea logs sobre las conexiones, aquí tenemos más información sobre el sistema de logs de MINA.
- codec: Define como se codifican/descodifican las tramas enviadas/recibidas, por ejemplo en el caso de las tramas recibidas dice como debe de interpretar los bytes la aplicación para poder utilizarlos. En este caso utiliza un codec que incorpora MINA para las tramas de texto que son las que necesita este servidor reverser, al igual que pasaba con el servidor de tiempo. Este sistema modular nos permite que la aplicación soporte nuevos protocolos siendo necesario solamente definir un nuevo codec.
4 - Establece en manejador para los eventos producidos por nuestro aceptor(conexiones entrantes), es el que contendrá la lógica de negocio de la aplicación y por lo tanto es donde centraremos nuestro desarrollo.
5 - Hace el bind del socket con el puerto.

Nos queda definir la lógica de negocio en el manejador de nuestra aplicación, en el ejemplo ésta reside en la clase ReverseProtocolHandler.java.




De aquí nos interesa el método messageReceived(), el propio framework nos ofrece en el parámetro message la trama de bits recibida y en el parámetro session la sesión TCP para poder responderle, el desarrollador no tiene que preocuparse de nada más. La lógica de negocio en este caso es convertir la trama a tipo String y darle la vuelta a los caracteres. A continuación se escribe en la sesión, en este caso en forma de tipo String por tener definido el codec para texto como se explicó mientras hablábamos de los filtros. Si no se hubiera definido niguno habría que enviar los bytes usando para ello algún método del objeto a enviar para obtenerlos(.getBytes() normalmente). Para hacer una pequeña prueba podemos crear una cadena nueva y enviarla añadiendo esta línea al final y vemos que ahora el reverser es más simpático... :) :
String probing = ":)"; session.write(probing);

(Nota: El telnet de Windows hace cosas un poco raras, desconozco el porqué pero para nuestras pruebas es suficiente, en el artículo anterior las pruebas eran con un Debian y funciona perfectamente.)




El método messageReceived() lo invoca el framework cuando se produce un evento(llegada de un mensaje) pero podemos añadir comportamiento a la aplicación definiendo otros, ¿cuales defino? Si miramos el código de ReverseProtocolHandler.java vemos que extiende a la clase IoHandlerAdapter.java redefiniendo sus métodos, así que nos vamos a la API y encontramos que además tiene los siguientes métodos: exceptionCaught(), messageSent(), sessionClosed(), sessionCreated(), sessionIdle() y sessionOpened(). Los nombres son lo suficientemente explicativos así que para terminar vamos a hacer otra pequeña prueba para comprender un poco mejor el funcionamiento y de paso hacer nuestro servidor un poco más educado y conseguimos que nos salude al conectarnos. Redefinimos el método sessionOpened() añadiendo el siguiente trozo de código antes del método messageReceived() y listo :) .


@Override
public void sessionOpened(final IoSession session){
String probing = "Hi!, how are you?";
session.write(probing);
}



Intentaré escribir alguna entrada más tratando más en profundidad temas como la gestión de logs, conexiones asíncornas o la creación de codecs nuevos.


Jesús Pérez

3/27/2010

Desarrollo rápido de aplicaciones de red: Apache Mina I - Primeros pasos

En mi primera entrada voy a empezar por algo sencillo pero que creo que puede servirle a alguien como una primera introducción a este framework ya que no hay demasiado en castellano por Internet sobre el tema. Una parte de mi PFC(Proyecto Fin de Carrera) consiste en el desarrollo de una pasarela que realiza una conversión entre protocolos distintos(ambos sobre TCP/IP), mi primera intención fue elegir C, por ser el lenguaje de programación que mejor conozco y el que utilizamos durante la carrera para las aplicaciones de este tipo. Debido a mi PFC es el objetivo de una beca de prácticas de trabajo en una empresa, por motivos corporativos, de entre las opciones que me ofrecieron me quedé con Java por haber desarrollado con anterioridad alguna aplicación web con el IDE Eclipse y el framework J2EE. Esto que en principio me pareció un pequeño problema resultó ser una ventaja porque aunque se necesite un tiempo de adaptación al nuevo entorno y unas cuantas horas de lectura y repaso de código, al final permite aprovechar las ventajas de la programación orientada a objetos para implementar una aplicación de red multipropósito teniendo solo que definir los protocolos involucrados y el comportamiento específico que se precise para el tipo de aplicación.

Las primeras pruebas que realicé fueron con las clases del framework JAVA.NIO (New Input Output) incluido en J2EE, recomiendo éste texto que introduce al lector al tiempo que se desarrolla una aplicación de chat típica por lo cual resulta bastante entretenido. Buscando un framework para el desarrollo de aplicaciones de red en Java(porque en Java hay frameworks para todo :) ) apareció Apache MINA(Multipurpose Infrastucture Networked Aplications) que es un proyecto de Apache que está aun madurando pero ya existen productos desarrollados con esta tecnología. Entre sus ventajas destaco las siguientes:
- Es multipropósito, como dije antes solo es preciso implementar el codec para los protocolos necesarios y el coportamiento específico de la aplicación(lógica de negocio)..
- Utiliza JAVA.NIO, el nuevo framework de Java con nuevas características y mejoras en rendimiento respecto a JAVA.IO.
- Gestiona eficientemente la respuesta a multiples peticiones simultáneas evitándole al programador la necesidad de gestionar de múltiples threads.
- Funciona sobre TCP o UDP.
- Diseñado especificamente para el fin, por ejemplo, los buffers de envío y recepción se expanden y se contraen solos(si queremos).
- Dirigido por eventos, solo hay que definir las acciones a llevar a cabo en funcion de los eventos(conexión creada, mensaje recibido ...) que se producen.
- Todas las ventajas de los lenguajes de programación orientados a objetos(reutilización, abstracción ... )


Vamos al tema, empezaremos por explicar los pasos necesarios para poner a andar todo esto ya que el framework es un proyecto de Apache Maven y quiero usar Eclipse para desarrollar. El primer paso es instalar el Maven, en mi Debian: apt-get install maven2 . En Windows los instaladores los encontramos con éstas instrucciones de instalación.


El siguiente paso es descargar el framework de MINA, en este caso elijo la versión 2.0.0-RC1 aunque es inestable porque hay bastantes cambios respecto a la 1 en algunas de las clases y en el fururo prodrían aparecer problemas de compatibilidad en la aplicación. A continuación se descomprime y se accede a la carpeta por consola ya que no hay plugins de Maven oficial para eclipse y los que probé no me acabaron de funcionar correctamente. Accedemos hasta la carpeta mina-example por ser los proyectos de ejemplo con los que vamos a realizar las pruebas: cd mina-2.0.0-RC1/src/mina-example/ y le decimos al Maven que cambie el proyecto al formato del Ecplise: mvn eclipse:eclipse





Tarda un rato en actualizar el propio Maven(si es preciso) y descargar las dependencias del proyecto. Cuando termina ya tenemos el proyecto en formato del Eclipse así que lo importamos (File/Import.../Existing Projects into Workspace y le damos a Next), en la siguiente pantalla elegimos la ruta del proyecto mina-examples y marcamos la opcion de copiarlo al espacio de trabajo:





Nos aparecerá el proyecto a la izquierda en el explorardor de proyectos y vemos que hay errores, falta definir la variable M2_REPO con la ruta del repositorio de Maven (donde están las dependencias que descargó antes) en la configuración del Eclipse: (Window/Preferences/Java/Classpath Variables/New...)
(Nota: En Linux la carpeta .m2 está en el home del usuario y en Windows la encontramos en Documents and Setting/Usuario en el XP y en Users/USUARIO en el Vista/7).





Se recomendará una recompilación y comprobamos que ahora sí encuentra las dependencias por lo que ya se puede ejecutar el primer ejemplo. Empezamos por el el ejemplo más sencillo, org.apache.mina.example.gettingstarted.timeserver es un servidor que lo único que hace cuando recibe cualquier petición es devolver la fecha y la hora del sistema. Ejecutamos la clase MinaTimeServer.java:





Para comprobar que funciona nos podemos conectar con telnet al puerto 9123(sale en el código fuente de la clase en la primera línea): telnet localhost 9123, lo que se muestra por consola son los logs de la aplicación, porque !MINA incorpora también la gestión de logs!.





Otro ejemplo con la aplicación reverser: telnet localhost 8080





Listo, ya tenemos MINA funcionando. En una próxima entrada intentaré resumir algunas nociones básicas para saber por donde empezar y no perdernos con tal cantidad de clases cuando todavía no escribimos una sola línea de código, pero eso sí, tenemos una implementación de una aplicación de red multipropósito con distintos ejemplos probados para poder experimentar y no empezar de 0, algunos incluso con sus correspondientes tests del JUnit (en el paquete src/test/java del proyecto). Asegurándonos además de que las clases que los componen, y por lo cual las que usaremos nosotros para nuestra aplicación, están diseñadas y probadas por los desarrolladores de Apache MINA con las ventajas que ello supone.



Jesús Pérez