CAMEL-22857 Convert camel to Jackson 3 - use jackson 3 in all camel components, use camel-jackson3 over camel-jackson#21800
CAMEL-22857 Convert camel to Jackson 3 - use jackson 3 in all camel components, use camel-jackson3 over camel-jackson#21800cunningt wants to merge 9 commits intoapache:mainfrom
Conversation
|
🌟 Thank you for your contribution to the Apache Camel project! 🌟 🐫 Apache Camel Committers, please review the following items:
|
b3b7d82 to
ee069f2
Compare
7963816 to
9b64b99
Compare
|
Are we planning to do this for 4.19? There wont be any Jackson 3 support in Quarkus until 4.x is released later this year. |
9b64b99 to
b664822
Compare
gnodet
left a comment
There was a problem hiding this comment.
In-depth Review of Jackson 3 Migration
Overall this is a thorough and well-executed migration across 500+ files. The import renames, API changes (e.g., JsonSerializer → ValueSerializer, fields() → properties(), etc.), and ObjectMapper immutability patterns are generally handled well. CI passes on both JDK 21 and JDK 25.
Bugs Found
1. CBORDataFormat: prettyPrint rebuild result discarded
In components/camel-cbor/src/main/java/org/apache/camel/component/cbor/CBORDataFormat.java, the prettyPrint block does:
objectMapper.rebuild()
.enable(SerializationFeature.INDENT_OUTPUT)
.build();The result of .build() is not assigned back to objectMapper, so INDENT_OUTPUT is silently lost. Compare with the enableFeatures/disableFeatures blocks in the same file which correctly do objectMapper = objectMapper.rebuild()...build().
2. ObjectMapperHelper (LevelDB): customModule registration discarded
In components/camel-leveldb/src/main/java/.../ObjectMapperHelper.java:
if (customModule != null) {
objectMapper.rebuild().addModule(customModule);
}
return objectMapper;Two issues: (a) .build() is never called, and (b) the result is never assigned back. The custom module is completely ignored. Should be:
if (customModule != null) {
objectMapper = objectMapper.rebuild().addModule(customModule).build();
}Concerns
3. Inconsistent asText() vs asString() usage
In Jackson 3, JsonNode.asText() was renamed to asString(). The PR mixes both:
DefaultJsonUriSchemaLoader.javausesnode.get("$schema").asText()(Jackson 2 name)AsNestedPropertyDeserializer.javausesnode.asText()(Jackson 2 name)YamlTestSupport.groovyusesSCHEMA_NODE.get('$schema').asString()(Jackson 3 name)
While asText() may still work as a deprecated alias, the PR should be consistent and use asString() everywhere.
4. Unused properties in parent POM
Two new properties appear unused:
nitrite-version(3.4.4) — no nitrite files changed in this PRjackson-datatype-version(3.0.0-rc2) — not referenced by any${jackson-datatype-version}
5. Alpha dependency: jackson-jq
jackson-jq-version is set to 2.0.0-alpha1. If the alpha API changes before GA, it could break the build.
6. Public API change (intentional but needs documentation)
Multiple components now expose tools.jackson.databind.ObjectMapper instead of com.fasterxml.jackson.databind.ObjectMapper in their public APIs (json-validator, salesforce, servicenow, ocsf). This is binary-incompatible. Should be documented in the migration guide/release notes.
Positive Observations
- Import migration is thorough — no stale
com.fasterxml.jacksonreferences in new code JavaTimeModuleremovals are correct (Jackson 3 has built-in java.time support)- Velocity templates for Salesforce code generation properly handle the annotation package split
networknt json-schema-validatormigration (1.5.9 → 3.0.0) is done comprehensively- Jackson 2 coexistence for remaining components (
camel-opensearch,camel-jslt) is correctly handled with version pinning
b664822 to
846d00a
Compare
|
Fixed 1-4.
Double checked jackson-jq and 2.0.0-alpha1 is still the only 2.0.0 release. The 2.0.0 milestone is necessary because it provides Jackson 3 support. |
|
For some of those components then we may need to add a camel-xxxx2 component that has the old jackson v2 that CEQ would need as Q is still jackson v2. And then when we can upgrade to Q4 then we can drop these v2 components. However we can maybe track this in another JIRA and the build up a list of components that CEQ then would need. The main jackson components are already there as both v2 and v3. |
…ang-mcp jackson-annotations uses a different versioning scheme since 2.20 (no patch version), so jackson-annotations:2.21.1 does not exist. Remove the explicit version so the Jackson 2 BOM manages it correctly to 2.21. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…n3-avro Remove the local Jackson 3 BOM import from camel-jackson3-avro. The parent POM already imports both Jackson 2 and Jackson 3 BOMs in the correct order (Jackson 2 first). The local import was overriding jackson-annotations to 2.20 (from Jackson 3 BOM) while jackson-databind remained at 2.21.1 (from parent's Jackson 2 BOM). Since jackson-databind 2.21.1 requires jackson-annotations 2.21 (for JsonSerializeAs), this caused NoClassDefFoundError at runtime. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
In-depth Review of Jackson 3 MigrationImpressive effort migrating 507 files across the entire Camel codebase. The dual-BOM approach (Jackson 2 first, Jackson 3 second) in the parent POM is well-structured, and the 5 excluded components are properly isolated. Here are the findings organized by severity. Critical / High1. Salesforce The old code configured: objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
objectMapper.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false);The new code drops both. 2. ServiceNow The method was drastically simplified, removing 3. CBOR registry lookup changed from In Set<ObjectMapper> set = camelContext.getRegistry().findByType(ObjectMapper.class);New code looks for Medium4.
5. Using an alpha release in production carries stability risk. This is necessary for Jackson 3 compatibility, but worth flagging. 6. Exception handling regressions from checked → unchecked migration Several places lost meaningful error handling when
7.
8. Deleted test In 9. Registration was removed entirely. If any Salesforce DTO uses Low / Minor10. Hardcoded version in
11. Inconsistent Some files use 12. Redundant Several components specify explicit versions on 13. Inefficient repeated Each feature toggle creates a new ObjectMapper. Should 14. Several files ( 15. Dead property Defined in parent POM but appears unused across the entire codebase. What looks good
|
- Salesforce JsonUtils: restore WRITE_DATES_AS_TIMESTAMPS and ADJUST_DATES_TO_CONTEXT_TIME_ZONE (moved to DateTimeFeature in Jackson 3) - ServiceNow: restore custom date/time format serializers using dateFormat/timeFormat/dateTimeFormat config properties - CBOR: fix registry lookup to find ObjectMapper (not TokenStreamFactory), and consolidate repeated rebuild().build() into single call - Micrometer: remove dead IOException throws and try-catch from serializer methods (Jackson 3 uses unchecked exceptions) - Salesforce: restore exception handling in AbstractDTOBase.toString(), LoginConfigHelper, StreamingApiConsumer, DefaultBulkApiV2Client Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
I've created PR #21921 with fixes for the critical and medium issues identified above. It should be merged after this PR lands. |
gnodet
left a comment
There was a problem hiding this comment.
Thorough Jackson 3 migration. CI is green and we've pushed fixes for the issues found during review (date feature restoration, ServiceNow date/time config, CBOR registry lookup, exception handling, Micrometer dead code). LGTM.
|
@davsclaus Good point about CEQ. Here's an analysis of the impact. Components that moved to Jackson 326 components switched from ~65 components now use Which ones need Jackson 2 wrappers for CEQ?Since Jackson 2 ( Jackson 2 wrappers would only be needed for components where Jackson 3 types leak into the component's public API (endpoint config, data types, etc.) AND the component has a CEQ extension. For components that only use Jackson internally, both versions coexist without conflict. The components most likely to need wrappers are: camel-salesforce, camel-servicenow, camel-cbor, camel-jsonpath, camel-jq, camel-xj. But this should be validated against what CEQ actually supports. Should we create a JIRA to track this? |
|
Yeah create a JIRA |
|
I'll create a JIRA ticket for tracking the CEQ Jackson 2 wrapper work. Key candidates that expose Jackson types in their public API and have CEQ extensions:
Also noting that the fixes from the review have been pushed directly to this branch (PR #21921 was closed in favor of direct commits). |
Not sure that's true. The native compiler will find paths to Jackson 3 types, regardless of whether they are exposed as public API or not. |
|
@jamesnetherton Good catch — you're right. My analysis was thinking about JVM mode where both Jackson 2 and 3 coexist fine via different packages. But in GraalVM native compilation, the native image builder traces all reachable code paths and will discover However, since core modules (camel-core, camel-support, camel-yaml-io, camel-yaml-dsl) are also migrated to Jackson 3 by this PR, and core modules can't be wrapped — CEQ would need to adopt Jackson 3 at the core level. Once Jackson 3 is on the classpath via core, components that only use Jackson 3 internally would work without any wrappers. The wrapper/compatibility approach would then only be needed for components that expose Jackson types in their public API (where CEQ users interact with Jackson types directly in code). Cross-reference: components modified by this PR that have a CEQ extension60 components touched by this PR have a corresponding Camel Quarkus extension:
Plus core modules: Since core already requires Jackson 3, most of these 60 components would "just work" once CEQ adopts Jackson 3 at the core level. The main work items for CEQ would be:
|
Analysis: Remaining Jackson 2 (
|
| Module | Jackson 2 Usage | Migration Status |
|---|---|---|
| camel-jackson | Core Jackson 2 data format component | Intentional — parallel camel-jackson3 exists |
| camel-jacksonxml | Core Jackson 2 XML data format component | Intentional — parallel camel-jackson3xml exists |
| camel-jslt | JsltEndpoint uses ObjectMapper, JsonNode |
Blocked — JSLT library (0.1.14) depends on com.fasterxml.jackson.core:jackson-databind at runtime |
| camel-opensearch | OpensearchActionRequestConverter, OpensearchProducer |
Blocked — OpenSearch client 3.7.0 depends on Jackson 2 |
| camel-openapi-java | RestOpenApiSupport, RestModelConverters |
Blocked — Swagger libraries (swagger-core-jakarta 2.2.36) depend on Jackson 2 |
| camel-jbang-mcp | No direct source code usage (test-scope deps only) | Blocked — Swagger + Quarkus transitive deps need Jackson 2 |
Conclusion
The Jackson 2 BOM cannot be removed from parent/pom.xml yet. All remaining Jackson 2 usages are either:
- Intentional (Jackson 2 data format components kept alongside Jackson 3 variants), or
- Blocked by upstream libraries (JSLT, OpenSearch client, Swagger) that haven't released Jackson 3-compatible versions
The camel-jbang-mcp pom already has a comment capturing this: "temporarily needed until a Jackson 3 capable swagger is released".
Final Analysis: Ready to MergeJackson 2 BOMThe Remaining modules (blocked by upstream)
GraalVM Native / CEQ ImpactRegarding @jamesnetherton's concern about native compilation: the GraalVM native compiler traces all reachable code paths and will discover Jackson 3 types even when they're only used internally (not in public API). This means CEQ will need to handle Jackson 3 at the core level, since core modules ( However, someone has to move forward first. CEQ is expected to stay on Camel 4.18 LTS for a while, and Quarkus 4.x (with Jackson 3 support) won't land until later this year. Delaying this PR won't change the upstream timeline — it just delays the Camel side of the migration. Once CEQ is ready to adopt Camel 4.19+, the Jackson 3 work will already be in place rather than blocking that upgrade. RecommendationThis PR should be merged as-is. The Jackson 3 migration is as complete as it can be — all remaining Jackson 2 usages are either intentional (dual Jackson 2/3 data format components) or blocked by upstream libraries that haven't released Jackson 3-compatible versions yet. Those can be migrated incrementally as upstream catches up. |
|
I think the better strategy than putting a dependencyManagement import of the Jackson 2 jackson-bom in parent/pom.xml https://github.com/apache/camel/pull/21800/changes#diff-b5a06276719e759fe07dfe6f75d781be5f83d2215179d82bdb195ad035348214R3281-R3291 would be to put the dependency management import of camel-bom in each of the individual modules that cannot be converted yet (camel-jackson, camel-jacksonxml, camel-jslt, camel-opensearch, camel-openapi-java, camel-jbang-mcp). That would minimize the recognition of Jackson 2 to just those components. Putting the Jackson 2 jackson-bom dependencyManagement entry in camel-bom is going to make it apply to all modules when we just want it to apply to a select six, and it may lead to situations where Jackson 2 dependencies are reintroduced in other modules by accident. |
|
Just want to restate the CQ situation (previous AI generated assumptions aside). If we do this change in 4.19, then CQ is effectively stuck on 4.18 until Quarkus 4 comes out in 6+ months. Otherwise, we'd need a bunch of Jackson 2 compatible components for the items listed above (which is maybe not feasible given how many there are). Or CQ needs to find some alternate strategy to deal with the change. |
Yes that is a good point Tom. Limit jackson 2 to only those components that directly use it. |
Can you remove/disable native mode for these components; and then for Q4 then if possible add it back. We have other CEQ extensions that are JVM only. |
In theory, yes (although that'd make life quite difficult elsewhere). The question is, do we need to make this specific change right now? We now have a bunch of camel-jackson3 components if users want to take advantage of those. But, for the Camel internals, are users going to care if they use Jackson 2.x for a short(ish) period? |
|
I agree with James, why the rush? from a Camel Spring Boot perspective this PR is fine, jackson2 and jackson3 can coexhist, therefore, we can stay on jackson2 for a while and wait for the quarkus jackson3 release, or am I missing something? |
|
The 4.18 LTS is the last stable release for Jackson 2 and Quarkus 3 as we know it. The 4.19 onwards releases are a bumpy road to bring SB v4 GA in the Camel 4.22 LTS to give the best SB4 experience we can give Camel users. At that point in time I don't really think it makes sense to have a CEQ LTS release knowing that Camel is not a well fit at this point, and that Quarkus4 is the major goal, and that Quarkus4 has a LTS planned for Q1 2027 which then would allow CEQ LTS as well. But as early as in Q3 2026 there can be Quarkus 4 developer releases that then allows us to jump forward with CEQ and the Jackson v3 migration. But it still stands that a Q3 2026 LTS release for CEQ that are based on Q3 and Jackson2 makes less sense, knowing the current state where Camel 4 is going, and that Camel 4.18 LTS is already there as a stable LTS release for Q3 and Jackson2 users. But in regards to this PR as it is now. Then I agree that this PR may be invasive with all the gazillion of already changes since 4.18. And this is a also a big huge and complex. So maybe we should park it for now and focus on getting current code base releases as 4.19, after the 4.18.1 patch release. And then after the 4.19 release we can split this PR into smaller upgrades over 4.20 and 4.21 releases when we identify that the best SB4 experience would be for the camel-xxx component to be Jackson 3 out of the box. Then we can judge whether it makes sense to make a duplicate camel-xxx2 component for Jackson2 for the sake of CEQ. |
|
Would it make sense to take this PR into draft for a while so it doesn't get accidentally merged and revisit in 4.21 or 4.23? |
|
This PR has a great analytics from the AIs and massive work from Tom and GNodet so we are well started for all the work ahead to bring Jackson3 upgrades to the remainder Camel components. |
|
This is such a large change, that I think it would be possible to consider merging it to an specific branch (i.e.: |
Proposed Migration StrategyAfter investigating Spring Boot 4's Jackson support and Quarkus 4's timeline, here's a phased approach that could work: Key Findings
Phased Plan
What To Do With This PRThis PR represents significant and valuable work. Rather than letting it go stale, consider:
This avoids rushing the migration while camel-quarkus is still on Jackson 2, and gives us the best shot at a clean 4.22 LTS with full Jackson 3 + Spring Boot 4 + Quarkus 4 alignment. |
Description
There are five components that cannot upgrade to Jackson 3. They are still using Jackson 2 but since Jackson 2 and Jackson 3 maintain different package names there do not seem to be any issues with them staying on Jackson 2 temporarily
Target
mainbranch)Tracking
Apache Camel coding standards and style
[x ] I checked that each commit in the pull request has a meaningful subject line and body.
[x ] I have run
mvn clean install -DskipTestslocally from root folder and I have committed all auto-generated changes.