Wednesday 27 July 2011

ICE PUSH PLUGIN EXAMPLE EXPLAINED


Recently in one of my project i was given the task to create a sort of notification area in my web page , some call it reverse-ajax , the thing i am talking about is like suppose you are on yahoo chat and some of your friend comes up and you are notified that your friend is available now to chat..

So in our grails world i searched a little but and found out about this plugin ICE PUSH which catered to my need just fine ..
So i will explain it in a form of an example..

Suppose i have a page whose name is actionToCall(wierd name i know.. silly me) , when a user hits the page it just shows how many instances are there of a particular table say User table , it just displays the number of instances of the users present in the system ..

So for that i need a controller UserController and action page
here's the code of UserController::

class UserController {

    def index = { }


    def actionToCall = {
        new User(name: 'nikhil').save(flush:true) // just makes a new User object
    }

}

Now the page ,

<%@ page contentType="text/html;charset=UTF-8" %>
<html>
  <head><title>Simple GSP page</title>
 
  </head>
  <body>
  Place your content here
  </body>
</html>

SO there is nothing on the page right now .. WHAT i want to do is if any user hits this page , i want to just save a new User to be added and also show up how many Users are there in the database.. I will clarify it further.

Suppose i login from chrome browser and hit this page , assuming that there are no Users present in the User table currently. I want 1 to come up in the page showing that there is 1 User row in the table..
Next when i start another browser like Mozilla or start a private session in chrome (can be done via ctr+shift+n key) , and hit the same page , i want the user count to show me 2 , ofcourse it would because before coming to the page i had hit the action like (http://localhost:8080/SampleApp/user/actionToCall) { you must be thinking what's cool in that huh..}
but what i also want here is that when i open up my previous window WITHOUT REFRESHING THE PAGE (yeah important thats why in caps) and i want the User count to be shown as 2 as well..

Try to understand the difference .. Normally the ajax call is intitiated by some event that the user initiates like clicking a button , clicking a link or so on .. But the magic here is that what seems to happen is the server actually pushes the content to the browser (server's client) automatically.. This is however not possible , its just an ILLUSION that it happens..

Actually we want to do something like Polling the server continuosly via Ajax calls and then that way the server can give us the new thing on that page ..
Polling is well like continuously asking someone for something .. Like when i used to do with my mom every time she called from office i would ask her that what she will bring for me today?? ..( Most of the times she did , lucky ME)..

So we do must do this polling (continously asking the server if there is anything new with him) ,
hmmm.. ok so when should we poll him ??
Well the obvious answer is that when someone comes at this page right ?? So we have 2 options .. the polling code should be written either
1) in the interceptor blocks (yeah.. same old things which either do something either before when request comes in or does something after the action has completed before view)
2) in the filter right .. hmmm.. same thing but scalable much easily so i will write the code in one of the filter class..


Wait i was here to tell you about this nice plugin right ??

Ok let me go step by step

Step1) Install the plugin
grails install-plugin icepush (http://www.grails.org/plugin/icepush) Now i have icepush plugin hahahaha.. so whats great in it use it silly me ..

Step2) Ok.. i want ice push capabilities in my page shown above .. so i modify the code of the page to..Spot the difference:

<%@ page contentType="text/html;charset=UTF-8" %>
<html>
  <head><title>Simple GSP page</title>
  %{--TO GET ICEPUSH CAPABILITIES AT THIS PAGE--}%
  <icep:bridge/> (JUST THIS LINE)
  </head>
  <body>
  Place your content here

  </body>
</html>

Step3) This is the hard part , i told you that we would make ajax calls to the server automatically when we would be notified
       or did i ?? Sorry if i didn't.. (This amnesia thing) ..
     
       Ok so we want a region from where we can make ajax calls so the plugin has it.. Do i need to say spot the difference again..


<%@ page contentType="text/html;charset=UTF-8" %>
<html>
  <head><title>Simple GSP page</title>
  %{--TO GET ICEPUSH CAPABILITIES AT THIS PAGE--}%
  <icep:bridge/>
  </head>
  <body>
  Place your content here

  %{--rendering from util controller's alert action--}%
  <icep:region group="notifyGroup" controller="util" action="alert" /> (ONLY THIS MUCH)

// THIS JUST CREATES A region from where ajax calls would be made to the action whose name is alert which is present in controller util (yeah... i need to create that as well ..)

hmm.. you would be thinking how it works , actually when the page loads up for the first time , it just loads the content initially from this controller action but subsequent Ajax GET (yes GET method) calls are made to this action (alert) when this region is notified by its GROUP-NAME attribute (did i say notify.. ??)

</body>
</html>

Well now with the notify part , well i gave you a hint earlier that notify code part i would put in a filter class..
Here is the code :

import org.icepush.PushContext
import org.codehaus.groovy.grails.web.context.ServletContextHolder as SCH


class FlowcheckFilters {

    def filters = {
        all(controller: '*', action: '*') { // NOW you can easily customise this for a specific controller action

            after = {
                push 'notifyGroup' // This is how the push call is made to a specific group named notifyGroup
            }
        }
    }

    def push(String s) {    // yeah the function definition..
     
        PushContext.getInstance(SCH.servletContext).push s // Now we call the method getInstance on the                                                                                     //                                                                 class  PushContext provided by the plugin which needs
//                                                              a ServletContextHolder and we call the actual
 //                                                             push method of the plugin easy right ??
    }
}

and yes , the util controller , well you can put anything here .. but as per our use case , this is the code:


class UtilController {

    def index = { }

    def alert = {
        render "${User.count()}" // Just renders the User count and we are done !!!
    }
}

yippi.. I thought i would not to be able to complete this blog .. Eaten up quiet some time of yours right..
So heres the flow::

User1 logs in from chrome , hits the page , the page loads up initially the user count rendered is 1
User2 logs in from firefox , hits the page , the page loads up and shows user count 2
But when you switch to chrome window it shows 2 there automatically because ---->
When user2 from firefox hit the action it also hit the after filter block which notified the grou i.e. told the browser to make a fresh ajax call in to the given controller - action pair as in the <icep:region> tag , so it made the request without user1 who is on firefox automatically on his behalf and thats why without refeshing the page , we also get User count is 2 on the firefox window.. Yippi we are done..

Now we can create some features from use of this plugin.. I know the demo is the most simplest it could have been but  if i get across the point , as one of my friend says ITS AWESOME . pls do refer to the plugin page http://www.grails.org/plugin/icepush
you will find a whole lot more there ..

Thanks for reading
Keep blogging