You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by ja...@apache.org on 2026/05/08 22:02:33 UTC

(grails-core) branch hotfix/forge-native-image-8.0.0-M1 updated: Restore micronaut-jackson-databind for grails-forge-web-netty JSON runtime

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

jamesfredley pushed a commit to branch hotfix/forge-native-image-8.0.0-M1
in repository https://gitbox.apache.org/repos/asf/grails-core.git


The following commit(s) were added to refs/heads/hotfix/forge-native-image-8.0.0-M1 by this push:
     new 0363e145e2 Restore micronaut-jackson-databind for grails-forge-web-netty JSON runtime
0363e145e2 is described below

commit 0363e145e29472ec039a436fd08cbddf7663add0
Author: James Fredley <ja...@users.noreply.github.com>
AuthorDate: Fri May 8 18:00:49 2026 -0400

    Restore micronaut-jackson-databind for grails-forge-web-netty JSON runtime
    
    After PR #15647 + #15648 fixed dockerBuildNative + Cloud Run startup
    for the analytics service, both Forge deploy workflows finally
    finished green and pushed `next.grails.org` (8.0.0-M1 hotfix branch)
    and `prev-snapshot.grails.org` (8.0.x) revisions live. start.grails.org
    still does not list 8.0.0-M1 / 8.0-SNAPSHOT in the Grails Version
    selector, though, because both backends return:
    
      $ curl -i -H 'Origin: https://start.grails.org' https://next.grails.org/versions
      HTTP/1.1 500 Internal Server Error
      Content-Type: application/json
      ...
      {"_embedded":{"errors":[{"message":"Internal Server Error: Error
       encoding object [org.grails.forge.api.VersionDTO@..] to JSON: No
       serializable introspection present for type VersionDTO. Consider
       adding Serdeable.Serializable annotate to type VersionDTO."}]},
       "message":"Internal Server Error"}
    
    Reproduced locally (`docker run grails-forge-local:web-netty` against
    the GraalVM Community 21.0.2-ol9 native image with CORS_ALLOWED_ORIGIN
    set, then `curl -H 'Origin: https://start.grails.org' /versions`) -
    identical 500.
    
    Root cause: PR #15365 review feedback (commit e1f3013070,
    "Address PR review feedback: telemetry override, forge BOM
    adoption, publish plugin bump") swapped grails-forge-web-netty's
    runtime JSON dependency:
    
      -    runtimeOnly 'io.micronaut:micronaut-jackson-databind'
      +    runtimeOnly 'io.micronaut.serde:micronaut-serde-jackson'
    
    with the rationale that "the forge stack is a Micronaut 4 serde-
    jackson application; the legacy jackson-databind runtime was a
    copy-paste from an earlier generator template". That is not yet
    true: every DTO returned by the Forge controllers
    (VersionDTO, ApplicationTypeDTO, FeatureDTO, FeatureList,
    SelectOptionsDTO, GormImplDTO, JdkVersionDTO, LanguageDTO,
    ServletImplDTO, DevelopmentReloadingDTO, GitHubCreateDTO,
    PreviewDTO, ApplicationTypeList, LinkDTO, the abstract Linkable
    parent, ...) carries `@Introspected` only - none of them are
    `@Serdeable` or registered via `@SerdeImport`. Micronaut Serde
    correctly rejects @Introspected-only classes at runtime, while
    Micronaut Jackson Databind serialises them via Jackson + bean
    introspection. The mismatch turns every JSON endpoint behind
    next.grails.org and prev-snapshot.grails.org into a 500, which
    is why Forge UI's `versions` fetch is empty for those backends
    and 8.0.0-M1 is missing from the start.grails.org Grails Version
    selector.
    
    The other Forge modules (forge-api tests, forge-core, analytics-
    postgres) all still use micronaut-jackson-databind; web-netty
    was the only inconsistency. Restore web-netty to
    micronaut-jackson-databind so the runtime matches the rest of the
    forge stack and the existing @Introspected DTOs serialise. A
    proper end-to-end Serde migration belongs in a separate PR that
    also annotates every Forge DTO with @Serdeable / @SerdeImport.
    
    Verified locally on Docker 29.3.0 with the same Java 21 GraalVM
    Community 21.0.2-ol9 native-image container the deploy workflows
    use:
    
      $ curl -i -H 'Origin: https://start.grails.org' http://localhost:18080/versions
      HTTP/1.1 200 OK
      Access-Control-Allow-Origin: https://start.grails.org
      Vary: Origin
      Access-Control-Allow-Credentials: true
      content-type: application/json
      ...
      {"_links":{"self":{"href":"http://.../versions","templated":false}},
       "versions":{...,"grails.version":"8.0.0-M1",...}}
    
      $ curl -i -H 'Origin: https://start.grails.org' http://localhost:18080/select-options
      HTTP/1.1 200 OK
      Access-Control-Allow-Origin: https://start.grails.org
      ...
    
    Endpoints serve JSON cleanly; CORS allow-origin header is present;
    the start.grails.org Grails Version selector will pick up 8.0.0-M1
    once next.grails.org is redeployed from this commit.
    
    Assisted-by: claude-code:claude-4.7-opus
---
 grails-forge/grails-forge-web-netty/build.gradle | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/grails-forge/grails-forge-web-netty/build.gradle b/grails-forge/grails-forge-web-netty/build.gradle
index 2f697cae65..6fc01b0263 100644
--- a/grails-forge/grails-forge-web-netty/build.gradle
+++ b/grails-forge/grails-forge-web-netty/build.gradle
@@ -32,7 +32,20 @@ dependencies {
     implementation 'io.micronaut.gcp:micronaut-gcp-http-client'
 
     runtimeOnly 'ch.qos.logback:logback-classic'
-    runtimeOnly 'io.micronaut.serde:micronaut-serde-jackson'
+    // Forge's grails-forge-api DTO classes (VersionDTO, ApplicationTypeDTO,
+    // FeatureDTO, SelectOptionsDTO, Linkable, ...) are annotated with
+    // @Introspected only. micronaut-serde-jackson requires @Serdeable on
+    // each serializable type and rejects @Introspected-only classes at
+    // runtime, which surfaces in the deployed native image as
+    //   "Internal Server Error: Error encoding object [VersionDTO@..] to
+    //    JSON: No serializable introspection present for type VersionDTO."
+    // and breaks every JSON endpoint behind start.grails.org / next.grails.org
+    // / prev-snapshot.grails.org. The rest of the forge stack (forge-api,
+    // forge-core, analytics-postgres) still depends on
+    // io.micronaut:micronaut-jackson-databind, so keep the netty service on
+    // the same JSON runtime to match. If forge ever migrates to Serde end-
+    // to-end, every DTO needs @Serdeable / @SerdeImport at the same time.
+    runtimeOnly 'io.micronaut:micronaut-jackson-databind'
 
     testImplementation 'io.micronaut:micronaut-http-client'
     testImplementation 'io.micronaut.test:micronaut-test-spock'