Implementing Reverse Ajax (Comet) in Grails using Websocket and Atmosphere plugin
Posted By : Nagesh Chauhaan | 02-Jan-2013
Today I am going to share my findings on how to Integrate websocket in grails using Atmosphere plugin. Initially I just wanted to implement all reverse ajax and server push stuff , so I created a simple servlet based application . Later on I realise that if I could added this in a Grails project than it would be very usefull. After a little effort and a few hour of working I am able to integrate atmosphere with java to take advantage of websocket.
I started all this by creating a simple Grails project in STS. Than I added atmosphere plugin to take advantage of its flexible nature. After this I added some code from sample chat app that is given on atmosphere official site and modified the code according to my need. After a number of failure and retry finaly I was able to see expected result.
I used STS and the default server in it does not support websocket ,so i was forced to add apache tomcat to sts before I start runing my application.
Let me now explain some of code sample and changes that I have implemented to make all this working. Added a simple service to the application that is used to map URL so that js files can attach this to websocket side of code.
\grails-app\services\chat\ChatService.groovy
package oodles.grails.chatApp import org.atmosphere.cpr.* import org.atmosphere.cpr.AtmosphereResource.TRANSPORT import grails.converters.JSON class GrailsChatService { static transactional = false static atmosphere = [mapping: '/atmosphere/chatty'] def onRequest = { event -> println "Inside onRequest!" try { AtmosphereRequest req = event.request if (req.method.equalsIgnoreCase("GET")) { println 'Suspending' event.suspend() } else if (req.method.equalsIgnoreCase("POST")) { event.broadcaster.broadcast(req.reader.readLine().trim()) } } catch (Exception e) { println "ERROR!!!!!" } } def onStateChange = { event -> println "Inside onStateChange!" AtmosphereResource r = event.resource AtmosphereResponse res = r.response try { if (event.isSuspended()) { def msg = JSON.parse(event.message) res.writer.write( createMessage(msg.author, msg.message) ) switch (r.transport()) { case TRANSPORT.JSONP: case TRANSPORT.LONG_POLLING: event.resource.resume() break default: res.writer.flush() } } else if (!event.isResuming()) { event.broadcaster().broadcast( createMessage('someone', 'buh bye') ) } } catch (Exception e) { println "ERROR in onStateChange: $e" } } private String createMessage(String author, String text) { return new JSON( [text : text, author : author, time : new Date().time] ) } }
Methods onStateChange and onRequest handles are responsible for most of the application flow from server to client side. onRequest() is responsible to accept a intial request from browser to send alerts automatically.Once a sse connection is establised all other server push of data is done by onStateChange() , as a notification is received it is sent to browser in form of a message. Application starts from index.gsp ,this file looks something like this :
\grails-app\views\index.gsp
<%@ page contentType="text/html;charset=UTF-8" %> <html> <head> <meta name="layout" content="main"> <title></title </head> <body> <atmosphere:resources/> <div id="header"><h3>Oodles | Websocket implementation in Grails</h3></div> <div id="detect"><h3>Websocket Support Status</h3></div> <div id="content"></div> <div> <span id="status">Connecting...</span> <input type="text" id="input"/> </div> <div id="header"><h3>Oodles Technologies Pvt Ltd</h3></div> </body> </html>
\grails-app\conf\AtmosphereConfig.groovy
atmospherePlugin { servlet { initParams = [ 'org.atmosphere.cpr.cometSupport': 'org.atmosphere.container.Tomcat7CometSupport' ] urlPattern = '/atmosphere/*' } handlers { atmosphereDotXml = { } } }
\web-app\WEB-INF\applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="grailsApplication" class="org.codehaus.groovy.grails.commons.GrailsApplicationFactoryBean"> >description>Grails application factory bean <bean id="grailsConfigurator" class="org.codehaus.groovy.grails.commons.spring.GrailsRuntimeConfigurator"> <constructor-arg> <ref bean="grailsApplication" /> </constructor-arg> <property name="pluginManager" ref="pluginManager" /> </bean> <bean id="grailsResourceLoader" class="org.codehaus.groovy.grails.commons.GrailsResourceLoaderFactoryBean" /> <bean id="characterEncodingFilter" class="org.springframework.web.filter.CharacterEncodingFilter"> <property name="encoding"> <value>utf-8>/value> </property> </bean> </beans>
\web-app\WEB-INF\atmosphere-decorators.xml
<decorators> <excludes> <pattern>/atmosphere/*</pattern> </excludes>
\web-app\WEB-INF\sitemesh.xml
<sitemesh> <page-parsers> <parser content-type="text/html" class="org.codehaus.groovy.grails.web.sitemesh.GrailsHTMLPageParser"/> <parser content-type="text/html;charset=ISO-8859-1" class="org.codehaus.groovy.grails.web.sitemesh.GrailsHTMLPageParser"/> <parser content-type="text/html;charset=UTF-8" class="org.codehaus.groovy.grails.web.sitemesh.GrailsHTMLPageParser"/> </page-parsers> <decorator-mappers> <mapper class="org.codehaus.groovy.grails.web.sitemesh.GrailsLayoutDecoratorMapper"/> </decorator-mappers> <excludes file="/WEB-INF/atmosphere-decorators.xml"/> </sitemesh>
Once everything is configured we can get the application running like something like these screens.
In first picture we can check out the status of our server and browser if they support websockts or not . If websocket is supported by the browser than we have to add a name to start chat.
The very first value is used as name and after that all other values are used as a message. First a message is send to the server and after that the server pushes that message to all users.
In last figure we can see that a user named Sridhar is just online and he receives a message from other user Nagesh . This message is a result of server push only as sridhar does not request any message or data from server. Thats all about server push technology that can be implemented in Grails Projects easily.
There are a number of files in a grails project but they all are same and are automatically provided by STS. Here I have added those code files that need to be added explicitly.
Hope it helps !
Nagesh Chauhan
Cookies are important to the proper functioning of a site. To improve your experience, we use cookies to remember log-in details and provide secure log-in, collect statistics to optimize site functionality, and deliver content tailored to your interests. Click Agree and Proceed to accept cookies and go directly to the site or click on View Cookie Settings to see detailed descriptions of the types of cookies and choose whether to accept certain cookies while on the site.
About Author
Nagesh Chauhaan