diff --git a/build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/CompilePlugin.groovy b/build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/CompilePlugin.groovy index fa515941a69..fb0ac407f0e 100644 --- a/build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/CompilePlugin.groovy +++ b/build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/CompilePlugin.groovy @@ -111,6 +111,7 @@ class CompilePlugin implements Plugin { it.options.encoding = StandardCharsets.UTF_8.name() it.options.fork = true it.options.forkOptions.jvmArgs = ['-Xms128M', '-Xmx2G'] + it.options.forkOptions.jvmArgs += ['-Dspock.iKnowWhatImDoing.disableGroovyVersionCheck=true'] if (System.getenv('SUPPRESS_DEPRECATION_WARNINGS') == 'true') { it.options.compilerArgs += ['-Xlint:-removal'] } diff --git a/build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/SbomPlugin.groovy b/build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/SbomPlugin.groovy index bbd16832938..0a98b129ae0 100644 --- a/build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/SbomPlugin.groovy +++ b/build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/SbomPlugin.groovy @@ -91,20 +91,30 @@ class SbomPlugin implements Plugin { 'pkg:maven/com.oracle.coherence.ce/coherence-bom@25.03.1?type=pom': 'UPL-1.0', // does not have map based on license id 'pkg:maven/com.oracle.coherence.ce/coherence-bom@25.03.2?type=pom': 'UPL-1.0', // does not have map based on license id 'pkg:maven/com.oracle.coherence.ce/coherence-bom@22.06.2?type=pom': 'UPL-1.0', // does not have map based on license id - 'pkg:maven/jline/jline@2.14.6?type=jar' : 'BSD-2-Clause', // maps incorrectly because of https://github.com/CycloneDX/cyclonedx-core-java/issues/205 + 'pkg:maven/jline/jline@2.14.6?type=jar' : 'BSD-2-Clause', // legacy jline:jline group, BSD-2; maps incorrectly because of https://github.com/CycloneDX/cyclonedx-core-java/issues/205 'pkg:maven/opensymphony/sitemesh@2.6.0?type=jar' : 'OpenSymphony', // custom license approved by legal LEGAL-707 'pkg:maven/org.antlr/antlr4-runtime@4.7.2?type=jar' : 'BSD-3-Clause', // maps incorrectly because of https://github.com/CycloneDX/cyclonedx-core-java/issues/205 - 'pkg:maven/org.jline/jansi@3.30.9?type=jar' : 'BSD-3-Clause', // jline group resolved at 3.30.9 transitively via groovy-groovysh; main org.jline:jline pinned at 3.30.6 directly - 'pkg:maven/org.jline/jline@3.30.6?type=jar' : 'BSD-3-Clause', // direct dependency declared at jline.version in dependencies.gradle - 'pkg:maven/org.jline/jline-builtins@3.30.9?type=jar' : 'BSD-3-Clause', // jline group resolved at 3.30.9 transitively via groovy-groovysh; main org.jline:jline pinned at 3.30.6 directly - 'pkg:maven/org.jline/jline-console@3.30.9?type=jar' : 'BSD-3-Clause', // jline group resolved at 3.30.9 transitively via groovy-groovysh; main org.jline:jline pinned at 3.30.6 directly - 'pkg:maven/org.jline/jline-native@3.30.9?type=jar' : 'BSD-3-Clause', // jline group resolved at 3.30.9 transitively via groovy-groovysh; main org.jline:jline pinned at 3.30.6 directly - 'pkg:maven/org.jline/jline-reader@3.30.9?type=jar' : 'BSD-3-Clause', // jline group resolved at 3.30.9 transitively via groovy-groovysh; main org.jline:jline pinned at 3.30.6 directly - 'pkg:maven/org.jline/jline-style@3.30.9?type=jar' : 'BSD-3-Clause', // jline group resolved at 3.30.9 transitively via groovy-groovysh; main org.jline:jline pinned at 3.30.6 directly - 'pkg:maven/org.jline/jline-terminal@3.30.9?type=jar' : 'BSD-3-Clause', // jline group resolved at 3.30.9 transitively via groovy-groovysh; main org.jline:jline pinned at 3.30.6 directly - 'pkg:maven/org.jline/jline-terminal-jansi@3.30.9?type=jar' : 'BSD-3-Clause', // jline group resolved at 3.30.9 transitively via groovy-groovysh; main org.jline:jline pinned at 3.30.6 directly - 'pkg:maven/org.jline/jline-terminal-jna@3.30.9?type=jar' : 'BSD-3-Clause', // jline group resolved at 3.30.9 transitively via groovy-groovysh; main org.jline:jline pinned at 3.30.6 directly - 'pkg:maven/org.jline/jline-terminal-jni@3.30.9?type=jar' : 'BSD-3-Clause', // jline group resolved at 3.30.9 transitively via groovy-groovysh; main org.jline:jline pinned at 3.30.6 directly + 'pkg:maven/org.jline/jansi@4.0.12?type=jar' : 'BSD-3-Clause', // jline 4.0.12 LICENSE.txt at https://github.com/jline/jline3/blob/jline-parent-4.0.12/LICENSE.txt confirms BSD-3-Clause; cyclonedx misreports as BSD-4-Clause (cyclonedx-core-java#205); resolved transitively via groovy-groovysh on Groovy 6 + 'pkg:maven/org.jline/jansi@4.1.0?type=jar' : 'BSD-3-Clause', // jline 4.1.0 LICENSE.txt at https://github.com/jline/jline3/blob/jline-parent-4.1.0/LICENSE.txt confirms BSD-3-Clause; cyclonedx misreports as BSD-4-Clause (cyclonedx-core-java#205); resolved transitively via groovy-groovysh on Groovy 6 + 'pkg:maven/org.jline/jline@3.30.6?type=jar' : 'BSD-3-Clause', // jline 3.30.6 LICENSE at https://github.com/jline/jline3/blob/jline-parent-3.30.6/LICENSE.txt confirms BSD-3-Clause; direct dependency declared at jline.version in dependencies.gradle + 'pkg:maven/org.jline/jline-builtins@4.0.12?type=jar' : 'BSD-3-Clause', // jline 4.0.12 LICENSE.txt at https://github.com/jline/jline3/blob/jline-parent-4.0.12/LICENSE.txt confirms BSD-3-Clause; resolved transitively via groovy-groovysh on Groovy 6 + 'pkg:maven/org.jline/jline-console@4.0.12?type=jar' : 'BSD-3-Clause', // jline 4.0.12 LICENSE.txt at https://github.com/jline/jline3/blob/jline-parent-4.0.12/LICENSE.txt confirms BSD-3-Clause; resolved transitively via groovy-groovysh on Groovy 6 + 'pkg:maven/org.jline/jline-console-ui@4.0.12?type=jar' : 'BSD-3-Clause', // jline 4.0.12 LICENSE.txt at https://github.com/jline/jline3/blob/jline-parent-4.0.12/LICENSE.txt confirms BSD-3-Clause; resolved transitively via groovy-groovysh on Groovy 6 + 'pkg:maven/org.jline/jline-native@4.0.12?type=jar' : 'BSD-3-Clause', // jline 4.0.12 LICENSE.txt at https://github.com/jline/jline3/blob/jline-parent-4.0.12/LICENSE.txt confirms BSD-3-Clause; resolved transitively via groovy-groovysh on Groovy 6 + 'pkg:maven/org.jline/jline-reader@4.0.12?type=jar' : 'BSD-3-Clause', // jline 4.0.12 LICENSE.txt at https://github.com/jline/jline3/blob/jline-parent-4.0.12/LICENSE.txt confirms BSD-3-Clause; resolved transitively via groovy-groovysh on Groovy 6 + 'pkg:maven/org.jline/jline-shell@4.0.12?type=jar' : 'BSD-3-Clause', // jline 4.0.12 LICENSE.txt at https://github.com/jline/jline3/blob/jline-parent-4.0.12/LICENSE.txt confirms BSD-3-Clause; resolved transitively via groovy-groovysh on Groovy 6 + 'pkg:maven/org.jline/jline-style@4.0.12?type=jar' : 'BSD-3-Clause', // jline 4.0.12 LICENSE.txt at https://github.com/jline/jline3/blob/jline-parent-4.0.12/LICENSE.txt confirms BSD-3-Clause; resolved transitively via groovy-groovysh on Groovy 6 + 'pkg:maven/org.jline/jline-terminal@4.0.12?type=jar' : 'BSD-3-Clause', // jline 4.0.12 LICENSE.txt at https://github.com/jline/jline3/blob/jline-parent-4.0.12/LICENSE.txt confirms BSD-3-Clause; resolved transitively via groovy-groovysh on Groovy 6 + 'pkg:maven/org.jline/jline-terminal-jni@4.0.12?type=jar' : 'BSD-3-Clause', // jline 4.0.12 LICENSE.txt at https://github.com/jline/jline3/blob/jline-parent-4.0.12/LICENSE.txt confirms BSD-3-Clause; resolved transitively via groovy-groovysh on Groovy 6 + 'pkg:maven/org.jline/jline-builtins@4.1.0?type=jar' : 'BSD-3-Clause', // jline 4.1.0 LICENSE.txt at https://github.com/jline/jline3/blob/jline-parent-4.1.0/LICENSE.txt confirms BSD-3-Clause; resolved transitively via groovy-groovysh on Groovy 6 + 'pkg:maven/org.jline/jline-console@4.1.0?type=jar' : 'BSD-3-Clause', // jline 4.1.0 LICENSE.txt at https://github.com/jline/jline3/blob/jline-parent-4.1.0/LICENSE.txt confirms BSD-3-Clause; resolved transitively via groovy-groovysh on Groovy 6 + 'pkg:maven/org.jline/jline-console-ui@4.1.0?type=jar' : 'BSD-3-Clause', // jline 4.1.0 LICENSE.txt at https://github.com/jline/jline3/blob/jline-parent-4.1.0/LICENSE.txt confirms BSD-3-Clause; resolved transitively via groovy-groovysh on Groovy 6 + 'pkg:maven/org.jline/jline-native@4.1.0?type=jar' : 'BSD-3-Clause', // jline 4.1.0 LICENSE.txt at https://github.com/jline/jline3/blob/jline-parent-4.1.0/LICENSE.txt confirms BSD-3-Clause; resolved transitively via groovy-groovysh on Groovy 6 + 'pkg:maven/org.jline/jline-reader@4.1.0?type=jar' : 'BSD-3-Clause', // jline 4.1.0 LICENSE.txt at https://github.com/jline/jline3/blob/jline-parent-4.1.0/LICENSE.txt confirms BSD-3-Clause; resolved transitively via groovy-groovysh on Groovy 6 + 'pkg:maven/org.jline/jline-shell@4.1.0?type=jar' : 'BSD-3-Clause', // jline 4.1.0 LICENSE.txt at https://github.com/jline/jline3/blob/jline-parent-4.1.0/LICENSE.txt confirms BSD-3-Clause; resolved transitively via groovy-groovysh on Groovy 6 + 'pkg:maven/org.jline/jline-style@4.1.0?type=jar' : 'BSD-3-Clause', // jline 4.1.0 LICENSE.txt at https://github.com/jline/jline3/blob/jline-parent-4.1.0/LICENSE.txt confirms BSD-3-Clause; resolved transitively via groovy-groovysh on Groovy 6 + 'pkg:maven/org.jline/jline-terminal@4.1.0?type=jar' : 'BSD-3-Clause', // jline 4.1.0 LICENSE.txt at https://github.com/jline/jline3/blob/jline-parent-4.1.0/LICENSE.txt confirms BSD-3-Clause; resolved transitively via groovy-groovysh on Groovy 6 + 'pkg:maven/org.jline/jline-terminal-jni@4.1.0?type=jar' : 'BSD-3-Clause', // jline 4.1.0 LICENSE.txt at https://github.com/jline/jline3/blob/jline-parent-4.1.0/LICENSE.txt confirms BSD-3-Clause; resolved transitively via groovy-groovysh on Groovy 6 'pkg:maven/org.jruby/jzlib@1.1.5?type=jar' : 'BSD-3-Clause', // https://web.archive.org/web/20240822213507/http://www.jcraft.com/jzlib/LICENSE.txt shows it's a 3 clause 'pkg:maven/org.liquibase.ext/liquibase-hibernate5@4.27.0?type=jar': 'Apache-2.0', // maps incorrectly because of https://github.com/liquibase/liquibase/issues/2445 & the base pom does not define a license ] diff --git a/dependencies.gradle b/dependencies.gradle index c60a7c1028f..64fe907d59c 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -80,7 +80,7 @@ ext { 'geb-spock.version' : '8.0.1', 'graphql-java.version' : '25.0', 'graphql-java-extended-scalars.version': '24.0', - 'groovy.version' : '5.0.7-SNAPSHOT', + 'groovy.version' : '6.0.0-SNAPSHOT', 'hibernate-groovy-proxy.version': '1.1', 'jakarta-servlet-api.version' : '6.1.0', 'jakarta-validation.version' : '3.1.1', @@ -223,9 +223,9 @@ ext { else if (project.name == 'grails-micronaut-bom') { customBomVersions = [ // Keep aligned with bomDependencyVersions['groovy.version']; the main bom now uses - // Groovy 5, so the micronaut bom must match to avoid validateDependencyVersions + // Groovy 6, so the micronaut bom must match to avoid validateDependencyVersions // failures from transitive dependencies upgrading groovy-bom past the pinned version. - 'groovy.version' : '5.0.7-SNAPSHOT', + 'groovy.version' : '6.0.0-SNAPSHOT', 'spock.version' : '2.4-groovy-5.0', 'protobuf.version': '4.30.2', ] diff --git a/gradle.properties b/gradle.properties index 1c26a51a967..82560c3d4e3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -85,4 +85,7 @@ org.gradle.daemon=true #org.gradle.configureondemand=true # Note: groovydoc requires almost a doubling of this memory; if it could run in a process isolation, we could reduce this # This is a future TODO see groovydoc-tool-rewrite branch for experiementations with this -org.gradle.jvmargs=-Dfile.encoding=UTF-8 -Xmx5G +# grails8-groovy6-canary: carry Spock's compile-time Groovy version-check opt-out on the build JVM so +# the forked gson/gsp view compiler (AbstractGroovyTemplateCompileTask) can propagate it; Spock's global +# AST transform otherwise aborts view compilation under Groovy 6. Remove once Spock ships a groovy-6.0 build. +org.gradle.jvmargs=-Dfile.encoding=UTF-8 -Xmx5G -Dspock.iKnowWhatImDoing.disableGroovyVersionCheck=true diff --git a/gradle/functional-test-config.gradle b/gradle/functional-test-config.gradle index 062752e5631..9dc5ad98ff0 100644 --- a/gradle/functional-test-config.gradle +++ b/gradle/functional-test-config.gradle @@ -54,11 +54,7 @@ configurations.configureEach { } } -tasks.named('compileTestGroovy') { - options.forkOptions.jvmArgs += ['-Dspock.iKnowWhatImDoing.disableGroovyVersionCheck=true'] -} - -tasks.named('compileGroovy') { +tasks.withType(GroovyCompile).configureEach { options.forkOptions.jvmArgs += ['-Dspock.iKnowWhatImDoing.disableGroovyVersionCheck=true'] } diff --git a/gradle/hibernate5-test-config.gradle b/gradle/hibernate5-test-config.gradle index 667b9e61b8e..85fadc0a888 100644 --- a/gradle/hibernate5-test-config.gradle +++ b/gradle/hibernate5-test-config.gradle @@ -26,6 +26,10 @@ dependencies { add('testRuntimeOnly', 'org.objenesis:objenesis') } +tasks.withType(GroovyCompile).configureEach { + options.forkOptions.jvmArgs += ['-Dspock.iKnowWhatImDoing.disableGroovyVersionCheck=true'] +} + tasks.withType(Test).configureEach { // Honor DO_NOT_CACHE_TESTS=1 so developers can repeatedly invoke the same test command // without --rerun-tasks (and without recompiling everything else). diff --git a/gradle/mongodb-forked-test-config.gradle b/gradle/mongodb-forked-test-config.gradle index 7166c983239..032fadde7f4 100644 --- a/gradle/mongodb-forked-test-config.gradle +++ b/gradle/mongodb-forked-test-config.gradle @@ -26,6 +26,10 @@ dependencies { add('testRuntimeOnly', 'org.objenesis:objenesis') } +tasks.withType(GroovyCompile).configureEach { + options.forkOptions.jvmArgs += ['-Dspock.iKnowWhatImDoing.disableGroovyVersionCheck=true'] +} + tasks.named('compileTestGroovy', GroovyCompile) { groovyOptions.forkOptions.jvmArgs = ['-Xmx768m'] } diff --git a/gradle/mongodb-test-config.gradle b/gradle/mongodb-test-config.gradle index 7d140e899f3..65e5cf0c6b7 100644 --- a/gradle/mongodb-test-config.gradle +++ b/gradle/mongodb-test-config.gradle @@ -26,6 +26,10 @@ dependencies { add('testRuntimeOnly', 'org.objenesis:objenesis') } +tasks.withType(GroovyCompile).configureEach { + options.forkOptions.jvmArgs += ['-Dspock.iKnowWhatImDoing.disableGroovyVersionCheck=true'] +} + tasks.named('compileTestGroovy', GroovyCompile) { groovyOptions.forkOptions.jvmArgs = ['-Xmx768m'] } diff --git a/gradle/test-config.gradle b/gradle/test-config.gradle index ddc0163fa16..32344e61857 100644 --- a/gradle/test-config.gradle +++ b/gradle/test-config.gradle @@ -35,11 +35,12 @@ dependencies { add('testRuntimeOnly', 'org.objenesis:objenesis') } -tasks.named('compileTestGroovy') { - options.forkOptions.jvmArgs += ['-Dspock.iKnowWhatImDoing.disableGroovyVersionCheck=true'] -} - -tasks.named('compileGroovy') { +// Disable Spock's compile-time Groovy version check on ALL Groovy compile +// tasks. Spock's SpockTransform is registered via META-INF services and +// the Groovy compiler loads every AST transform on the classpath, so even +// main source sets trip the version check when Groovy is newer than the +// Spock artifact's groovy variant. +tasks.withType(GroovyCompile).configureEach { options.forkOptions.jvmArgs += ['-Dspock.iKnowWhatImDoing.disableGroovyVersionCheck=true'] } diff --git a/grails-controllers/src/main/groovy/org/grails/compiler/web/ControllerActionTransformer.java b/grails-controllers/src/main/groovy/org/grails/compiler/web/ControllerActionTransformer.java index 280249d6fed..8bd79d2fa33 100644 --- a/grails-controllers/src/main/groovy/org/grails/compiler/web/ControllerActionTransformer.java +++ b/grails-controllers/src/main/groovy/org/grails/compiler/web/ControllerActionTransformer.java @@ -73,7 +73,6 @@ import org.codehaus.groovy.classgen.GeneratorContext; import org.codehaus.groovy.control.CompilationUnit; import org.codehaus.groovy.control.SourceUnit; -import org.codehaus.groovy.runtime.DefaultGroovyMethods; import org.codehaus.groovy.syntax.Token; import org.codehaus.groovy.syntax.Types; import org.codehaus.groovy.transform.trait.Traits; @@ -267,12 +266,12 @@ private void processMethods(ClassNode classNode, SourceUnit source, if (methodShouldBeConfiguredAsControllerAction(method)) { final List declaredMethodsWithThisName = classNode.getDeclaredMethods(method.getName()); if (declaredMethodsWithThisName != null) { - final int numberOfNonExceptionHandlerMethodsWithThisName = DefaultGroovyMethods.count((Iterable) declaredMethodsWithThisName, new Closure(this) { - @Override - public Object call(Object object) { - return !isExceptionHandlingMethod((MethodNode) object); + int numberOfNonExceptionHandlerMethodsWithThisName = 0; + for (MethodNode candidate : declaredMethodsWithThisName) { + if (!isExceptionHandlingMethod(candidate)) { + numberOfNonExceptionHandlerMethodsWithThisName++; } - }).intValue(); + } if (numberOfNonExceptionHandlerMethodsWithThisName > 1) { String message = "Controller actions may not be overloaded. The [" + method.getName() + diff --git a/grails-core/src/main/groovy/grails/config/external/WriterFilteringMap.groovy b/grails-core/src/main/groovy/grails/config/external/WriterFilteringMap.groovy index 9667f677594..1a20e747338 100644 --- a/grails-core/src/main/groovy/grails/config/external/WriterFilteringMap.groovy +++ b/grails-core/src/main/groovy/grails/config/external/WriterFilteringMap.groovy @@ -26,7 +26,14 @@ class WriteFilteringMap implements Map { String keyPrefix private Map proxied // source map - @Delegate + // Groovy 6 / Spock workaround: exclude the mutating Map methods this class already overrides. + // Otherwise @Delegate also generates put(Object,Object)/remove(Object)/putAll(Map) forwarding + // straight to `overlap`, competing with the tracking overrides below. Under Groovy 6 (notably + // when specs are compiled by the Spock groovy-5.0 artifact) a put(...) call can dispatch to the + // generated delegate method instead of the override, so writes land in `overlap` but never in + // nestedDestinationMap and getWrittenValues() comes back empty. Excluding them leaves only the + // overrides (plus their bridge methods), so every mutation is tracked regardless of dispatch. + @Delegate(excludes = ['put', 'putAll', 'remove']) private Map overlap // written values, flattened -- shared private Map nestedDestinationMap // written keys at this level diff --git a/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/GormEntity.groovy b/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/GormEntity.groovy index dae59760d87..ca8b8b19d2c 100644 --- a/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/GormEntity.groovy +++ b/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/GormEntity.groovy @@ -39,6 +39,7 @@ import org.grails.datastore.mapping.model.types.ToOne import org.grails.datastore.mapping.query.api.BuildableCriteria import org.grails.datastore.mapping.query.api.Criteria import org.grails.datastore.mapping.reflect.EntityReflector +import org.codehaus.groovy.runtime.InvokerHelper /** * @@ -590,7 +591,7 @@ trait GormEntity implements GormValidateable, DirtyCheckable, GormEntityApi implements GormValidateable, DirtyCheckable, GormEntityApi + apply plugin: 'org.apache.grails.gradle.grails-web' dependencies { diff --git a/grails-test-examples/micronaut-groovy-only/build.gradle b/grails-test-examples/micronaut-groovy-only/build.gradle index 62abb364315..32b731c1a40 100644 --- a/grails-test-examples/micronaut-groovy-only/build.gradle +++ b/grails-test-examples/micronaut-groovy-only/build.gradle @@ -25,6 +25,13 @@ plugins { version = '0.1' group = 'micronautgroovyonly' +// Groovy 6.0.0-SNAPSHOT requires asm 9.10 (for JDK 27 bytecode support), while +// the grails-micronaut-bom inherits asm 9.9.1 from Spring Boot 4 / Micronaut +// platform. The divergence is intentional on this canary; un-pin via the +// validator plugin's allowedBomOverrides mechanism (see GrailsDependencyValidatorPlugin +// `ALLOWED_OVERRIDES_EXT` for the contract). +ext.allowedBomOverrides = ['org.ow2.asm:asm', 'org.ow2.asm:asm-util'] as Set + apply plugin: 'org.apache.grails.gradle.grails-web' // This module intentionally has NO annotationProcessor dependencies. diff --git a/grails-test-examples/micronaut/build.gradle b/grails-test-examples/micronaut/build.gradle index 3a3c4e527b7..906e84f6d3e 100644 --- a/grails-test-examples/micronaut/build.gradle +++ b/grails-test-examples/micronaut/build.gradle @@ -27,6 +27,13 @@ plugins { version = '0.1' group = 'micronaut' +// Groovy 6.0.0-SNAPSHOT requires asm 9.10 (for JDK 27 bytecode support), while +// the grails-micronaut-bom inherits asm 9.9.1 from Spring Boot 4 / Micronaut +// platform. The divergence is intentional on this canary; un-pin via the +// validator plugin's allowedBomOverrides mechanism (see GrailsDependencyValidatorPlugin +// `ALLOWED_OVERRIDES_EXT` for the contract). +ext.allowedBomOverrides = ['org.ow2.asm:asm', 'org.ow2.asm:asm-util'] as Set + apply plugin: 'org.apache.grails.gradle.grails-web' apply plugin: 'cloud.wondrify.asset-pipeline' diff --git a/grails-test-examples/plugins/micronaut-singleton/build.gradle b/grails-test-examples/plugins/micronaut-singleton/build.gradle index 96d97930f73..d0154439656 100644 --- a/grails-test-examples/plugins/micronaut-singleton/build.gradle +++ b/grails-test-examples/plugins/micronaut-singleton/build.gradle @@ -25,6 +25,13 @@ plugins { version = '0.1-SNAPSHOT' group = 'com.example.grails.plugins' +// Groovy 6.0.0-SNAPSHOT requires asm 9.10 (for JDK 27 bytecode support), while +// the grails-micronaut-bom inherits asm 9.9.1 from Spring Boot 4 / Micronaut +// platform. The divergence is intentional on this canary; un-pin via the +// validator plugin's allowedBomOverrides mechanism (see GrailsDependencyValidatorPlugin +// `ALLOWED_OVERRIDES_EXT` for the contract). +ext.allowedBomOverrides = ['org.ow2.asm:asm', 'org.ow2.asm:asm-util'] as Set + apply plugin: 'org.apache.grails.gradle.grails-plugin' dependencies { diff --git a/grails-testing-support-http-client/src/main/groovy/org/apache/grails/testing/http/client/utils/XmlUtils.groovy b/grails-testing-support-http-client/src/main/groovy/org/apache/grails/testing/http/client/utils/XmlUtils.groovy index 8051154047a..6d350a77162 100644 --- a/grails-testing-support-http-client/src/main/groovy/org/apache/grails/testing/http/client/utils/XmlUtils.groovy +++ b/grails-testing-support-http-client/src/main/groovy/org/apache/grails/testing/http/client/utils/XmlUtils.groovy @@ -45,12 +45,14 @@ import org.xml.sax.SAXException @CompileStatic class XmlUtils { - private static final String DISALLOW_DOCTYPE_DECL = 'https://apache.org/xml/features/disallow-doctype-decl' - private static final String EXTERNAL_GENERAL_ENTITIES = 'https://xml.org/sax/features/external-general-entities' - private static final String EXTERNAL_PARAMETER_ENTITIES = 'https://xml.org/sax/features/external-parameter-entities' + // SAX/Xerces feature identifiers are namespace-style URIs that use the http scheme; the parser + // matches them by exact string, so https variants throw SAXNotRecognizedException, get swallowed + // below, and silently leave the parser at its (JDK-version-dependent) defaults. + private static final String DISALLOW_DOCTYPE_DECL = 'http://apache.org/xml/features/disallow-doctype-decl' + private static final String EXTERNAL_PARAMETER_ENTITIES = 'http://xml.org/sax/features/external-parameter-entities' private static final String FEATURE_SECURE_PROCESSING = XMLConstants.FEATURE_SECURE_PROCESSING - private static final String LOAD_DTD_GRAMMAR = 'https://apache.org/xml/features/nonvalidating/load-dtd-grammar' - private static final String LOAD_EXTERNAL_DTD = 'https://apache.org/xml/features/nonvalidating/load-external-dtd' + private static final String LOAD_DTD_GRAMMAR = 'http://apache.org/xml/features/nonvalidating/load-dtd-grammar' + private static final String LOAD_EXTERNAL_DTD = 'http://apache.org/xml/features/nonvalidating/load-external-dtd' private static final Pattern SPACE_AND_EMPTY_ELEMENT_CLOSE = ~/ \/>/ private static final String EMPTY_ELEMENT_CLOSE = '/>' @@ -58,15 +60,27 @@ class XmlUtils { private static final Pattern LINE_ENDINGS = ~/\r\n|[\r\n]/ private static final Pattern XML_DECLARATION = ~/^\s*(<\?xml\b.*?\?>)/ + // Inline DOCTYPE with internal entities is allowed (disallow-doctype-decl=false). External general + // entities are intentionally left enabled so a SYSTEM reference is *attempted* and then blocked by + // the accessExternalDTD/Schema properties below, which throws a SAXParseException ("External Entity: + // ... access is not allowed") rather than silently dropping the reference. private static final Map SECURE_XML_SLURPER_FEATURES = [ (DISALLOW_DOCTYPE_DECL): false, - (EXTERNAL_GENERAL_ENTITIES): false, (EXTERNAL_PARAMETER_ENTITIES): false, (FEATURE_SECURE_PROCESSING): true, (LOAD_DTD_GRAMMAR): false, (LOAD_EXTERNAL_DTD): false ].asImmutable() + // JAXP parser properties: an empty value forbids every protocol for external DTD/entity access, + // so an inline DOCTYPE with internal entities still parses while any external SYSTEM reference + // throws a SAXParseException ("External Entity: ... access is not allowed"). Disabling the + // external-general-entities feature alone only skips the entity silently; these throw. + private static final Map SECURE_XML_SLURPER_PROPERTIES = [ + (XMLConstants.ACCESS_EXTERNAL_DTD): '', + (XMLConstants.ACCESS_EXTERNAL_SCHEMA): '' + ].asImmutable() + /** * Renders XML from the given {@link groovy.xml.MarkupBuilder} DSL closure * using the optionally provided rendering options. @@ -235,7 +249,16 @@ class XmlUtils { } } - saxParserFactory.newSAXParser() + def saxParser = saxParserFactory.newSAXParser() + SECURE_XML_SLURPER_PROPERTIES.each { name, value -> + try { + saxParser.setProperty(name, value) + } + catch (Exception ignored) { + // ignore, parser doesn't support + } + } + saxParser } } diff --git a/grails-validation/src/main/groovy/grails/validation/Validateable.groovy b/grails-validation/src/main/groovy/grails/validation/Validateable.groovy index 827c851e8f4..bdcc1ec1977 100644 --- a/grails-validation/src/main/groovy/grails/validation/Validateable.groovy +++ b/grails-validation/src/main/groovy/grails/validation/Validateable.groovy @@ -286,9 +286,10 @@ trait Validateable { * override semantics. Starting in Groovy 5, {@code TraitReceiverTransformer} * rewrites {@code this.defaultNullable()} from another method inside the * trait body to a direct call into the trait helper's static method, - * silently losing any implementing-class override. - * {@link java.lang.reflect.Method#invoke} is opaque to the transform so it - * dispatches to the implementing-class bytecode directly. + * silently losing any implementing-class override. The same regression + * is present in Groovy 6.0.0-SNAPSHOT. {@link java.lang.reflect.Method#invoke} + * is opaque to the transform so it dispatches to the implementing-class + * bytecode directly. * * Standalone reproducer: * https://github.com/jamesfredley/groovy-trait-static-method-override-bug diff --git a/grails-views-gson/src/main/groovy/grails/plugin/json/view/api/GrailsJsonViewHelper.groovy b/grails-views-gson/src/main/groovy/grails/plugin/json/view/api/GrailsJsonViewHelper.groovy index 114317700ea..6639e97071b 100644 --- a/grails-views-gson/src/main/groovy/grails/plugin/json/view/api/GrailsJsonViewHelper.groovy +++ b/grails-views-gson/src/main/groovy/grails/plugin/json/view/api/GrailsJsonViewHelper.groovy @@ -45,7 +45,18 @@ interface GrailsJsonViewHelper extends GrailsViewHelper { * @param arguments The named arguments: 'template', 'collection', 'model', 'var' and 'bean' * @return The unescaped JSON */ - JsonOutput.JsonWritable render(Map arguments) + // Groovy 6 Verifier workaround (blocker #6): declared as `default` (concrete) rather than + // abstract. Under Groovy 6.0.0-SNAPSHOT the concrete render(...) overrides in + // DefaultGrailsJsonViewHelper get a different return-type descriptor than these interface + // methods (inner-class return type JsonOutput.JsonWritable; groovy.json.JsonOutput.JsonWritable + // was removed in Groovy 6), so the abstract-method check in + // ClassCompletionVerifier.checkNoAbstractMethodsNonAbstractClass spuriously reports them + // unimplemented. Making them default removes them from getAbstractMethods() so the check has + // nothing to flag; every real implementor overrides them. Remove once the upstream regression + // is fixed. + default JsonOutput.JsonWritable render(Map arguments) { + throw new UnsupportedOperationException() + } /** * Renders the given object to JSON, typically a domain class, ignoring lazy and internal properties @@ -55,7 +66,9 @@ interface GrailsJsonViewHelper extends GrailsViewHelper { * @param customizer Used to customize the contents * @return The unescaped JSON */ - JsonOutput.JsonWritable render(Object object, Map arguments, @DelegatesTo(StreamingJsonBuilder.StreamingJsonDelegate) Closure customizer) + default JsonOutput.JsonWritable render(Object object, Map arguments, @DelegatesTo(StreamingJsonBuilder.StreamingJsonDelegate) Closure customizer) { + throw new UnsupportedOperationException() + } /** * Renders the given object to JSON, typically a domain class, ignoring lazy and internal properties @@ -64,7 +77,9 @@ interface GrailsJsonViewHelper extends GrailsViewHelper { * @param arguments The supported named arguments: 'includes' or 'excludes' list * @return The unescaped JSON */ - JsonOutput.JsonWritable render(Object object, Map arguments) + default JsonOutput.JsonWritable render(Object object, Map arguments) { + throw new UnsupportedOperationException() + } /** * Renders the given object to JSON, typically a domain class, ignoring lazy and internal properties @@ -72,7 +87,9 @@ interface GrailsJsonViewHelper extends GrailsViewHelper { * @param object The object to render * @return The unescaped JSON */ - JsonOutput.JsonWritable render(Object object) + default JsonOutput.JsonWritable render(Object object) { + throw new UnsupportedOperationException() + } /** * Renders the given object to JSON, typically a domain class, ignoring lazy and internal properties @@ -81,7 +98,9 @@ interface GrailsJsonViewHelper extends GrailsViewHelper { * @param customizer the customizer * @return The unescaped JSON */ - JsonOutput.JsonWritable render(Object object, @DelegatesTo(StreamingJsonBuilder.StreamingJsonDelegate) Closure customizer) + default JsonOutput.JsonWritable render(Object object, @DelegatesTo(StreamingJsonBuilder.StreamingJsonDelegate) Closure customizer) { + throw new UnsupportedOperationException() + } /** * Renders the given object inline within the current JSON object instead of creating a new JSON object diff --git a/grails-views-gson/src/main/groovy/grails/plugin/json/view/api/internal/TemplateRenderer.groovy b/grails-views-gson/src/main/groovy/grails/plugin/json/view/api/internal/TemplateRenderer.groovy index 905a7ad385f..d079a46a8e4 100644 --- a/grails-views-gson/src/main/groovy/grails/plugin/json/view/api/internal/TemplateRenderer.groovy +++ b/grails-views-gson/src/main/groovy/grails/plugin/json/view/api/internal/TemplateRenderer.groovy @@ -19,8 +19,10 @@ package grails.plugin.json.view.api.internal +import groovy.json.StreamingJsonBuilder import groovy.transform.CompileStatic +import grails.plugin.json.builder.JsonOutput import grails.plugin.json.view.api.GrailsJsonViewHelper import grails.util.GrailsNameUtils @@ -39,6 +41,36 @@ class TemplateRenderer { this.jsonViewHelper = jsonViewHelper } + // Explicit forwarders for the 5 GrailsJsonViewHelper#render(...) overloads. + // Under Groovy 6.0.0-SNAPSHOT the @Delegate AST transform no longer satisfies + // the abstract-method-implementation check for interface methods whose return + // type is an inner class (here JsonOutput.JsonWritable): the @CompileStatic + // verifier runs before @Delegate generates the forwarders, so the compiler + // reports "Can't have an abstract method in a non-abstract class". + // The 5 inline(...) overloads return void and are unaffected, so @Delegate + // still handles them. Behaviour is identical to what @Delegate generates on + // Groovy 5. + + JsonOutput.JsonWritable render(Map arguments) { + jsonViewHelper.render(arguments) + } + + JsonOutput.JsonWritable render(Object object, Map arguments, @DelegatesTo(StreamingJsonBuilder.StreamingJsonDelegate) Closure customizer) { + jsonViewHelper.render(object, arguments, customizer) + } + + JsonOutput.JsonWritable render(Object object, Map arguments) { + jsonViewHelper.render(object, arguments) + } + + JsonOutput.JsonWritable render(Object object) { + jsonViewHelper.render(object) + } + + JsonOutput.JsonWritable render(Object object, @DelegatesTo(StreamingJsonBuilder.StreamingJsonDelegate) Closure customizer) { + jsonViewHelper.render(object, customizer) + } + @Override Object invokeMethod(String name, Object args) { Object[] argArray = (Object[]) args