You are on page 1of 10

The MetaSieve Blog

August 25, 2010


Using Cometd 2.x with Grails
Filed under: Uncategorized Tags: grails, cometd, bayeux, ajax Bjrn Wilmsmann @ 8:16 pm Ive been struggling quite a bit to get CometD (see this for more information on the Comet Pattern) working with Grails and the corresponding Grails CometD plugin, the documentation being sparse and the new version of the CometD API being somewhat confusing with lots of new interfaces and abstractions. Anyway, Ive got it working so I thought, I might share the experience (see code sections at the end of the article for copying source code): Assuming that you would like a Grails service to communicate via CometD, this is how a basic implementation of a simple service that finds Account entities for search parameters would look like:

AccountService.groovy There are a few things to note here. The service implements the interface InitializingBean provided by Spring. This interface supplies an afterPropertiesSet() method that allows you to initialize additional properties after Spring has wrought its magic. This way you can initialize and handshake with the Bayeux server using the bayeux bean thats injected by the CometD plugin. The runAsync closure in findAccounts(def query, def params = [:]) is provided by the Grails Executor plugin, which allows you to run background threads in your application without losing the Hibernate session. This is exactly why runAsync is needed here. As findAccounts(def query, def params = [:]) will be called asynchronously we have to make sure everything that occurs inside this method is thread-safe. Via the subscribe() method you can have a so-called SubscriberListener subscribe to a messaging channel:

MySubscriberListener.groovy Such a SubscriberListener has to extend the abstract class SessionChannel.SubscriberListener as provided by org.cometd.bayeux.client.SessionChannel. This class demands that its sub-classes provide an onMessage(SessionChannel channel, Message message) method, which will act as a callback method upon new messages on the channels the listener is subscribed to. Apart from that, theres some nice Groovy magic happening here that allows you to instantiate a which calls a service method without actually hard-coding that service method in the MySubscriberListener class. In this case, the listener will call the services findAccounts(def query, def params = [:]) method.
MySubscriberListener

The client side of this little example would look something like this (assuming youre using the jQuery version of the CometD client), with cometd-subscriptions.js being loaded first and init.js afterwards:

cometd-subscriptions.js

init.js Once this is all set up, you can do something like this to nudge AccountService from your JavaScript client:
$.cometd.publish('/requests/search', { 'payload': { q: 'some query', params: { offset: 10 } } });

I hope this primer helps you to overcome initial problems with CometD and Grails. If youve got any questions or additional contributions, please feel free to leave a comment. Source code for copy & paste: AccountService.groovy:
package myapp import grails.converters.JSON import org.springframework.beans.factory.InitializingBean class AccountService implements InitializingBean { def bayeux

def bayeuxSession static transactional = true void afterPropertiesSet() { bayeuxSession = bayeux.newLocalSession() bayeuxSession.handshake() bayeuxSession.getChannel('/requests/search').subscribe(new MySubscriberListener (this, 'findAccountsToFollow', ['q', 'params'])) } def findAccounts(def query, def params = [:]) { runAsync { def accounts = Account.findAllByName(query, params) // publish to search results channel bayeuxSession.getChannel('/results/search').publish(['payload':['accounts':accounts]] as JSON) } } }

MySubscriberListener.groovy:
package myapp import org.cometd.bayeux.Message import org.cometd.bayeux.client.SessionChannel class MySubscriberListener extends SessionChannel.SubscriberListener { def callbackService def callbackMethod def callbackParams public MySubscriberListener(def service, def methodName, def params = []) { callbackService = service callbackMethod = methodName callbackParams = params } public void onMessage(SessionChannel channel, Message message) { // callback def callbackParamValues = [] callbackParams.each { param -> callbackParamValues.add(message.data.payload."${param}") } callbackService."${callbackMethod}"(*callbackParamValues) } }

cometd-subscriptions.js:
var subscriptions = {}; function refreshCometSubscriptions(channels, callbackFunctions) { for (var i in channels) { if (typeof channels[i] == 'string') { unsubscribeFromCometChannel(channels[i]); subscribeToCometChannel(channels[i], callbackFunctions[channels[i]]); } } } function unsubscribeFromCometChannel(channel) { if (subscriptions[channel]) { $.cometd.unsubscribe(subscriptions[channel]);

} subscriptions[channel] = null; } function subscribeToCometChannel(channel, callbackFunction) { subscriptions[channel] = $.cometd.subscribe(channel, callbackFunction); }

init.js:
// initialize cometd var channels = ['/test', '/results', '/results/search']; var testCallback = function() { }; var resultsCallback = function() { }; var searchResultsCallback = function(message) { renderFollowerSearchResults(JSON.parse(message.data).payload.accounts, '#resultsContainer', 'resultList', '#box2'); }; var callbackFunctions = { '/test' : testCallback, '/results' : resultsCallback, '/results/search' : searchResultsCallback }; $.cometd.init('../../cometd'); $.cometd.addListener('/meta/connect', function(message) { if ($.cometd.isDisconnected()){ return; } if (message.successful) { $.cometd.publish('/test', { 'data': { 'message':'Connection with CometD server has been established.' } }); } }); refreshCometSubscriptions(channels, callbackFunctions);

Share this:

StumbleUpon

Digg

Reddit

Possibly related posts: (automatically generated) Starting Out With Comet (Orbited) Part 3 The Client Comments (10)

10 Comments
1. Thx for that! Very handy that I started the cometd part of my project today and not last week Comment by Jim August 26, 2010 @ 3:34 pm Reply 2. One additional note: Ive refactored MySubscriberListener a little. Instead of extending SessionChannel it now implements ClientSessionChannel and also takes ClientSessionChannel instead of SessionChannel as an argument for onMessage. Sorry for this but some old beta Bayeux JavaDoc still mentioned SessionChannel as a valid API end point whereas a more recent beta JavaDoc deprecates SessionChannel. As I said the Bayeux documentation currently still is a bit of a mess

Comment by Bjrn Wilmsmann August 27, 2010 @ 2:08 am Reply 3. Hi, I have created a grails application, installed the cometd plugin, the executor plugin and the jquery plugin. And also create the service and the listener. I have the following p1.gsp but does post requests to http://localhost:8080/cometd/handshake with error 400 bad request. Any idea? Sample title $(document).ready(function(){ }); $.cometd.publish(/requests/search, { payload: { q: some query, params: { offset: 10 } } }); Sample line Comment by Franki September 1, 2010 @ 11:58 am Reply There some typo errors: -in AccountService.groovy the MySubscriberListener constructor use findAccountsToFollow instead of findAccount. -also use /requests/search instead of /results/search I also have change $.cometd.init(../../cometd); on init.js to use an absolute url,in my case http://localhost:8080/poc-cometd/cometd Comment by Franki September 1, 2010 @ 2:13 pm Reply 4. Thanks for the corrections. Comment by Bjrn Wilmsmann September 3, 2010 @ 11:25 am Reply 5. Hi, Could you tell me what version of Grails this app was created with? Im trying to use the plugin with Grails 1.3.4 but it doesnt seem to want to play ball. Regards. J. Comment by J. Morgan September 13, 2010 @ 11:41 pm Reply 6. I did this using Grails 1.3.4, too.

Comment by Bjrn Wilmsmann September 14, 2010 @ 8:11 am Reply Hi, Im new to Grails but would love to get the Cometd plugin working for a project at work. Im still having trouble and I have a few questions: 1. The MySubscriberListener cant find the org.cometd.bayeux.Message or the import org.cometd.bayeux.client.ClientSessionChannel classes. Do you include the bayeux-api-2.0.0.jar as a libarary? 2. If I do include the bayauex-api library, I get the following error: MySubscriberListener.groovy: 4: Cant have an abstract method in a non-abstract class. The class MySubscriberListener must be declared abstract or the method boolean isService() must be implemented. Comment by J. Morgan September 14, 2010 @ 4:43 pm Reply 7. Oh ok, thanks. I shall persevere. J. Comment by J. Morgan September 14, 2010 @ 10:54 am Reply 8. No, you dont have to include that JAR yourself. Its included in the plugin. Comment by Bjrn Wilmsmann September 14, 2010 @ 4:56 pm Reply

RSS feed for comments on this post. TrackBack URI

Leave a comment
Name (required) E-mail (required) Website

Submit Comment

Notify me of follow-up comments via email. Notify me of site updates

Archives
August 2010 July 2010 June 2010 May 2010 April 2010 March 2010 January 2010 December 2009 September 2009 June 2009 May 2009 April 2009 March 2009 February 2009 January 2009 December 2008 October 2008 September 2008 July 2008

Blogroll
allmyTea blog Baynados Suchmaschinen Blog BusinessSear.ch Deutsche Startups Deutschland braucht Witten Emtain Your Multimedia Find Engine GameSear.ch Google Webmaster Central Inexpensive Project Management Software MetaSieve Search Technology MusicSear.ch PetSear.ch PokerSear.ch

ScienceSear.ch Search Engine Land TechCrunch Topicalizer

General
Impressum
Search

Recent Posts
Using Cometd 2.x with Grails i18n in web apps with Dashcode A UI concept for iOS 4 multitasking on the iPad PlzFinder & GermanPostalCode Plugin verffentlicht Natural Language Processing in Groovy: A Primer Using Groovy for processing and analysing textual data

Tags
Bjrn Blog Cheats

ContentSieve Cuil Debunking emTain emtain 0.4 Facebook Game Google

Gamers Games

Gamesear.ch Game Search


Online Marketing

Game Search Engine german

grails groovy

industry joke jsf Market Entry Meta

MetaSieve News

plugin Press Release

rails Rambler Reviews Scoring Scoring Algorithm

Search Search Engine Serendipity shopping cart Software

software development StudiVZ turbogears video marketing web application frameworks Web Page Wordpress
Recent Comments
vgrichina on Continuous Testing with G Bjrn Wilmsmann on Using Cometd 2.x with Gra J. Morgan on Using Cometd 2.x with Gra J. Morgan on Using Cometd 2.x with Gra Bjrn Wilmsmann on Using Cometd 2.x with Gra

Twitter Updates
MetaSieve: MetaSieve Blog Using Cometd 2.x with Grails - I've been struggling quite a bit to get CometD (see this for more info... http://ow.ly/18J873 August 26, 2010 MetaSieve: MetaSieve Blog Using Cometd 2.x with Grails - I've been struggling quite a bit to get CometD (see this for more info... http://ow.ly/18J873 MetaSieve: MetaSieve Blog i18n in web apps with Dashcode - For quite some time I've been searching for a way to properly locali... http://ow.ly/18F2qc August 21, 2010

MetaSieve: MetaSieve Blog i18n in web apps with Dashcode - For quite some time I've been searching for a way to properly locali... http://ow.ly/18F2qc MetaSieve: MetaSieve Blog A UI concept for iOS 4 multitasking on the iPad http://ow.ly/189C8f July 16, 2010 MetaSieve: MetaSieve Blog A UI concept for iOS 4 multitasking on the iPad http://ow.ly/189C8f MetaSieve: MetaSieve Blog PlzFinder & GermanPostalCode Plugin verffentlicht http://ow.ly/17BNWX June 4, 2010 MetaSieve: MetaSieve Blog PlzFinder & GermanPostalCode Plugin verffentlicht http://ow.ly/17BNWX MetaSieve: MetaSieve Blog Natural Language Processing in Groovy: A Primer Using Groovy for processing and analysing textual d... http://ow.ly/17nyYa May 15, 2010 MetaSieve: MetaSieve Blog Natural Language Processing in Groovy: A Primer Using Groovy for processing and analysing textual d... http://ow.ly/17nyYa

Schneier Facts
Bruce Schneier Fact Number 863 Bruce Schneier's first pet name, mother's maiden name and all other answers to his secret questions are random 1024 byte blocks.
NetworkedBlogs Blog: The MetaSieve Blog Topics: search, technology, web Follow my blog

Blog at WordPress.com.

You might also like