You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by ma...@apache.org on 2026/06/08 13:40:03 UTC

(grails-core) 01/01: refactor: replace usages of deprecated external APIs

This is an automated email from the ASF dual-hosted git repository.

matrei pushed a commit to branch remove-depr-warnings
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit 504105fbbfbaf8c6ea327355b8b121984ad7cf9a
Author: Mattias Reichel <ma...@gmail.com>
AuthorDate: Mon Jun 8 15:39:14 2026 +0200

    refactor: replace usages of deprecated external APIs
---
 .../internal/DelegateAsyncTransformation.java      |  30 ++----
 .../groovy/grails/async/DelegateAsyncSpec.groovy   | 105 +++++++++++++++++++++
 .../transform/DelegateAsyncTransformation.java     |  27 ------
 .../datastore/mapping/reflect/NameUtils.java       |   4 +-
 .../org/grails/buffer/GrailsPrintWriter.java       |   3 +-
 .../Sitemesh3EnvironmentPostProcessor.java         |   2 +-
 .../java/grails/gsp/boot/GspAutoConfiguration.java |   2 +-
 .../org/grails/support/MockApplicationContext.java |   3 +-
 .../grails/web/databinding/DataBindingUtils.java   |  12 ++-
 .../org/grails/web/mapping/RegexUrlMapping.java    |   4 +-
 .../src/main/java/grails/init/GrailsUpdater.java   |   9 +-
 11 files changed, 141 insertions(+), 60 deletions(-)

diff --git a/grails-async/core/src/main/groovy/org/grails/async/transform/internal/DelegateAsyncTransformation.java b/grails-async/core/src/main/groovy/org/grails/async/transform/internal/DelegateAsyncTransformation.java
index a776e0644d..7ba30826f6 100644
--- a/grails-async/core/src/main/groovy/org/grails/async/transform/internal/DelegateAsyncTransformation.java
+++ b/grails-async/core/src/main/groovy/org/grails/async/transform/internal/DelegateAsyncTransformation.java
@@ -20,9 +20,7 @@ package org.grails.async.transform.internal;
 
 import java.beans.Introspector;
 import java.lang.reflect.Modifier;
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
 
 import groovy.lang.Closure;
@@ -173,28 +171,14 @@ public class DelegateAsyncTransformation implements ASTTransformation, Transform
     }
 
     private static ClassNode alignReturnType(final ClassNode receiver, final ClassNode originalReturnType) {
-        ClassNode copiedReturnType = originalReturnType.getPlainNodeReference();
-
-        List<GenericsType> redirectTypes = new ArrayList<>();
-        if (receiver.redirect().getGenericsTypes() != null) {
-            Collections.addAll(redirectTypes, receiver.redirect().getGenericsTypes());
+        if (!originalReturnType.isUsingGenerics()) {
+            return originalReturnType.getPlainNodeReference();
         }
-        if (!redirectTypes.isEmpty()) {
-            GenericsType[] redirectReceiverTypes = redirectTypes.toArray(new GenericsType[0]);
-
-            GenericsType[] receiverParameterizedTypes = receiver.getGenericsTypes();
-            if (receiverParameterizedTypes == null) {
-                receiverParameterizedTypes = redirectReceiverTypes;
-            }
-
-            if (originalReturnType.isUsingGenerics()) {
-                GenericsType[] alignmentTypes = originalReturnType.getGenericsTypes();
-                GenericsType[] genericsTypes = GenericsUtils.alignGenericTypes(redirectReceiverTypes, receiverParameterizedTypes, alignmentTypes);
-                copiedReturnType.setGenericsTypes(genericsTypes);
-            }
-        }
-
-        return copiedReturnType;
+        var genericsSpec = GenericsUtils.createGenericsSpec(receiver);
+        var correctedReturnType = GenericsUtils.correctToGenericsSpecRecurse(genericsSpec, originalReturnType);
+        return correctedReturnType == null
+            ? originalReturnType.getPlainNodeReference()
+            : correctedReturnType;
     }
 
     protected DelegateAsyncTransactionalMethodTransformer lookupAsyncTransactionalMethodTransformer() {
diff --git a/grails-async/core/src/test/groovy/grails/async/DelegateAsyncSpec.groovy b/grails-async/core/src/test/groovy/grails/async/DelegateAsyncSpec.groovy
index 6ed9ea4d54..2504e74835 100644
--- a/grails-async/core/src/test/groovy/grails/async/DelegateAsyncSpec.groovy
+++ b/grails-async/core/src/test/groovy/grails/async/DelegateAsyncSpec.groovy
@@ -75,6 +75,92 @@ class DelegateAsyncSpec extends Specification {
         then: 'the decorator is applied to the value'
             val == 6
     }
+
+    void 'Delegate async transform preserves generic return type for static compilation'() {
+        when:
+        def result = new GroovyShell().evaluate('''
+            import grails.async.*
+            import groovy.transform.CompileStatic
+
+            class GenericService<T> {
+                T echo(T value) { value }
+            }
+
+            class AsyncStringService {
+                @DelegateAsync
+                GenericService<String> service = new GenericService<String>()
+            }
+
+            @CompileStatic
+            String test() {
+                Promise<String> p = new AsyncStringService().echo('hello')
+                p.get()
+            }
+
+            test()
+        ''')
+
+        then:
+        result == 'hello'
+    }
+
+    void 'Delegate async transform aligns generic return type from inherited interface method'() {
+        when:
+        def service = new AsyncStringHolderService()
+        def p = service.get()
+
+        then:
+        p instanceof Promise
+
+        and:
+        p.get() == 'hello'
+    }
+
+    void 'Delegate async transform preserves generic return type from inherited interface method for static compilation'() {
+        when:
+        def result = new GroovyShell().evaluate('''
+            import grails.async.*
+            import groovy.transform.CompileStatic
+
+            interface Holder<T> {
+                T get()
+            }
+
+            class StringHolder implements Holder<String> {
+                @Override
+                String get() {
+                    'hello'
+                }
+            }
+
+            @DelegateAsync(StringHolder)
+            class AsyncStringHolderService {
+            }
+
+            @CompileStatic
+            String test() {
+                Promise<String> p = new AsyncStringHolderService().get()
+                p.get()
+            }
+
+            test()
+        ''')
+
+        then:
+        result == 'hello'
+    }
+
+    void 'Delegate async transform aligns generic return type from parameterized interface field'() {
+        when:
+        def service = new AsyncParameterizedHolderService()
+        def p = service.get()
+
+        then:
+        p instanceof Promise
+
+        and:
+        p.get() == 'hello'
+    }
 }
 
 class MathService {
@@ -103,3 +189,22 @@ class AsyncMathService3 implements PromiseDecoratorProvider {
     ]
 }
 
+interface Holder<T> {
+    T get()
+}
+
+class StringHolder implements Holder<String> {
+    @Override
+    String get() {
+        'hello'
+    }
+}
+
+@DelegateAsync(StringHolder)
+class AsyncStringHolderService {
+}
+
+class AsyncParameterizedHolderService {
+    @DelegateAsync
+    Holder<String> holder = new StringHolder()
+}
\ No newline at end of file
diff --git a/grails-datastore-async/src/main/groovy/org/grails/datastore/gorm/async/transform/DelegateAsyncTransformation.java b/grails-datastore-async/src/main/groovy/org/grails/datastore/gorm/async/transform/DelegateAsyncTransformation.java
index 11e20dbad3..f7042f25e2 100644
--- a/grails-datastore-async/src/main/groovy/org/grails/datastore/gorm/async/transform/DelegateAsyncTransformation.java
+++ b/grails-datastore-async/src/main/groovy/org/grails/datastore/gorm/async/transform/DelegateAsyncTransformation.java
@@ -23,7 +23,6 @@ import java.beans.Introspector;
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -182,32 +181,6 @@ public class DelegateAsyncTransformation implements ASTTransformation, Transform
         return names;
     }
 
-    private static ClassNode alignReturnType(final ClassNode receiver, final ClassNode originalReturnType) {
-        ClassNode copiedReturnType = originalReturnType.getPlainNodeReference();
-
-        ClassNode actualReceiver = receiver;
-        List<GenericsType> redirectTypes = new ArrayList<>();
-        if (actualReceiver.redirect().getGenericsTypes() != null) {
-            Collections.addAll(redirectTypes, actualReceiver.redirect().getGenericsTypes());
-        }
-        if (!redirectTypes.isEmpty()) {
-            GenericsType[] redirectReceiverTypes = redirectTypes.toArray(new GenericsType[redirectTypes.size()]);
-
-            GenericsType[] receiverParameterizedTypes = actualReceiver.getGenericsTypes();
-            if (receiverParameterizedTypes == null) {
-                receiverParameterizedTypes = redirectReceiverTypes;
-            }
-
-            if (originalReturnType.isUsingGenerics()) {
-                GenericsType[] alignmentTypes = originalReturnType.getGenericsTypes();
-                GenericsType[] genericsTypes = GenericsUtils.alignGenericTypes(redirectReceiverTypes, receiverParameterizedTypes, alignmentTypes);
-                copiedReturnType.setGenericsTypes(genericsTypes);
-            }
-        }
-
-        return copiedReturnType;
-    }
-
     private static boolean isCandidateMethod(MethodNode declaredMethod) {
         ClassNode groovyMethods = GROOVY_OBJECT_CLASS_NODE;
         String methodName = declaredMethod.getName();
diff --git a/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/reflect/NameUtils.java b/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/reflect/NameUtils.java
index c831c62f4f..d3a988c731 100644
--- a/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/reflect/NameUtils.java
+++ b/grails-datastore-core/src/main/groovy/org/grails/datastore/mapping/reflect/NameUtils.java
@@ -20,7 +20,7 @@ import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 
-import org.codehaus.groovy.runtime.MetaClassHelper;
+import org.apache.groovy.util.BeanUtils;
 
 import org.grails.datastore.mapping.model.config.GormProperties;
 
@@ -158,6 +158,6 @@ public class NameUtils {
      * @return The class name
      */
     public static String capitalize(String name) {
-        return MetaClassHelper.capitalize(name);
+        return BeanUtils.capitalize(name);
     }
 }
diff --git a/grails-encoder/src/main/groovy/org/grails/buffer/GrailsPrintWriter.java b/grails-encoder/src/main/groovy/org/grails/buffer/GrailsPrintWriter.java
index 08fac4e59c..a387f9ac9a 100644
--- a/grails-encoder/src/main/groovy/org/grails/buffer/GrailsPrintWriter.java
+++ b/grails-encoder/src/main/groovy/org/grails/buffer/GrailsPrintWriter.java
@@ -25,6 +25,7 @@ import java.io.Writer;
 import groovy.lang.GroovyObject;
 import groovy.lang.MetaClass;
 import groovy.lang.Writable;
+import org.codehaus.groovy.runtime.FormatHelper;
 import org.codehaus.groovy.runtime.GStringImpl;
 import org.codehaus.groovy.runtime.InvokerHelper;
 import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
@@ -133,7 +134,7 @@ public class GrailsPrintWriter extends Writer implements GrailsWrappedWriter, En
             }
         }
         else {
-            InvokerHelper.write(this, obj);
+            FormatHelper.write(this, obj);
         }
         return this;
     }
diff --git a/grails-gsp/grails-sitemesh3/src/main/java/org/grails/plugins/sitemesh3/Sitemesh3EnvironmentPostProcessor.java b/grails-gsp/grails-sitemesh3/src/main/java/org/grails/plugins/sitemesh3/Sitemesh3EnvironmentPostProcessor.java
index b43dfa88dc..becc35034f 100644
--- a/grails-gsp/grails-sitemesh3/src/main/java/org/grails/plugins/sitemesh3/Sitemesh3EnvironmentPostProcessor.java
+++ b/grails-gsp/grails-sitemesh3/src/main/java/org/grails/plugins/sitemesh3/Sitemesh3EnvironmentPostProcessor.java
@@ -21,8 +21,8 @@ package org.grails.plugins.sitemesh3;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.springframework.boot.EnvironmentPostProcessor;
 import org.springframework.boot.SpringApplication;
-import org.springframework.boot.env.EnvironmentPostProcessor;
 import org.springframework.core.env.ConfigurableEnvironment;
 import org.springframework.core.env.MapPropertySource;
 
diff --git a/grails-gsp/spring-boot/src/main/java/grails/gsp/boot/GspAutoConfiguration.java b/grails-gsp/spring-boot/src/main/java/grails/gsp/boot/GspAutoConfiguration.java
index 79ec59002f..d32d50f72c 100644
--- a/grails-gsp/spring-boot/src/main/java/grails/gsp/boot/GspAutoConfiguration.java
+++ b/grails-gsp/spring-boot/src/main/java/grails/gsp/boot/GspAutoConfiguration.java
@@ -157,7 +157,7 @@ public class GspAutoConfiguration {
                     if (rootPath.endsWith("/")) {
                         rootPath = rootPath.substring(0, rootPath.length() - 1);
                     }
-                    if (!StringUtils.isEmpty(rootPath)) {
+                    if (StringUtils.hasLength(rootPath)) {
                         rootPaths.add(rootPath);
                     }
                 }
diff --git a/grails-test-suite-base/src/main/groovy/org/grails/support/MockApplicationContext.java b/grails-test-suite-base/src/main/groovy/org/grails/support/MockApplicationContext.java
index 2e0efbad08..d0e7ce29df 100644
--- a/grails-test-suite-base/src/main/groovy/org/grails/support/MockApplicationContext.java
+++ b/grails-test-suite-base/src/main/groovy/org/grails/support/MockApplicationContext.java
@@ -36,6 +36,8 @@ import groovy.lang.GroovyObjectSupport;
 
 import jakarta.servlet.ServletContext;
 
+import org.jspecify.annotations.NonNull;
+
 import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.BeanCreationException;
 import org.springframework.beans.factory.BeanFactory;
@@ -53,7 +55,6 @@ import org.springframework.core.env.Environment;
 import org.springframework.core.io.AbstractResource;
 import org.springframework.core.io.ClassPathResource;
 import org.springframework.core.io.Resource;
-import org.springframework.lang.NonNull;
 import org.springframework.mock.web.MockServletContext;
 import org.springframework.util.AntPathMatcher;
 import org.springframework.util.PathMatcher;
diff --git a/grails-web-databinding/src/main/groovy/grails/web/databinding/DataBindingUtils.java b/grails-web-databinding/src/main/groovy/grails/web/databinding/DataBindingUtils.java
index 55d23ed67f..4290f85550 100644
--- a/grails-web-databinding/src/main/groovy/grails/web/databinding/DataBindingUtils.java
+++ b/grails-web-databinding/src/main/groovy/grails/web/databinding/DataBindingUtils.java
@@ -19,6 +19,7 @@
 package grails.web.databinding;
 
 import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Modifier;
 import java.util.Collection;
 import java.util.Collections;
@@ -179,7 +180,16 @@ public class DataBindingUtils {
         }
         final List<DataBindingSource> dataBindingSources = collectionBindingSource.getDataBindingSources();
         for (final DataBindingSource dataBindingSource : dataBindingSources) {
-            final T newObject = targetType.newInstance();
+            T newObject;
+            try {
+                newObject = targetType.getDeclaredConstructor().newInstance();
+            } catch(NoSuchMethodException | InvocationTargetException ex) {
+                throw new IllegalStateException(
+                    "Could not instantiate class [" + targetType.getName() + "]",
+                    ex
+                );
+            }
+
             bindObjectToDomainInstance(entity, newObject, dataBindingSource, getBindingIncludeList(newObject), Collections.emptyList(), null);
             collectionToPopulate.add(newObject);
         }
diff --git a/grails-web-url-mappings/src/main/groovy/org/grails/web/mapping/RegexUrlMapping.java b/grails-web-url-mappings/src/main/groovy/org/grails/web/mapping/RegexUrlMapping.java
index 5069852412..8aa4143d78 100644
--- a/grails-web-url-mappings/src/main/groovy/org/grails/web/mapping/RegexUrlMapping.java
+++ b/grails-web-url-mappings/src/main/groovy/org/grails/web/mapping/RegexUrlMapping.java
@@ -656,7 +656,7 @@ public class RegexUrlMapping extends AbstractUrlMapping {
                     }
                 }
                 // if the format is specified but the value is empty, ignore it
-                if (!(FORMAT_PARAMETER.equals(propertyName) && GrailsStringUtils.isEmpty(lastGroup))) {
+                if (!(FORMAT_PARAMETER.equals(propertyName) && !GrailsStringUtils.hasLength(lastGroup))) {
                     params.put(propertyName, lastGroup);
                 }
                 break;
@@ -680,7 +680,7 @@ public class RegexUrlMapping extends AbstractUrlMapping {
                         lastGroup = lastGroup.substring(1);
                     }
                     // if the format is specified but the value is empty, ignore it
-                    if (!(FORMAT_PARAMETER.equals(propertyName) && GrailsStringUtils.isEmpty(lastGroup))) {
+                    if (!(FORMAT_PARAMETER.equals(propertyName) && !GrailsStringUtils.hasLength(lastGroup))) {
                         params.put(propertyName, lastGroup);
                     }
                 }
diff --git a/grails-wrapper/src/main/java/grails/init/GrailsUpdater.java b/grails-wrapper/src/main/java/grails/init/GrailsUpdater.java
index d1c7e84c0b..073901a04f 100644
--- a/grails-wrapper/src/main/java/grails/init/GrailsUpdater.java
+++ b/grails-wrapper/src/main/java/grails/init/GrailsUpdater.java
@@ -21,6 +21,8 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.net.URL;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -328,7 +330,12 @@ public class GrailsUpdater {
     }
 
     private static HttpURLConnection createHttpURLConnection(String mavenMetadataFileUrl) throws IOException {
-        final URL url = new URL(mavenMetadataFileUrl);
+        final URL url;
+        try {
+            url = new URI(mavenMetadataFileUrl).toURL();
+        } catch (URISyntaxException | IllegalArgumentException e) {
+            throw new IOException("Invalid Maven metadata URL: " + mavenMetadataFileUrl, e);
+        }
         HttpURLConnection conn = (HttpURLConnection) url.openConnection();
         conn.setRequestProperty("User-Agent", "Apache-Maven/3.9.6");
         conn.setInstanceFollowRedirects(true);