jamesfredley opened a new issue, #16124:
URL: https://github.com/apache/grails-core/issues/16124

   ### Summary
   
   A Grails 8 web application fails to start with `NoClassDefFoundError: 
com/fasterxml/jackson/databind/exc/InvalidDefinitionException` as soon as any 
dependency puts **Jackson 2 `jackson-core` / `jackson-annotations` on the 
runtime classpath without Jackson 2 `jackson-databind`**.
   
   Grails 8 ships Jackson 3 for databind (`tools.jackson`), but the managed 
platform still supplies Jackson 2 `jackson-core`, `jackson-annotations`, and 
the Jackson 2 dataformat artifacts. When a plugin or library pulls in Jackson 2 
core, the resulting classpath has "half of Jackson 2": Spring Framework 7's 
`DefaultHttpMessageConverters.detectMessageConverters()` concludes Jackson 2 is 
available and then hard-references a `jackson-databind` 2.x class that is not 
present.
   
   The failure happens while creating `OrderedFormContentFilter`, so **Tomcat 
never starts and the whole application is dead**, regardless of whether the app 
itself uses Jackson at all.
   
   Found while testing released Grails 7 plugins against `8.0.0-M5`.
   
   ### Grails Version
   
   8.0.0-M5
   
   ### Java / Groovy Version
   
   Java 21.0.11 (Corretto), Groovy 5.0.8, Spring Boot 4.1.0, Spring Framework 
7.0.8
   
   ### Steps to Reproduce
   
   1. Generate a stock web app with the published 8.0.0-M5 distribution:
   
   ```
   grails create-app elasticsearch --profile=web
   ```
   
   2. Add a single dependency that transitively brings Jackson 2 core. Any such 
dependency reproduces it; the one used here is a released Grails 7 plugin:
   
   ```groovy
   implementation "org.grails.plugins:grails-elasticsearch:5.1.0"
   ```
   
   3. Run `gradlew test integrationTest bootJar`, or simply boot the app.
   
   ### Actual Behaviour
   
   ```
   Caused by: org.springframework.beans.BeanInstantiationException: Failed to 
instantiate
     [org.springframework.boot.servlet.filter.OrderedFormContentFilter]:
     Factory method 'formContentFilter' threw exception with message:
     com/fasterxml/jackson/databind/exc/InvalidDefinitionException
   
   Caused by: java.lang.NoClassDefFoundError: 
com/fasterxml/jackson/databind/exc/InvalidDefinitionException
        at 
org.springframework.http.converter.DefaultHttpMessageConverters$DefaultBuilder.detectMessageConverters(DefaultHttpMessageConverters.java:334)
        at 
org.springframework.http.converter.DefaultHttpMessageConverters$DefaultClientBuilder.build(DefaultHttpMessageConverters.java:468)
        at 
org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter.<init>(AllEncompassingFormHttpMessageConverter.java:42)
        at 
org.springframework.web.filter.FormContentFilter.<init>(FormContentFilter.java:61)
        at 
org.springframework.boot.servlet.filter.OrderedFormContentFilter.<init>(OrderedFormContentFilter.java:29)
        at 
org.springframework.boot.webmvc.autoconfigure.WebMvcAutoConfiguration.formContentFilter(WebMvcAutoConfiguration.java:189)
   
   Caused by: java.lang.ClassNotFoundException: 
com.fasterxml.jackson.databind.exc.InvalidDefinitionException
   ```
   
   Wrapped in `WebServerException` from `TomcatWebServer.initialize`, so the 
context never refreshes.
   
   ### Root cause evidence
   
   Contents of `BOOT-INF/lib` in the built `bootJar` - note Jackson 2 core and 
annotations are present, Jackson 3 databind is present, and **Jackson 2 
databind is missing**:
   
   ```
   BOOT-INF/lib/jackson-core-2.21.5.jar
   BOOT-INF/lib/jackson-annotations-2.21.jar
   BOOT-INF/lib/jackson-dataformat-cbor-2.21.4.jar
   BOOT-INF/lib/jackson-dataformat-smile-2.21.4.jar
   BOOT-INF/lib/jackson-dataformat-yaml-2.21.4.jar
   BOOT-INF/lib/jackson-databind-3.1.5.jar
   BOOT-INF/lib/jackson-core-3.1.5.jar
   BOOT-INF/lib/spring-boot-jackson-4.1.0.jar
   ```
   
   `dependencyInsight` shows the Jackson 2 core arriving transitively and being 
upgraded by the managed platform:
   
   ```
   com.fasterxml.jackson.core:jackson-core:2.14.2 -> 2.21.5
   \--- org.elasticsearch:elasticsearch-x-content:7.17.29
        \--- org.elasticsearch:elasticsearch:7.17.29
             \--- org.grails.plugins:grails-elasticsearch:5.1.0
   
   com.fasterxml.jackson.core:jackson-core:2.21.4 -> 2.21.5
   \--- org.springframework.boot:spring-boot-dependencies:4.1.0
   ```
   
   ### Confirmation of the diagnosis
   
   Adding the missing Jackson 2 databind to the same app:
   
   ```groovy
   runtimeOnly "com.fasterxml.jackson.core:jackson-databind:2.21.5"
   ```
   
   makes the startup crash disappear entirely. The app then boots, the plugin 
wires up, and the only remaining failure is the plugin's own `ConnectException` 
reaching an Elasticsearch server - an unrelated, expected environmental error:
   
   ```
   Caused by: org.elasticsearch.ElasticsearchException at 
RestHighLevelClient.java:2695
       Caused by: java.util.concurrent.ExecutionException at BaseFuture.java:257
           Caused by: java.net.ConnectException at Net.java:694
   ```
   
   So the startup failure is purely the incomplete Jackson 2 classpath, not the 
plugin's behaviour.
   
   ### Expected Behaviour
   
   Either the managed platform keeps Jackson 2 internally consistent (if 
Jackson 2 `jackson-core` / `jackson-annotations` are managed and can reach the 
runtime classpath, Jackson 2 `jackson-databind` should be managed and present 
too), or Jackson 2 artifacts should not be able to reach the application 
runtime classpath at all, so Spring's Jackson 2 detection never triggers.
   
   Right now a user gets a hard, non-obvious startup crash in an unrelated 
filter, with nothing pointing at the actual cause.
   
   ### Impact
   
   This is not specific to one plugin. Any Grails 7 plugin or third-party 
library that brings Jackson 2 core without databind - a very common shape, 
since many libraries depend on `jackson-core` or a Jackson 2 dataformat only - 
will take down a Grails 8 application at startup. The error message names 
neither Grails nor the offending dependency.
   
   ### Suggested resolution
   
   Options for discussion:
   
   1. Manage `com.fasterxml.jackson.core:jackson-databind` (2.x) in the Grails 
platform alongside the Jackson 2 `jackson-core` / `jackson-annotations` / 
dataformat entries so the Jackson 2 classpath is never half-present.
   2. Alternatively, keep Jackson 2 fully off the application runtime classpath 
and let Jackson 3 be the only Jackson present.
   3. At minimum, document this failure mode and the `runtimeOnly 
"com.fasterxml.jackson.core:jackson-databind:<2.x>"` workaround in the Grails 8 
upgrade guide's Jackson section, since the stack trace gives no hint.
   
   ### Notes
   
   Part of a compatibility sweep of released Grails 7 plugins against 8.0.0-M5. 
Related plugin-compatibility issues found in the same sweep: #16122 and #16123. 
Existing compatibility-shim precedents: #16011 and #16101.
   


-- 
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]

Reply via email to