codeconsole opened a new pull request, #16398: URL: https://github.com/apache/grails-core/pull/16398
#16385 keeps precompiled scaffold pages in the controllers' view directories, so the build has to predict every precedence decision the runtime resolver makes: declared views, plugin views, namespaces, template overrides, controllers sharing a name. Each review round found another place that prediction drifted, and where it could not predict safely it gave up precompilation. Namespaced controllers, same-named controllers scaffolding different domains, and namespace-specific templates are all still expanded at runtime, which a native image cannot do. This moves the decision back to the runtime and precompiles only the expensive part. - **Build.** `generateScaffoldedViews` finds the scaffolded domain classes (ASM, as before) and the templates (application overrides, then the classpath in order, namespace-specific ones included). It then runs `org.apache.grails.scaffolding.ScaffoldedPagesGenerator` in a JVM on the application's runtime classpath, which expands each template for each domain class with the runtime's own `ModelBuilder`, `ScaffoldedPages` and Groovy, and writes it to `grails-scaffolded/<domain class>/<template path>-<digest>.gsp`. `compileGroovyPages` compiles it with the application's views. The digest covers the template and the model names the template mentions. - **Runtime.** `ScaffoldingViewResolver` chooses the template exactly as before. Where it used to expand and compile it, it computes the same name and serves the compiled page when there is one; otherwise it expands the template as before and warns once. Consequences: - A compiled page cannot shadow a declared view. `grails-scaffolded` cannot be a controller's view directory (it has a hyphen), and a page is used only for the exact template and model it was expanded from. The plugin view-index scanning, namespace detection and view-directory collision handling from #16385 are no longer needed and are removed. - Namespaced controllers, same-named controllers scaffolding different domains, and namespace-specific and custom-named templates are all precompiled. - A template the build did not see (from a template-override plugin, or the legacy `static scaffold` property) is expanded at runtime, and the resolver warns once per template and domain class. - The build and the resolver share one implementation, so they cannot drift, and templates are read as UTF-8 in both. Model names a template does not mention are left out of the digest, so a page is still found when a value such as `packagePath` differs between the build machine and the runtime. - Native images: the templates are registered as resources, because the resolver reads a template to name its page. When the template a native image resolves has no compiled page, it is served the page compiled from another copy of the same template on the classpath, with a warning; the JVM expands instead. Checked against GraalVM 25 with a standalone probe: `getResource` returns the first copy in classpath order, as on the JVM, and `getResources` returns every copy. - `GroovyPageViewResolver.createGroovyPageView` is now protected. `ScaffoldingViewResolver.tryGenerateScaffoldedView` is now protected and takes the candidate template paths. Cost: once a view is resolved, a request costs one more view-cache key computation and map lookup than with #16385 (the parent resolver caches the null result for a scaffolded view). The first request for each view reads and hashes its template. The build forks one JVM when scaffolding inputs change; the task stays cacheable, with the generator classpath as an input. Validation: ```text ./gradlew :grails-scaffolding:test :grails-scaffolding:codeStyle :grails-web-gsp:test :grails-web-gsp:codeStyle :grails-test-examples-scaffolding:test cd grails-gradle ./gradlew :grails-gradle-plugins:test :grails-gradle-plugins:codeStyle :grails-gradle-plugins:validateDependencyVersions ``` All pass (62, 19, 12 and 281 tests). `grails-test-examples-scaffolding:test` gains `PrecompiledScaffoldPagesSpec`, which runs the real resolver against the real templates and the pages the example's own build compiled. It checks that every view of a controller, a namespaced controller, and a same-named controller scaffolding a different domain is served from a compiled page, with nothing expanded at runtime. Not run, and **not claimed to pass**: the example's Geb integration tests, the repository-wide `aggregateViolations` check, and a native build of a full application. Resource precedence in a native image was checked only with the standalone probe. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
