Converting Http Session Events into Grails 3 Events

Grails 3 introduced a new Events API based on Reactor.  Unfortunately, as far as I can tell, HttpSessionEvents are not natively part of the Grails 3 Events system.  Bringing them in to the fold, however, is pretty easy.  I based this off of Oliver Wahlen's immensely helpful blog post about sending the HttpSessionEvents to a Grails service.

First, let's create our Spring HttpSessionServletListener.  Create this file somewhere in the /src/ path where Grails will find it:

File: .../grailsProject/src/main/groovy/com/example/HttpSessionServletListener.groovy
package com.example

import grails.events.*
import javax.servlet.http.HttpSession
import javax.servlet.http.HttpSessionEvent
import javax.servlet.http.HttpSessionListener

class HttpSessionServletListener implements HttpSessionListener, Events {
  
    // called by servlet container upon session creation
    void sessionCreated(HttpSessionEvent event) {
        notify("example:httpSessionCreated", event.session)
    }

    // called by servlet container upon session destruction
    void sessionDestroyed(HttpSessionEvent event) {
        notify("example:httpSessionDestroyed", event.session)
    }
}

Now register the HttpSessionServletListener as a Spring Bean.  If you don't already have a resources.groovy file, create one and add the following.

.../grailsProject/grails-app/conf/spring/resources.groovy
import org.springframework.boot.context.embedded.ServletListenerRegistrationBean
import com.example.HttpSessionServletListener

beans = {
    
    httpSessionServletListener(ServletListenerRegistrationBean) {
        listener = bean(HttpSessionServletListener)
    }
    
}
// Yes this is the entire file

Now you are all set to listen for the "example:httpSessionCreated" and "example:httpSessionDestroyed" events using the Grails 3 Events API.  "Example" is the namespace of the event, which in my real code I set to the last part of the package name, so I made it match the package name of "example".  Just use something so you don't have to worry about naming collisions.

Here's an example of listening for the events in a standard Grails Controller.  Note that the event handlers are attached after construction, and before the Controller bean is made available, by using the PostConstruct annotation.

.../grailsProject/grails-app/controllers/com/example/ExampleController.groovy
package com.example

import grails.events.*
import javax.annotation.PostConstruct

class ExampleController {
    
    @PostConstruct
    void init() {
        
        on("example:httpSessionCreated") { session ->
            println "sessionCreated: ${session.id}"
        }
        
        on("example:httpSessionDestroyed") { session ->
            println "sessionDestroyed: ${session.id}"
        }
    }
}