From 036d34d1943f09c27c44d62cf93a89233e8f161c Mon Sep 17 00:00:00 2001 From: Scott Murphy Heiberg Date: Tue, 7 Jul 2026 11:46:33 -0600 Subject: [PATCH] Detect web applications without relying on the dispatcherServlet bean Neo4jDataStoreSpringInitializer gated the open-session-in-view interceptor on the dispatcherServlet bean definition. That bean is registered by Spring Boot auto-configuration, so under the Grails 8 lifecycle (plugin beans drain before auto-configuration, apache/grails-core#15934) the check always returned false and the interceptor silently stopped registering in web applications. The check still honours a dispatcherServlet definition when present but otherwise tests whether the registry is the web application context itself, a signal independent of auto-configuration ordering. The logic mirrors AbstractDatastoreInitializer.isWebApplicationRegistry() added to grails-datamapping-core in apache/grails-core#15934 (where it is exercised by the hibernate5/hibernate7 OpenSessionInViewSpec integration tests); it is inlined here because this build resolves grails-datamapping-core from published artifacts that do not yet contain the shared method, and the override becomes removable once it does. --- .../Neo4jDataStoreSpringInitializer.groovy | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/grails-data-neo4j/grails-plugin/src/main/groovy/grails/neo4j/bootstrap/Neo4jDataStoreSpringInitializer.groovy b/grails-data-neo4j/grails-plugin/src/main/groovy/grails/neo4j/bootstrap/Neo4jDataStoreSpringInitializer.groovy index ecff8354ceb..7221625fe6d 100644 --- a/grails-data-neo4j/grails-plugin/src/main/groovy/grails/neo4j/bootstrap/Neo4jDataStoreSpringInitializer.groovy +++ b/grails-data-neo4j/grails-plugin/src/main/groovy/grails/neo4j/bootstrap/Neo4jDataStoreSpringInitializer.groovy @@ -106,7 +106,7 @@ class Neo4jDataStoreSpringInitializer extends AbstractDatastoreInitializer { } ClassLoader classLoader = getClass().getClassLoader() - if (beanDefinitionRegistry.containsBeanDefinition('dispatcherServlet') && ClassUtils.isPresent(OSIV_CLASS_NAME, classLoader)) { + if (isWebApplicationRegistry(beanDefinitionRegistry) && ClassUtils.isPresent(OSIV_CLASS_NAME, classLoader)) { String interceptorName = "neo4jOpenSessionInViewInterceptor" "${interceptorName}"(ClassUtils.forName(OSIV_CLASS_NAME, classLoader)) { datastore = ref("neo4jDatastore") @@ -123,6 +123,32 @@ class Neo4jDataStoreSpringInitializer extends AbstractDatastoreInitializer { } } + /** + * Determines whether the given registry belongs to a web application. The presence of the + * {@code dispatcherServlet} bean definition is only a reliable signal after Spring Boot + * auto-configuration has been processed; Grails 8 registers plugin bean definitions before + * that, so the type of the registry (the web application context itself) is checked as well. + * + *

Mirrors {@code AbstractDatastoreInitializer#isWebApplicationRegistry} in + * grails-datamapping-core; once this build depends on a version that ships it, this + * override is redundant and can be removed. + * + * @param registry The bean definition registry + * @return true if the registry belongs to a web application + */ + protected boolean isWebApplicationRegistry(BeanDefinitionRegistry registry) { + if (registry == null) { + return false + } + if (registry.containsBeanDefinition('dispatcherServlet')) { + return true + } + String webApplicationContextClassName = 'org.springframework.web.context.WebApplicationContext' + ClassLoader classLoader = getClass().classLoader + return ClassUtils.isPresent(webApplicationContextClassName, classLoader) && + ClassUtils.forName(webApplicationContextClassName, classLoader).isInstance(registry) + } + /** * Sets the default Neo4j GORM mapping configuration */