Seed URL mappings index with runtime fallback - #15956
Conversation
Add UrlMappingsIndexProperties and load path with fallback to runtime UrlMappingsHolder behavior. Assisted-by: Sisyphus:xai/grok-4.5 [gpt-coding]
There was a problem hiding this comment.
Pull request overview
This PR introduces an initial SPI “landing pad” for future build-time URL-mappings indexing by reserving a descriptor location and exposing its presence to the runtime URL mappings holder, while preserving the existing runtime URL-matching behavior. It also adds an upgrade-note doc page and a unit test validating the “no index present” fallback path.
Changes:
- Added
UrlMappingsIndexPropertiesto load optionalMETA-INF/grails/url-mappings-index.propertiesfrom the classpath. - Wired
DefaultUrlMappingsHolderto load/expose the descriptor and emit a debug message when it’s present. - Added upgrade documentation and a unit test for the missing-descriptor fallback behavior.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| grails-web-url-mappings/src/main/groovy/org/grails/web/mapping/UrlMappingsIndexProperties.java | New classpath descriptor loader for future URL mappings index metadata. |
| grails-web-url-mappings/src/main/groovy/org/grails/web/mapping/DefaultUrlMappingsHolder.java | Loads and exposes the descriptor; logs when present while keeping runtime routing behavior. |
| grails-web-url-mappings/src/test/groovy/org/grails/web/mapping/UrlMappingsIndexPropertiesSpec.groovy | Adds fallback-path coverage when the descriptor is absent. |
| grails-doc/src/en/guide/upgrading/urlMappingsPrecompute.adoc | Documents the reserved descriptor location and current runtime behavior. |
| grails-doc/src/en/guide/toc.yml | Adds the new upgrade note page to the guide TOC. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
The current implementation of To address these issues, it is recommended to:
grails-web-url-mappings/src/main/groovy/org/grails/web/mapping/UrlMappingsIndexProperties.java |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## 8.0.x #15956 +/- ##
================================================
+ Coverage 0 51.5363% +51.5363%
- Complexity 0 17800 +17800
================================================
Files 0 2040 +2040
Lines 0 95620 +95620
Branches 0 16597 +16597
================================================
+ Hits 0 49279 +49279
- Misses 0 39029 +39029
- Partials 0 7312 +7312
🚀 New features to boost your workflow:
|
Address review feedback on UrlMappingsIndexProperties.load: - Try the thread context classloader first, then the provided classloader (null-guarded), so an application-packaged descriptor is discovered. - A malformed (IllegalArgumentException) or unreadable (IOException) descriptor is now logged at debug and treated as absent (EMPTY) instead of throwing IllegalStateException, so an optional descriptor can never abort application startup. Assisted-by: Sisyphus:openai/gpt-5.6-terra [gpt-coding]
Follow-up to review feedback on the optional descriptor: - Broaden load() error handling to treat any IOException or non-fatal RuntimeException (e.g. SecurityException) during classloader discovery or descriptor parsing as an absent descriptor, without swallowing Error, so it can never abort application startup. - Add the ASF license header to the upgrading guide page, correct the version to 8.0 (matching the target branch and @SInCE), and clarify that runtime URL-mapping evaluation stays authoritative whether or not a descriptor is present. - Add tests for an unreadable (throwing) classloader and a valid descriptor. Assisted-by: Sisyphus:openai/gpt-5.6-terra [gpt-coding]
A throwing or restricted thread context classloader (e.g. a SecurityException) must not prevent descriptor discovery through the provided classloader. Acquire the TCCL defensively and catch load failures per loader iteration, only returning EMPTY after every classloader has been tried, so the optional descriptor stays non-fatal while still resolving through the fallback loader. Assisted-by: Sisyphus:openai/gpt-5.6-terra [gpt-coding]
Review feedback addressedMerged the latest Copilot review comment (classloader / exception handling on
Review-pass fixes: added the ASF license header to the new Local verification: |
Assisted-by: opencode:gpt-5.6-sol
Codecov flagged 70.59% patch coverage (5 missing, 3 partial lines across UrlMappingsIndexProperties.java and DefaultUrlMappingsHolder.java). Verified jamesfredley's prior review-feedback fix (TCCL-first classloader order, per-loader IOException|RuntimeException isolation) against the current code first - it's correct as claimed. Closed the reachable gaps: - propertyNames() was never called by any test. - The null-thread-context-classloader fallback path (loader == null -> continue to the provided classloader) was untested; a real JVM's TCCL is essentially never null in practice, but it's legal to set explicitly and is a real defensive branch worth locking in. - The LOG.debug(...) line reporting a discovered descriptor never ran because the module's tests default to INFO. Added a per-logger DEBUG override in simplelogger.properties (scoped to just these two loggers) rather than a fragile system-property timing dependency, verified the full module suite still passes with it enabled. Left two gaps deliberately uncovered, consistent with judgment calls made elsewhere in this PR-review pass today: the TCCL-retrieval RuntimeException catch (Thread.getContextClassLoader() only throws under a SecurityManager, which is disallowed by default since JDK 18 on this JDK 21 baseline - a test would need to force-enable a deprecated-for-removal mechanism to reach genuinely dead code on this baseline), and DefaultUrlMappingsHolder's own debug-log line (only reachable when a real META-INF/grails/url-mappings-index.properties exists on DefaultUrlMappingsHolder's own hardcoded classloader - not injectable - so covering it needs a classpath-crafted forked-process test for two lines of pure logging with no behavioral significance). Result: UrlMappingsIndexProperties.java goes from 5 missing/3 partial to 2 missing/2 partial lines; full module test suite remains green. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
✅ All tests passed ✅🏷️ Commit: 3034e47 Learn more about TestLens at testlens.app. |
Description
What was found
What changed
UrlMappingsIndexPropertiesfor optionalMETA-INF/grails/url-mappings-index.propertiesDefaultUrlMappingsHolderonly detects the descriptor (debug log); runtime mapping evaluation stays authoritative whether or not a descriptor is presentReview feedback addressed
load()skipped the TCCL and did not wrapIllegalArgumentExceptionlikeIOExceptionIOException/RuntimeExceptionper loader so a throwing TCCL still falls through to the fallback loader and an optional/malformed descriptor never aborts startup (Errorstill propagates).adocmissing ASF header;@since/docs said 8.1; wording implied conditional fallbackOut of scope / follow-up
Related MD topics
Contributor Checklist
Issue and Scope
8.0.xas an experimental starter for 8.1 work.Code Quality
Licensing and Attribution
ai-generated-starting-pointlabel applied.Documentation
Assisted-by: Sisyphus:xai/grok-4.5 [gpt-coding]