Desarrollo de un Chat con Grails y Ajax Push
Puedes ver la demo del chat en la siguiente dirección: http://jlmonteagudo.cloudfoundry.com/chat
Puedes descargarte el código fuente en la siguiente dirección: chat
Muchas de las aplicaciones con interfaces ricas que actualmente tenemos que desarrollar requieren actualizaciones automáticas, es decir, el servidor envía información al cliente, y éste actualiza los datos de su interfaz de forma automática. Este tipo de actualizaciones actualmente suelen llevarse a cabo a través de AJAX, y con AJAX podemos utilizar dos sistemas de comunicación diferente, pull y push.
Mediante pull, es el cliente quien solicita información al servidor, que puede estar programado para esta solicitud la realice cada cierto periodo de tiempo. Mediante push, el cliente se subscribe a un canal, y cada vez que éste se actualiza el servidor envía automáticamente la información a todos los clientes subscritos al canal.
Con este post quiero demostrar cuán sencillo y rápido es desarrollar un sistema con comunicación AJAX Push y con Grails. Una aplicación de chat nos sirve perfectamente para este cometido, ya que un cliente envía información al servidor y éste automáticamente envía esa misma información a cada uno de los clientes subscritos al canal.
Vamos a ver cómo con unas pocas líneas de Groovy, con el plugin Atmosphere de Grails y con un poco de Javascript podemos crear una aplicación de chat. Toda la documentación del plugin puedes consultarla en la siguiente dirección: https://docs.google.com/View?id=dfdr5d2x_14ccbgm8dm
Una vez creada la aplicación de Grails tenemos que instalar el plugin Atmosphere, que es un framework que nos permite establecer una comunicación Ajax Push:
1 |
grails install-plugin atmosphere |
A continuación creamos un controlador que sólo necesita declarar una closure, en nuestro caso la closure broadcast. El plugin Atmosphere inyecta a todos los controladores de la aplicación el objeto Broadcaster, que como vemos en la aplicación se encarga de difundir un mensaje a todos los clientes subscritos al canal /atmosphere/chat (tan sólo con una línea de código):
1 2 3 4 5 6 7 8 9 |
class ChatController { def index = { } def broadcast = { broadcaster['/atmosphere/chat'].broadcast(params.data) } } |
Por otra parte, creamos un servicio en el que declaramos una propiedad estática llamada atmosphere, y dos closures onRequest y onStateChange:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class ChatService { static transactional = false static atmosphere = [mapping: '/atmosphere/chat'] def onRequest = { event -> event.suspend() } def onStateChange = { event -> if (!event.message) return event.resource.response.writer.with { write "<script>parent.callback('${event.message}');</script>" flush() } } } |
Lo importante aquí es saber que cuando algún cliente invoca la closure broadcast del controlador, se desencadena la ejecución de la closure onStateChange del servicio. Como vemos, esta closure llama el método javascript callback del cliente, que a continuación veremos cómo se define.
Finalmente, tenemos la siguiente página gsp, que es la que mediante Javascript envía la información al servidor y procesa la información recibida por el servidor:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
<html> <head> <title>Chat - jlmonteagudo</title> <meta name="layout" content="main" /> <link rel="stylesheet" href="${resource(dir:'css',file:'chat.css')}" type="text/css" /> <atmosphere:resources/> <script type="text/javascript"> $(function(){ function callback(response) { if (response.status == 200) { var data = response.responseBody; if (data.length > 0) { var mensajeData = $.parseJSON(data); var chatValue = $('#chat').html(); chatValue += "<span class='chatUser'>" + mensajeData.usuario + " dice:</span> " + mensajeData.mensaje + "<br/>"; $('#chat').html(chatValue); $("#chat").attr({ scrollTop: $("#chat").attr("scrollHeight") }); } } } $.atmosphere.subscribe('${resource(dir: '/atmosphere/chat')}', callback, $.atmosphere.request = {transport: 'streaming'}); $('#buttonPost').click(function() { var data = '{"usuario":"' + $('#usuario').val() + '", "mensaje":"' + $('#mensaje').val() + '"}'; $.get('${createLink(action: "broadcast")}?data=' + data); $("#mensaje").val(''); $("#mensaje").focus(); }); }); </script> </head> <body> <div id="pageBody"> <div class="contenedor"> <h3>Sala de Chat - Grails y Ajax Push</h3> <div id="chat"> </div> <div id="chatForm"> <table cellpadding="0" cellspacing="0"> <tr> <td><label>Usuario:</label></td> <td><input type="text" id="usuario" name="usuario" value="" size="10"/></td> </tr> <tr> <td><label>Mensaje:</label></td> <td> <input type="text" id="mensaje" name="mensaje" value="" size="40"/> <input type="submit" id="buttonPost" name="buttonPost" value="Enviar"/> </td> </tr> </table> </div> </div> <div id="chatWarning"> (*) Para probar el funcionamiento de la aplicación se recomienda abrir distintos navegadores y enviar mensajes desde cada uno de ellos. </div> </div> </body> </html> |
En esta página son tres las cosas que tenemos que tener en cuenta:
- Al cargarse la página el cliente se conecta automática al canal /atmosphere/chat:
1 2 3 |
$.atmosphere.subscribe('${resource(dir: '/atmosphere/chat')}', callback, $.atmosphere.request = {transport: 'streaming'}); |
- Al pulsar el botón Enviar, mediante Javascript se genera un mensaje JSON (que contiene el usuario que ha escrito el mensaje, así como el mensaje en sí), y se envía el JSON al servidor:
1 2 |
var data = '{"usuario":"' + $('#usuario').val() + '", "mensaje":"' + $('#mensaje').val() + '"}'; $.get('${createLink(action: "broadcast")}?data=' + data); |
- Finalmente, cuando el servidor envía información al cliente, se ejecuta la función Javascript callback, que proceso el mensaje JSON y lo agrega al div llamado chat.
Esta aplicación es extremadamente sencilla y en ningún momento pretende explotar todas las funcionalidades que el plugin ofrece, pero sirve para poder ver el tipo de aplicaciones que pueden desarrollarse con este framework.
[…] Desarrollo de un Chat con Grails y Ajax Push […]
Vi el demo del chat pero esta extremadamente lento… se supone que tendria q ser rapido y eficiente. xq pasa eso?
Hola Efren,
Lo acabo de probar en mi red local con dos ordenadores diferentes y me ha funcionado correctamente. Lo he probado con Chrome y con Firefox. ¿Qué navegadores utilizas tú?
De antemano muchas gracias es lo que estaba buscando pero creo que no sirve para todos los navegadores en especifico Chrome, intente debugearlo rapidamente pero pude ubicar el error.
Intentare mejorar en todo caso
Soy nueva en Grails…lo he provado en Grails version 2.2.0 y no funciona el index.gsp marca error en toda la parte del script … y no sé el por qué, actualize el plugin, será por eso?
Hola he visto tu aplicacion y se me hace muy interesante, ya lo he implementado y todo va muy bien silo tengo un a pregunta.
¿Como se puede crear salas de conversacion?
ya que solo esta una sala general.
[…] Desarrollo de un Chat con Grails y Ajax Push […]