Add an end-to-end module verifying message bundles resolve - #16180
Draft
codeconsole wants to merge 3 commits into
Draft
Add an end-to-end module verifying message bundles resolve#16180codeconsole wants to merge 3 commits into
codeconsole wants to merge 3 commits into
Conversation
…ated to find out In development, every message bundle contributed by a plugin, and every base name an application configured itself, silently failed to resolve. Spring Boot's messageSource was built with only the default base name, and nothing warned. The produced type of a factory bean has to be answerable from the bean definition, because a property value cannot be. Asked for the type of grailsUrlMappingsHolder without it, Spring builds a constructor-only ProxyFactoryBean - no property values applied, so no targetSource and no proxyInterfaces - whose getObjectType() returns null, and then falls back to creating the factory bean in full. That resolves the target source and the inner UrlMappingsHolderFactoryBean, evaluates every mapping, reaches the constraints machinery and its List<MessageSource> injection, and so creates Spring Boot's message source - all while bean definition registry post processors are still running. Anything created that early misses ConfigurationPropertiesBindingPostProcessor, so MessageSourceProperties is never bound and keeps its constructor defaults. Not just the base names: encoding, cache-duration and fallback-to-system-locale are lost with it. Only the reload branch built the proxy over an inner bean, so deployed applications were unaffected and the whole of development was not. Each factory-bean definition now declares its produced type through FactoryBean.OBJECT_TYPE_ATTRIBUTE, which Spring consults before instantiating anything. Reload mode is also switched off when running generated bean definitions, since AOT code generation drops custom definition attributes and hot-swapping mappings is meaningless in an image. The regression test issues the eager by-type lookup directly against a bare DefaultListableBeanFactory, so it reproduces with no Grails runtime and no ordering luck: it asserts the holder is still found by type, and that nothing was created in order to answer.
Resolves apache#16176 for the i18n side. The application resolves one code from each way a bundle reaches the message source - its own, a plugin's namespaced one, and one it configured outside grails-app/i18n in dotted form - each in English and in a locale variant, plus an unknown code to prove a non-basename property was bound too. The JVM half runs in check and needs nothing special. It already earns its keep: it is what caught the url-mapping factory beans creating the message source before its configuration could be bound. The native half is opt-in with -PnativeTests, because it needs a GraalVM toolchain and minutes of CPU. The README records the two things it needs that nothing else here does - a GraalVM JDK set before anything is published, and invokedynamic on both the framework and the application - and why each fails confusingly when forgotten.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## 8.0.x #16180 +/- ##
==================================================
- Coverage 53.6425% 53.6318% -0.0106%
+ Complexity 19783 19778 -5
==================================================
Files 2086 2086
Lines 99630 99633 +3
Branches 17594 17594
==================================================
- Hits 53444 53435 -9
- Misses 38542 38551 +9
- Partials 7644 7647 +3
🚀 New features to boost your workflow:
|
Apache RAT excludes **/resources/*, so the property bundles under src/main/resources were never scanned, but the grails-app/i18n bundles and the application.yml are, and they were added without headers.
✅ All tests passed ✅🏷️ Commit: c5844d5 Learn more about TestLens at testlens.app/docs. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds the end-to-end module #16176 asks for: an application that resolves messages from each way a bundle reaches the message source, so a missing resource hint can actually be falsified.
Stacked on #16179 — the branch contains that fix, because without it the JVM check cannot pass. Review that one first; this diff reduces to the
end-to-end/module once it merges.What it checks
messagesnative-messagesmessages*patterns never reach it. The plugin name is multi-word, so it also covers the descriptor's hyphenated spelling being matched against the plugin's camel-case one.config.i18n.customgrails-app/i18n, dotted, so it proves the base name is converted toconfig/i18n/custombefore use.Each is resolved in English and in a locale variant. An unknown code is resolved too, under
use-code-as-default-message, because base names alone would not proveMessageSourcePropertieswas bound — the whole object binds at once or not at all.Running it
The JVM half runs in
checkand needs nothing special:cd end-to-end ./gradlew :native-i18n:checkThe native half is opt-in, because it needs a GraalVM toolchain and minutes of CPU:
The README documents the two things it needs that nothing else in this repository does — a GraalVM JDK exported before anything is published, and invokedynamic on both the framework and the application — and what each failure looks like when forgotten.
Limitations
nativeCompileis minutes of CPU and needs a toolchain no other job uses, so this stays a local opt-in until someone decides where it belongs.