Fix KotlinObjectMapperFactory on newer Jackson - #3095
Closed
kevinrobayna wants to merge 1 commit into
Closed
kevinrobayna wants to merge 1 commit into
kevinrobayna wants to merge 1 commit into
Conversation
KotlinObjectMapperFactory.new() constructed KotlinModule with all
default arguments. Kotlin compiles that into a call to the synthetic
all-defaults overload of KotlinModule's deprecated constructor, so the
emitted descriptor pins whatever parameter list that constructor had in
the Jackson version the SDK was built against.
That parameter list has changed repeatedly - it gained arguments in
2.11, 2.12 and 2.16, and SingletonSupport later became a boolean - so a
call compiled against 2.15.4 links only against 2.12 through 2.15 and
throws NoSuchMethodError on anything else. Built against 2.15.4 and run
against 2.21.4:
java.lang.NoSuchMethodError: 'void com.fasterxml.jackson.module.
kotlin.KotlinModule.<init>(int, boolean, boolean, boolean, com.
fasterxml.jackson.module.kotlin.SingletonSupport, boolean, int,
kotlin.jvm.internal.DefaultConstructorMarker)'
The deprecated constructor was chosen to keep compatibility with old
Jackson versions, but it never achieved that: the same call also fails
on 2.9.0 through 2.11, the low end of the supported [2.9.0,) range.
registerKotlinModule() leaves the constructor choice to
jackson-module-kotlin, and its own signature has been unchanged since
2.9.0. Compiling against 2.15.4 and running against 2.9.0, 2.10, 2.11,
2.12, 2.13, 2.14, 2.15.4, 2.16, 2.17, 2.18, 2.19 and 2.21.4, the
current code passes only on 2.12 through 2.15 while this change passes
on every one of them.
No public API change: new() is still a static method returning
ObjectMapper.
Contributor
|
Hi, there is already an approved open PR for this #2072 that cover more then this. Please make sure to check open PRs before starting work on an issue |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed?
KotlinObjectMapperFactory.new()now registers the Kotlin module through theregisterKotlinModule()extension instead of constructingKotlinModuleitself, plus atemporal-kotlintest that round-trips a Kotlin data class through the returned mapper (a dataclass cannot be deserialized unless the module is registered).
Why?
KotlinModule()with all-default arguments compiles to a call to the synthetic all-defaultsoverload of
KotlinModule's deprecated constructor, so the emitted descriptor pins whateverparameter list that constructor happened to have in the Jackson version the SDK was built against:
That parameter list has changed repeatedly — it gained arguments in 2.11, 2.12 and 2.16, and
SingletonSupportlater became aboolean— so the call links only against the versions sharingthe shape it was compiled against. Built against 2.15.4 (the current
jacksonVersion) and runagainst 2.21.4, every caller of
new()gets:The comment being removed says the deprecated constructor was used "to maintain compatibility with
old jackson versions", but it has the opposite effect: the same call also fails on 2.9.0 through
2.11 — the low end of the
[2.9.0,)range declared next tojacksonVersioninbuild.gradle.registerKotlinModule()leaves the constructor choice to jackson-module-kotlin itself, and its ownsignature has been unchanged since 2.9.0, so the emitted call site stays valid across the whole
supported range.
Validation
./gradlew :temporal-kotlin:testpasses in full (40 tests, including the new one), and./gradlew :temporal-kotlin:spotlessCheckpasses.Since the module's own suite only ever runs against one Jackson version, I also checked the
cross-version behaviour two ways, both compiling against 2.15.4 to mirror how the SDK is released:
The real
KotlinObjectMapperFactory.ktand the new test, compiled with kotlinc 1.9.24 againstthe published
temporal-sdkjar, then run under JUnit against Jackson 2.15.4 and 2.21.4. Thenew test passes on both with this change; against current
mainit passes on 2.15.4 and failson 2.21.4 with the
NoSuchMethodErrorabove.A reduced two-line reproduction of the old and new module-registration paths, run against every
Jackson minor in the supported range:
NoSuchMethodErrorNoSuchMethodErrorOne thing worth flagging: the new test passes both before and after on 2.15.4, which is the only
Jackson version any CI job uses.
jacksonVersionis not among theedgeDepsTestoverrides(unlike
micrometerVersion,slf4jVersionandlogbackVersion), so nothing in CI ever resolvesthis constructor against a newer Jackson — which is why this went unnoticed. Adding Jackson to that
matrix would catch the whole class of bug; I've left it out to keep this PR to a single logical
change, but I'm happy to follow up with it if you'd like.
Breaking changes?
None. The public API is unchanged —
javapstill reportspublic static final com.fasterxml.jackson.databind.ObjectMapper new()— and only themodule-construction path inside the method changes. The resulting mapper is configured identically:
registerKotlinModule()builds aKotlinModulewith the same defaults the previous all-defaultsconstructor call requested.
Server PR
Not required.