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 8dfc6d05a0b..3ceeaeefce3 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,20 @@ 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/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.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 800e49731e8..7c64c6f44a0 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -78,7 +78,7 @@ ext { 'commons-codec.version' : '1.21.0', 'commons-lang3.version' : '3.20.0', 'geb-spock.version' : '8.0.1', - 'groovy.version' : '5.0.6-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', @@ -217,9 +217,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.6-SNAPSHOT', + 'groovy.version' : '6.0.0-SNAPSHOT', 'spock.version' : '2.4-groovy-5.0', 'protobuf.version': '4.30.2', ] 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-data-hibernate5/core/src/main/groovy/org/grails/orm/hibernate/connections/HibernateConnectionSourceSettings.groovy b/grails-data-hibernate5/core/src/main/groovy/org/grails/orm/hibernate/connections/HibernateConnectionSourceSettings.groovy index 0c9aab26a2d..dd232fa49fc 100644 --- a/grails-data-hibernate5/core/src/main/groovy/org/grails/orm/hibernate/connections/HibernateConnectionSourceSettings.groovy +++ b/grails-data-hibernate5/core/src/main/groovy/org/grails/orm/hibernate/connections/HibernateConnectionSourceSettings.groovy @@ -78,6 +78,33 @@ class HibernateConnectionSourceSettings extends ConnectionSourceSettings { @AutoClone static class HibernateSettings extends LinkedHashMap { + // Groovy 6.0.0-SNAPSHOT (build #546+) stub generator regression: when + // @AutoClone is applied to a class that extends a JDK type whose + // clone() does not declare CloneNotSupportedException (here + // LinkedHashMap.clone()), the Java stub generator still emits the + // override with `throws CloneNotSupportedException`, and javac + // rejects it as not a valid override. Defining clone() explicitly + // suppresses the @AutoClone-generated method (AutoClone skips when + // a user-supplied clone() already exists) and keeps the stub + // signature in lock-step with LinkedHashMap.clone(). The body + // mirrors what @AutoClone(style = CLONE) would produce: a shallow + // copy from LinkedHashMap.clone() followed by deep-cloning of the + // Cloneable typed fields so that tenant-specific + // HibernateConnectionSourceSettings instances (cloned in + // HibernateDatastore.createTenantConnectionSource) do not share + // mutable nested settings. Removable once upstream Groovy fixes + // the stub generator. + @Override + HibernateSettings clone() { + HibernateSettings copy = (HibernateSettings) super.clone() + copy.osiv = osiv != null ? (OsivSettings) osiv.clone() : null + copy.cache = cache != null ? (CacheSettings) cache.clone() : null + copy.flush = flush != null ? (FlushSettings) flush.clone() : null + copy.additionalProperties = additionalProperties != null ? + (Properties) additionalProperties.clone() : null + return copy + } + /** * Whether OpenSessionInView should be read-only */ diff --git a/grails-datamapping-core/src/main/groovy/org/grails/compiler/gorm/GormEntityTransformation.groovy b/grails-datamapping-core/src/main/groovy/org/grails/compiler/gorm/GormEntityTransformation.groovy index a154019d157..0b75c73214c 100644 --- a/grails-datamapping-core/src/main/groovy/org/grails/compiler/gorm/GormEntityTransformation.groovy +++ b/grails-datamapping-core/src/main/groovy/org/grails/compiler/gorm/GormEntityTransformation.groovy @@ -292,6 +292,33 @@ class GormEntityTransformation extends AbstractASTTransformation implements Comp classNode.addMethod('$static_propertyMissing', Modifier.PUBLIC | Modifier.STATIC, AstUtils.OBJECT_CLASS_NODE, propertyMissingGetParameters, null, propertyMissingGetBody) markAsGenerated(classNode, propertyMissingNodeGetter) + // INSTANCE Object get(String name) - Groovy 6 generic-getter MOP regression workaround. + // On Groovy 6, MetaClassImpl picks up the inherited GormEntity.get(Serializable) + // entity-by-ID method as the genericGetMethod for instance property access on the + // implementing class, hijacking every dynamic property read - including ones that + // should fall through to propertyMissing(String) for datasource qualifiers. Result: + // book.someConnection.delete(flush: true) silently returns the get(Serializable) value + // (an entity row or null) instead of the expected DelegatingGormEntityApi, which then + // surfaces as "Unknown entity: java.util.LinkedHashMap" deep in Hibernate or as NPEs + // in HibernateRuntimeUtils.setupErrorsProperty. + // Workaround: add an instance Object get(String) directly on every @Entity class via + // AST. Groovy's instance MOP picks the more-specific String overload over the + // inherited Serializable one, so the generic-getter winds up routing through the + // existing propertyMissing(String) and yields a DelegatingGormEntityApi as expected. + // Standalone reproducer: https://github.com/jamesfredley/groovy6-get-as-generic-getter + // No upstream Apache Groovy JIRA filed yet; remove this shim once one is filed and fixed. + def instanceGetBody = new BlockStatement() + def instanceGetNameParam = new Parameter(ClassHelper.make(String), 'name') + def instanceGetArgs = new ArgumentListExpression(instanceGetNameParam) + def instanceGetMethodCall = new MethodCallExpression(new VariableExpression('this'), 'propertyMissing', instanceGetArgs) + instanceGetBody.addStatement( + new ExpressionStatement(instanceGetMethodCall) + ) + def instanceGetParameters = [instanceGetNameParam] as Parameter[] + MethodNode instanceGetNode = + classNode.addMethod('get', Modifier.PUBLIC, AstUtils.OBJECT_CLASS_NODE, instanceGetParameters, null, instanceGetBody) + markAsGenerated(classNode, instanceGetNode) + // now process named query associations // see https://grails.apache.org/docs/latest/ref/Domain%20Classes/namedQueries.html 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..4c6e998069c 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,22 @@ trait GormEntity implements GormValidateable, DirtyCheckable, GormEntityApi implements GormValidateable, DirtyCheckable, GormEntityApi