Injecting Service Returns Null In Grails
2019-10-01
category: SoftwareDev

Environment

Error

I wanted to inform front-end through websocket after GORM made any updates to the database. So I extended AbstractPersistenceEventListener and injected a socket service in it :

class CustomPersistenceEventListenerImpl extends AbstractPersistenceEventListener {

    @Autowired
    SocketService socketService

    protected CustomPersistenceEventListenerImpl(Datastore datastore,SocketService service) {
        super(datastore)
        this.socketService = service
    }

    @Override
    protected void onPersistenceEvent(AbstractPersistenceEvent event) {
        if (event.source != this.datastore) {
            log.trace("Event received for other datastore. Ignoring event")
            return
        }

        switch (event.eventType) {
            case EventType.PostInsert:
                break
            case EventType.PostDelete:
                break
            case EventType.PostUpdate:
                break
        }

        socketService.inform()
    }

    @Override
    boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
        return eventType.isAssignableFrom(PostInsertEvent) ||
                eventType.isAssignableFrom(PostDeleteEvent) ||
                eventType.isAssignableFrom(PostUpdateEvent)
    }
}

Then initialize this listener in Bootstrap.groovy:

class BootStrap {
   def init = { servletContext ->

      def ctx = servletContext.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
        ctx.getBeansOfType(Datastore).values().each { Datastore d ->
            ctx.addApplicationListener new CustomPersistenceEventListenerImpl(d, socketService)
        }

   }
}

But my socket did not send anything. Then I found it was because socket service returns null in this listener.

Reason

Bootstrap runs before service is initialized.

Solution

Initialize socket in Bootstrap.groovy before this listener:

class BootStrap {

    def socketService

    def init = { servletContext ->
    ...
    }
}

why don’t I use GORM event handler in domains?

  1. I have to enable autowire in domains:
static mapping = {
       autowire true
   }
  1. when I call domain.properties, it also returns the injected service.

If you don’t mind these, you can continue reading GORM: Events and Auto Timestamping

In this method, you don’t need to initialize service in Bootstrap.groovy