How to easily implement "who is online" in Grails or Java Application?

20,167

Solution 1

You need to collect all logged in users in a Set<User> in the application scope. Just hook on login and logout and add and remove the User accordingly. Basically:

public void login(User user) {
    // Do your business thing and then
    logins.add(user);
}

public void logout(User user) {
    // Do your business thing and then
    logins.remove(user);
}

If you're storing the logged-in users in the session, then you'd like to add another hook on session destroy to issue a logout on any logged-in user. I am not sure about how Grails fits in the picture, but talking in Java Servlet API, you'd like to use HttpSessionListener#sessionDestroyed() for this.

public void sessionDestroyed(HttpSessionEvent event) {
    User user = (User) event.getSession().getAttribute("user");
    if (user != null) {
        Set<User> logins = (Set<User>) event.getSession().getServletContext().getAttribute("logins");
        logins.remove(user);
    }
}

You can also just let the User model implement HttpSessionBindingListener. The implemented methods will be invoked automagically whenever the User instance is been put in session or removed from it (which would also happen on session destroy).

public class User implements HttpSessionBindingListener {

    @Override
    public void valueBound(HttpSessionBindingEvent event) {
        Set<User> logins = (Set<User>) event.getSession().getServletContext().getAttribute("logins");
        logins.add(this);
    }

    @Override
    public void valueUnbound(HttpSessionBindingEvent event) {
        Set<User> logins = (Set<User>) event.getSession().getServletContext().getAttribute("logins");
        logins.remove(this);
    }

    // @Override equals() and hashCode() as well!

}

Solution 2

This has been discussed some time ago on the mailing list: http://grails.1312388.n4.nabble.com/Information-about-all-logged-in-users-with-Acegi-or-SpringSecurity-in-Grails-td1372911.html

Share:
20,167
Kapil Gund
Author by

Kapil Gund

My mojo is Quality is Free &amp; I believe in xDD : DDD, BDD, TDD and Clean Code.

Updated on July 18, 2022

Comments

  • Kapil Gund
    Kapil Gund almost 2 years

    I am building a community website in grails (using Apache Shiro for security and authentication system) and I would like to implement the feature "who is online?".

    This url http://cksource.com/forums/viewonline.php (see snapshot below if you do not have acess to this Url) gives an example of what I would like to achieve.

    How can I do that in the most simple way? Is there any existing solution in Grails or in Java ?

    Thank you.

    Snapshot : Snapshot of Who is online page http://www.freeimagehosting.net/uploads/th.2de8468a86.png or see here : http://www.freeimagehosting.net/image.php?2de8468a86.png

  • b_erb
    b_erb almost 14 years
    Perhaps also add a lease and refresh it upon user actions in order to filter out inactive sessions without proper logout.
  • Burt Beckwith
    Burt Beckwith almost 14 years
    What if users don't logout explicitly but just close their browser?
  • BalusC
    BalusC almost 14 years
    @Partly and @Burt: just hooking on servletcontainer-managed session destroy as described in the last paragraph is sufficient.
  • Kapil Gund
    Kapil Gund almost 14 years
    Thank you Stefan. However the thread you are pointing is about Spring Security and unfortunately I am using Shiro.
  • Kapil Gund
    Kapil Gund almost 14 years
    @BalusC. Thank you for your answer. However, this will not work for returning users that have enabled the "Remember me" cookie, right ?
  • BalusC
    BalusC almost 14 years
    @fabien: this will work. The "remember me" cookie doesn't rely on the session. The "remember me" logic usually checks the presence of both the long-living cookie and the logged in User and if the User is absent, then it will invoke the login (putting in session). You just hook (listen) on whatever method/approach it is using to login the User.
  • Stefan Armbruster
    Stefan Armbruster almost 14 years
    Hi Fabien, I did not read that carefully enough - the link I've posted just works for acegi.
  • Kapil Gund
    Kapil Gund almost 14 years
    @BalusC. I'll try your solution and update this thread on the results. Thank you very much.
  • JMB
    JMB over 10 years
    This solution doesn't work across server restarts if the web server is serializing and restoring sessions. Still looking for a workaround to this.
  • madness
    madness almost 8 years
    @BalusC If I go with the first approach, with HttpSessionListener, at what point do I set the "logins" attribute in the session?
  • BalusC
    BalusC almost 8 years
    @madness: It's not set in session scope, it's set in application scope. Look, it's an attribute of ServletContext. Just do it during application init in some ServletContextListener, or manually when it's absent. Or, when you're using CDI, just use an @ApplicationScoped bean.