fix: add null safety for getDatastoreForConnection in data service factory bean#15474
fix: add null safety for getDatastoreForConnection in data service factory bean#15474jamesfredley wants to merge 4 commits into7.1.xfrom
Conversation
…ctory bean Guard against datastore implementations that may return null from getDatastoreForConnection() by throwing a clear ConfigurationException with the connection name and service class, rather than silently setting a null datastore. Assisted-by: Claude Code <Claude@Claude.ai>
There was a problem hiding this comment.
Pull request overview
This PR makes DatastoreServiceMethodInvokingFactoryBean.resolveEffectiveDatastore() defensive against MultipleConnectionSourceCapableDatastore.getDatastoreForConnection(...) returning null, by throwing a ConfigurationException instead of allowing a null datastore to be set on a data service.
Changes:
- Add null-guards after
getDatastoreForConnection(...)for explicit@Transactional(connection=...)selection. - Add null-guards after
getDatastoreForConnection(...)for domain-class mapping-derived connection selection. - Introduce
ConfigurationExceptionerror messages intended to provide clearer misconfiguration diagnostics.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
.../groovy/org/grails/datastore/mapping/config/DatastoreServiceMethodInvokingFactoryBean.groovy
Show resolved
Hide resolved
.../groovy/org/grails/datastore/mapping/config/DatastoreServiceMethodInvokingFactoryBean.groovy
Show resolved
Hide resolved
.../groovy/org/grails/datastore/mapping/config/DatastoreServiceMethodInvokingFactoryBean.groovy
Show resolved
Hide resolved
…solveEffectiveDatastore() Add comprehensive Spock tests covering all code paths in resolveEffectiveDatastore(), including both null safety guard branches that throw ConfigurationException when getDatastoreForConnection() returns null. 7 test cases cover: - Non-multi-connection datastore passthrough - Default connection resolution - @transactional(connection) annotation resolution - Domain class datasource mapping resolution - Both null guard paths (ConfigurationException) - DEFAULT connection name does not override Assisted-by: Claude Code <Claude@Claude.ai>
|
Added unit tests in Regarding functional tests in |
The @service AST transform registers annotated classes in META-INF/services/. When DomainService and SampleDomain were private static, ServiceLoader could not instantiate them, breaking DefaultServiceRegistrySpec. Removing private allows ServiceLoader to instantiate these classes without affecting test behavior. Assisted-by: Claude Code <Claude@Claude.ai>
Move DomainService and SampleDomain from inner classes to top-level classes so the @service AST transformation can register them in META-INF/services and ServiceLoader can instantiate them. As inner classes, ServiceLoader could not instantiate DomainService, causing ServiceConfigurationError in DefaultServiceRegistrySpec. Assisted-by: Claude Code <Claude@Claude.ai>
Summary
getDatastoreForConnection()calls inDatastoreServiceMethodInvokingFactoryBean.resolveEffectiveDatastore(), throwing a clearConfigurationExceptionwith the connection name, domain class, and service class rather than silently setting a null datastore.@Transactional(connection=...)path and the domain class mapping inheritance path.The
MultipleConnectionSourceCapableDatastoreinterface doesn't guarantee non-null returns, and some implementations (e.g. Neo4j, SimpleMapDatastore) do a raw map lookup without validation. Hibernate already throws on missing connections, but this makes the factory bean defensive regardless of the underlying datastore implementation.Flagged by Copilot review on #15472.
Tests
Added
DatastoreServiceMethodInvokingFactoryBeanSpecwith 7 Spock tests covering allresolveEffectiveDatastore()code paths:@Transactional(connection)resolution@Transactional(connection)null guardDEFAULTconnection name passthroughFunctional tests in
grails-test-exampleswere assessed but are not feasible for this change - the null guard paths triggerConfigurationExceptionduring bean initialization, which prevents application startup and cannot be caught in a running functional test.