Spring Web Sockets con STOMP y SockJS (y Spring Boot)

Este nuevo post presenta un sencillo ejemplo del desarrollo de una aplicación web (lanzada e inicializada mediante Spring Boot) que hace uso de web sockets para establecer una comunicación bidireccional asíncrona entre el cliente (el navegador web) y el servidor (la aplicación ejecutada en el Tomcat embebido de Spring Boot).

Para ello usaremos:

- Spring Boot, para inicializar la aplicación, resolver las dependencias, ejecutarla en el Tomcat embebido, etc.

- Spring Web Sockets, para implementar la parte correspondiente a ellos.

- STOMP y SockJS para crear el cliente en JavaScript que se ejecutará desde una página web en el navegador y facilitar las comunicaciones via JSON.

El objetivo es desarrollar una aplicación web que:

- Conecte al servidor, estableciendo el canal de comunicación para recibir los mensajes desde el servidor.

- Envíe un mensaje al servidor y reciba una respuesta que mostrará por pantalla.

- Permanezca a la escucha de los mensajes que le lleguen desde el servidor y los imprima por pantalla.

- Finalice la conexión al servidor, cerrando el canal de comunicación establecido por los web sockets.

Los pasos a seguir serían los siguientes:

1. Inicialización de la aplicación con Spring Boot

Para conocer algo más sobre Spring Boot se puede consultar este otro post sobre el tema dentro del blog, o la documentación oficial. Basicamente lo que se pretende es usar Spring Boot para preparar una aplicación con todas las dependencias y configuraciones iniciales necesarias para desarrollar una aplicación web que soporte Spring Web Sockets. Usaremo Spring MVC y Spring Web Sockets para esto. Para ello creamos un proyecto Java en Eclipse, lo mavenizamos y añadimos las siguientes dependencias al pom.xml (podemos partir de la aplicación desarrollada en el post antes mencionado sobre Spring Boot):

 

2. Creamos los DTOs que usaremos para transferir la información entre el cliente y el servidor y viceversa

La aplicación que vamos a desarrollar es una sencilla web que facilita información meteorológica en tiempo real. Contaremos con un sencillo formulario web que permite introducir el nombre de una ciudad. Se envía el nombre de la ciudad al servidor y se recibe la información meteorológica sobre esa ciudad en ese momento. Además cualquier cliente que este escuchando en ese momento en el mismo canal, recibirá la misma información. También se puede recibir la información meteorológica en tiempo real si se escucha en el web socket y esta se envía de forma periódica desde el servidor.

Por lo tanto, tenemos dos tipos de mensajes. El que envía el cliente al servidor con el nombre de la ciudad, y el que envía el servidor al cliente con la información meteorológica. Esta información se enviará en JSON pero dentro del servidor se implementará como un par de DTOs o POJOs o Java beans, es decir, como un par de clases sencillas con sus atributos y metodos de acceso (get/set).

Como no tenemos capa de persistencia ni acceso a base de datos o similar, crearemos un mapa con datos en memoria (WeatherData.java) donde insertaremos unos cuantos DTOs con información meteorológica de tres lozalizaciones, con datos como el tipo de tiempo, la hora en formato ISO o el lugar. 3. Configuración de los Web Sockets La configuración de los web sockets se hace en este caso como casi todo en Spring Boot, mediante una clase java y usando anotaciones.

En este caso, indicamos con @Configuration que es una clase de configuración de Spring (lo que eran antes los antiguos XML). Ademas con @EnableWebSocketMessageBroker indicamos que vamos a configurar los web sockets. En el primer método establecemos el mecanismo (broker) que escribira en el canal de mensajes del servidor a los clientes y cuya URI (del canal) empezará por “/topic”. En este caso es un broker sencillo que solo mantiene sus mensajes en memoria (sin persistencia). También indicamos que los mensajes de los clientes al servidor irán dirigidos a una URL que empezará por “/app”. En el segundo método se establece “/weather” como la URI del web socket creado y se indica que se espera SockJS para establecer la comunicación desde el cliente.

4. El controlador (y los posibles servicios)

Esta parte comprende la implementación del web socket en sí. En este caso vamos a usar un @Controller de Spring MVC para desarrollar un servicio que recibe el mensaje desde el cliente y responde desde el servidor usando el web socket antes configurado.

También se puede desarrollar un servicio, bien a nivel de controlador web, o bien a nivel de servicio, que escriba directamente en el web socket desde el servidor. El cliente que este escuchando en el canal recibirá el mensaje igualmente.

 

Las anotaciones que aplican a los Web Sockets son @MessageMapping(“/weather”) y @SendTo(“/topic/weatherinfo”). La primera indica que la URL a la que hay que invocar para enviar el mensaje al web socket es “/weather”. Entonces el JSON enviado se parseará automaticamente al DTO que es el argumento del método, como en un servicio web o un método de Spring MVC. La segunda anotación indica que la respuesta del método se enviará automaticamente a “/topic/weatherinfo”. Esa sería la URL del canal de respuestas desde el servidor en el que tendría que escuchar el cliente para recibir las respuestas.

Tenemos por lo tanto un método (o servicio) que recibe mensajes de los cliente y los contesta usando el web socket (getWeatherInfo). Pero además tenemos un método (runWInfo) que escribe directamente en el web socket sin más (si se descomenta la anotacion de @Scheduled se ejecutará cada 2 segundos). Para ello hace uso del SimpMessagingTemplate, indica la URL del web socket a escribir y el mensaje a enviar (el DTO).

Resumiendo:

- Hemos configurado un web socket “llamado” “/weather” (ver punto 3).

- Los clientes escriben al web socket si envían un mensaje a “/app/weather” (ver punto 3).

- El servidor escribe al web socket si envía un mensaje a “/topic/weatherinfo” (ver punto 3 y 4).

- Se puede usar el web socket para recibir del cliente y enviar desde el servidor (como en un servicio web o peticion HTTP estandar) o solo para enviar desde el servidor.

5. El cliente de JavaScript

El cliente es una página HTML muy sencilla que hace uso de las siguientes funciones de JavaScript:

 

A su vez estas funciones hacen uso de las librerias stomp.js y sockjs.js. El objetivo es implementar un cliente en JavaScript que:

a. Establezca la conexión con el web socket del servidor.

b. Envie mensajes por el web socket.

c. Escuche todos los mensajes que le lleguen por el web socket y los muestre por pantalla.

Para ello primero ha de conectar. Al conectar, se suscribe al canal del web socket, es decir, ya lo puede usar para enviar y recibir. Despues puede enviar o simplemente permanecer a la escucha. En el momento en el que lea algo del canal, lo procesará. Esto es lo que hace los metodos antes expuestos. El método connect establece la conexión con el web socket “/weather” y suscribe el cliente al canal de escucha “/topic/weatherinfo” que es donde escribe el servidor. En el momento en que recibe algo, lo pasa al método showWeather para que lo procese como un JSON. El método sendPlace() escribe un JSON a la URL “/app/weather”, que como establecimos en la configuración, es la URL en la que el servidor recibirá los mensajes del cliente.

6. El ejemplo

Este sería el aspecto del ejemplo. En un primer momento aparece como desconectado:

Desconectado

Desconectado

Pulsando en el boton de “Connect” establecemos la conexión entre el cliente y el servidor a través del web socket.

Conectado

Conectado

Ahora podemos simplemente esperar a que el servidor nos envie algo, o podemos preguntar nosotros, usando el campo y el boton de “Send place”. Si ponemos “Madrid” y pulsamos…

Envio/Respuesta

Envio/Respuesta

Para apreciar la potencia de la comunicación via web sockets, si abrimos otro navegador y vamos a la misma página, recibiremos también el mismo mensaje del servidor aunque no hayamos preguntado. Si estamos conectados estamos escuchando al mismo canal de comunicación y recibiendo los mismos mensajes que el resto de los clientes. Un ejemplo práctico de esto podría ser un chat o cualquier aplicación en tiempo real.

Modo chat

Modo chat

Se puede descargar el proyecto para Eclipse a modo de ejemplo de este enlace. Y el código fuente desde Github. Más información sobre Spring Web Sockets desde la web oficial.