I'm adapting an existing Grails 3 project to a multi-tenant structure, using the schema mode provided by GORM, and I'm having trouble getting the GORM listeners to work when I specify a tenant.
My listener looks like this:
@CompileStatic
class VehicleListenerService {
@Listener(Vehicle)
void onPreInsertEvent(PreInsertEvent event) {
println "*** Vehicle preInsert"
event.entityAccess.setProperty('model', 'preInsert')
}
@Listener(Vehicle)
void onPreUpdateEvent(PreUpdateEvent event) {
println "*** Vehicle preUpdate"
event.entityAccess.setProperty('model', 'preUpdate')
}
}
So every time a vehicle is created or updated, its model should be changed to preInsert or preUpdate.
The current tenant is determined by the subdomain specified in the URL. If I access the app with no subdomain (via http://localhost:8080), the listener works as expected, but if I provide a subdomain (http://test.localhost:8080), the listener doesn't do anything, and the vehicle model doesn't change.
What do I have to do to make the GORM listener work with any tenant?
I've created a sample project (https://github.com/sneira/mtschema) which reproduces the error.
With help from the Grails Slack channel and some more research, I've come up with a solution to this.
First, the listener service has to extend AbstractPersistenceEventListener:
@CompileStatic
class VehicleListenerService extends AbstractPersistenceEventListener {
protected VehicleListenerService(Datastore datastore) {
super(datastore)
}
@Override
protected void onPersistenceEvent(AbstractPersistenceEvent event) {
String newModel =
event.eventType == EventType.PreInsert ? 'preInsert' : 'preUpdate'
event.entityAccess.setProperty('model', newModel)
}
@Override
boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
boolean supportsEvent = eventType.isAssignableFrom(PreInsertEvent) ||
eventType.isAssignableFrom(PreUpdateEvent)
return supportsEvent
}
}
Now we can create a service instance for each schema (except for the default) in Bootstrap.groovy, and add it to our app:
def init = { servletContext ->
def ctx = grailsApplication.mainContext
['TEST', 'TEST2'].each { String name ->
HibernateDatastore ds = hibernateDatastore.getDatastoreForConnection(name)
VehicleListenerService listener = new VehicleListenerService(ds)
ctx.addApplicationListener(listener)
}
}
I've uploaded the complete code to https://github.com/sneira/mtschema/tree/listeners.