miércoles, 22 de mayo de 2013

WebSockets en Java EE 7 (JSR 356)

Los WebSockets son una manera de poder comunicarse vía web entre un cliente y un servidor. A diferencia con otras tecnologías parecidas como los RESTful WebService, es que esta tecnología es bidireccional. El RESTful tiene que constantemente pedir al servidor para ver si hay un cambio, y con algunas técnicas "push" se puede simular una comunicación bidireccional. Con WebSockets, la comunicación es nativa.

Ya que estamos cerca del lanzamiento de Java EE 7 implementado en GlassFish 4.0, veremos un pequeño esbozo de esta tecnología.



WebSockets no es nuevo. Ya existe una norma en W3C que regula su implementación (aunque aún está en revisión, creo que su uso será como el HTML5.. todos lo usan asumiendo que está aprobado). Existen plugins en JQuery para consumirlos, Dojo Toolkit ya lo tiene implementado en su extensión Dojox; en los navegadores desde las versiones Chrome 4, Firefox 8, y Safari 5 ya está implementado (no responderé sobre MSIE)... ahora nos falta el lado del servidor, y esto es lo que veremos en este post. En la versión Java EE 7 vendrá como API para poder montar nuestro servicio WebSockets.

Preparando el Software

A la fecha de este post, he utilizado la versión desarrollo de NetBeans. (Cuando ya sea oficial, pueden descargar la versión completa). Debemos descargar la versión completa para la plataforma, es decir, la de Windows (si usamos Windows), la de Linux (si usamos Linux), etc.. pero no la versión .zip, ya que en esta no viene incluido el GlassFish.

Después de instalarlo, nos preparamos para crear nuestro primer proyecto Web.

Proyecto Web

Creamos nuestro proyecto llamado WebSocketsDemoWeb y usamos el servidor "GlassFish Server 4.0" y elegimos la versión Java EE: Java EE 7 Web.

... y le damos clic en "Finish".

Ahora, crearemos nuestra clase que será el punto de acceso al WebSocket. Creamos nuevo archivo desde File > New (Ctrl+N), y de la categoría "Web" seleccionamos "WebSocket Endpoint"


... clic en "Next".

Ahora debemos declarar las característica que tendrá nuestro WebSocket, como el nombre de la clase, la ruta del websocket, etc.

nombre de clase: HolaTodosEndPoint
paquete: com.apuntesdejava.websocket
WebSocket URI_: /holaTodos



(El "Hola mundo" y "Hola todos" nunca fallan)

Y listo, la clase está creada. Como ahora vienen las clases de java se pueden declarar sin más archivos de configuración (algún archivo de despliegue o xml que configure los websockets) entonces se pudo haber creado una clase simple y a ella colocarle las anotaciones necesarias.

Ciclo de vida de una conexión WebSocket

Las conexiones AJAX con RESTful o SOAP (y toda aplicación web) tienen esta particularidad:
  1. El cliente se conecta al servidor, 
  2. se establece la comunicación, 
  3. el cliente hace el requerimiento, 
  4. el servidor responde 
  5. y terminó la conexión. 
Si se tiene que hacer otro requerimiento, se vuelve a realizar todos los pasos anteriores. De ahí el uso de sesiones en cookies, variables locales y demás artilugios para  asegurar que la siguiente petición se refiere al mismo cliente.

Con el WebSocket esto se hace más simple.
Es decir:
  1. El cliente inicia la conexión con el servidor
  2. Ambos se comunican. El cliente al servidor o el servidor al cliente.
  3. Se termina la conexión.
Ya se parece a una aplicación Desktop (por qué no lo inventaron desde un inicio!? El modelo MVC2 se puede ir al tacho!... bueno, es lo que yo opino)

Implementando el WebSocket

Ahora nos toca escribir el código que estará del lado del servidor.
Escribiremos este código co
n algunas anotaciones del WebSocket:

Código:http://pastebin.com/9bbPPhUw

Notemos que - gracias a las anotaciones - ya no necesitamos implementar ni extender alguna clase. Basta con describir la anotación, Java sabrá que hacer con ese método.

Implementado el cliente

En el lado del cliente lo haremos usando HTML5, así que funcionará con FF, GC y Safari (sorry MSIE). El código es bastante simple, y está comentado para no perder el hilo.

Código: http://pastebin.com/CAgYxzqX


Probando la aplicación

Bastará con ejecutar la aplicación.

Si estamos con Chrome activemos la consola de desarrollo con la tecla F12. Veremos en la pestaña de NetWork solo ha tenido dos peticiones, la primera es la misma HTML y la segunda es la conexión al WebSocket... y tiene estado pendiente!


Hacemos clic en ese nodo para ver el detalle. Podremos ver la cabecera de conexión...

... y los frames de comunicación. Cada vez que escribimos un mensaje en el input veremos cómo se envían los frames entre el servidor y el cliente.


Broadcast a todos los conectados

Este ejemplo es bastante interesante, ya que la idea es enviar un mensaje a todos los usuarios conectados. A diferencia del ejemplo anterior - donde el cliente espera una respuesta de la petición que ha hecho - este ejemplo solo espera que el servidor le diga algo. Notaremos que el cliente no tiene que hacer ni nada, ni tampoco estará consultando cada cierto tiempo al servidor si hay mensajes nuevos.

Lo que haremos es colocar en el servidor un EJB que cada cierto tiempo envíe una comunicación a los clientes. Para ello haremos que nuestro ServerEndPoint sea un ejb singleton, y colocamos un método programado para que envíe cada 10 segundos un mensaje.  Ahora, para saber quienes están conectados, debemos crear una lista de todas las sesiones. Cada vez que se ejecuta el evento @OnOpen se recibirá como parámetro la sesión y se agregará a una lista; y cada vez que se llame al evento @OnClose, haremos que cierra la conexión y quitamos el evento de la lista.

Aquí el código del Servidor:

Código: http://pastebin.com/6UFi1gHv

Y el lado del cliente es lo más simple que puede haber:


Código: http://pastebin.com/ZuSZBkL9

Ahora lo ejecutamos, abrimos desde un navegador, luego abrimos el mismo enlace desde otro navegador y veremos el efecto.


No se qué opinan ustedes, pero para mi es la mejor solución Cliente/Servidor que haya visto para Web.

Conclusión

En este post solo se vió algo muy simple de implementar, pero podemos ir más allá. Por ejemplo, podemos notificar alertas a los clientes - sea web o mobil (que podríamos verlo en otro post) - enviar objetos (que también será tema de otro post), etc.

Mi opinión es que esta tecnología debería enseñarse a la nueva generación de desarrolladores web, y que AJAX sea tomado como un tema de historia.

Referencias

Como yo no me invento estas cosas, naturalmente las he tomado de alguien. Arun Gupta (@arungupta)   ha prestando varias presentaciones  sobre Java EE 7 que es muy conveniente revisarlas.
Además, me he basado de otros links del mismo autor y otros ejemplos.

Código fuente

Y por si las dudas, aquí el código fuente del proyecto. Recuerden utilizar GlassFish 4.0 y que el IDE sea compatible con Java EE .

https://java.net/projects/apuntes/downloads/download/web/WebSocketsDemoWeb.tar.gz

Luego subiré el código en el Mercurial de java.net.

Buen día y bendiciones a todos!